зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1147403 part 5 - Add Debugger::onIonCompilation hook. r=shu
This commit is contained in:
Родитель
2b8e638ee8
Коммит
ee77187f06
|
@ -225,6 +225,45 @@ compartment.
|
|||
thereby escaping the capability-based limits. For this reason,
|
||||
`onNewGlobalObject` is only available to privileged code.
|
||||
|
||||
<code>onIonCompilation(<i>graph</i>)</code>
|
||||
: A new IonMonkey compilation result is attached to a script instance of
|
||||
the Debuggee, the <i>graph</i> contains the internal intermediate
|
||||
representations of the compiler.
|
||||
|
||||
The value <i>graph</i> is an object composed of the following properties:
|
||||
|
||||
`json`
|
||||
: String containing a JSON of the intermediate representation used by
|
||||
the compiler. This JSON string content is composed of 2 intermediate
|
||||
representation of the graph, a `mir` (Middle-level IR), and a
|
||||
`lir` (Low-level IR).
|
||||
|
||||
Both have a property `blocks`, which is an array of basic
|
||||
blocks in [SSA form][ssa-form] which are used to construct the
|
||||
control flow graph. All elements of these arrays are objects which
|
||||
have a `number`, and an `instructions` properties.
|
||||
|
||||
The MIR blocks have additional properties such as the
|
||||
`predecessors` and `successors` of each block, which can
|
||||
be used to reconstruct the control flow graph, with the
|
||||
`number` properties of the blocks.
|
||||
|
||||
The `instructions` properties are array of objects which have
|
||||
an `id` and an `opcode`. The `id` corresponds to the
|
||||
[SSA form][ssa-form] identifier (number) of each instruction, and the
|
||||
`opcode` is a string which represents the instruction.
|
||||
|
||||
This JSON string contains even more detailed internal information
|
||||
which remains undocummented, as it is potentially subject to
|
||||
frequent modifications.
|
||||
|
||||
`scripts`
|
||||
: Array of [`Debugger.Script`][script] instances. For a block at
|
||||
`mir.blocks[i]` or `lir.blocks[i]` in the JSON, `scripts[i]` is the
|
||||
[`Debugger.Script`][script] containing that block's code.
|
||||
|
||||
This method's return value is ignored.
|
||||
|
||||
|
||||
|
||||
## Function Properties of the Debugger Prototype Object
|
||||
|
|
|
@ -64,3 +64,4 @@ resource 'img-alloc-plot' alloc-plot-console.png $RBASE/8461
|
|||
absolute-label 'protocol' https://wiki.mozilla.org/Remote_Debugging_Protocol "Remote Debugging Protocol"
|
||||
absolute-label 'saved-frame' https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/SavedFrame "SavedFrame"
|
||||
absolute-label 'bernoulli-trial' https://en.wikipedia.org/wiki/Bernoulli_trial "Bernoulli Trial"
|
||||
absolute-label 'ssa-form' https://en.wikipedia.org/wiki/Static_single_assignment_form "SSA form"
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
|
||||
function test() {
|
||||
// Force Ion compilation with OSR.
|
||||
for (var res = false; !res; res = inIon()) {};
|
||||
if (typeof res == "string")
|
||||
throw "Skipping test: Ion compilation is disabled/prevented.";
|
||||
};
|
||||
|
||||
// Skip this test if we cannot reliably compile with Ion.
|
||||
try {
|
||||
test();
|
||||
} catch (x) {
|
||||
if (typeof x == "string")
|
||||
quit();
|
||||
}
|
||||
|
||||
// Functions used to assert the representation of the graph which is exported in
|
||||
// the JSON string argument.
|
||||
function assertInstruction(ins) {
|
||||
assertEq(typeof ins.id, "number");
|
||||
assertEq(ins.id | 0, ins.id);
|
||||
assertEq(typeof ins.opcode, "string");
|
||||
}
|
||||
|
||||
function assertBlock(block) {
|
||||
assertEq(typeof block, "object");
|
||||
assertEq(typeof block.number, "number");
|
||||
assertEq(block.number | 0, block.number);
|
||||
assertEq(typeof block.instructions, "object");
|
||||
for (var ins of block.instructions)
|
||||
assertInstruction(ins);
|
||||
}
|
||||
|
||||
function assertGraph(graph, scripts) {
|
||||
assertEq(typeof graph, "object");
|
||||
assertEq(typeof graph.blocks, "object");
|
||||
assertEq(graph.blocks.length, scripts.length);
|
||||
for (var block of graph.blocks)
|
||||
assertBlock(block);
|
||||
}
|
||||
|
||||
function assertJSON(str, scripts) {
|
||||
assertEq(typeof str, "string");
|
||||
|
||||
var json = JSON.parse(str);
|
||||
assertGraph(json.mir, scripts);
|
||||
assertGraph(json.lir, scripts);
|
||||
}
|
||||
|
||||
function assertOnIonCompilationArgument(obj) {
|
||||
assertEq(typeof obj, "object");
|
||||
assertEq(typeof obj.scripts, "object");
|
||||
assertJSON(obj.json, obj.scripts);
|
||||
}
|
||||
|
||||
// Attach the current global to a debugger.
|
||||
var hits = 0;
|
||||
var g = newGlobal();
|
||||
g.parent = this;
|
||||
g.eval(`
|
||||
var dbg = new Debugger();
|
||||
var parentw = dbg.addDebuggee(parent);
|
||||
var testw = parentw.makeDebuggeeValue(parent.test);
|
||||
var scriptw = testw.script;
|
||||
`);
|
||||
|
||||
// Wrap the testing function.
|
||||
function check() {
|
||||
// print('reset compilation counter.');
|
||||
with ({}) { // Prevent Ion compilation.
|
||||
gc(); // Flush previous compilation.
|
||||
hits = 0; // Synchronized hit counts.
|
||||
test(); // Wait until the next Ion compilation.
|
||||
}
|
||||
}
|
||||
|
||||
// With the compilation graph inhibited, we should have no output.
|
||||
g.eval(`
|
||||
dbg.onIonCompilation = function (graph) {
|
||||
// print('Compiled ' + graph.scripts[0].displayName + ':' + graph.scripts[0].startLine);
|
||||
if (graph.scripts[0] !== scriptw)
|
||||
return;
|
||||
parent.assertOnIonCompilationArgument(graph);
|
||||
parent.hits++;
|
||||
};
|
||||
`);
|
||||
check();
|
||||
// '>= 1' is needed because --ion-eager is too eager.
|
||||
assertEq(hits >= 1, true);
|
||||
|
||||
|
||||
// Try re-entering the same compartment as the compiled script.
|
||||
g.dbg.onIonCompilation = function (graph) {
|
||||
// print('Compiled ' + graph.scripts[0].displayName + ':' + graph.scripts[0].startLine);
|
||||
if (graph.scripts[0] !== g.scriptw)
|
||||
return;
|
||||
assertOnIonCompilationArgument(graph);
|
||||
hits++;
|
||||
};
|
||||
check();
|
||||
assertEq(hits >= 1, true);
|
||||
|
||||
// Disable the debugger, and redo the last 2 tests.
|
||||
g.eval(`
|
||||
dbg.enabled = false;
|
||||
dbg.onIonCompilation = function (graph) {
|
||||
parent.hits++;
|
||||
};
|
||||
`);
|
||||
check();
|
||||
assertEq(hits, 0);
|
||||
|
||||
g.dbg.enabled = false;
|
||||
g.dbg.onIonCompilation = function (graph) {
|
||||
hits++;
|
||||
};
|
||||
check();
|
||||
assertEq(hits, 0);
|
|
@ -41,11 +41,13 @@
|
|||
#include "jit/Sink.h"
|
||||
#include "jit/StupidAllocator.h"
|
||||
#include "jit/ValueNumbering.h"
|
||||
#include "vm/Debugger.h"
|
||||
#include "vm/HelperThreads.h"
|
||||
#include "vm/TraceLogging.h"
|
||||
|
||||
#include "jscompartmentinlines.h"
|
||||
#include "jsobjinlines.h"
|
||||
#include "vm/Debugger-inl.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::jit;
|
||||
|
@ -392,6 +394,49 @@ JitCompartment::ensureIonStubsExist(JSContext* cx)
|
|||
return true;
|
||||
}
|
||||
|
||||
// This function initializes the values which are given to the Debugger
|
||||
// onIonCompilation hook, if the compilation was successful, and if Ion
|
||||
// compilations of this compartment are watched by any debugger.
|
||||
//
|
||||
// This function must be called in the same AutoEnterAnalysis section as the
|
||||
// CodeGenerator::link. Failing to do so might leave room to interleave other
|
||||
// allocations which can invalidate any JSObject / JSFunction referenced by the
|
||||
// MIRGraph.
|
||||
//
|
||||
// This function ignores any allocation failure and returns whether the
|
||||
// Debugger::onIonCompilation should be called.
|
||||
static inline bool
|
||||
PrepareForDebuggerOnIonCompilationHook(JSContext* cx, bool success, jit::MIRGraph& graph,
|
||||
AutoScriptVector* scripts, LSprinter* spew)
|
||||
{
|
||||
if (!success)
|
||||
return false;
|
||||
|
||||
if (!Debugger::observesIonCompilation(cx))
|
||||
return false;
|
||||
|
||||
// fireOnIonCompilation failures are ignored, do the same here.
|
||||
if (!scripts->reserve(graph.numBlocks())) {
|
||||
cx->clearPendingException();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Collect the list of scripts which are inlined in the MIRGraph.
|
||||
for (jit::MBasicBlockIterator block(graph.begin()); block != graph.end(); block++)
|
||||
scripts->infallibleAppend(block->info().script());
|
||||
|
||||
// Spew the JSON graph made for the Debugger at the end of the LifoAlloc
|
||||
// used by the compiler. This would not prevent unexpected GC from the
|
||||
// compartment of the Debuggee, but do them as part of the compartment of
|
||||
// the Debugger when the content is copied over to a JSString.
|
||||
jit::JSONSpewer spewer(*spew);
|
||||
spewer.spewDebuggerGraph(&graph);
|
||||
if (spew->hadOutOfMemory())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
jit::FinishOffThreadBuilder(JSContext* cx, IonBuilder* builder)
|
||||
{
|
||||
|
@ -472,33 +517,47 @@ jit::LazyLinkTopActivation(JSContext* cx)
|
|||
IonBuilder* builder = calleeScript->ionScript()->pendingBuilder();
|
||||
calleeScript->setPendingIonBuilder(cx, nullptr);
|
||||
|
||||
AutoEnterAnalysis enterTypes(cx);
|
||||
RootedScript script(cx, builder->script());
|
||||
|
||||
// See PrepareForDebuggerOnIonCompilationHook
|
||||
bool callOnIonCompilation = false;
|
||||
AutoScriptVector debugScripts(cx);
|
||||
LSprinter debugPrinter(builder->alloc().lifoAlloc());
|
||||
|
||||
// Remove from pending.
|
||||
builder->remove();
|
||||
|
||||
if (CodeGenerator* codegen = builder->backgroundCodegen()) {
|
||||
CodeGenerator* codegen = builder->backgroundCodegen();
|
||||
JitContext jctx(cx, &builder->alloc());
|
||||
bool success = false;
|
||||
if (codegen) {
|
||||
AutoEnterAnalysis enterTypes(cx);
|
||||
js::TraceLoggerThread* logger = TraceLoggerForMainThread(cx->runtime());
|
||||
TraceLoggerEvent event(logger, TraceLogger_AnnotateScripts, script);
|
||||
AutoTraceLog logScript(logger, event);
|
||||
AutoTraceLog logLink(logger, TraceLogger_IonLinking);
|
||||
|
||||
JitContext jctx(cx, &builder->alloc());
|
||||
|
||||
// Root the assembler until the builder is finished below. As it
|
||||
// was constructed off thread, the assembler has not been rooted
|
||||
// previously, though any GC activity would discard the builder.
|
||||
codegen->masm.constructRoot(cx);
|
||||
|
||||
if (!codegen->link(cx, builder->constraints())) {
|
||||
success = codegen->link(cx, builder->constraints());
|
||||
if (!success) {
|
||||
// Silently ignore OOM during code generation. The assembly code
|
||||
// doesn't has code to handle it after linking happened. So it's
|
||||
// not OK to throw a catchable exception from there.
|
||||
cx->clearPendingException();
|
||||
}
|
||||
|
||||
callOnIonCompilation = PrepareForDebuggerOnIonCompilationHook(
|
||||
cx, success, builder->graph(), &debugScripts, &debugPrinter);
|
||||
}
|
||||
|
||||
// Without AutoEnterAnalysis scope.
|
||||
if (callOnIonCompilation)
|
||||
Debugger::onIonCompilation(cx, debugScripts, debugPrinter);
|
||||
|
||||
FinishOffThreadBuilder(cx, builder);
|
||||
|
||||
MOZ_ASSERT(script->hasBaselineScript());
|
||||
|
@ -1633,7 +1692,6 @@ AttachFinishedCompilations(JSContext* cx)
|
|||
if (!ion)
|
||||
return;
|
||||
|
||||
AutoEnterAnalysis enterTypes(cx);
|
||||
AutoLockHelperThreadState lock;
|
||||
|
||||
GlobalHelperThreadState::IonBuilderVector& finished = HelperThreadState().ionFinishedList();
|
||||
|
@ -1685,9 +1743,17 @@ AttachFinishedCompilations(JSContext* cx)
|
|||
}
|
||||
}
|
||||
|
||||
if (CodeGenerator* codegen = builder->backgroundCodegen()) {
|
||||
RootedScript script(cx, builder->script());
|
||||
JitContext jctx(cx, &builder->alloc());
|
||||
// See PrepareForDebuggerOnIonCompilationHook
|
||||
bool callOnIonCompilation = false;
|
||||
AutoScriptVector debugScripts(cx);
|
||||
LSprinter debugPrinter(builder->alloc().lifoAlloc());
|
||||
|
||||
RootedScript script(cx, builder->script());
|
||||
CodeGenerator* codegen = builder->backgroundCodegen();
|
||||
JitContext jctx(cx, &builder->alloc());
|
||||
bool success = false;
|
||||
if (codegen) {
|
||||
AutoEnterAnalysis enterTypes(cx);
|
||||
TraceLoggerEvent event(logger, TraceLogger_AnnotateScripts, script);
|
||||
AutoTraceLog logScript(logger, event);
|
||||
AutoTraceLog logLink(logger, TraceLogger_IonLinking);
|
||||
|
@ -1697,7 +1763,6 @@ AttachFinishedCompilations(JSContext* cx)
|
|||
// previously, though any GC activity would discard the builder.
|
||||
codegen->masm.constructRoot(cx);
|
||||
|
||||
bool success;
|
||||
{
|
||||
AutoUnlockHelperThreadState unlock;
|
||||
success = codegen->link(cx, builder->constraints());
|
||||
|
@ -1710,6 +1775,15 @@ AttachFinishedCompilations(JSContext* cx)
|
|||
// exception from there.
|
||||
cx->clearPendingException();
|
||||
}
|
||||
|
||||
callOnIonCompilation = PrepareForDebuggerOnIonCompilationHook(
|
||||
cx, success, builder->graph(), &debugScripts, &debugPrinter);
|
||||
}
|
||||
|
||||
if (callOnIonCompilation) {
|
||||
// Without AutoEnterAnalysis scope.
|
||||
AutoUnlockHelperThreadState unlock;
|
||||
Debugger::onIonCompilation(cx, debugScripts, debugPrinter);
|
||||
}
|
||||
|
||||
FinishOffThreadBuilder(cx, builder);
|
||||
|
@ -1862,8 +1936,6 @@ IonCompile(JSContext* cx, JSScript* script,
|
|||
|
||||
JitContext jctx(cx, temp);
|
||||
|
||||
AutoEnterAnalysis enter(cx);
|
||||
|
||||
if (!cx->compartment()->ensureJitCompartmentExists(cx))
|
||||
return AbortReason_Alloc;
|
||||
|
||||
|
@ -1920,8 +1992,12 @@ IonCompile(JSContext* cx, JSScript* script,
|
|||
|
||||
SpewBeginFunction(builder, builderScript);
|
||||
|
||||
bool succeeded = builder->build();
|
||||
builder->clearForBackEnd();
|
||||
bool succeeded;
|
||||
{
|
||||
AutoEnterAnalysis enter(cx);
|
||||
succeeded = builder->build();
|
||||
builder->clearForBackEnd();
|
||||
}
|
||||
|
||||
if (!succeeded) {
|
||||
AbortReason reason = builder->abortReason();
|
||||
|
@ -1985,15 +2061,29 @@ IonCompile(JSContext* cx, JSScript* script,
|
|||
return AbortReason_NoAbort;
|
||||
}
|
||||
|
||||
ScopedJSDeletePtr<CodeGenerator> codegen(CompileBackEnd(builder));
|
||||
if (!codegen) {
|
||||
JitSpew(JitSpew_IonAbort, "Failed during back-end compilation.");
|
||||
return AbortReason_Disable;
|
||||
// See PrepareForDebuggerOnIonCompilationHook
|
||||
bool callOnIonCompilation = false;
|
||||
AutoScriptVector debugScripts(cx);
|
||||
LSprinter debugPrinter(builder->alloc().lifoAlloc());
|
||||
|
||||
ScopedJSDeletePtr<CodeGenerator> codegen;
|
||||
{
|
||||
AutoEnterAnalysis enter(cx);
|
||||
codegen = CompileBackEnd(builder);
|
||||
if (!codegen) {
|
||||
JitSpew(JitSpew_IonAbort, "Failed during back-end compilation.");
|
||||
return AbortReason_Disable;
|
||||
}
|
||||
|
||||
succeeded = codegen->link(cx, builder->constraints());
|
||||
callOnIonCompilation = PrepareForDebuggerOnIonCompilationHook(
|
||||
cx, succeeded, builder->graph(), &debugScripts, &debugPrinter);
|
||||
}
|
||||
|
||||
bool success = codegen->link(cx, builder->constraints());
|
||||
if (callOnIonCompilation)
|
||||
Debugger::onIonCompilation(cx, debugScripts, debugPrinter);
|
||||
|
||||
if (success)
|
||||
if (succeeded)
|
||||
return AbortReason_NoAbort;
|
||||
if (cx->isExceptionPending())
|
||||
return AbortReason_Error;
|
||||
|
|
|
@ -398,3 +398,12 @@ JSONSpewer::endFunction()
|
|||
endList();
|
||||
endObject();
|
||||
}
|
||||
|
||||
void
|
||||
JSONSpewer::spewDebuggerGraph(MIRGraph* graph)
|
||||
{
|
||||
beginObject();
|
||||
spewMIR(graph);
|
||||
spewLIR(graph);
|
||||
endObject();
|
||||
}
|
||||
|
|
|
@ -58,6 +58,8 @@ class JSONSpewer
|
|||
void spewIntervals(BacktrackingAllocator* regalloc);
|
||||
void endPass();
|
||||
void endFunction();
|
||||
|
||||
void spewDebuggerGraph(MIRGraph* mir);
|
||||
};
|
||||
|
||||
} // namespace jit
|
||||
|
|
|
@ -753,9 +753,7 @@ class MDefinition : public MNode
|
|||
|
||||
void setVirtualRegister(uint32_t vreg) {
|
||||
virtualRegister_ = vreg;
|
||||
#ifdef DEBUG
|
||||
setLoweredUnchecked();
|
||||
#endif
|
||||
}
|
||||
uint32_t virtualRegister() const {
|
||||
MOZ_ASSERT(isLowered());
|
||||
|
|
|
@ -58,4 +58,27 @@ js::Debugger::onExceptionUnwind(JSContext* cx, AbstractFramePtr frame)
|
|||
return slowPathOnExceptionUnwind(cx, frame);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
js::Debugger::observesIonCompilation(JSContext* cx)
|
||||
{
|
||||
// If the current compartment is observed by any Debugger.
|
||||
if (!cx->compartment()->isDebuggee())
|
||||
return false;
|
||||
|
||||
// If any attached Debugger watch for Jit compilation results.
|
||||
if (!Debugger::hasLiveHook(cx->global(), Debugger::OnIonCompilation))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
js::Debugger::onIonCompilation(JSContext* cx, AutoScriptVector& scripts, LSprinter& graph)
|
||||
{
|
||||
if (!observesIonCompilation(cx))
|
||||
return;
|
||||
|
||||
slowPathOnIonCompilation(cx, scripts, graph);
|
||||
}
|
||||
|
||||
#endif /* vm_Debugger_inl_h */
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
#include "gc/Marking.h"
|
||||
#include "jit/BaselineDebugModeOSR.h"
|
||||
#include "jit/BaselineJIT.h"
|
||||
#include "jit/JSONSpewer.h"
|
||||
#include "jit/MIRGraph.h"
|
||||
#include "js/GCAPI.h"
|
||||
#include "js/UbiNodeTraverse.h"
|
||||
#include "js/Vector.h"
|
||||
|
@ -1284,6 +1286,75 @@ Debugger::fireOnGarbageCollectionHook(JSContext* cx,
|
|||
handleUncaughtException(ac, true);
|
||||
}
|
||||
|
||||
JSTrapStatus
|
||||
Debugger::fireOnIonCompilationHook(JSContext* cx, AutoScriptVector& scripts, LSprinter& graph)
|
||||
{
|
||||
RootedObject hook(cx, getHook(OnIonCompilation));
|
||||
MOZ_ASSERT(hook);
|
||||
MOZ_ASSERT(hook->isCallable());
|
||||
|
||||
Maybe<AutoCompartment> ac;
|
||||
ac.emplace(cx, object);
|
||||
|
||||
// Copy the vector of scripts to a JS Array of Debugger.Script
|
||||
RootedObject tmpObj(cx);
|
||||
RootedValue tmpVal(cx);
|
||||
AutoValueVector dbgScripts(cx);
|
||||
for (size_t i = 0; i < scripts.length(); i++) {
|
||||
tmpObj = wrapScript(cx, scripts[i]);
|
||||
if (!tmpObj)
|
||||
return handleUncaughtException(ac, false);
|
||||
|
||||
tmpVal.setObject(*tmpObj);
|
||||
if (!dbgScripts.append(tmpVal))
|
||||
return handleUncaughtException(ac, false);
|
||||
}
|
||||
|
||||
RootedObject dbgScriptsArray(cx, JS_NewArrayObject(cx, dbgScripts));
|
||||
if (!dbgScriptsArray)
|
||||
return handleUncaughtException(ac, false);
|
||||
|
||||
// Copy the JSON compilation graph to a JS String which is allocated as part
|
||||
// of the Debugger compartment.
|
||||
Sprinter jsonPrinter(cx);
|
||||
if (!jsonPrinter.init())
|
||||
return handleUncaughtException(ac, false);
|
||||
|
||||
graph.exportInto(jsonPrinter);
|
||||
if (jsonPrinter.hadOutOfMemory())
|
||||
return handleUncaughtException(ac, false);
|
||||
|
||||
RootedString json(cx, JS_NewStringCopyZ(cx, jsonPrinter.string()));
|
||||
if (!json)
|
||||
return handleUncaughtException(ac, false);
|
||||
|
||||
// Create a JS Object which has the array of scripts, and the string of the
|
||||
// JSON graph.
|
||||
const char* names[] = { "scripts", "json" };
|
||||
JS::AutoValueArray<2> values(cx);
|
||||
values[0].setObject(*dbgScriptsArray);
|
||||
values[1].setString(json);
|
||||
|
||||
RootedObject obj(cx, JS_NewObject(cx, nullptr));
|
||||
if (!obj)
|
||||
return handleUncaughtException(ac, false);
|
||||
|
||||
MOZ_ASSERT(mozilla::ArrayLength(names) == values.length());
|
||||
for (size_t i = 0; i < mozilla::ArrayLength(names); i++) {
|
||||
if (!JS_DefineProperty(cx, obj, names[i], values[i], JSPROP_ENUMERATE, nullptr, nullptr))
|
||||
return handleUncaughtException(ac, false);
|
||||
}
|
||||
|
||||
// Call Debugger.onIonCompilation hook.
|
||||
JS::AutoValueArray<1> argv(cx);
|
||||
argv[0].setObject(*obj);
|
||||
|
||||
RootedValue rv(cx);
|
||||
if (!Invoke(cx, ObjectValue(*object), ObjectValue(*hook), 1, argv.begin(), &rv))
|
||||
return handleUncaughtException(ac, true);
|
||||
return JSTRAP_CONTINUE;
|
||||
}
|
||||
|
||||
template <typename HookIsEnabledFun /* bool (Debugger*) */,
|
||||
typename FireHookFun /* JSTrapStatus (Debugger*) */>
|
||||
/* static */ JSTrapStatus
|
||||
|
@ -1602,6 +1673,25 @@ Debugger::slowPathOnLogAllocationSite(JSContext* cx, HandleObject obj, HandleSav
|
|||
return true;
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
Debugger::slowPathOnIonCompilation(JSContext* cx, AutoScriptVector& scripts, LSprinter& graph)
|
||||
{
|
||||
JSTrapStatus status = dispatchHook(
|
||||
cx,
|
||||
[](Debugger* dbg) -> bool { return dbg->getHook(OnIonCompilation); },
|
||||
[&](Debugger* dbg) -> JSTrapStatus {
|
||||
(void) dbg->fireOnIonCompilationHook(cx, scripts, graph);
|
||||
return JSTRAP_CONTINUE;
|
||||
});
|
||||
|
||||
if (status == JSTRAP_ERROR) {
|
||||
cx->clearPendingException();
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(status == JSTRAP_CONTINUE);
|
||||
}
|
||||
|
||||
bool
|
||||
Debugger::isDebuggee(const JSCompartment* compartment) const
|
||||
{
|
||||
|
@ -2815,6 +2905,20 @@ Debugger::getMemory(JSContext* cx, unsigned argc, Value* vp)
|
|||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
Debugger::getOnIonCompilation(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
THIS_DEBUGGER(cx, argc, vp, "(get onIonCompilation)", args, dbg);
|
||||
return getHookImpl(cx, args, *dbg, OnIonCompilation);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
Debugger::setOnIonCompilation(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
THIS_DEBUGGER(cx, argc, vp, "(set onIonCompilation)", args, dbg);
|
||||
return setHookImpl(cx, args, *dbg, OnIonCompilation);
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a value used to designate a global (there's quite a variety; see the
|
||||
* docs), return the actual designee.
|
||||
|
@ -4230,6 +4334,7 @@ const JSPropertySpec Debugger::properties[] = {
|
|||
JS_PSGS("allowUnobservedAsmJS", Debugger::getAllowUnobservedAsmJS,
|
||||
Debugger::setAllowUnobservedAsmJS, 0),
|
||||
JS_PSG("memory", Debugger::getMemory, 0),
|
||||
JS_PSGS("onIonCompilation", Debugger::getOnIonCompilation, Debugger::setOnIonCompilation, 0),
|
||||
JS_PS_END
|
||||
};
|
||||
const JSFunctionSpec Debugger::methods[] = {
|
||||
|
|
|
@ -35,6 +35,8 @@ enum JSTrapStatus {
|
|||
|
||||
namespace js {
|
||||
|
||||
class LSprinter;
|
||||
|
||||
class Breakpoint;
|
||||
class DebuggerMemory;
|
||||
|
||||
|
@ -204,6 +206,7 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
|
|||
OnNewPromise,
|
||||
OnPromiseSettled,
|
||||
OnGarbageCollection,
|
||||
OnIonCompilation,
|
||||
HookCount
|
||||
};
|
||||
enum {
|
||||
|
@ -479,6 +482,8 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
|
|||
static bool getAllowUnobservedAsmJS(JSContext* cx, unsigned argc, Value* vp);
|
||||
static bool setAllowUnobservedAsmJS(JSContext* cx, unsigned argc, Value* vp);
|
||||
static bool getMemory(JSContext* cx, unsigned argc, Value* vp);
|
||||
static bool getOnIonCompilation(JSContext* cx, unsigned argc, Value* vp);
|
||||
static bool setOnIonCompilation(JSContext* cx, unsigned argc, Value* vp);
|
||||
static bool addDebuggee(JSContext* cx, unsigned argc, Value* vp);
|
||||
static bool addAllGlobalsAsDebuggees(JSContext* cx, unsigned argc, Value* vp);
|
||||
static bool removeDebuggee(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
@ -546,6 +551,8 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
|
|||
static bool slowPathOnLogAllocationSite(JSContext* cx, HandleObject obj, HandleSavedFrame frame,
|
||||
int64_t when, GlobalObject::DebuggerVector& dbgs);
|
||||
static void slowPathPromiseHook(JSContext* cx, Hook hook, HandleObject promise);
|
||||
static void slowPathOnIonCompilation(JSContext* cx, AutoScriptVector& scripts, LSprinter& graph);
|
||||
|
||||
template <typename HookIsEnabledFun /* bool (Debugger*) */,
|
||||
typename FireHookFun /* JSTrapStatus (Debugger*) */>
|
||||
static JSTrapStatus dispatchHook(JSContext* cx, HookIsEnabledFun hookIsEnabled,
|
||||
|
@ -582,6 +589,13 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
|
|||
void fireOnGarbageCollectionHook(JSContext* cx,
|
||||
const JS::dbg::GarbageCollectionEvent::Ptr& gcData);
|
||||
|
||||
/*
|
||||
* Receive a "Ion compilation" event from the engine. An Ion compilation with
|
||||
* the given summary just got linked.
|
||||
*/
|
||||
JSTrapStatus fireOnIonCompilationHook(JSContext* cx, AutoScriptVector& scripts,
|
||||
LSprinter& graph);
|
||||
|
||||
/*
|
||||
* Gets a Debugger.Frame object. If maybeIter is non-null, we eagerly copy
|
||||
* its data if we need to make a new Debugger.Frame.
|
||||
|
@ -699,6 +713,8 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
|
|||
static inline void onNewGlobalObject(JSContext* cx, Handle<GlobalObject*> global);
|
||||
static inline bool onLogAllocationSite(JSContext* cx, JSObject* obj, HandleSavedFrame frame,
|
||||
int64_t when);
|
||||
static inline bool observesIonCompilation(JSContext* cx);
|
||||
static inline void onIonCompilation(JSContext* cx, AutoScriptVector& scripts, LSprinter& graph);
|
||||
static JSTrapStatus onTrap(JSContext* cx, MutableHandleValue vp);
|
||||
static JSTrapStatus onSingleStep(JSContext* cx, MutableHandleValue vp);
|
||||
static bool handleBaselineOsr(JSContext* cx, InterpreterFrame* from, jit::BaselineFrame* to);
|
||||
|
|
Загрузка…
Ссылка в новой задаче