summaryrefslogtreecommitdiffstats
path: root/devtools/shared/test-helpers/thread-helpers.sys.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/shared/test-helpers/thread-helpers.sys.mjs')
-rw-r--r--devtools/shared/test-helpers/thread-helpers.sys.mjs143
1 files changed, 143 insertions, 0 deletions
diff --git a/devtools/shared/test-helpers/thread-helpers.sys.mjs b/devtools/shared/test-helpers/thread-helpers.sys.mjs
new file mode 100644
index 0000000000..b99fc16f00
--- /dev/null
+++ b/devtools/shared/test-helpers/thread-helpers.sys.mjs
@@ -0,0 +1,143 @@
+/* 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/. */
+
+/**
+ * Helper code to play with the javascript thread
+ **/
+
+function getSandboxWithDebuggerSymbol() {
+ // Bug 1835268 - Changing this to an ES module import currently throws an
+ // assertion in test_javascript_tracer.js in debug builds.
+ const { addDebuggerToGlobal } = ChromeUtils.import(
+ "resource://gre/modules/jsdebugger.jsm"
+ );
+ const systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
+
+ const debuggerSandbox = Cu.Sandbox(systemPrincipal, {
+ // This sandbox is also reused for ChromeDebugger implementation.
+ // As we want to load the `Debugger` API for debugging chrome contexts,
+ // we have to ensure loading it in a distinct compartment from its debuggee.
+ freshCompartment: true,
+ invisibleToDebugger: true,
+ });
+ addDebuggerToGlobal(debuggerSandbox);
+
+ return debuggerSandbox;
+}
+
+/**
+ * Implementation of a Javascript tracer logging traces to stdout.
+ *
+ * To be used like this:
+
+ const { traceAllJSCalls } = ChromeUtils.importESModule(
+ "resource://devtools/shared/test-helpers/thread-helpers.sys.mjs"
+ );
+ const jsTracer = traceAllJSCalls();
+ [... execute some code to tracer ...]
+ jsTracer.stop();
+
+ * @param prefix String
+ * Optional, if passed, this will be displayed in front of each
+ * line reporting a new frame execution.
+ * @param pause Number
+ * Optional, if passed, hold off each frame for `pause` ms,
+ * by letting the other event loops run in between.
+ * Be careful that it can introduce unexpected race conditions
+ * that can't necessarily be reproduced without this.
+ */
+export function traceAllJSCalls({ prefix = "", pause } = {}) {
+ const debuggerSandbox = getSandboxWithDebuggerSymbol();
+
+ debuggerSandbox.Services = Services;
+ const f = Cu.evalInSandbox(
+ "(" +
+ function (pauseInMs, prefixString) {
+ const dbg = new Debugger();
+ // Add absolutely all the globals...
+ dbg.addAllGlobalsAsDebuggees();
+ // ...but avoid tracing this sandbox code
+ const global = Cu.getGlobalForObject(this);
+ dbg.removeDebuggee(global);
+
+ // Add all globals created later on
+ dbg.onNewGlobalObject = g => dbg.addDebuggee(g);
+
+ function formatDisplayName(frame) {
+ if (frame.type === "call") {
+ const callee = frame.callee;
+ return callee.name || callee.userDisplayName || callee.displayName;
+ }
+
+ return `(${frame.type})`;
+ }
+
+ function stop() {
+ dbg.onEnterFrame = undefined;
+ dbg.removeAllDebuggees();
+ }
+ global.stop = stop;
+
+ let depth = 0;
+ dbg.onEnterFrame = frame => {
+ if (depth == 100) {
+ dump(
+ "Looks like an infinite loop? We stop the js tracer, but code may still be running!\n"
+ );
+ stop();
+ return;
+ }
+
+ const { script } = frame;
+ const { lineNumber, columnNumber } = script.getOffsetMetadata(
+ frame.offset
+ );
+ const padding = new Array(depth).join(" ");
+ dump(
+ `${prefixString}${padding}--[${frame.implementation}]--> ${
+ script.source.url
+ } @ ${lineNumber}:${columnNumber} - ${formatDisplayName(frame)}\n`
+ );
+
+ depth++;
+ frame.onPop = () => {
+ depth--;
+ };
+
+ // Optionaly pause the frame execute by letting the other event loop to run in between.
+ if (typeof pauseInMs == "number") {
+ let freeze = true;
+ const timer = Cc["@mozilla.org/timer;1"].createInstance(
+ Ci.nsITimer
+ );
+ timer.initWithCallback(
+ () => {
+ freeze = false;
+ },
+ pauseInMs,
+ Ci.nsITimer.TYPE_ONE_SHOT
+ );
+ Services.tm.spinEventLoopUntil("debugger-slow-motion", function () {
+ return !freeze;
+ });
+ }
+ };
+
+ return { stop };
+ } +
+ ")",
+ debuggerSandbox,
+ undefined,
+ "debugger-javascript-tracer",
+ 1,
+ /* enforceFilenameRestrictions */ false
+ );
+ f(pause, prefix);
+
+ return {
+ stop() {
+ debuggerSandbox.stop();
+ },
+ };
+}