Bug 1566885 - Factor uses of internal Debugger state out of non-debugger-related files, r=jimb.

--HG--
extra : rebase_source : 80d5d339e673f4f943a354e126a6c3f96bb557f6
This commit is contained in:
Brian Hackett 2019-07-24 13:28:25 -10:00
Родитель 1a3b16017c
Коммит e777b59fb6
22 изменённых файлов: 735 добавлений и 519 удалений

Просмотреть файл

@ -13,6 +13,21 @@
namespace js {
/* static */
bool DebugAPI::stepModeEnabled(JSScript* script) {
return script->hasDebugScript() && stepModeEnabledSlow(script);
}
/* static */
bool DebugAPI::hasBreakpointsAt(JSScript* script, jsbytecode* pc) {
return script->hasDebugScript() && hasBreakpointsAtSlow(script, pc);
}
/* static */
bool DebugAPI::hasAnyBreakpointsOrStepMode(JSScript* script) {
return script->hasDebugScript();
}
/* static */
void DebugAPI::onNewScript(JSContext* cx, HandleScript script) {
// We early return in slowPathOnNewScript for self-hosted scripts, so we can
@ -43,6 +58,15 @@ void DebugAPI::onNewGlobalObject(JSContext* cx,
}
}
/* static */
void DebugAPI::notifyParticipatesInGC(GlobalObject* global,
uint64_t majorGCNumber) {
GlobalObject::DebuggerVector* dbgs = global->getDebuggers();
if (dbgs && !dbgs->empty()) {
slowPathNotifyParticipatesInGC(majorGCNumber, *dbgs);
}
}
/* static */
bool DebugAPI::onLogAllocationSite(JSContext* cx, JSObject* obj,
HandleSavedFrame frame,
@ -64,7 +88,7 @@ bool DebugAPI::onLeaveFrame(JSContext* cx, AbstractFramePtr frame,
frame.isDebuggee());
/* Traps must be cleared from eval frames, see slowPathOnLeaveFrame. */
mozilla::DebugOnly<bool> evalTraps =
frame.isEvalFrame() && frame.script()->hasAnyBreakpointsOrStepMode();
frame.isEvalFrame() && frame.script()->hasDebugScript();
MOZ_ASSERT_IF(evalTraps, frame.isDebuggee());
if (frame.isDebuggee()) {
ok = slowPathOnLeaveFrame(cx, frame, pc, ok);
@ -149,6 +173,13 @@ void DebugAPI::onPromiseSettled(JSContext* cx, Handle<PromiseObject*> promise) {
}
}
/* static */
void DebugAPI::sweepBreakpoints(FreeOp* fop, JSScript* script) {
if (script->hasDebugScript()) {
sweepBreakpointsSlow(fop, script);
}
}
} // namespace js
#endif /* debugger_DebugAPI_inl_h */

Просмотреть файл

@ -65,6 +65,9 @@ enum class ResumeMode {
Return,
};
class DebugScript;
class DebuggerVector;
class DebugAPI {
public:
friend class Debugger;
@ -91,6 +94,19 @@ class DebugAPI {
static void traceAllForMovingGC(JSTracer* trc);
static void sweepAll(FreeOp* fop);
static MOZ_MUST_USE bool findSweepGroupEdges(JSRuntime* rt);
static inline void sweepBreakpoints(FreeOp* fop, JSScript* script);
static void destroyDebugScript(FreeOp* fop, JSScript* script);
#ifdef JSGC_HASH_TABLE_CHECKS
static void checkDebugScriptAfterMovingGC(DebugScript* ds);
#endif
/*** Methods for querying script breakpoint state. **************************/
static inline bool stepModeEnabled(JSScript* script);
static inline bool hasBreakpointsAt(JSScript* script, jsbytecode* pc);
static inline bool hasAnyBreakpointsOrStepMode(JSScript* script);
/*** Methods for interacting with the runtime. ******************************/
@ -213,6 +229,15 @@ class DebugAPI {
static inline void onPromiseSettled(JSContext* cx,
Handle<PromiseObject*> promise);
// Whether any debugger is observing execution in a global.
static bool debuggerObservesAllExecution(GlobalObject* global);
// Whether any debugger is observing JS execution coverage in a global.
static bool debuggerObservesCoverage(GlobalObject* global);
// Whether any Debugger is observing asm.js execution in a global.
static bool debuggerObservesAsmJS(GlobalObject* global);
/*
* Announce to the debugger that a generator object has been created,
* via JSOP_GENERATOR.
@ -238,10 +263,36 @@ class DebugAPI {
static bool isObservedByDebuggerTrackingAllocations(
const GlobalObject& debuggee);
// Announce to the debugger that a global object is being collected by the
// specified major GC.
static inline void notifyParticipatesInGC(GlobalObject* global,
uint64_t majorGCNumber);
static mozilla::Maybe<double> allocationSamplingProbability(
GlobalObject* global);
// Allocate an object which holds a GlobalObject::DebuggerVector.
static JSObject* newGlobalDebuggersHolder(JSContext* cx);
// Get the GlobalObject::DebuggerVector for an object allocated by
// newGlobalDebuggersObject.
static GlobalObject::DebuggerVector* getGlobalDebuggers(JSObject* holder);
// Whether any debugger is observing exception unwinds in a realm.
static bool hasExceptionUnwindHook(GlobalObject* global);
// Whether any debugger is observing debugger statements in a realm.
static bool hasDebuggerStatementHook(GlobalObject* global);
private:
static bool stepModeEnabledSlow(JSScript* script);
static bool hasBreakpointsAtSlow(JSScript* script, jsbytecode* pc);
static void sweepBreakpointsSlow(FreeOp* fop, JSScript* script);
static void slowPathOnNewScript(JSContext* cx, HandleScript script);
static void slowPathOnNewGlobalObject(JSContext* cx,
Handle<GlobalObject*> global);
static void slowPathNotifyParticipatesInGC(
uint64_t majorGCNumber, GlobalObject::DebuggerVector& dbgs);
static MOZ_MUST_USE bool slowPathOnLogAllocationSite(
JSContext* cx, HandleObject obj, HandleSavedFrame frame,
mozilla::TimeStamp when, GlobalObject::DebuggerVector& dbgs);

Просмотреть файл

@ -0,0 +1,321 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
* 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/. */
#include "debugger/DebugScript.h"
#include "jit/BaselineJIT.h"
#include "gc/FreeOp-inl.h"
#include "gc/GC-inl.h"
#include "gc/Marking-inl.h"
#include "vm/JSContext-inl.h"
#include "vm/Realm-inl.h"
namespace js {
/* static */
DebugScript* DebugScript::get(JSScript* script) {
MOZ_ASSERT(script->hasDebugScript());
DebugScriptMap* map = script->realm()->debugScriptMap.get();
MOZ_ASSERT(map);
DebugScriptMap::Ptr p = map->lookup(script);
MOZ_ASSERT(p);
return p->value().get();
}
/* static */
DebugScript* DebugScript::getOrCreate(JSContext* cx, JSScript* script) {
if (script->hasDebugScript()) {
return get(script);
}
size_t nbytes = allocSize(script->length());
UniqueDebugScript debug(
reinterpret_cast<DebugScript*>(cx->pod_calloc<uint8_t>(nbytes)));
if (!debug) {
return nullptr;
}
/* Create realm's debugScriptMap if necessary. */
if (!script->realm()->debugScriptMap) {
auto map = cx->make_unique<DebugScriptMap>();
if (!map) {
return nullptr;
}
script->realm()->debugScriptMap = std::move(map);
}
DebugScript* borrowed = debug.get();
if (!script->realm()->debugScriptMap->putNew(script, std::move(debug))) {
ReportOutOfMemory(cx);
return nullptr;
}
// It is safe to set this: we can't fail after this point.
script->setHasDebugScript(true);
AddCellMemory(script, nbytes, MemoryUse::ScriptDebugScript);
/*
* Ensure that any Interpret() instances running on this script have
* interrupts enabled. The interrupts must stay enabled until the
* debug state is destroyed.
*/
for (ActivationIterator iter(cx); !iter.done(); ++iter) {
if (iter->isInterpreter()) {
iter->asInterpreter()->enableInterruptsIfRunning(script);
}
}
return borrowed;
}
/* static */
BreakpointSite* DebugScript::getBreakpointSite(JSScript* script,
jsbytecode* pc) {
uint32_t offset = script->pcToOffset(pc);
return script->hasDebugScript() ? get(script)->breakpoints[offset] : nullptr;
}
/* static */
BreakpointSite* DebugScript::getOrCreateBreakpointSite(JSContext* cx,
JSScript* script,
jsbytecode* pc) {
AutoRealm ar(cx, script);
DebugScript* debug = getOrCreate(cx, script);
if (!debug) {
return nullptr;
}
BreakpointSite*& site = debug->breakpoints[script->pcToOffset(pc)];
if (!site) {
site = cx->new_<JSBreakpointSite>(script, pc);
if (!site) {
return nullptr;
}
debug->numSites++;
AddCellMemory(script, sizeof(JSBreakpointSite), MemoryUse::BreakpointSite);
}
return site;
}
/* static */
void DebugScript::destroyBreakpointSite(FreeOp* fop, JSScript* script,
jsbytecode* pc) {
DebugScript* debug = get(script);
BreakpointSite*& site = debug->breakpoints[script->pcToOffset(pc)];
MOZ_ASSERT(site);
size_t size = site->type() == BreakpointSite::Type::JS
? sizeof(JSBreakpointSite)
: sizeof(WasmBreakpointSite);
fop->delete_(script, site, size, MemoryUse::BreakpointSite);
site = nullptr;
debug->numSites--;
if (!debug->needed()) {
DebugAPI::destroyDebugScript(fop, script);
}
}
/* static */
void DebugScript::clearBreakpointsIn(FreeOp* fop, Realm* realm,
Debugger* dbg, JSObject* handler) {
for (auto script = realm->zone()->cellIter<JSScript>(); !script.done();
script.next()) {
if (script->realm() == realm && script->hasDebugScript()) {
clearBreakpointsIn(fop, script, dbg, handler);
}
}
}
/* static */
void DebugScript::clearBreakpointsIn(FreeOp* fop, JSScript* script,
Debugger* dbg, JSObject* handler) {
if (!script->hasDebugScript()) {
return;
}
for (jsbytecode* pc = script->code(); pc < script->codeEnd(); pc++) {
BreakpointSite* site = getBreakpointSite(script, pc);
if (site) {
Breakpoint* nextbp;
for (Breakpoint* bp = site->firstBreakpoint(); bp; bp = nextbp) {
nextbp = bp->nextInSite();
if ((!dbg || bp->debugger == dbg) &&
(!handler || bp->getHandler() == handler)) {
bp->destroy(fop);
}
}
}
}
}
#ifdef DEBUG
/* static */
uint32_t DebugScript::getStepperCount(JSScript* script) {
return script->hasDebugScript() ? get(script)->stepperCount : 0;
}
#endif // DEBUG
/* static */
bool DebugScript::incrementStepperCount(JSContext* cx, JSScript* script) {
cx->check(script);
MOZ_ASSERT(cx->realm()->isDebuggee());
AutoRealm ar(cx, script);
DebugScript* debug = getOrCreate(cx, script);
if (!debug) {
return false;
}
debug->stepperCount++;
if (debug->stepperCount == 1) {
if (script->hasBaselineScript()) {
script->baselineScript()->toggleDebugTraps(script, nullptr);
}
}
return true;
}
/* static */
void DebugScript::decrementStepperCount(FreeOp* fop, JSScript* script) {
DebugScript* debug = get(script);
MOZ_ASSERT(debug);
MOZ_ASSERT(debug->stepperCount > 0);
debug->stepperCount--;
if (debug->stepperCount == 0) {
if (script->hasBaselineScript()) {
script->baselineScript()->toggleDebugTraps(script, nullptr);
}
if (!debug->needed()) {
DebugAPI::destroyDebugScript(fop, script);
}
}
}
/* static */
bool DebugScript::incrementGeneratorObserverCount(JSContext* cx,
JSScript* script) {
cx->check(script);
MOZ_ASSERT(cx->realm()->isDebuggee());
AutoRealm ar(cx, script);
DebugScript* debug = getOrCreate(cx, script);
if (!debug) {
return false;
}
debug->generatorObserverCount++;
// It is our caller's responsibility, before bumping the generator observer
// count, to make sure that the baseline code includes the necessary
// JS_AFTERYIELD instrumentation by calling
// {ensure,update}ExecutionObservabilityOfScript.
MOZ_ASSERT_IF(script->hasBaselineScript(),
script->baselineScript()->hasDebugInstrumentation());
return true;
}
/* static */
void DebugScript::decrementGeneratorObserverCount(FreeOp* fop,
JSScript* script) {
DebugScript* debug = get(script);
MOZ_ASSERT(debug);
MOZ_ASSERT(debug->generatorObserverCount > 0);
debug->generatorObserverCount--;
if (!debug->needed()) {
DebugAPI::destroyDebugScript(fop, script);
}
}
/* static */
void DebugAPI::destroyDebugScript(FreeOp* fop, JSScript* script) {
if (script->hasDebugScript()) {
DebugScriptMap* map = script->realm()->debugScriptMap.get();
MOZ_ASSERT(map);
DebugScriptMap::Ptr p = map->lookup(script);
MOZ_ASSERT(p);
DebugScript* debug = p->value().release();
map->remove(p);
script->setHasDebugScript(false);
fop->free_(script, debug, DebugScript::allocSize(script->length()),
MemoryUse::ScriptDebugScript);
}
}
#ifdef JSGC_HASH_TABLE_CHECKS
/* static */
void DebugAPI::checkDebugScriptAfterMovingGC(DebugScript* ds) {
for (uint32_t i = 0; i < ds->numSites; i++) {
BreakpointSite* site = ds->breakpoints[i];
if (site && site->type() == BreakpointSite::Type::JS) {
CheckGCThingAfterMovingGC(site->asJS()->script);
}
}
}
#endif // JSGC_HASH_TABLE_CHECKS
/* static */
void DebugAPI::sweepBreakpointsSlow(FreeOp* fop, JSScript* script) {
bool scriptGone = IsAboutToBeFinalizedUnbarriered(&script);
for (unsigned i = 0; i < script->length(); i++) {
BreakpointSite* site =
DebugScript::getBreakpointSite(script, script->offsetToPC(i));
if (!site) {
continue;
}
Breakpoint* nextbp;
for (Breakpoint* bp = site->firstBreakpoint(); bp; bp = nextbp) {
nextbp = bp->nextInSite();
GCPtrNativeObject& dbgobj = bp->debugger->toJSObjectRef();
// If we are sweeping, then we expect the script and the
// debugger object to be swept in the same sweep group, except
// if the breakpoint was added after we computed the sweep
// groups. In this case both script and debugger object must be
// live.
MOZ_ASSERT_IF(script->zone()->isGCSweeping() &&
dbgobj->zone()->isCollecting(),
dbgobj->zone()->isGCSweeping() ||
(!scriptGone && dbgobj->asTenured().isMarkedAny()));
bool dying = scriptGone || IsAboutToBeFinalized(&dbgobj);
MOZ_ASSERT_IF(!dying, !IsAboutToBeFinalized(&bp->getHandlerRef()));
if (dying) {
bp->destroy(fop);
}
}
}
}
/* static */
bool DebugAPI::stepModeEnabledSlow(JSScript* script) {
return DebugScript::get(script)->stepperCount > 0;
}
/* static */
bool DebugAPI::hasBreakpointsAtSlow(JSScript* script, jsbytecode* pc) {
BreakpointSite* site = DebugScript::getBreakpointSite(script, pc);
return site && site->enabledCount > 0;
}
} // namespace js

Просмотреть файл

@ -0,0 +1,107 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
* 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/. */
#ifndef dbg_DebugScript_h
#define dbg_DebugScript_h
#include "debugger/Debugger.h"
namespace js {
// DebugScript manages the internal debugger state for a JSScript, which may be
// associated with multiple Debuggers.
class DebugScript {
friend class DebugAPI;
/*
* If this is a generator script, this is the number of Debugger.Frames
* referring to calls to this generator, whether live or suspended. Closed
* generators do not contribute a count.
*
* When greater than zero, this script should be compiled with debug
* instrumentation to call Debugger::onResumeFrame at each resumption site, so
* that Debugger can reconnect any extant Debugger.Frames with the new
* concrete frame.
*/
uint32_t generatorObserverCount;
/*
* The number of Debugger.Frame objects that refer to frames running this
* script and that have onStep handlers. When nonzero, the interpreter and JIT
* must arrange to call Debugger::onSingleStep before each bytecode, or at
* least at some useful granularity.
*/
uint32_t stepperCount;
/*
* Number of breakpoint sites at opcodes in the script. This is the number
* of populated entries in DebugScript::breakpoints, below.
*/
uint32_t numSites;
/*
* Breakpoints set in our script. For speed and simplicity, this array is
* parallel to script->code(): the BreakpointSite for the opcode at
* script->code()[offset] is debugScript->breakpoints[offset]. Naturally,
* this array's true length is script->length().
*/
BreakpointSite* breakpoints[1];
/*
* True if this DebugScript carries any useful information. If false, it
* should be removed from its JSScript.
*/
bool needed() const {
return generatorObserverCount > 0 || stepperCount > 0 || numSites > 0;
}
static size_t allocSize(size_t codeLength) {
return offsetof(DebugScript, breakpoints) +
codeLength * sizeof(BreakpointSite*);
}
static DebugScript* get(JSScript* script);
static DebugScript* getOrCreate(JSContext* cx, JSScript* script);
public:
static BreakpointSite* getBreakpointSite(JSScript* script, jsbytecode* pc);
static BreakpointSite* getOrCreateBreakpointSite(JSContext* cx,
JSScript* script,
jsbytecode* pc);
static void destroyBreakpointSite(FreeOp* fop, JSScript* script,
jsbytecode* pc);
static void clearBreakpointsIn(FreeOp* fop, Realm* realm,
Debugger* dbg, JSObject* handler);
static void clearBreakpointsIn(FreeOp* fop, JSScript* script,
Debugger* dbg, JSObject* handler);
#ifdef DEBUG
static uint32_t getStepperCount(JSScript* script);
#endif
/*
* Increment or decrement the single-step count. If the count is non-zero
* then the script is in single-step mode.
*
* Only incrementing is fallible, as it could allocate a DebugScript.
*/
static bool incrementStepperCount(JSContext* cx, JSScript* script);
static void decrementStepperCount(FreeOp* fop, JSScript* script);
/*
* Increment or decrement the generator observer count. If the count is
* non-zero then the script reports resumptions to the debugger.
*
* Only incrementing is fallible, as it could allocate a DebugScript.
*/
static bool incrementGeneratorObserverCount(JSContext* cx, JSScript* script);
static void decrementGeneratorObserverCount(FreeOp* fop, JSScript* script);
};
} /* namespace js */
#endif /* dbg_DebugScript_h */

Просмотреть файл

@ -18,6 +18,7 @@
#include "builtin/Promise.h"
#include "debugger/DebuggerMemory.h"
#include "debugger/DebugScript.h"
#include "debugger/Environment.h"
#include "debugger/Frame.h"
#include "debugger/NoExecute.h"
@ -54,6 +55,7 @@
#include "vm/WrapperObject.h"
#include "wasm/WasmInstance.h"
#include "debugger/DebugAPI-inl.h"
#include "debugger/Frame-inl.h"
#include "debugger/Script-inl.h"
#include "gc/GC-inl.h"
@ -332,7 +334,7 @@ Breakpoint* Breakpoint::nextInSite() { return siteLink.mNext; }
JSBreakpointSite::JSBreakpointSite(JSScript* script, jsbytecode* pc)
: BreakpointSite(Type::JS), script(script), pc(pc) {
MOZ_ASSERT(!script->hasBreakpointsAt(pc));
MOZ_ASSERT(!DebugAPI::hasBreakpointsAt(script, pc));
}
void JSBreakpointSite::recompile(FreeOp* fop) {
@ -343,7 +345,7 @@ void JSBreakpointSite::recompile(FreeOp* fop) {
void JSBreakpointSite::destroyIfEmpty(FreeOp* fop) {
if (isEmpty()) {
script->destroyBreakpointSite(fop, pc);
DebugScript::destroyBreakpointSite(fop, script, pc);
}
}
@ -459,6 +461,57 @@ DebuggerMemory& Debugger::memory() const {
.as<DebuggerMemory>();
}
/*** DebuggerVectorHolder *****************************************************/
static void GlobalDebuggerVectorHolder_finalize(FreeOp* fop, JSObject* obj) {
MOZ_ASSERT(fop->maybeOnHelperThread());
void* ptr = obj->as<NativeObject>().getPrivate();
auto debuggers = static_cast<GlobalObject::DebuggerVector*>(ptr);
fop->delete_(obj, debuggers, MemoryUse::GlobalDebuggerVector);
}
static const ClassOps GlobalDebuggerVectorHolder_classOps = {
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
GlobalDebuggerVectorHolder_finalize
};
static const Class GlobalDebuggerVectorHolder_class = {
"GlobalDebuggerVectorHolder",
JSCLASS_HAS_PRIVATE | JSCLASS_BACKGROUND_FINALIZE,
&GlobalDebuggerVectorHolder_classOps};
/* static */
JSObject* DebugAPI::newGlobalDebuggersHolder(JSContext* cx) {
NativeObject* obj =
NewNativeObjectWithGivenProto(cx, &GlobalDebuggerVectorHolder_class,
nullptr);
if (!obj) {
return nullptr;
}
GlobalObject::DebuggerVector* debuggers =
cx->new_<GlobalObject::DebuggerVector>(cx->zone());
if (!debuggers) {
return nullptr;
}
InitObjectPrivate(obj, debuggers, MemoryUse::GlobalDebuggerVector);
return obj;
}
/* static */
GlobalObject::DebuggerVector* DebugAPI::getGlobalDebuggers(JSObject* holder) {
MOZ_ASSERT(holder->getClass() == &GlobalDebuggerVectorHolder_class);
return (GlobalObject::DebuggerVector*)holder->as<NativeObject>().getPrivate();
}
/*** Debugger accessors *******************************************************/
bool Debugger::getFrame(JSContext* cx, const FrameIter& iter,
MutableHandleValue vp) {
RootedDebuggerFrame result(cx);
@ -551,12 +604,17 @@ bool Debugger::getFrame(JSContext* cx, const FrameIter& iter,
return true;
}
/* static */
bool Debugger::hasLiveHook(GlobalObject* global, Hook which) {
static bool DebuggerExists(GlobalObject* global,
const std::function<bool(Debugger* dbg)>& predicate) {
// The GC analysis can't determine that the predicate can't GC, so let it know
// explicitly.
JS::AutoSuppressGCAnalysis nogc;
if (GlobalObject::DebuggerVector* debuggers = global->getDebuggers()) {
for (auto p = debuggers->begin(); p != debuggers->end(); p++) {
Debugger* dbg = *p;
if (dbg->enabled && dbg->getHook(which)) {
// Callbacks should not create new references to the debugger, so don't
// use a barrier. This allows this method to be called during GC.
if (predicate(p->unbarrieredGet())) {
return true;
}
}
@ -564,6 +622,44 @@ bool Debugger::hasLiveHook(GlobalObject* global, Hook which) {
return false;
}
/* static */
bool Debugger::hasLiveHook(GlobalObject* global, Hook which) {
return DebuggerExists(global, [=](Debugger* dbg) {
return dbg->enabled && dbg->getHook(which);
});
}
/* static */
bool DebugAPI::debuggerObservesAllExecution(GlobalObject* global) {
return DebuggerExists(global, [=](Debugger* dbg) {
return dbg->observesAllExecution();
});
}
/* static */
bool DebugAPI::debuggerObservesCoverage(GlobalObject* global) {
return DebuggerExists(global, [=](Debugger* dbg) {
return dbg->observesCoverage();
});
}
/* static */
bool DebugAPI::debuggerObservesAsmJS(GlobalObject* global) {
return DebuggerExists(global, [=](Debugger* dbg) {
return dbg->observesAsmJS();
});
}
/* static */
bool DebugAPI::hasExceptionUnwindHook(GlobalObject* global) {
return Debugger::hasLiveHook(global, Debugger::OnExceptionUnwind);
}
/* static */
bool DebugAPI::hasDebuggerStatementHook(GlobalObject* global) {
return Debugger::hasLiveHook(global, Debugger::OnDebuggerStatement);
}
JSObject* Debugger::getHook(Hook hook) const {
MOZ_ASSERT(hook >= 0 && hook < HookCount);
const Value& v = object->getReservedSlot(JSSLOT_DEBUG_HOOK_START + hook);
@ -2197,7 +2293,7 @@ ResumeMode DebugAPI::onTrap(JSContext* cx, MutableHandleValue vp) {
isJS = true;
pc = iter.pc();
bytecodeOffset = 0;
site = script->getBreakpointSite(pc);
site = DebugScript::getBreakpointSite(script, pc);
} else {
MOZ_ASSERT(iter.isWasm());
global.set(&iter.wasmInstance()->object()->global());
@ -2271,7 +2367,7 @@ ResumeMode DebugAPI::onTrap(JSContext* cx, MutableHandleValue vp) {
// Calling JS code invalidates site. Reload it.
if (isJS) {
site = iter.script()->getBreakpointSite(pc);
site = DebugScript::getBreakpointSite(iter.script(), pc);
} else {
site = iter.wasmInstance()->debug().getBreakpointSite(cx,
bytecodeOffset);
@ -2367,7 +2463,7 @@ ResumeMode DebugAPI::onSingleStep(JSContext* cx, MutableHandleValue vp) {
}
MOZ_ASSERT(liveStepperCount + suspendedStepperCount ==
trappingScript->stepperCount());
DebugScript::getStepperCount(trappingScript));
}
#endif
@ -2509,6 +2605,52 @@ void DebugAPI::slowPathOnNewGlobalObject(JSContext* cx,
MOZ_ASSERT(!cx->isExceptionPending());
}
/* static */
void DebugAPI::slowPathNotifyParticipatesInGC(
uint64_t majorGCNumber,
GlobalObject::DebuggerVector& dbgs) {
for (GlobalObject::DebuggerVector::Range r = dbgs.all(); !r.empty();
r.popFront()) {
if (!r.front().unbarrieredGet()->debuggeeIsBeingCollected(majorGCNumber)) {
#ifdef DEBUG
fprintf(stderr,
"OOM while notifying observing Debuggers of a GC: The "
"onGarbageCollection\n"
"hook will not be fired for this GC for some Debuggers!\n");
#endif
return;
}
}
}
/* static */
Maybe<double> DebugAPI::allocationSamplingProbability(GlobalObject* global) {
GlobalObject::DebuggerVector* dbgs = global->getDebuggers();
if (!dbgs || dbgs->empty()) {
return Nothing();
}
DebugOnly<WeakHeapPtr<Debugger*>*> begin = dbgs->begin();
double probability = 0;
bool foundAnyDebuggers = false;
for (auto p = dbgs->begin(); p < dbgs->end(); p++) {
// The set of debuggers had better not change while we're iterating,
// such that the vector gets reallocated.
MOZ_ASSERT(dbgs->begin() == begin);
// Use unbarrieredGet() to prevent triggering read barrier while collecting,
// this is safe as long as dbgp does not escape.
Debugger* dbgp = p->unbarrieredGet();
if (dbgp->trackingAllocationSites && dbgp->enabled) {
foundAnyDebuggers = true;
probability = std::max(dbgp->allocationSamplingProbability, probability);
}
}
return foundAnyDebuggers ? Some(probability) : Nothing();
}
/* static */
bool DebugAPI::slowPathOnLogAllocationSite(JSContext* cx, HandleObject obj,
HandleSavedFrame frame,
@ -4158,8 +4300,8 @@ bool Debugger::clearAllBreakpoints(JSContext* cx, unsigned argc, Value* vp) {
THIS_DEBUGGER(cx, argc, vp, "clearAllBreakpoints", args, dbg);
for (WeakGlobalObjectSet::Range r = dbg->debuggees.all(); !r.empty();
r.popFront()) {
r.front()->realm()->clearBreakpointsIn(cx->runtime()->defaultFreeOp(), dbg,
nullptr);
DebugScript::clearBreakpointsIn(cx->runtime()->defaultFreeOp(),
r.front()->realm(), dbg, nullptr);
}
return true;
}
@ -6114,8 +6256,8 @@ void Debugger::removeFromFrameMapsAndClearBreakpointsIn(JSContext* cx,
// script is about to be destroyed. Remove any breakpoints in it.
if (frame.isEvalFrame()) {
RootedScript script(cx, frame.script());
script->clearBreakpointsIn(cx->runtime()->defaultFreeOp(), nullptr,
nullptr);
DebugScript::clearBreakpointsIn(cx->runtime()->defaultFreeOp(), script,
nullptr, nullptr);
}
}

Просмотреть файл

@ -1178,8 +1178,8 @@ class WasmBreakpoint;
class WasmBreakpointSite;
class BreakpointSite {
friend class DebugAPI;
friend class Breakpoint;
friend class ::JSScript;
friend class Debugger;
public:

Просмотреть файл

@ -9,6 +9,7 @@
#include "mozilla/Assertions.h"
#include "mozilla/ScopeExit.h"
#include "debugger/DebugScript.h"
#include "debugger/Environment.h"
#include "debugger/NoExecute.h"
#include "debugger/Object.h"
@ -303,7 +304,7 @@ bool DebuggerFrame::setGenerator(JSContext* cx,
{
AutoRealm ar(cx, script);
if (!script->incrementGeneratorObserverCount(cx)) {
if (!DebugScript::incrementGeneratorObserverCount(cx, script)) {
return false;
}
}
@ -334,11 +335,11 @@ void DebuggerFrame::clearGenerator(FreeOp* fop) {
// calls may.
HeapPtr<JSScript*>& generatorScript = info->generatorScript();
if (!IsAboutToBeFinalized(&generatorScript)) {
generatorScript->decrementGeneratorObserverCount(fop);
DebugScript::decrementGeneratorObserverCount(fop, generatorScript);
OnStepHandler* handler = onStepHandler();
if (handler) {
generatorScript->decrementStepperCount(fop);
DebugScript::decrementStepperCount(fop, generatorScript);
handler->drop(fop, this);
setReservedSlot(ONSTEP_HANDLER_SLOT, UndefinedValue());
}
@ -667,12 +668,13 @@ bool DebuggerFrame::setOnStepHandler(JSContext* cx, HandleDebuggerFrame frame,
referent.script())) {
return false;
}
if (!referent.script()->incrementStepperCount(cx)) {
if (!DebugScript::incrementStepperCount(cx, referent.script())) {
return false;
}
} else if (!handler && prior) {
// Single stepping toggled on->off.
referent.script()->decrementStepperCount(cx->runtime()->defaultFreeOp());
DebugScript::decrementStepperCount(cx->runtime()->defaultFreeOp(),
referent.script());
}
}
@ -1008,7 +1010,7 @@ void DebuggerFrame::maybeDecrementFrameScriptStepperCount(
instance->debug().decrementStepperCount(
fop, frame.asWasmDebugFrame()->funcIndex());
} else {
frame.script()->decrementStepperCount(fop);
DebugScript::decrementStepperCount(fop, frame.script());
}
// In the case of generator frames, we may end up trying to clean up the step

Просмотреть файл

@ -7,6 +7,7 @@
#include "debugger/Script-inl.h"
#include "debugger/Debugger.h"
#include "debugger/DebugScript.h"
#include "wasm/WasmInstance.h"
#include "vm/BytecodeUtil-inl.h"
@ -1749,7 +1750,8 @@ struct DebuggerScript::SetBreakpointMatcher {
}
jsbytecode* pc = script->offsetToPC(offset_);
BreakpointSite* site = script->getOrCreateBreakpointSite(cx_, pc);
BreakpointSite* site =
DebugScript::getOrCreateBreakpointSite(cx_, script, pc);
if (!site) {
return false;
}
@ -1844,7 +1846,8 @@ bool DebuggerScript::getBreakpoints(JSContext* cx, unsigned argc, Value* vp) {
}
for (unsigned i = 0; i < script->length(); i++) {
BreakpointSite* site = script->getBreakpointSite(script->offsetToPC(i));
BreakpointSite* site =
DebugScript::getBreakpointSite(script, script->offsetToPC(i));
if (!site) {
continue;
}
@ -1874,7 +1877,8 @@ class DebuggerScript::ClearBreakpointMatcher {
using ReturnType = bool;
ReturnType match(HandleScript script) {
script->clearBreakpointsIn(cx_->runtime()->defaultFreeOp(), dbg_, handler_);
DebugScript::clearBreakpointsIn(cx_->runtime()->defaultFreeOp(), script,
dbg_, handler_);
return true;
}
ReturnType match(Handle<LazyScript*> lazyScript) {

Просмотреть файл

@ -24,6 +24,7 @@ include('../js-cxxflags.mozbuild')
SOURCES = [
'Debugger.cpp',
'DebuggerMemory.cpp',
'DebugScript.cpp',
'Environment.cpp',
'Frame.cpp',
'NoExecute.cpp',

Просмотреть файл

@ -8,7 +8,6 @@
#include "jsutil.h"
#include "debugger/Debugger.h"
#include "gc/FreeOp.h"
#include "gc/Policy.h"
#include "gc/PublicIterators.h"
@ -19,6 +18,7 @@
#include "vm/Runtime.h"
#include "wasm/WasmInstance.h"
#include "debugger/DebugAPI-inl.h"
#include "gc/GC-inl.h"
#include "gc/Marking-inl.h"
#include "gc/Nursery-inl.h"
@ -191,39 +191,7 @@ void Zone::sweepBreakpoints(FreeOp* fop) {
MOZ_ASSERT(isGCSweepingOrCompacting());
for (auto iter = cellIterUnsafe<JSScript>(); !iter.done(); iter.next()) {
JSScript* script = iter;
if (!script->hasAnyBreakpointsOrStepMode()) {
continue;
}
bool scriptGone = IsAboutToBeFinalizedUnbarriered(&script);
MOZ_ASSERT(script == iter);
for (unsigned i = 0; i < script->length(); i++) {
BreakpointSite* site = script->getBreakpointSite(script->offsetToPC(i));
if (!site) {
continue;
}
Breakpoint* nextbp;
for (Breakpoint* bp = site->firstBreakpoint(); bp; bp = nextbp) {
nextbp = bp->nextInSite();
GCPtrNativeObject& dbgobj = bp->debugger->toJSObjectRef();
// If we are sweeping, then we expect the script and the
// debugger object to be swept in the same sweep group, except
// if the breakpoint was added after we computed the sweep
// groups. In this case both script and debugger object must be
// live.
MOZ_ASSERT_IF(isGCSweeping() && dbgobj->zone()->isCollecting(),
dbgobj->zone()->isGCSweeping() ||
(!scriptGone && dbgobj->asTenured().isMarkedAny()));
bool dying = scriptGone || IsAboutToBeFinalized(&dbgobj);
MOZ_ASSERT_IF(!dying, !IsAboutToBeFinalized(&bp->getHandlerRef()));
if (dying) {
bp->destroy(fop);
}
}
}
DebugAPI::sweepBreakpoints(fop, script);
}
for (RealmsInZoneIter realms(this); !realms.done(); realms.next()) {
@ -475,24 +443,7 @@ void Zone::notifyObservingDebuggers() {
continue;
}
GlobalObject::DebuggerVector* dbgs = global->getDebuggers();
if (!dbgs) {
continue;
}
for (GlobalObject::DebuggerVector::Range r = dbgs->all(); !r.empty();
r.popFront()) {
if (!r.front().unbarrieredGet()->debuggeeIsBeingCollected(
rt->gc.majorGCCount())) {
#ifdef DEBUG
fprintf(stderr,
"OOM while notifying observing Debuggers of a GC: The "
"onGarbageCollection\n"
"hook will not be fired for this GC for some Debuggers!\n");
#endif
return;
}
}
DebugAPI::notifyParticipatesInGC(global, rt->gc.majorGCCount());
}
}

Просмотреть файл

@ -29,6 +29,7 @@
#include "vm/TraceLogging.h"
#include "vtune/VTuneWrapper.h"
#include "debugger/DebugAPI-inl.h"
#include "jit/BaselineFrameInfo-inl.h"
#include "jit/MacroAssembler-inl.h"
#include "jit/SharedICHelpers-inl.h"
@ -1578,7 +1579,8 @@ bool BaselineCompiler::emitDebugTrap() {
JSScript* script = handler.script();
bool enabled =
script->stepModeEnabled() || script->hasBreakpointsAt(handler.pc());
DebugAPI::stepModeEnabled(script) ||
DebugAPI::hasBreakpointsAt(script, handler.pc());
#if defined(JS_CODEGEN_ARM64)
// Flush any pending constant pools to prevent incorrect

Просмотреть файл

@ -22,6 +22,7 @@
#include "vm/Interpreter.h"
#include "vm/TraceLogging.h"
#include "debugger/DebugAPI-inl.h"
#include "gc/PrivateIterators-inl.h"
#include "jit/JitFrames-inl.h"
#include "jit/MacroAssembler-inl.h"
@ -855,7 +856,8 @@ void BaselineScript::toggleDebugTraps(JSScript* script, jsbytecode* pc) {
if (!pc || pc == curPC) {
bool enabled =
script->stepModeEnabled() || script->hasBreakpointsAt(curPC);
DebugAPI::stepModeEnabled(script) ||
DebugAPI::hasBreakpointsAt(script, curPC);
// Patch the trap.
CodeLocationLabel label(method(), CodeOffset(nativeOffset));

Просмотреть файл

@ -10,7 +10,6 @@
#include "jsutil.h"
#include "debugger/Debugger.h"
#include "gc/Marking.h"
#include "jit/BaselineDebugModeOSR.h"
#include "jit/BaselineFrame.h"
@ -160,8 +159,7 @@ static void HandleExceptionIon(JSContext* cx, const InlineFrameIterator& frame,
// We need to bail when there is a catchable exception, and we are the
// debuggee of a Debugger with a live onExceptionUnwind hook, or if a
// Debugger has observed this frame (e.g., for onPop).
bool shouldBail =
Debugger::hasLiveHook(cx->global(), Debugger::OnExceptionUnwind);
bool shouldBail = DebugAPI::hasExceptionUnwindHook(cx->global());
RematerializedFrame* rematFrame = nullptr;
if (!shouldBail) {
JitActivation* act = cx->activation()->asJit();

Просмотреть файл

@ -8,7 +8,6 @@
#include "builtin/Promise.h"
#include "builtin/TypedObject.h"
#include "debugger/Debugger.h"
#include "frontend/BytecodeCompiler.h"
#include "jit/arm/Simulator-arm.h"
#include "jit/BaselineIC.h"
@ -1127,9 +1126,10 @@ bool HandleDebugTrap(JSContext* cx, BaselineFrame* frame, uint8_t* retAddr,
// is in step mode or has breakpoints. The Baseline Compiler can toggle
// breakpoints more granularly for specific bytecode PCs.
if (frame->runningInInterpreter()) {
MOZ_ASSERT(script->hasAnyBreakpointsOrStepMode());
MOZ_ASSERT(DebugAPI::hasAnyBreakpointsOrStepMode(script));
} else {
MOZ_ASSERT(script->stepModeEnabled() || script->hasBreakpointsAt(pc));
MOZ_ASSERT(DebugAPI::stepModeEnabled(script) ||
DebugAPI::hasBreakpointsAt(script, pc));
}
if (*pc == JSOP_AFTERYIELD) {
@ -1157,11 +1157,12 @@ bool HandleDebugTrap(JSContext* cx, BaselineFrame* frame, uint8_t* retAddr,
RootedValue rval(cx);
ResumeMode resumeMode = ResumeMode::Continue;
if (script->stepModeEnabled()) {
if (DebugAPI::stepModeEnabled(script)) {
resumeMode = DebugAPI::onSingleStep(cx, &rval);
}
if (resumeMode == ResumeMode::Continue && script->hasBreakpointsAt(pc)) {
if (resumeMode == ResumeMode::Continue &&
DebugAPI::hasBreakpointsAt(script, pc)) {
resumeMode = DebugAPI::onTrap(cx, &rval);
}
@ -1212,7 +1213,7 @@ bool OnDebuggerStatement(JSContext* cx, BaselineFrame* frame, jsbytecode* pc,
bool GlobalHasLiveOnDebuggerStatement(JSContext* cx) {
AutoUnsafeCallWithABI unsafe;
return cx->realm()->isDebuggee() &&
Debugger::hasLiveHook(cx->global(), Debugger::OnDebuggerStatement);
DebugAPI::hasDebuggerStatementHook(cx->global());
}
bool PushLexicalEnv(JSContext* cx, BaselineFrame* frame,

Просмотреть файл

@ -25,7 +25,7 @@
#include "builtin/TypedObject.h"
#include "builtin/WeakMapObject.h"
#include "builtin/WeakSetObject.h"
#include "debugger/Debugger.h"
#include "debugger/DebugAPI.h"
#include "gc/FreeOp.h"
#include "js/ProtoKey.h"
#include "vm/DateObject.h"
@ -841,32 +841,12 @@ bool js::DefineToStringTag(JSContext* cx, HandleObject obj, JSAtom* tag) {
return DefineDataProperty(cx, obj, toStringTagId, tagString, JSPROP_READONLY);
}
static void GlobalDebuggees_finalize(FreeOp* fop, JSObject* obj) {
MOZ_ASSERT(fop->maybeOnHelperThread());
void* ptr = obj->as<NativeObject>().getPrivate();
auto debuggers = static_cast<GlobalObject::DebuggerVector*>(ptr);
fop->delete_(obj, debuggers, MemoryUse::GlobalDebuggerVector);
}
static const ClassOps GlobalDebuggees_classOps = {nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
GlobalDebuggees_finalize};
static const Class GlobalDebuggees_class = {
"GlobalDebuggee", JSCLASS_HAS_PRIVATE | JSCLASS_BACKGROUND_FINALIZE,
&GlobalDebuggees_classOps};
GlobalObject::DebuggerVector* GlobalObject::getDebuggers() const {
Value debuggers = getReservedSlot(DEBUGGERS);
if (debuggers.isUndefined()) {
return nullptr;
}
MOZ_ASSERT(debuggers.toObject().getClass() == &GlobalDebuggees_class);
return (DebuggerVector*)debuggers.toObject().as<NativeObject>().getPrivate();
return DebugAPI::getGlobalDebuggers(&debuggers.toObject());
}
/* static */ GlobalObject::DebuggerVector* GlobalObject::getOrCreateDebuggers(
@ -877,18 +857,13 @@ GlobalObject::DebuggerVector* GlobalObject::getDebuggers() const {
return debuggers;
}
NativeObject* obj =
NewNativeObjectWithGivenProto(cx, &GlobalDebuggees_class, nullptr);
JSObject* obj = DebugAPI::newGlobalDebuggersHolder(cx);
if (!obj) {
return nullptr;
}
debuggers = cx->new_<DebuggerVector>(cx->zone());
if (!debuggers) {
return nullptr;
}
InitObjectPrivate(obj, debuggers, MemoryUse::GlobalDebuggerVector);
global->setReservedSlot(DEBUGGERS, ObjectValue(*obj));
return debuggers;
return global->getDebuggers();
}
/* static */

Просмотреть файл

@ -1797,7 +1797,7 @@ static MOZ_NEVER_INLINE JS_HAZ_JSNATIVE_CALLER bool Interpret(JSContext* cx,
JS_BEGIN_MACRO \
script = (s); \
MOZ_ASSERT(cx->realm() == script->realm()); \
if (script->hasAnyBreakpointsOrStepMode() || script->hasScriptCounts()) \
if (DebugAPI::hasAnyBreakpointsOrStepMode(script) || script->hasScriptCounts()) \
activation.enableInterruptsUnconditionally(); \
JS_END_MACRO
@ -1888,7 +1888,7 @@ static MOZ_NEVER_INLINE JS_HAZ_JSNATIVE_CALLER bool Interpret(JSContext* cx,
}
if (script->isDebuggee()) {
if (script->stepModeEnabled()) {
if (DebugAPI::stepModeEnabled(script)) {
RootedValue rval(cx);
ResumeMode mode = DebugAPI::onSingleStep(cx, &rval);
switch (mode) {
@ -1910,11 +1910,11 @@ static MOZ_NEVER_INLINE JS_HAZ_JSNATIVE_CALLER bool Interpret(JSContext* cx,
moreInterrupts = true;
}
if (script->hasAnyBreakpointsOrStepMode()) {
if (DebugAPI::hasAnyBreakpointsOrStepMode(script)) {
moreInterrupts = true;
}
if (script->hasBreakpointsAt(REGS.pc)) {
if (DebugAPI::hasBreakpointsAt(script, REGS.pc)) {
RootedValue rval(cx);
ResumeMode mode = DebugAPI::onTrap(cx, &rval);
switch (mode) {

Просмотреть файл

@ -29,7 +29,6 @@
#include "jstypes.h"
#include "jsutil.h"
#include "debugger/Debugger.h"
#include "frontend/BytecodeCompiler.h"
#include "frontend/BytecodeEmitter.h"
#include "frontend/SharedContext.h"
@ -4249,7 +4248,7 @@ void JSScript::finalize(FreeOp* fop) {
jit::DestroyJitScripts(fop, this);
destroyScriptCounts();
destroyDebugScript(fop);
DebugAPI::destroyDebugScript(fop, this);
#ifdef MOZ_VTUNE
if (realm()->scriptVTuneIdMap) {
@ -4795,234 +4794,6 @@ JSScript* js::CloneScriptIntoFunction(
return dst;
}
DebugScript* JSScript::debugScript() {
MOZ_ASSERT(hasDebugScript());
DebugScriptMap* map = realm()->debugScriptMap.get();
MOZ_ASSERT(map);
DebugScriptMap::Ptr p = map->lookup(this);
MOZ_ASSERT(p);
return p->value().get();
}
DebugScript* JSScript::releaseDebugScript() {
MOZ_ASSERT(hasDebugScript());
DebugScriptMap* map = realm()->debugScriptMap.get();
MOZ_ASSERT(map);
DebugScriptMap::Ptr p = map->lookup(this);
MOZ_ASSERT(p);
DebugScript* debug = p->value().release();
map->remove(p);
clearFlag(MutableFlags::HasDebugScript);
return debug;
}
void JSScript::destroyDebugScript(FreeOp* fop) {
if (hasDebugScript()) {
#ifdef DEBUG
for (jsbytecode* pc = code(); pc < codeEnd(); pc++) {
MOZ_ASSERT(!getBreakpointSite(pc));
}
#endif
freeDebugScript(fop);
}
}
void JSScript::freeDebugScript(FreeOp* fop) {
MOZ_ASSERT(hasDebugScript());
fop->free_(this, releaseDebugScript(), DebugScript::allocSize(length()),
MemoryUse::ScriptDebugScript);
}
DebugScript* JSScript::getOrCreateDebugScript(JSContext* cx) {
if (hasDebugScript()) {
return debugScript();
}
size_t nbytes = DebugScript::allocSize(length());
UniqueDebugScript debug(
reinterpret_cast<DebugScript*>(cx->pod_calloc<uint8_t>(nbytes)));
if (!debug) {
return nullptr;
}
/* Create realm's debugScriptMap if necessary. */
if (!realm()->debugScriptMap) {
auto map = cx->make_unique<DebugScriptMap>();
if (!map) {
return nullptr;
}
realm()->debugScriptMap = std::move(map);
}
DebugScript* borrowed = debug.get();
if (!realm()->debugScriptMap->putNew(this, std::move(debug))) {
ReportOutOfMemory(cx);
return nullptr;
}
setFlag(MutableFlags::HasDebugScript); // safe to set this; we can't fail
// after this point
AddCellMemory(this, nbytes, MemoryUse::ScriptDebugScript);
/*
* Ensure that any Interpret() instances running on this script have
* interrupts enabled. The interrupts must stay enabled until the
* debug state is destroyed.
*/
for (ActivationIterator iter(cx); !iter.done(); ++iter) {
if (iter->isInterpreter()) {
iter->asInterpreter()->enableInterruptsIfRunning(this);
}
}
return borrowed;
}
bool JSScript::incrementGeneratorObserverCount(JSContext* cx) {
cx->check(this);
MOZ_ASSERT(cx->realm()->isDebuggee());
AutoRealm ar(cx, this);
DebugScript* debug = getOrCreateDebugScript(cx);
if (!debug) {
return false;
}
debug->generatorObserverCount++;
// It is our caller's responsibility, before bumping the generator observer
// count, to make sure that the baseline code includes the necessary
// JS_AFTERYIELD instrumentation by calling
// {ensure,update}ExecutionObservabilityOfScript.
MOZ_ASSERT_IF(hasBaselineScript(), baseline->hasDebugInstrumentation());
return true;
}
void JSScript::decrementGeneratorObserverCount(js::FreeOp* fop) {
DebugScript* debug = debugScript();
MOZ_ASSERT(debug);
MOZ_ASSERT(debug->generatorObserverCount > 0);
debug->generatorObserverCount--;
if (!debug->needed()) {
destroyDebugScript(fop);
}
}
bool JSScript::incrementStepperCount(JSContext* cx) {
cx->check(this);
MOZ_ASSERT(cx->realm()->isDebuggee());
AutoRealm ar(cx, this);
DebugScript* debug = getOrCreateDebugScript(cx);
if (!debug) {
return false;
}
debug->stepperCount++;
if (debug->stepperCount == 1) {
if (hasBaselineScript()) {
baseline->toggleDebugTraps(this, nullptr);
}
}
return true;
}
void JSScript::decrementStepperCount(FreeOp* fop) {
DebugScript* debug = debugScript();
MOZ_ASSERT(debug);
MOZ_ASSERT(debug->stepperCount > 0);
debug->stepperCount--;
if (debug->stepperCount == 0) {
if (hasBaselineScript()) {
baseline->toggleDebugTraps(this, nullptr);
}
if (!debug->needed()) {
freeDebugScript(fop);
}
}
}
BreakpointSite* JSScript::getOrCreateBreakpointSite(JSContext* cx,
jsbytecode* pc) {
AutoRealm ar(cx, this);
DebugScript* debug = getOrCreateDebugScript(cx);
if (!debug) {
return nullptr;
}
BreakpointSite*& site = debug->breakpoints[pcToOffset(pc)];
if (!site) {
site = cx->new_<JSBreakpointSite>(this, pc);
if (!site) {
return nullptr;
}
debug->numSites++;
AddCellMemory(this, sizeof(JSBreakpointSite), MemoryUse::BreakpointSite);
}
return site;
}
void JSScript::destroyBreakpointSite(FreeOp* fop, jsbytecode* pc) {
DebugScript* debug = debugScript();
BreakpointSite*& site = debug->breakpoints[pcToOffset(pc)];
MOZ_ASSERT(site);
size_t size = site->type() == BreakpointSite::Type::JS
? sizeof(JSBreakpointSite)
: sizeof(WasmBreakpointSite);
fop->delete_(this, site, size, MemoryUse::BreakpointSite);
site = nullptr;
debug->numSites--;
if (!debug->needed()) {
freeDebugScript(fop);
}
}
void JSScript::clearBreakpointsIn(FreeOp* fop, js::Debugger* dbg,
JSObject* handler) {
if (!hasAnyBreakpointsOrStepMode()) {
return;
}
for (jsbytecode* pc = code(); pc < codeEnd(); pc++) {
BreakpointSite* site = getBreakpointSite(pc);
if (site) {
Breakpoint* nextbp;
for (Breakpoint* bp = site->firstBreakpoint(); bp; bp = nextbp) {
nextbp = bp->nextInSite();
if ((!dbg || bp->debugger == dbg) &&
(!handler || bp->getHandler() == handler)) {
bp->destroy(fop);
}
}
}
}
}
bool JSScript::hasBreakpointsAt(jsbytecode* pc) {
BreakpointSite* site = getBreakpointSite(pc);
if (!site) {
return false;
}
return site->enabledCount > 0;
}
/* static */ bool ImmutableScriptData::InitFromEmitter(
JSContext* cx, js::HandleScript script, frontend::BytecodeEmitter* bce,
uint32_t nslots) {

Просмотреть файл

@ -68,14 +68,14 @@ class JitScript;
#define BASELINE_DISABLED_SCRIPT ((js::jit::BaselineScript*)0x1)
class AutoSweepJitScript;
class BreakpointSite;
class Debugger;
class GCParallelTask;
class LazyScript;
class ModuleObject;
class RegExpObject;
class SourceCompressionTask;
class Shape;
class DebugAPI;
class DebugScript;
namespace frontend {
struct BytecodeEmitter;
@ -253,58 +253,6 @@ using ScriptVTuneIdMap =
HashMap<JSScript*, uint32_t, DefaultHasher<JSScript*>, SystemAllocPolicy>;
#endif
class DebugScript {
friend class ::JSScript;
friend class JS::Realm;
/*
* If this is a generator script, this is the number of Debugger.Frames
* referring to calls to this generator, whether live or suspended. Closed
* generators do not contribute a count.
*
* When greater than zero, this script should be compiled with debug
* instrumentation to call Debugger::onResumeFrame at each resumption site, so
* that Debugger can reconnect any extant Debugger.Frames with the new
* concrete frame.
*/
uint32_t generatorObserverCount;
/*
* The number of Debugger.Frame objects that refer to frames running this
* script and that have onStep handlers. When nonzero, the interpreter and JIT
* must arrange to call Debugger::onSingleStep before each bytecode, or at
* least at some useful granularity.
*/
uint32_t stepperCount;
/*
* Number of breakpoint sites at opcodes in the script. This is the number
* of populated entries in DebugScript::breakpoints, below.
*/
uint32_t numSites;
/*
* Breakpoints set in our script. For speed and simplicity, this array is
* parallel to script->code(): the BreakpointSite for the opcode at
* script->code()[offset] is debugScript->breakpoints[offset]. Naturally,
* this array's true length is script->length().
*/
BreakpointSite* breakpoints[1];
/*
* True if this DebugScript carries any useful information. If false, it
* should be removed from its JSScript.
*/
bool needed() const {
return generatorObserverCount > 0 || stepperCount > 0 || numSites > 0;
}
static size_t allocSize(size_t codeLength) {
return offsetof(DebugScript, breakpoints) +
codeLength * sizeof(BreakpointSite*);
}
};
using UniqueDebugScript = js::UniquePtr<DebugScript, JS::FreePolicy>;
using DebugScriptMap = HashMap<JSScript*, UniqueDebugScript,
DefaultHasher<JSScript*>, SystemAllocPolicy>;
@ -3104,65 +3052,14 @@ class JSScript : public js::BaseScript {
bool formalIsAliased(unsigned argSlot);
bool formalLivesInArgumentsObject(unsigned argSlot);
private:
/* Change this->stepMode to |newValue|. */
void setNewStepMode(js::FreeOp* fop, uint32_t newValue);
js::DebugScript* getOrCreateDebugScript(JSContext* cx);
js::DebugScript* debugScript();
js::DebugScript* releaseDebugScript();
void destroyDebugScript(js::FreeOp* fop);
void freeDebugScript(js::FreeOp* fop);
bool hasDebugScript() const { return hasFlag(MutableFlags::HasDebugScript); }
public:
bool hasBreakpointsAt(jsbytecode* pc);
bool hasAnyBreakpointsOrStepMode() { return hasDebugScript(); }
// See comment above 'debugMode' in Realm.h for explanation of
// invariants of debuggee compartments, scripts, and frames.
inline bool isDebuggee() const;
js::BreakpointSite* getBreakpointSite(jsbytecode* pc) {
return hasDebugScript() ? debugScript()->breakpoints[pcToOffset(pc)]
: nullptr;
}
js::BreakpointSite* getOrCreateBreakpointSite(JSContext* cx, jsbytecode* pc);
void destroyBreakpointSite(js::FreeOp* fop, jsbytecode* pc);
void clearBreakpointsIn(js::FreeOp* fop, js::Debugger* dbg,
JSObject* handler);
/*
* Increment or decrement the single-step count. If the count is non-zero
* then the script is in single-step mode.
*
* Only incrementing is fallible, as it could allocate a DebugScript.
*/
bool incrementStepperCount(JSContext* cx);
void decrementStepperCount(js::FreeOp* fop);
bool stepModeEnabled() {
return hasDebugScript() && debugScript()->stepperCount > 0;
}
#ifdef DEBUG
uint32_t stepperCount() {
return hasDebugScript() ? debugScript()->stepperCount : 0;
}
#endif
/*
* Increment or decrement the generator observer count. If the count is
* non-zero then the script reports resumptions to the debugger.
*
* Only incrementing is fallible, as it could allocate a DebugScript.
*/
bool incrementGeneratorObserverCount(JSContext* cx);
void decrementGeneratorObserverCount(js::FreeOp* fop);
// Access the flag for whether this script has a DebugScript in its realm's
// map. This should only be used by the DebugScript class.
bool hasDebugScript() const { return hasFlag(MutableFlags::HasDebugScript); }
void setHasDebugScript(bool b) { setFlag(MutableFlags::HasDebugScript, b); }
void finalize(js::FreeOp* fop);

Просмотреть файл

@ -12,7 +12,7 @@
#include "jsfriendapi.h"
#include "debugger/Debugger.h"
#include "debugger/DebugAPI.h"
#include "gc/Policy.h"
#include "gc/PublicIterators.h"
#include "jit/JitOptions.h"
@ -565,12 +565,7 @@ void Realm::checkScriptMapsAfterMovingGC() {
MOZ_ASSERT(script->realm() == this);
CheckGCThingAfterMovingGC(script);
DebugScript* ds = r.front().value().get();
for (uint32_t i = 0; i < ds->numSites; i++) {
BreakpointSite* site = ds->breakpoints[i];
if (site && site->type() == BreakpointSite::Type::JS) {
CheckGCThingAfterMovingGC(site->asJS()->script);
}
}
DebugAPI::checkDebugScriptAfterMovingGC(ds);
auto ptr = debugScriptMap->lookup(script);
MOZ_RELEASE_ASSERT(ptr.found() && &*ptr == &r.front());
}
@ -775,22 +770,20 @@ void Realm::updateDebuggerObservesFlag(unsigned flag) {
zone()->runtimeFromMainThread()->gc.isForegroundSweeping()
? unsafeUnbarrieredMaybeGlobal()
: maybeGlobal();
const GlobalObject::DebuggerVector* v = global->getDebuggers();
for (auto p = v->begin(); p != v->end(); p++) {
// Use unbarrieredGet() to prevent triggering read barrier while collecting,
// this is safe as long as dbg does not escape.
Debugger* dbg = p->unbarrieredGet();
if (flag == DebuggerObservesAllExecution
? dbg->observesAllExecution()
: flag == DebuggerObservesCoverage
? dbg->observesCoverage()
: flag == DebuggerObservesAsmJS && dbg->observesAsmJS()) {
debugModeBits_ |= flag;
return;
}
bool observes = false;
if (flag == DebuggerObservesAllExecution) {
observes = DebugAPI::debuggerObservesAllExecution(global);
} else if (flag == DebuggerObservesCoverage) {
observes = DebugAPI::debuggerObservesCoverage(global);
} else if (flag == DebuggerObservesAsmJS) {
observes = DebugAPI::debuggerObservesAsmJS(global);
}
debugModeBits_ &= ~flag;
if (observes) {
debugModeBits_ |= flag;
} else {
debugModeBits_ &= ~flag;
}
}
void Realm::setIsDebuggee() {
@ -871,16 +864,6 @@ void Realm::clearScriptCounts() {
void Realm::clearScriptNames() { scriptNameMap.reset(); }
void Realm::clearBreakpointsIn(FreeOp* fop, js::Debugger* dbg,
HandleObject handler) {
for (auto script = zone()->cellIter<JSScript>(); !script.done();
script.next()) {
if (script->realm() == this && script->hasAnyBreakpointsOrStepMode()) {
script->clearBreakpointsIn(fop, dbg, handler);
}
}
}
void ObjectRealm::addSizeOfExcludingThis(
mozilla::MallocSizeOf mallocSizeOf, size_t* innerViewsArg,
size_t* lazyArrayBuffersArg, size_t* objectMetadataTablesArg,

Просмотреть файл

@ -768,9 +768,6 @@ class JS::Realm : public JS::shadow::Realm {
// scripts.
bool ensureDelazifyScriptsForDebugger(JSContext* cx);
void clearBreakpointsIn(js::FreeOp* fop, js::Debugger* dbg,
JS::HandleObject handler);
// Initializes randomNumberGenerator if needed.
mozilla::non_crypto::XorShift128PlusRNG& getOrCreateRandomNumberGenerator();

Просмотреть файл

@ -26,7 +26,6 @@
#include "jsmath.h"
#include "builtin/Promise.h"
#include "debugger/DebugAPI.h"
#include "gc/FreeOp.h"
#include "gc/GCInternals.h"
#include "gc/PublicIterators.h"
@ -53,6 +52,7 @@
#include "vm/TraceLoggingGraph.h"
#include "wasm/WasmSignalHandlers.h"
#include "debugger/DebugAPI-inl.h"
#include "gc/GC-inl.h"
#include "vm/JSContext-inl.h"
#include "vm/Realm-inl.h"
@ -434,7 +434,7 @@ static bool HandleInterrupt(JSContext* cx, bool invokeCallback) {
if (cx->realm()->isDebuggee()) {
ScriptFrameIter iter(cx);
if (!iter.done() && cx->compartment() == iter.compartment() &&
iter.script()->stepModeEnabled()) {
DebugAPI::stepModeEnabled(iter.script())) {
RootedValue rval(cx);
switch (DebugAPI::onSingleStep(cx, &rval)) {
case ResumeMode::Terminate:

Просмотреть файл

@ -19,7 +19,6 @@
#include "jsmath.h"
#include "jsnum.h"
#include "debugger/Debugger.h"
#include "gc/FreeOp.h"
#include "gc/HashUtil.h"
#include "gc/Marking.h"
@ -1816,31 +1815,12 @@ void SavedStacks::chooseSamplingProbability(Realm* realm) {
return;
}
GlobalObject::DebuggerVector* dbgs = global->getDebuggers();
if (!dbgs || dbgs->empty()) {
Maybe<double> probability = DebugAPI::allocationSamplingProbability(global);
if (probability.isNothing()) {
return;
}
mozilla::DebugOnly<WeakHeapPtr<Debugger*>*> begin = dbgs->begin();
mozilla::DebugOnly<bool> foundAnyDebuggers = false;
double probability = 0;
for (auto p = dbgs->begin(); p < dbgs->end(); p++) {
// The set of debuggers had better not change while we're iterating,
// such that the vector gets reallocated.
MOZ_ASSERT(dbgs->begin() == begin);
// Use unbarrieredGet() to prevent triggering read barrier while collecting,
// this is safe as long as dbgp does not escape.
Debugger* dbgp = p->unbarrieredGet();
if (dbgp->trackingAllocationSites && dbgp->enabled) {
foundAnyDebuggers = true;
probability = std::max(dbgp->allocationSamplingProbability, probability);
}
}
MOZ_ASSERT(foundAnyDebuggers);
this->setSamplingProbability(probability);
this->setSamplingProbability(*probability);
}
void SavedStacks::setSamplingProbability(double probability) {