зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1617666 - Use a separate Debugger to improve performance of eval. r=jlast
The SpiderMonkey Debugger API maintains a flag per-debuggee that tracks whether the SpiderMonkey should notify the debugger when new frames are entered, and whether the JIT scripts associated with the debuggee realm have been compiled with debug instrumentation. To avoid constantly needing to clear and regenerate JIT scripts, the flag is permanently enabled once "onEnterFrame" has been used with a given Debugger instance, and when the flag is enabled, there is a runtime performance cost. This runtime cost is what is causing the performance regression here, so to ensure that the flag is cleared at the end of a given instant-eval, we only set "onEnterFrame" on a new temporary Debugger instance, instead of setting it on th existing persistent Debugger instance. Differential Revision: https://phabricator.services.mozilla.com/D64912 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
411af42272
Коммит
cde4370d90
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
const Debugger = require("Debugger");
|
||||||
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
|
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
|
||||||
|
|
||||||
loader.lazyRequireGetter(
|
loader.lazyRequireGetter(
|
||||||
|
@ -162,7 +163,7 @@ exports.evalWithDebugger = function(string, options = {}, webConsole) {
|
||||||
);
|
);
|
||||||
|
|
||||||
if (options.eager) {
|
if (options.eager) {
|
||||||
allowSideEffects(dbg, sideEffectData);
|
allowSideEffects(sideEffectData);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempt to initialize any declarations found in the evaluated string
|
// Attempt to initialize any declarations found in the evaluated string
|
||||||
|
@ -268,17 +269,17 @@ function preventSideEffects(dbg) {
|
||||||
throw new Error("Debugger has hook installed");
|
throw new Error("Debugger has hook installed");
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = {
|
// Note: It is critical for debuggee performance that we implement all of
|
||||||
executedScripts: new Set(),
|
// this debuggee tracking logic with a separate Debugger instance.
|
||||||
debuggees: dbg.getDebuggees(),
|
// Bug 1617666 arises otherwise if we set an onEnterFrame hook on the
|
||||||
|
// existing debugger object and then later clear it.
|
||||||
handler: {
|
const newDbg = new Debugger();
|
||||||
hit: () => null,
|
for (const debuggee of dbg.getDebuggees()) {
|
||||||
},
|
newDbg.addDebuggee(debuggee.unsafeDereference());
|
||||||
};
|
}
|
||||||
|
|
||||||
// TODO: re-enable addAllGlobalsAsDebuggees(bug #1610532)
|
// TODO: re-enable addAllGlobalsAsDebuggees(bug #1610532)
|
||||||
// dbg.addAllGlobalsAsDebuggees();
|
// newDbg.addAllGlobalsAsDebuggees();
|
||||||
|
|
||||||
const timeoutDuration = 100;
|
const timeoutDuration = 100;
|
||||||
const endTime = Date.now() + timeoutDuration;
|
const endTime = Date.now() + timeoutDuration;
|
||||||
|
@ -290,7 +291,12 @@ function preventSideEffects(dbg) {
|
||||||
return ++count % 100 === 0 && Date.now() > endTime;
|
return ++count % 100 === 0 && Date.now() > endTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
dbg.onEnterFrame = frame => {
|
const executedScripts = new Set();
|
||||||
|
const handler = {
|
||||||
|
hit: () => null,
|
||||||
|
};
|
||||||
|
|
||||||
|
newDbg.onEnterFrame = frame => {
|
||||||
if (shouldCancel()) {
|
if (shouldCancel()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -303,19 +309,22 @@ function preventSideEffects(dbg) {
|
||||||
|
|
||||||
const script = frame.script;
|
const script = frame.script;
|
||||||
|
|
||||||
if (data.executedScripts.has(script)) {
|
if (executedScripts.has(script)) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
data.executedScripts.add(script);
|
executedScripts.add(script);
|
||||||
|
|
||||||
const offsets = script.getEffectfulOffsets();
|
const offsets = script.getEffectfulOffsets();
|
||||||
for (const offset of offsets) {
|
for (const offset of offsets) {
|
||||||
script.setBreakpoint(offset, data.handler);
|
script.setBreakpoint(offset, handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// The debugger only calls onNativeCall handlers on the debugger that is
|
||||||
|
// explicitly calling eval, so we need to add this hook on "dbg" even though
|
||||||
|
// the rest of our hooks work via "newDbg".
|
||||||
dbg.onNativeCall = (callee, reason) => {
|
dbg.onNativeCall = (callee, reason) => {
|
||||||
// Getters are never considered effectful, and setters are always effectful.
|
// Getters are never considered effectful, and setters are always effectful.
|
||||||
// Natives called normally are handled with a whitelist.
|
// Natives called normally are handled with a whitelist.
|
||||||
|
@ -330,22 +339,15 @@ function preventSideEffects(dbg) {
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
return data;
|
return {
|
||||||
|
dbg,
|
||||||
|
newDbg,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function allowSideEffects(dbg, data) {
|
function allowSideEffects(data) {
|
||||||
for (const script of data.executedScripts) {
|
data.dbg.onNativeCall = undefined;
|
||||||
script.clearBreakpoint(data.handler);
|
data.newDbg.removeAllDebuggees();
|
||||||
}
|
|
||||||
|
|
||||||
for (const global of dbg.getDebuggees()) {
|
|
||||||
if (!data.debuggees.includes(global)) {
|
|
||||||
dbg.removeDebuggee(global);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dbg.onEnterFrame = undefined;
|
|
||||||
dbg.onNativeCall = undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Native functions which are considered to be side effect free.
|
// Native functions which are considered to be side effect free.
|
||||||
|
|
Загрузка…
Ссылка в новой задаче