Differential Revision: https://phabricator.services.mozilla.com/D285950
178 lines
5.5 KiB
JavaScript
178 lines
5.5 KiB
JavaScript
/* 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 that the sources table is always present in the profile,
|
|
* even when the "jssources" feature is not enabled.
|
|
*/
|
|
add_task(async function test_source_table() {
|
|
Assert.ok(
|
|
!Services.profiler.IsActive(),
|
|
"The profiler is not currently active"
|
|
);
|
|
|
|
info("Start profiler with 'js' feature but without the 'jssources' feature");
|
|
await ProfilerTestUtils.startProfiler({
|
|
entries: 1000000,
|
|
interval: 1,
|
|
threads: ["GeckoMain"],
|
|
features: ["js"],
|
|
});
|
|
|
|
// Execute some JavaScript to create sources
|
|
function testFunction() {
|
|
return Math.random();
|
|
}
|
|
|
|
for (let i = 0; i < 100; i++) {
|
|
testFunction();
|
|
}
|
|
|
|
await ProfilerTestUtils.captureAtLeastOneJsSample();
|
|
|
|
const profile = await ProfilerTestUtils.stopNowAndGetProfile();
|
|
const [thread] = profile.threads;
|
|
|
|
// Verify that the sources table exists
|
|
Assert.ok(profile.sources, "Profile has a sources table");
|
|
Assert.ok(profile.sources.schema, "Sources table has a schema");
|
|
Assert.ok(profile.sources.data, "Sources table has data");
|
|
|
|
// Verify the schema has the expected fields
|
|
Assert.ok("id" in profile.sources.schema, "Sources schema has 'id' field");
|
|
Assert.ok(
|
|
"filename" in profile.sources.schema,
|
|
"Sources schema has 'filename' field"
|
|
);
|
|
Assert.ok(
|
|
"sourceMapURL" in profile.sources.schema,
|
|
"Sources schema has 'sourceMapURL' field"
|
|
);
|
|
|
|
// Verify that we have at least one source entry
|
|
Assert.greater(
|
|
profile.sources.data.length,
|
|
0,
|
|
"Sources table has at least one entry"
|
|
);
|
|
|
|
// Verify each source has the expected structure
|
|
const idCol = profile.sources.schema.id;
|
|
const filenameCol = profile.sources.schema.filename;
|
|
const sourceMapURLCol = profile.sources.schema.sourceMapURL;
|
|
|
|
for (const source of profile.sources.data) {
|
|
Assert.ok(source[idCol], "Source entry has an ID");
|
|
Assert.ok(source[filenameCol], "Source entry has a filename");
|
|
// sourceMapURL can be null or absent when not set.
|
|
Assert.ok(
|
|
source[sourceMapURLCol] === null ||
|
|
source[sourceMapURLCol] === undefined ||
|
|
typeof source[sourceMapURLCol] === "string",
|
|
"Source entry has a nullable sourceMapURL field"
|
|
);
|
|
}
|
|
|
|
// Verify that frames reference valid source indices
|
|
const { frameTable, stringTable } = thread;
|
|
const FRAME_LOCATION_SLOT = frameTable.schema.location;
|
|
|
|
let framesWithSourceIndex = 0;
|
|
|
|
for (const frame of frameTable.data) {
|
|
const location = stringTable[frame[FRAME_LOCATION_SLOT]];
|
|
const sourceIndexMatch = location.match(/\[(\d+)\]$/);
|
|
if (sourceIndexMatch) {
|
|
framesWithSourceIndex++;
|
|
const sourceIndexFromFrame = parseInt(sourceIndexMatch[1]);
|
|
Assert.ok(
|
|
sourceIndexFromFrame >= 0 &&
|
|
sourceIndexFromFrame < profile.sources.data.length,
|
|
`Source index ${sourceIndexFromFrame} is valid and references an entry in the sources table`
|
|
);
|
|
const filenameFromFrame = location.match(/\((.+):\d+:\d+\)/)[1];
|
|
Assert.equal(
|
|
filenameFromFrame,
|
|
profile.sources.data[sourceIndexFromFrame][filenameCol],
|
|
`Frame filename matches the source table entry for source index ${sourceIndexFromFrame}`
|
|
);
|
|
}
|
|
}
|
|
|
|
Assert.greater(
|
|
framesWithSourceIndex,
|
|
0,
|
|
"Found at least one frame with a source index"
|
|
);
|
|
|
|
info(
|
|
`Found ${profile.sources.data.length} sources in the table, ` +
|
|
`referenced by ${framesWithSourceIndex} frames.`
|
|
);
|
|
});
|
|
|
|
/**
|
|
* Test that sourceMapURL is properly captured from `//# sourceMappingURL=` comments.
|
|
*/
|
|
add_task(async function test_source_table_with_sourcemap() {
|
|
Assert.ok(
|
|
!Services.profiler.IsActive(),
|
|
"The profiler is not currently active"
|
|
);
|
|
|
|
info("Start profiler with 'js' feature");
|
|
await ProfilerTestUtils.startProfiler({
|
|
entries: 1000000,
|
|
interval: 1,
|
|
threads: ["GeckoMain"],
|
|
features: ["js"],
|
|
});
|
|
|
|
// Execute JavaScript with a sourceMappingURL comment
|
|
const testCode = `
|
|
function testFunctionWithSourceMap() {
|
|
const startTime = Date.now();
|
|
while (Date.now() - startTime < 10) {
|
|
// Busy wait for 10ms to ensure profiler captures samples
|
|
}
|
|
return 42;
|
|
}
|
|
testFunctionWithSourceMap();
|
|
//# sourceMappingURL=test-source-map.js.map
|
|
`;
|
|
|
|
// Eval the code to create a source with a sourceMapURL.
|
|
// eslint-disable-next-line no-eval
|
|
eval(testCode);
|
|
|
|
await ProfilerTestUtils.captureAtLeastOneJsSample();
|
|
|
|
const profile = await ProfilerTestUtils.stopNowAndGetProfile();
|
|
|
|
// Verify that the sources table exists.
|
|
Assert.ok(profile.sources, "Profile has a sources table");
|
|
Assert.ok(profile.sources.data, "Sources table has data");
|
|
|
|
const idCol = profile.sources.schema.id;
|
|
const filenameCol = profile.sources.schema.filename;
|
|
const sourceMapURLCol = profile.sources.schema.sourceMapURL;
|
|
|
|
// Find the source entry with the sourceMapURL.
|
|
let foundSourceWithMap = false;
|
|
for (const source of profile.sources.data) {
|
|
const sourceMapURL = source[sourceMapURLCol];
|
|
if (sourceMapURL === "test-source-map.js.map") {
|
|
foundSourceWithMap = true;
|
|
Assert.ok(source[idCol], "Source with sourceMapURL has an ID");
|
|
Assert.ok(source[filenameCol], "Source with sourceMapURL has a filename");
|
|
break;
|
|
}
|
|
}
|
|
|
|
Assert.ok(
|
|
foundSourceWithMap,
|
|
"Should find at least one source with the test sourceMapURL"
|
|
);
|
|
});
|