Bug 1140569 - Show async stacks attached to timeline markers. r=vporof

This commit is contained in:
Tom Tromey 2015-03-11 07:44:00 -04:00
parent 764a676c31
commit 8d77d6ea8a
5 changed files with 83 additions and 5 deletions

View File

@ -177,12 +177,26 @@ MarkerDetails.prototype = {
labelName.setAttribute("value", L10N.getStr(property));
parent.appendChild(labelName);
let wasAsyncParent = false;
while (frameIndex > 0) {
let frame = frames[frameIndex];
let url = frame.source;
let displayName = frame.functionDisplayName;
let line = frame.line;
// If the previous frame had an async parent, then the async
// cause is in this frame and should be displayed.
if (wasAsyncParent) {
let asyncBox = this._document.createElement("hbox");
let asyncLabel = this._document.createElement("label");
asyncLabel.className = "devtools-monospace";
asyncLabel.setAttribute("value", L10N.getFormatStr("timeline.markerDetail.asyncStack",
frame.asyncCause));
asyncBox.appendChild(asyncLabel);
parent.appendChild(asyncBox);
wasAsyncParent = false;
}
let hbox = this._document.createElement("hbox");
if (displayName) {
@ -219,7 +233,12 @@ MarkerDetails.prototype = {
parent.appendChild(hbox);
frameIndex = frame.parent;
if (frame.asyncParent) {
frameIndex = frame.asyncParent;
wasAsyncParent = true;
} else {
frameIndex = frame.parent;
}
}
},

View File

@ -70,3 +70,4 @@ timeline.markerDetail.stack=Stack:
timeline.markerDetail.startStack=Stack at start:
timeline.markerDetail.endStack=Stack at end:
timeline.markerDetail.unknownFrame=<unknown location>
timeline.markerDetail.asyncStack=(Async: %S)

View File

@ -21,6 +21,21 @@ function testConsoleTimeEnd() {
content.console.timeEnd("cats");
}
function makePromise() {
let resolver;
new Promise(function(resolve, reject) {
testConsoleTime();
resolver = resolve;
}).then(function(val) {
testConsoleTimeEnd();
});
return resolver;
}
function resolvePromise(resolver) {
resolver(23);
}
let TESTS = [{
desc: "Stack trace on sync reflow",
searchFor: "Reflow",
@ -63,6 +78,24 @@ let TESTS = [{
ok(markers[0].endStack.functionDisplayName == "testConsoleTimeEnd",
"testConsoleTimeEnd is on the stack");
}
}, {
desc: "Async stack trace on Promise",
searchFor: "ConsoleTime",
setup: function(docShell) {
let resolver = makePromise();
resolvePromise(resolver);
},
check: function(markers) {
markers = markers.filter(m => m.name == "ConsoleTime");
ok(markers.length > 0, "Promise marker includes stack");
let frame = markers[0].endStack;
ok(frame.parent.asyncParent !== null, "Parent frame has async parent");
is(frame.parent.asyncParent.asyncCause, "Promise",
"Async parent has correct cause");
is(frame.parent.asyncParent.functionDisplayName, "makePromise",
"Async parent has correct function name");
}
}];
timelineContentTest(TESTS);

View File

@ -111,6 +111,8 @@ let StackFrameCache = Class({
* source: <filename string for this frame>,
* functionDisplayName: <this frame's inferred function name function or null>,
* parent: <frame ID -- an index into the concatenated array mentioned above>
* asyncCause: the async cause, or null
* asyncParent: <frame ID -- an index into the concatenated array mentioned above>
* }
*
* The intent of this approach is to make it simpler to efficiently
@ -151,6 +153,7 @@ let StackFrameCache = Class({
if (frame) {
this._assignFrameIndices(frame.parent);
this._assignFrameIndices(frame.asyncParent);
}
const index = this._framesToIndices.size;
@ -175,9 +178,12 @@ let StackFrameCache = Class({
column: frame.column,
source: frame.source,
functionDisplayName: frame.functionDisplayName,
parent: this._framesToIndices.get(frame.parent)
parent: this._framesToIndices.get(frame.parent),
asyncParent: this._framesToIndices.get(frame.asyncParent),
asyncCause: frame.asyncCause
};
this._createFrameForms(frame.parent);
this._createFrameForms(frame.asyncParent);
}
this._framesToForms.set(frame, form);

View File

@ -11,16 +11,35 @@ function run_test() {
let cache = new StackFrameCache();
cache.initFrames();
cache.addFrame({
let baseFrame = {
line: 23,
column: 77,
source: "nowhere",
functionDisplayName: "nobody",
parent: null
});
parent: null,
asyncParent: null,
asyncCause: null
};
cache.addFrame(baseFrame);
let event = cache.makeEvent();
do_check_eq(event[0], null);
do_check_eq(event[1].functionDisplayName, "nobody");
do_check_eq(event.length, 2);
cache.addFrame({
line: 24,
column: 78,
source: "nowhere",
functionDisplayName: "still nobody",
parent: null,
asyncParent: baseFrame,
asyncCause: "async"
});
event = cache.makeEvent();
do_check_eq(event[0].functionDisplayName, "still nobody");
do_check_eq(event[0].parent, 0);
do_check_eq(event[0].asyncParent, 1);
do_check_eq(event.length, 1);
}