forked from mirrors/gecko-dev
The 'asyncStack' flag on JS execution contexts is used as a general switch to enable async stack capture across all locations in SpiderMonkey, but this causes problems because it can at times be too much of a performance burden to general and track all of these stacks. Since the introduction of this option, we have only enabled it on Nightly and DevEdition for non-mobile builds, which has left a lot of users unable to take advantage of this data while debugging. This patch enables async stack traces across all of Firefox, but introduces a new pref to toggle the scope of the actual expensive part of async stacks, which is _capturing_ them and keeping them alive in memory. The new pref limits the capturing of async stack traces to only debuggees, unless an explicit pref is flipped to capture async traces for all cases. This means that while async stacks are technically enabled, and code could manually capture a stack and pass it back to SpiderMonkey and see that stack reflected in later captured stacks, SpiderMonkey itself and related async DOM APIs, among others, will not capture stacks or pass them to SpiderMonkey, so there should be no general change in performance by enabling the broader feature itself, unless the user is actively debugging the page. One effect of this patch is that if you have the debugger open and then close it, objects that have async stacks associated with them will retain those stacks and they will continue to show up in stack traces, no _new_ stacks will be captured. jorendorff and I have decided that this is okay because the expectation that the debugger fully revert every possible effect that it could have on a page is a nice goal but not a strict requirement. Differential Revision: https://phabricator.services.mozilla.com/D68503
123 lines
3.7 KiB
JavaScript
123 lines
3.7 KiB
JavaScript
/* Any copyright is dedicated to the Public Domain.
|
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
|
|
// This file expects frame-head.js to be loaded in the environment.
|
|
/* import-globals-from frame-head.js */
|
|
|
|
"use strict";
|
|
|
|
// Test that the docShell profile timeline API returns the right
|
|
// markers for XMLHttpRequest events.
|
|
|
|
var TESTS = [
|
|
{
|
|
desc: "Event dispatch from XMLHttpRequest",
|
|
searchFor(markers) {
|
|
return markers.filter(m => m.name == "DOMEvent").length >= 5;
|
|
},
|
|
setup(docShell) {
|
|
content.dispatchEvent(new content.Event("dog"));
|
|
},
|
|
check(markers) {
|
|
let domMarkers = markers.filter(m => m.name == "DOMEvent");
|
|
// One subtlety here is that we have five events: the event we
|
|
// inject in "setup", plus the four state transition events. The
|
|
// first state transition is reported synchronously and so should
|
|
// show up as a nested marker.
|
|
is(domMarkers.length, 5, "Got 5 markers");
|
|
|
|
// We should see some Javascript markers, and they should have a
|
|
// cause.
|
|
let jsMarkers = markers.filter(
|
|
m => m.name == "Javascript" && m.causeName
|
|
);
|
|
ok(jsMarkers.length > 0, "Got some Javascript markers");
|
|
is(
|
|
jsMarkers[0].stack.functionDisplayName,
|
|
"do_xhr",
|
|
"Javascript marker has entry point name"
|
|
);
|
|
},
|
|
},
|
|
];
|
|
|
|
if (
|
|
!Services.prefs.getBoolPref(
|
|
"javascript.options.asyncstack_capture_debuggee_only"
|
|
)
|
|
) {
|
|
TESTS.push(
|
|
{
|
|
desc: "Async stack trace on Javascript marker",
|
|
searchFor: markers => {
|
|
return markers.some(
|
|
m => m.name == "Javascript" && m.causeName == "promise callback"
|
|
);
|
|
},
|
|
setup(docShell) {
|
|
content.dispatchEvent(new content.Event("promisetest"));
|
|
},
|
|
check(markers) {
|
|
markers = markers.filter(
|
|
m => m.name == "Javascript" && m.causeName == "promise callback"
|
|
);
|
|
ok(markers.length > 0, "Found a Javascript marker");
|
|
|
|
let frame = markers[0].stack;
|
|
ok(frame.asyncParent !== null, "Parent frame has async parent");
|
|
is(
|
|
frame.asyncParent.asyncCause,
|
|
"promise callback",
|
|
"Async parent has correct cause"
|
|
);
|
|
let asyncFrame = frame.asyncParent;
|
|
// Skip over self-hosted parts of our Promise implementation.
|
|
while (asyncFrame.source === "self-hosted") {
|
|
asyncFrame = asyncFrame.parent;
|
|
}
|
|
is(
|
|
asyncFrame.functionDisplayName,
|
|
"do_promise",
|
|
"Async parent has correct function name"
|
|
);
|
|
},
|
|
},
|
|
{
|
|
desc: "Async stack trace on Javascript marker with script",
|
|
searchFor: markers => {
|
|
return markers.some(
|
|
m => m.name == "Javascript" && m.causeName == "promise callback"
|
|
);
|
|
},
|
|
setup(docShell) {
|
|
content.dispatchEvent(new content.Event("promisescript"));
|
|
},
|
|
check(markers) {
|
|
markers = markers.filter(
|
|
m => m.name == "Javascript" && m.causeName == "promise callback"
|
|
);
|
|
ok(markers.length > 0, "Found a Javascript marker");
|
|
|
|
let frame = markers[0].stack;
|
|
ok(frame.asyncParent !== null, "Parent frame has async parent");
|
|
is(
|
|
frame.asyncParent.asyncCause,
|
|
"promise callback",
|
|
"Async parent has correct cause"
|
|
);
|
|
let asyncFrame = frame.asyncParent;
|
|
// Skip over self-hosted parts of our Promise implementation.
|
|
while (asyncFrame.source === "self-hosted") {
|
|
asyncFrame = asyncFrame.parent;
|
|
}
|
|
is(
|
|
asyncFrame.functionDisplayName,
|
|
"do_promise_script",
|
|
"Async parent has correct function name"
|
|
);
|
|
},
|
|
}
|
|
);
|
|
}
|
|
|
|
timelineContentTest(TESTS);
|