diff options
Diffstat (limited to 'testing/web-platform/tests/js-self-profiling/resources')
7 files changed, 195 insertions, 0 deletions
diff --git a/testing/web-platform/tests/js-self-profiling/resources/__dir__.headers b/testing/web-platform/tests/js-self-profiling/resources/__dir__.headers new file mode 100644 index 0000000000..93537b44de --- /dev/null +++ b/testing/web-platform/tests/js-self-profiling/resources/__dir__.headers @@ -0,0 +1 @@ +Document-Policy: js-profiling diff --git a/testing/web-platform/tests/js-self-profiling/resources/child-frame.html b/testing/web-platform/tests/js-self-profiling/resources/child-frame.html new file mode 100644 index 0000000000..7f8125eae7 --- /dev/null +++ b/testing/web-platform/tests/js-self-profiling/resources/child-frame.html @@ -0,0 +1,8 @@ +<!DOCTYPE html> +<html> +<head> +</head> +<body> + <script src="profile-utils.js"></script> +</body> +</html> diff --git a/testing/web-platform/tests/js-self-profiling/resources/external-script.js b/testing/web-platform/tests/js-self-profiling/resources/external-script.js new file mode 100644 index 0000000000..6f6bd42b25 --- /dev/null +++ b/testing/web-platform/tests/js-self-profiling/resources/external-script.js @@ -0,0 +1,9 @@ +// NOTE: Modifying the location of functions in this file will cause +// `external-script.html` to fail! Please update the following constants +// accordingly. +const EXTERNAL_SCRIPT_FUNCTION_LINE = 7; +const EXTERNAL_SCRIPT_FUNCTION_COLUMN = 32; + +function externalScriptFunction(sample) { + sample(); +} diff --git a/testing/web-platform/tests/js-self-profiling/resources/external-script.js.headers b/testing/web-platform/tests/js-self-profiling/resources/external-script.js.headers new file mode 100644 index 0000000000..cb762eff80 --- /dev/null +++ b/testing/web-platform/tests/js-self-profiling/resources/external-script.js.headers @@ -0,0 +1 @@ +Access-Control-Allow-Origin: * diff --git a/testing/web-platform/tests/js-self-profiling/resources/profile-utils.js b/testing/web-platform/tests/js-self-profiling/resources/profile-utils.js new file mode 100644 index 0000000000..2c32f34608 --- /dev/null +++ b/testing/web-platform/tests/js-self-profiling/resources/profile-utils.js @@ -0,0 +1,149 @@ +(function(global) { + const TEST_SAMPLE_INTERVAL = 10; + const ENSURE_SAMPLE_SPIN_WAIT_MS = 500; + + function forceSample() { + // Spin for |TEST_SAMPLE_INTERVAL + 500|ms to ensure that a sample occurs + // before this function returns. As periodic sampling is enforced by a + // SHOULD clause, it is indeed testable. + // + // More reliable sampling will be handled in a future testdriver RFC + // (https://github.com/web-platform-tests/rfcs/pull/81). + for (const deadline = performance.now() + TEST_SAMPLE_INTERVAL + + ENSURE_SAMPLE_SPIN_WAIT_MS; + performance.now() < deadline;) + ; + } + + // Creates a new profile that captures the execution of when the given + // function calls the `sample` function passed to it. + async function profileFunction(func) { + const profiler = new Profiler({ + sampleInterval: TEST_SAMPLE_INTERVAL, + maxBufferSize: Number.MAX_SAFE_INTEGER, + }); + + func(() => forceSample()); + + const trace = await profiler.stop(); + + // Sanity check ensuring that we captured a sample. + assert_greater_than(trace.resources.length, 0); + assert_greater_than(trace.frames.length, 0); + assert_greater_than(trace.stacks.length, 0); + assert_greater_than(trace.samples.length, 0); + + return trace; + } + + async function testFunction(func, frame) { + const trace = await profileFunction(func); + assert_true(containsFrame(trace, frame), 'trace contains frame'); + } + + function substackMatches(trace, stackId, expectedStack) { + if (expectedStack.length === 0) { + return true; + } + if (stackId === undefined) { + return false; + } + + const stackElem = trace.stacks[stackId]; + const expectedFrame = expectedStack[0]; + + if (!frameMatches(trace.frames[stackElem.frameId], expectedFrame)) { + return false; + } + return substackMatches(trace, stackElem.parentId, expectedStack.slice(1)); + } + + // Returns true if the trace contains a frame matching the given specification. + // We define a "match" as follows: a frame A matches an expectation E if (and + // only if) for each field of E, A has the same value. + function containsFrame(trace, expectedFrame) { + return trace.frames.find(frame => { + return frameMatches(frame, expectedFrame); + }) !== undefined; + } + + // Returns true if a trace contains a substack in one of its samples, ordered + // leaf to root. + function containsSubstack(trace, expectedStack) { + return trace.samples.find(sample => { + let stackId = sample.stackId; + while (stackId !== undefined) { + if (substackMatches(trace, stackId, expectedStack)) { + return true; + } + stackId = trace.stacks[stackId].parentId; + } + return false; + }) !== undefined; + } + + function containsResource(trace, expectedResource) { + return trace.resources.includes(expectedResource); + } + + // Returns true if a trace contains a sample matching the given specification. + // We define a "match" as follows: a sample A matches an expectation E if (and + // only if) for each field of E, A has the same value. + function containsSample(trace, expectedSample) { + return trace.samples.find(sample => { + return sampleMatches(sample, expectedSample); + }) !== undefined; + } + + // Compares each set field of `expected` against the given frame `actual`. + function sampleMatches(actual, expected) { + return (expected.timestamp === undefined || + expected.timestamp === actual.timestamp) && + (expected.stackId === undefined || + expected.stackId === actual.stackId) && + (expected.marker === undefined || expected.marker === actual.marker); + } + + // Compares each set field of `expected` against the given frame `actual`. + function frameMatches(actual, expected) { + return (expected.name === undefined || expected.name === actual.name) && + (expected.resourceId === undefined || expected.resourceId === actual.resourceId) && + (expected.line === undefined || expected.line === actual.line) && + (expected.column === undefined || expected.column === actual.column); + } + + function forceSampleFrame(frame) { + const channel = new MessageChannel(); + const replyPromise = new Promise(res => { + channel.port1.onmessage = res; + }); + frame.postMessage('', '*', [channel.port2]); + return replyPromise; + } + + window.addEventListener('message', message => { + // Force sample in response to messages received. + (function sampleFromMessage() { + ProfileUtils.forceSample(); + message.ports[0].postMessage(''); + })(); + }); + + global.ProfileUtils = { + // Capturing + profileFunction, + forceSample, + + // Containment checks + containsFrame, + containsSubstack, + containsResource, + containsSample, + + // Cross-frame sampling + forceSampleFrame, + + // Assertions + testFunction, + }; +})(this); diff --git a/testing/web-platform/tests/js-self-profiling/resources/profiling-script.js b/testing/web-platform/tests/js-self-profiling/resources/profiling-script.js new file mode 100644 index 0000000000..9f09e05b34 --- /dev/null +++ b/testing/web-platform/tests/js-self-profiling/resources/profiling-script.js @@ -0,0 +1,26 @@ +(function(global) { + let counter = 0; + + // Spins up a new profiler and performs some work in a new top-level task, + // calling some builtins. Returns a promise for the resulting trace. + const profileBuiltinsInNewTask = () => { + // Run profiling logic in a new task to eliminate the caller stack. + return new Promise(resolve => { + setTimeout(async () => { + const profiler = new Profiler({ sampleInterval: 10, maxBufferSize: 10000 }); + for (const deadline = performance.now() + 500; performance.now() < deadline;) { + // Run a range of builtins to ensure they get included in the trace. + // Store this computation in a variable to prevent getting optimized out. + counter += Math.random(); + counter += performance.now(); + } + const trace = await profiler.stop(); + resolve(trace); + }); + }); + } + + global.ProfilingScript = { + profileBuiltinsInNewTask, + } +})(window); diff --git a/testing/web-platform/tests/js-self-profiling/resources/profiling-script.js.headers b/testing/web-platform/tests/js-self-profiling/resources/profiling-script.js.headers new file mode 100644 index 0000000000..cb762eff80 --- /dev/null +++ b/testing/web-platform/tests/js-self-profiling/resources/profiling-script.js.headers @@ -0,0 +1 @@ +Access-Control-Allow-Origin: * |