michael@0: /* Any copyright is dedicated to the Public Domain. michael@0: http://creativecommons.org/publicdomain/zero/1.0/ */ michael@0: michael@0: "use strict"; michael@0: michael@0: const Profiler = Cc["@mozilla.org/tools/profiler;1"].getService(Ci.nsIProfiler); michael@0: michael@0: function run_test() michael@0: { michael@0: // Ensure the profiler is not running when the test starts (it could michael@0: // happen if the MOZ_PROFILER_STARTUP environment variable is set) michael@0: Profiler.StopProfiler(); michael@0: DebuggerServer.init(function () { return true; }); michael@0: DebuggerServer.addBrowserActors(); michael@0: var client = new DebuggerClient(DebuggerServer.connectPipe()); michael@0: client.connect(function () { michael@0: client.listTabs(function(aResponse) { michael@0: test_profiler_actor(client, aResponse.profilerActor); michael@0: }); michael@0: }); michael@0: do_test_pending(); michael@0: } michael@0: michael@0: function test_profiler_actor(aClient, aProfiler) michael@0: { michael@0: aClient.request({ to: aProfiler, type: "isActive" }, function (aResponse) { michael@0: do_check_false(aResponse.isActive); michael@0: michael@0: aClient.request({ to: aProfiler, type: "getFeatures" }, function (aResponse) { michael@0: var features = Profiler.GetFeatures([]); michael@0: do_check_eq(aResponse.features.length, features.length); michael@0: for (var i = 0; i < features.length; i++) michael@0: do_check_eq(aResponse.features[i], features[i]); michael@0: michael@0: aClient.request({ to: aProfiler, type: "startProfiler", features: ['jank', 'js'] }, function (aResponse) { michael@0: do_check_eq(typeof aResponse.msg, "string"); michael@0: aClient.request({ to: aProfiler, type: "isActive" }, function (aResponse) { michael@0: do_check_true(aResponse.isActive); michael@0: michael@0: aClient.request({ to: aProfiler, type: "getResponsivenessTimes" }, function (aResponse) { michael@0: do_check_eq(typeof aResponse.responsivenessTimes, "object"); michael@0: michael@0: aClient.request({ to: aProfiler, type: "getSharedLibraryInformation" }, function (aResponse) { michael@0: do_check_eq(typeof aResponse.sharedLibraryInformation, "string"); michael@0: try { michael@0: JSON.parse(aResponse.sharedLibraryInformation); michael@0: } catch(e) { michael@0: do_throw(e.toString(), Components.stack.caller); michael@0: } michael@0: michael@0: test_event_notifications(aClient, aProfiler); michael@0: }); michael@0: }); michael@0: }); michael@0: }); michael@0: }); michael@0: }); michael@0: } michael@0: michael@0: function test_event_notifications(aClient, aProfiler) michael@0: { michael@0: aClient.request({ to: aProfiler, type: "registerEventNotifications", events: ["foo", "bar"] }, function (aResponse) { michael@0: do_check_eq(typeof aResponse.registered, "object"); michael@0: do_check_eq(aResponse.registered.length, 2); michael@0: do_check_eq(aResponse.registered[0], "foo"); michael@0: do_check_eq(aResponse.registered[1], "bar"); michael@0: michael@0: aClient.request({ to: aProfiler, type: "registerEventNotifications", events: ["foo"] }, function (aResponse) { michael@0: do_check_eq(typeof aResponse.registered, "object"); michael@0: do_check_eq(aResponse.registered.length, 0); michael@0: michael@0: aClient.addListener("eventNotification", function (aType, aData) { michael@0: do_check_eq(aType, "eventNotification"); michael@0: do_check_eq(aData.event, "foo"); michael@0: do_check_eq(typeof aData.subject, "object"); michael@0: do_check_eq(aData.subject.foo, "foo"); michael@0: do_check_eq(aData.data, "foo"); michael@0: }); michael@0: var subject = { foo: "foo" }; michael@0: subject.wrappedJSObject = subject; michael@0: Services.obs.notifyObservers(subject, "foo", "foo"); michael@0: michael@0: aClient.request({ to: aProfiler, type: "unregisterEventNotifications", events: ["foo", "bar", "qux"] }, function (aResponse) { michael@0: do_check_eq(typeof aResponse.unregistered, "object"); michael@0: do_check_eq(aResponse.unregistered.length, 2); michael@0: do_check_eq(aResponse.unregistered[0], "foo"); michael@0: do_check_eq(aResponse.unregistered[1], "bar"); michael@0: michael@0: // All events being now unregistered, sending an event shouldn't michael@0: // do anything. If it does, the eventNotification listener above michael@0: // will catch the event and fail on the aData.event test. michael@0: Services.obs.notifyObservers(null, "bar", null); michael@0: michael@0: test_profile(aClient, aProfiler); michael@0: }); michael@0: }); michael@0: }); michael@0: } michael@0: michael@0: function test_profile(aClient, aProfiler) michael@0: { michael@0: // No idea why, but Components.stack.sourceLine returns null. michael@0: var funcLine = Components.stack.lineNumber - 3; michael@0: // Busy wait a few milliseconds michael@0: var start = Date.now(); michael@0: var stack; michael@0: while (Date.now() - start < 200) { stack = Components.stack; } michael@0: aClient.request({ to: aProfiler, type: "getProfile" }, function (aResponse) { michael@0: do_check_eq(typeof aResponse.profile, "object"); michael@0: do_check_eq(typeof aResponse.profile.meta, "object"); michael@0: do_check_eq(typeof aResponse.profile.meta.platform, "string"); michael@0: do_check_eq(typeof aResponse.profile.threads, "object"); michael@0: do_check_eq(typeof aResponse.profile.threads[0], "object"); michael@0: do_check_eq(typeof aResponse.profile.threads[0].samples, "object"); michael@0: do_check_neq(aResponse.profile.threads[0].samples.length, 0); michael@0: michael@0: let location = stack.name + " (" + stack.filename + ":" + funcLine + ")"; michael@0: // At least one sample is expected to have been in the busy wait above. michael@0: do_check_true(aResponse.profile.threads[0].samples.some(function(sample) { michael@0: return typeof sample.frames == "object" && michael@0: sample.frames.length != 0 && michael@0: sample.frames.some(function(f) { michael@0: return (f.line == stack.lineNumber) && michael@0: (f.location == location); michael@0: }); michael@0: })); michael@0: michael@0: aClient.request({ to: aProfiler, type: "stopProfiler" }, function (aResponse) { michael@0: do_check_eq(typeof aResponse.msg, "string"); michael@0: aClient.request({ to: aProfiler, type: "isActive" }, function (aResponse) { michael@0: do_check_false(aResponse.isActive); michael@0: aClient.close(function() { michael@0: test_profiler_status(); michael@0: }); michael@0: }); michael@0: }); michael@0: }); michael@0: } michael@0: michael@0: function test_profiler_status() michael@0: { michael@0: var connectionClosed = DebuggerServer._connectionClosed; michael@0: var client = new DebuggerClient(DebuggerServer.connectPipe()); michael@0: michael@0: client.connect(() => { michael@0: client.listTabs((aResponse) => { michael@0: DebuggerServer._connectionClosed = function (conn) { michael@0: connectionClosed.call(this, conn); michael@0: michael@0: // Check that closing the last (only?) connection stops the profiler. michael@0: do_check_false(Profiler.IsActive()); michael@0: do_test_finished(); michael@0: } michael@0: michael@0: var profiler = aResponse.profilerActor; michael@0: do_check_false(Profiler.IsActive()); michael@0: client.request({ michael@0: to: profiler, michael@0: type: "startProfiler", michael@0: features: [] michael@0: }, function (aResponse) { michael@0: do_check_true(Profiler.IsActive()); michael@0: client.close(); michael@0: }); michael@0: }); michael@0: }); michael@0: }