mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-27 06:43:32 +00:00
Bug 1911021 - Cover JavaScript tracing via the profiler with a mochitest. r=profiler-reviewers,aabh
Differential Revision: https://phabricator.services.mozilla.com/D220849
This commit is contained in:
parent
9a9ec02fd6
commit
e8009bc6c7
@ -11,6 +11,13 @@ support-files = ["simple.html"]
|
||||
["browser_test_feature_jsallocations.js"]
|
||||
support-files = ["do_work_500ms.html"]
|
||||
|
||||
["browser_test_feature_jstracing.js"]
|
||||
support-files = ["tracing.html"]
|
||||
skip-if = [
|
||||
# This features is only enabled on nightly via MOZ_EXECUTION_TRACING build flag
|
||||
"!nightly_build"
|
||||
]
|
||||
|
||||
["browser_test_feature_multiprocess_capture_with_signal.js"]
|
||||
support-files = ["do_work_500ms.html"]
|
||||
skip-if = [
|
||||
|
215
tools/profiler/tests/browser/browser_test_feature_jstracing.js
Normal file
215
tools/profiler/tests/browser/browser_test_feature_jstracing.js
Normal file
@ -0,0 +1,215 @@
|
||||
/* 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
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* 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"
|
||||
);
|
||||
|
||||
// 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
|
||||
);
|
||||
|
||||
// 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
|
||||
);
|
||||
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(functionBFrame),
|
||||
"'click' event fired from 'b()' method call"
|
||||
);
|
||||
Assert.equal(
|
||||
customEventFrame[STACK_PREFIX_SLOT],
|
||||
stackTable.data.indexOf(functionBFrame),
|
||||
"'CustomEvent' event fired from 'b()' 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();
|
||||
}
|
24
tools/profiler/tests/browser/tracing.html
Normal file
24
tools/profiler/tests/browser/tracing.html
Normal file
@ -0,0 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Do some naive JS executions</title>
|
||||
<script>
|
||||
function a() {
|
||||
b();
|
||||
}
|
||||
function b() {
|
||||
document.body.setAttribute("foo", "bar");
|
||||
navigator.userAgent;
|
||||
window.dispatchEvent(new Event("CustomEvent"));
|
||||
document.body.click();
|
||||
}
|
||||
window.onload = a;
|
||||
window.onclick = () => console.log("click!");
|
||||
window.addEventListener("CustomEvent", function customEventHandler() {});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
Execute some naive code which should be traced.
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user