summaryrefslogtreecommitdiffstats
path: root/devtools/server/tracer/tests/xpcshell/test_tracer.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/server/tracer/tests/xpcshell/test_tracer.js')
-rw-r--r--devtools/server/tracer/tests/xpcshell/test_tracer.js264
1 files changed, 264 insertions, 0 deletions
diff --git a/devtools/server/tracer/tests/xpcshell/test_tracer.js b/devtools/server/tracer/tests/xpcshell/test_tracer.js
index fe9a984aa8..0f38052ba5 100644
--- a/devtools/server/tracer/tests/xpcshell/test_tracer.js
+++ b/devtools/server/tracer/tests/xpcshell/test_tracer.js
@@ -238,3 +238,267 @@ add_task(async function testTracingFunctionReturnAndValues() {
info("Stop tracing");
stopTracing();
});
+
+add_task(async function testTracingStep() {
+ // Test the `traceStep` flag
+ const sandbox = Cu.Sandbox("https://example.com");
+ const source = `
+function foo() {
+ bar(); /* line 3 */
+ second(); /* line 4 */
+}
+function bar() {
+ let res; /* line 7 */
+ if (1 === 1) { /* line 8 */
+ res = "string"; /* line 9 */
+ } else {
+ res = "nope"
+ }
+ return res; /* line 13 */
+};
+function second() {
+ let x = 0; /* line 16 */
+ for (let i = 0; i < 2; i++) { /* line 17 */
+ x++; /* line 18 */
+ }
+ return null; /* line 20 */
+};
+foo();`;
+ Cu.evalInSandbox(source, sandbox, null, "file.js", 1);
+
+ // Pass an override method to catch all strings tentatively logged to stdout
+ const logs = [];
+ function loggingMethod(str) {
+ logs.push(str);
+ }
+
+ info("Start tracing");
+ startTracing({
+ global: sandbox,
+ traceSteps: true,
+ loggingMethod,
+ });
+
+ info("Call some code");
+ sandbox.foo();
+
+ Assert.equal(logs.length, 19);
+ Assert.equal(logs[0], "Start tracing JavaScript\n");
+ Assert.stringContains(logs[1], "λ foo");
+ Assert.stringContains(logs[1], "file.js:3:3");
+
+ // Each "step" only prints the location and nothing more
+ Assert.stringContains(logs[2], "file.js:3:3");
+
+ Assert.stringContains(logs[3], "λ bar");
+ Assert.stringContains(logs[3], "file.js:6:16");
+
+ Assert.stringContains(logs[4], "file.js:8:7");
+
+ Assert.stringContains(logs[5], "file.js:9:5");
+
+ Assert.stringContains(logs[6], "file.js:13:3");
+
+ Assert.stringContains(logs[7], "file.js:4:3");
+
+ Assert.stringContains(logs[8], "λ second");
+ Assert.stringContains(logs[8], "file.js:15:19");
+
+ Assert.stringContains(logs[9], "file.js:16:11");
+
+ // For loop
+ Assert.stringContains(logs[10], "file.js:17:16");
+
+ Assert.stringContains(logs[11], "file.js:17:19");
+
+ Assert.stringContains(logs[12], "file.js:18:5");
+
+ Assert.stringContains(logs[13], "file.js:17:26");
+
+ Assert.stringContains(logs[14], "file.js:17:19");
+
+ Assert.stringContains(logs[15], "file.js:18:5");
+
+ Assert.stringContains(logs[16], "file.js:17:26");
+
+ Assert.stringContains(logs[17], "file.js:17:19");
+ // End of for loop
+
+ Assert.stringContains(logs[18], "file.js:20:3");
+
+ info("Stop tracing");
+ stopTracing();
+});
+
+add_task(async function testTracingPauseOnStep() {
+ // Test the `pauseOnStep` flag
+ const sandbox = Cu.Sandbox("https://example.com");
+ sandbox.dump = dump;
+ const source = `var counter = 0; function incrementCounter() { let x = 0; dump("++\\n"); counter++; };`;
+ Cu.evalInSandbox(source, sandbox);
+
+ // Pass an override method to catch all strings tentatively logged to stdout
+ const logs = [];
+ let loggingMethodResolve;
+ function loggingMethod(str) {
+ logs.push(str);
+ if (loggingMethodResolve) {
+ loggingMethodResolve();
+ }
+ }
+
+ info("Start tracing without pause");
+ startTracing({
+ global: sandbox,
+ loggingMethod,
+ });
+
+ info("Call some code");
+ sandbox.incrementCounter();
+
+ Assert.equal(logs.length, 2);
+ Assert.equal(logs[0], "Start tracing JavaScript\n");
+ Assert.stringContains(logs[1], "λ incrementCounter");
+
+ info(
+ "When pauseOnStep isn't used, the traced code runs synchronously to completion"
+ );
+ Assert.equal(sandbox.counter, 1);
+
+ info("Stop tracing");
+ stopTracing();
+
+ logs.length = 0;
+ sandbox.counter = 0;
+
+ info("Start tracing with 0ms pause");
+ startTracing({
+ global: sandbox,
+ pauseOnStep: 0,
+ loggingMethod,
+ });
+
+ let onTraces = Promise.withResolvers();
+ let onResumed = Promise.withResolvers();
+ // This is used when receiving new traces in `loggingMethod()`
+ loggingMethodResolve = onTraces.resolve;
+
+ info(
+ "Run the to-be-traced code in a distinct event loop as it would be paused synchronously and would prevent further test script execution"
+ );
+ Services.tm.dispatchToMainThread(() => {
+ sandbox.incrementCounter();
+ onResumed.resolve();
+ });
+
+ info("Wait for tracer to call the listener");
+ await onTraces.promise;
+
+ Assert.equal(logs.length, 2);
+ Assert.equal(logs[0], "Start tracing JavaScript\n");
+ Assert.stringContains(logs[1], "λ incrementCounter");
+
+ info(
+ "When pauseInStep is used, the tracer listener is called, but the traced function is paused and doesn't run synchronously to completion"
+ );
+ Assert.equal(
+ sandbox.counter,
+ 0,
+ "The increment method was called but its execution flow was blocked and couldn't increment"
+ );
+
+ info("Wait for traced code to be resumed");
+ await onResumed.promise;
+ info(
+ "If we release the event loop, we can see the traced function completion"
+ );
+ Assert.equal(sandbox.counter, 1);
+
+ info("Stop tracing");
+ stopTracing();
+
+ logs.length = 0;
+ sandbox.counter = 0;
+
+ info("Start tracing with 250ms pause");
+ startTracing({
+ global: sandbox,
+ pauseOnStep: 250,
+ loggingMethod,
+ });
+
+ onTraces = Promise.withResolvers();
+ onResumed = Promise.withResolvers();
+ // This is used when receiving new traces in `loggingMethod()`
+ loggingMethodResolve = onTraces.resolve;
+
+ info(
+ "Run the to-be-traced code in a distinct event loop as it would be paused synchronously and would prevent further test script execution"
+ );
+ const startTimestamp = Cu.now();
+ Services.tm.dispatchToMainThread(() => {
+ sandbox.incrementCounter();
+ onResumed.resolve();
+ });
+
+ info("Wait for tracer to call the listener");
+ await onTraces.promise;
+
+ Assert.equal(logs.length, 2);
+ Assert.equal(logs[0], "Start tracing JavaScript\n");
+ Assert.stringContains(logs[1], "λ incrementCounter");
+
+ info(
+ "When pauseInStep is used, the tracer lsitener is called, but the traced function is paused and doesn't run synchronously to completion"
+ );
+ Assert.equal(sandbox.counter, 0);
+
+ info("Wait for traced code to be resumed");
+ await onResumed.promise;
+ info(
+ "If we release the event loop, we can see the traced function completion"
+ );
+ Assert.equal(sandbox.counter, 1);
+ info("The thread should have paused at least the pauseOnStep's duration");
+ Assert.greater(Cu.now() - startTimestamp, 250);
+
+ info("Stop tracing");
+ stopTracing();
+});
+
+add_task(async function testTracingFilterSourceUrl() {
+ // Test the `filterFrameSourceUrl` flag
+ const sandbox = Cu.Sandbox("https://example.com");
+
+ // Use a unique global (sandbox), but with two distinct scripts (first.js and second.js)
+ const source1 = `function foo() { bar(); }`;
+ Cu.evalInSandbox(source1, sandbox, null, "first.js", 1);
+
+ // Only code running in that second source should be traced.
+ const source2 = `function bar() { }`;
+ Cu.evalInSandbox(source2, sandbox, null, "second.js", 1);
+
+ // Pass an override method to catch all strings tentatively logged to stdout
+ const logs = [];
+ function loggingMethod(str) {
+ logs.push(str);
+ }
+
+ info("Start tracing");
+ startTracing({
+ global: sandbox,
+ filterFrameSourceUrl: "second",
+ loggingMethod,
+ });
+
+ info("Call some code");
+ sandbox.foo();
+
+ Assert.equal(logs.length, 2);
+ Assert.equal(logs[0], "Start tracing JavaScript\n");
+ Assert.stringContains(logs[1], "λ bar");
+ Assert.stringContains(logs[1], "second.js:1:18");
+
+ info("Stop tracing");
+ stopTracing();
+});