Source code
Revision control
Copy as Markdown
Other Tools
Test Info: Warnings
- This test gets skipped with pattern: tsan OR !nightly_build
- Manifest: tools/profiler/tests/browser/browser.toml
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
/**
* Test the JS Tracing feature.
*/
add_task(async function test_profile_feature_jstracing() {
Assert.ok(
!Services.profiler.IsActive(),
"The profiler is not currently active"
);
await startProfiler({ features: ["tracing"] });
const url = BASE_URL_HTTPS + "tracing.html";
await BrowserTestUtils.withNewTab(url, async contentBrowser => {
const contentPid = await SpecialPowers.spawn(
contentBrowser,
[],
() => Services.appinfo.processID
);
{
const { contentThread } = await stopProfilerNowAndGetThreads(contentPid);
// First lookup for all our expected symbols in the string table
const functionAFrameStringIdx = contentThread.stringTable.indexOf(
`a (${url}:7:15)`
);
Assert.greater(
functionAFrameStringIdx,
0,
"Found string for 'a' method call"
);
const functionBFrameStringIdx = contentThread.stringTable.indexOf(
`b (${url}:10:15)`
);
Assert.greater(
functionBFrameStringIdx,
0,
"Found string for 'b' method call"
);
const clickEventStringIdx = contentThread.stringTable.indexOf(`click`);
Assert.greater(
clickEventStringIdx,
0,
"Found string for 'click' DOM event"
);
const customEventStringIdx =
contentThread.stringTable.indexOf(`CustomEvent`);
Assert.greater(
customEventStringIdx,
0,
"Found string for 'CustomEvent' DOM event"
);
const customEventHandlerStringIdx = contentThread.stringTable.indexOf(
`customEventHandler (${url}:18:71)`
);
Assert.greater(
customEventHandlerStringIdx,
0,
"Found string for 'customEventHandler' method call"
);
const navigatorUserAgentStringIdx = contentThread.stringTable.indexOf(
`(DOM) Navigator.userAgent`
);
Assert.greater(
navigatorUserAgentStringIdx,
0,
"Found string for 'navigator.userAgent' getter"
);
const clickMethodStringIdx = contentThread.stringTable.indexOf(
`(DOM) HTMLElement.click`
);
Assert.greater(
clickMethodStringIdx,
0,
"Found string for 'body.click' call"
);
const eventTargetDispatchEventStringIdx =
contentThread.stringTable.indexOf(`(DOM) EventTarget.dispatchEvent`);
Assert.greater(
clickMethodStringIdx,
0,
"Found string for 'window.dispatchEvent' call"
);
// Then lookup for the matching frame, based on the string index
const { frameTable } = contentThread;
const FRAME_LOCATION_SLOT = frameTable.schema.location;
const FRAME_CATEGORY_SLOT = frameTable.schema.category;
const FUNCTION_CALL_CATEGORY = 4;
const EVENT_CATEGORY = 8;
const functionAFrameIdx = frameTable.data.findIndex(
frame => frame[FRAME_LOCATION_SLOT] == functionAFrameStringIdx
);
Assert.greater(functionAFrameIdx, 0, "Found frame for 'a' method call");
Assert.equal(
frameTable.data[functionAFrameIdx][FRAME_CATEGORY_SLOT],
FUNCTION_CALL_CATEGORY
);
const functionBFrameIdx = frameTable.data.findIndex(
frame => frame[FRAME_LOCATION_SLOT] == functionBFrameStringIdx
);
Assert.greater(functionBFrameIdx, 0, "Found frame for 'b' method call");
Assert.equal(
frameTable.data[functionBFrameIdx][FRAME_CATEGORY_SLOT],
FUNCTION_CALL_CATEGORY
);
const clickEventFrameIdx = frameTable.data.findIndex(
frame => frame[FRAME_LOCATION_SLOT] == clickEventStringIdx
);
Assert.greater(
clickEventFrameIdx,
0,
"Found frame for 'click' DOM event"
);
Assert.equal(
frameTable.data[clickEventFrameIdx][FRAME_CATEGORY_SLOT],
EVENT_CATEGORY
);
const customEventFrameIdx = frameTable.data.findIndex(
frame => frame[FRAME_LOCATION_SLOT] == customEventStringIdx
);
Assert.greater(
customEventFrameIdx,
0,
"Found frame for 'CustomEvent' DOM event"
);
Assert.equal(
frameTable.data[customEventFrameIdx][FRAME_CATEGORY_SLOT],
EVENT_CATEGORY
);
const customEventHandlerFrameIdx = frameTable.data.findIndex(
frame => frame[FRAME_LOCATION_SLOT] == customEventHandlerStringIdx
);
Assert.greater(
customEventHandlerFrameIdx,
0,
"Found frame for 'b' method call"
);
Assert.equal(
frameTable.data[customEventHandlerFrameIdx][FRAME_CATEGORY_SLOT],
FUNCTION_CALL_CATEGORY
);
const clickMethodFrameIdx = frameTable.data.findIndex(
frame => frame[FRAME_LOCATION_SLOT] == clickMethodStringIdx
);
Assert.greater(
clickMethodFrameIdx,
0,
"Found frame for 'body.click' method call"
);
const navigatorUserAgentFrameIdx = frameTable.data.findIndex(
frame => frame[FRAME_LOCATION_SLOT] == navigatorUserAgentStringIdx
);
Assert.greater(
navigatorUserAgentFrameIdx,
0,
"Found frame for 'navigator.userAgent' getter"
);
Assert.equal(
frameTable.data[navigatorUserAgentFrameIdx][FRAME_CATEGORY_SLOT],
EVENT_CATEGORY
);
const eventTargetDispatchEventFrameIdx = frameTable.data.findIndex(
frame => frame[FRAME_LOCATION_SLOT] == eventTargetDispatchEventStringIdx
);
Assert.greater(
eventTargetDispatchEventFrameIdx,
0,
"Found frame for 'window.dispatchEvent' call"
);
Assert.equal(
frameTable.data[eventTargetDispatchEventFrameIdx][FRAME_CATEGORY_SLOT],
EVENT_CATEGORY
);
// Finally, assert that the stacks are correct.
// Each symbol's frame is visible in a stack, and the stack tree is valid
const { stackTable } = contentThread;
const STACK_FRAME_SLOT = stackTable.schema.frame;
const STACK_PREFIX_SLOT = stackTable.schema.prefix;
const functionAFrame = stackTable.data.find(
stack => stack[STACK_FRAME_SLOT] == functionAFrameIdx
);
const functionBFrame = stackTable.data.find(
stack => stack[STACK_FRAME_SLOT] == functionBFrameIdx
);
const clickEventFrame = stackTable.data.find(
stack => stack[STACK_FRAME_SLOT] == clickEventFrameIdx
);
const customEventFrame = stackTable.data.find(
stack => stack[STACK_FRAME_SLOT] == customEventFrameIdx
);
const customEventHandlerEventFrame = stackTable.data.find(
stack => stack[STACK_FRAME_SLOT] == customEventHandlerFrameIdx
);
const clickMethodFrame = stackTable.data.find(
stack => stack[STACK_FRAME_SLOT] == clickMethodFrameIdx
);
const navigatorUserAgentFrame = stackTable.data.find(
stack => stack[STACK_FRAME_SLOT] == navigatorUserAgentFrameIdx
);
const eventTargetDispatchEventFrame = stackTable.data.find(
stack => stack[STACK_FRAME_SLOT] == eventTargetDispatchEventFrameIdx
);
Assert.equal(
getCallerNameFromStackIdx(
contentThread,
functionAFrame[STACK_PREFIX_SLOT]
),
"load",
"'a' was called from 'load'"
);
Assert.equal(
functionBFrame[STACK_PREFIX_SLOT],
stackTable.data.indexOf(functionAFrame),
"'b' was called from 'a'"
);
Assert.equal(
clickEventFrame[STACK_PREFIX_SLOT],
stackTable.data.indexOf(clickMethodFrame),
"'click' event fired from 'body.click' method call"
);
Assert.equal(
clickMethodFrame[STACK_PREFIX_SLOT],
stackTable.data.indexOf(functionBFrame),
"'body.click' method was called from 'b'"
);
Assert.equal(
navigatorUserAgentFrame[STACK_PREFIX_SLOT],
stackTable.data.indexOf(functionBFrame),
"'navigator.userAgent' was queried from 'b'"
);
Assert.equal(
customEventFrame[STACK_PREFIX_SLOT],
stackTable.data.indexOf(eventTargetDispatchEventFrame),
"'CustomEvent' event fired from 'window.dispatchEvent()' method call"
);
Assert.equal(
customEventHandlerEventFrame[STACK_PREFIX_SLOT],
stackTable.data.indexOf(customEventFrame),
"'customEventHandler' function is called because of the CustomEvent Event"
);
}
});
});
function getCallerNameFromStackIdx(thread, stackIdx) {
const { stackTable, frameTable } = thread;
const frameIdx = stackTable.data[stackIdx][stackTable.schema.frame];
return thread.stringTable[
frameTable.data[frameIdx][frameTable.schema.location]
];
}
/**
* This function takes a thread, and a sample tuple from the "data" array, and
* inflates the frame to be an array of strings.
*
* @param {Object} thread - The thread from the profile.
* @param {Array} sample - The tuple from the thread.samples.data array.
* @returns {Array<string>} An array of function names.
*/
function getInflatedStackLocations(thread, sample) {
let stackTable = thread.stackTable;
let frameTable = thread.frameTable;
let stringTable = thread.stringTable;
let SAMPLE_STACK_SLOT = thread.samples.schema.stack;
let STACK_PREFIX_SLOT = stackTable.schema.prefix;
let STACK_FRAME_SLOT = stackTable.schema.frame;
let FRAME_LOCATION_SLOT = frameTable.schema.location;
// Build the stack from the raw data and accumulate the locations in
// an array.
let stackIndex = sample[SAMPLE_STACK_SLOT];
let locations = [];
while (stackIndex !== null) {
let stackEntry = stackTable.data[stackIndex];
let frame = frameTable.data[stackEntry[STACK_FRAME_SLOT]];
locations.push(stringTable[frame[FRAME_LOCATION_SLOT]]);
stackIndex = stackEntry[STACK_PREFIX_SLOT];
}
// The profiler tree is inverted, so reverse the array.
return locations.reverse();
}