mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-29 07:42:04 +00:00
Bug 1001678 - Part 1: Implement an allocation log for Debugger.Memory. r=jimb
This commit is contained in:
parent
87acb958cd
commit
8e6cd27902
31
js/src/jit-test/tests/debug/Memory-drainAllocationsLog-01.js
Normal file
31
js/src/jit-test/tests/debug/Memory-drainAllocationsLog-01.js
Normal file
@ -0,0 +1,31 @@
|
||||
// Test basic usage of drainAllocationsLog()
|
||||
|
||||
const root = newGlobal();
|
||||
const dbg = new Debugger();
|
||||
const wrappedRoot = dbg.addDebuggee(root)
|
||||
dbg.memory.trackingAllocationSites = true;
|
||||
|
||||
root.eval("(" + function immediate() {
|
||||
this.tests = [
|
||||
({}),
|
||||
[],
|
||||
/(two|2)\s*problems/,
|
||||
new function Ctor(){},
|
||||
new Object(),
|
||||
new Array(),
|
||||
new Date(),
|
||||
];
|
||||
} + "());");
|
||||
|
||||
const allocs = dbg.memory.drainAllocationsLog();
|
||||
print(allocs.join("\n--------------------------------------------------------------------------\n"));
|
||||
print("Total number of allocations logged: " + allocs.length);
|
||||
|
||||
let idx = -1;
|
||||
for (let object of root.tests) {
|
||||
let wrappedObject = wrappedRoot.makeDebuggeeValue(object);
|
||||
let allocSite = wrappedObject.allocationSite;
|
||||
let newIdx = allocs.indexOf(allocSite);
|
||||
assertEq(newIdx > idx, true);
|
||||
idx = newIdx;
|
||||
}
|
14
js/src/jit-test/tests/debug/Memory-drainAllocationsLog-02.js
Normal file
14
js/src/jit-test/tests/debug/Memory-drainAllocationsLog-02.js
Normal file
@ -0,0 +1,14 @@
|
||||
// Test that drainAllocationsLog fails when we aren't trackingAllocationSites.
|
||||
|
||||
load(libdir + 'asserts.js');
|
||||
|
||||
const root = newGlobal();
|
||||
const dbg = new Debugger();
|
||||
|
||||
dbg.memory.trackingAllocationSites = true;
|
||||
root.eval("this.alloc1 = {}");
|
||||
dbg.memory.trackingAllocationSites = false;
|
||||
root.eval("this.alloc2 = {};");
|
||||
|
||||
assertThrowsInstanceOf(() => dbg.memory.drainAllocationsLog(),
|
||||
Error);
|
24
js/src/jit-test/tests/debug/Memory-drainAllocationsLog-03.js
Normal file
24
js/src/jit-test/tests/debug/Memory-drainAllocationsLog-03.js
Normal file
@ -0,0 +1,24 @@
|
||||
// Test when there are more allocations than the maximum log length.
|
||||
|
||||
const root = newGlobal();
|
||||
const dbg = new Debugger();
|
||||
dbg.addDebuggee(root)
|
||||
|
||||
dbg.memory.maxAllocationsLogLength = 3;
|
||||
dbg.memory.trackingAllocationSites = true;
|
||||
|
||||
root.eval([
|
||||
"this.alloc1 = {};", // line 1
|
||||
"this.alloc2 = {};", // line 2
|
||||
"this.alloc3 = {};", // line 3
|
||||
"this.alloc4 = {};", // line 4
|
||||
].join("\n"));
|
||||
|
||||
const allocs = dbg.memory.drainAllocationsLog();
|
||||
|
||||
// Should have stayed at the maximum length.
|
||||
assertEq(allocs.length, 3);
|
||||
// Should have kept the most recent allocation.
|
||||
assertEq(allocs[2].line, 4);
|
||||
// Should have thrown away the oldest allocation.
|
||||
assertEq(allocs.map(x => x.line).indexOf(1), -1);
|
21
js/src/jit-test/tests/debug/Memory-drainAllocationsLog-04.js
Normal file
21
js/src/jit-test/tests/debug/Memory-drainAllocationsLog-04.js
Normal file
@ -0,0 +1,21 @@
|
||||
// Test that when we shorten the maximum log length, we won't get a longer log
|
||||
// than that new maximum afterwards.
|
||||
|
||||
const root = newGlobal();
|
||||
const dbg = new Debugger();
|
||||
dbg.addDebuggee(root)
|
||||
|
||||
dbg.memory.trackingAllocationSites = true;
|
||||
|
||||
root.eval([
|
||||
"this.alloc1 = {};", // line 1
|
||||
"this.alloc2 = {};", // line 2
|
||||
"this.alloc3 = {};", // line 3
|
||||
"this.alloc4 = {};", // line 4
|
||||
].join("\n"));
|
||||
|
||||
dbg.memory.maxAllocationsLogLength = 1;
|
||||
const allocs = dbg.memory.drainAllocationsLog();
|
||||
|
||||
// Should have trimmed down to the new maximum length.
|
||||
assertEq(allocs.length, 1);
|
@ -0,0 +1,9 @@
|
||||
// Test an empty allocation log.
|
||||
|
||||
const root = newGlobal();
|
||||
const dbg = new Debugger();
|
||||
dbg.addDebuggee(root)
|
||||
|
||||
dbg.memory.trackingAllocationSites = true;
|
||||
const allocs = dbg.memory.drainAllocationsLog();
|
||||
assertEq(allocs.length, 0);
|
23
js/src/jit-test/tests/debug/Memory-drainAllocationsLog-06.js
Normal file
23
js/src/jit-test/tests/debug/Memory-drainAllocationsLog-06.js
Normal file
@ -0,0 +1,23 @@
|
||||
// Test doing a GC while we have a non-empty log.
|
||||
|
||||
const root = newGlobal();
|
||||
const dbg = new Debugger();
|
||||
dbg.addDebuggee(root)
|
||||
dbg.memory.trackingAllocationSites = true;
|
||||
|
||||
root.eval("(" + function immediate() {
|
||||
this.tests = [
|
||||
({}),
|
||||
[],
|
||||
/(two|2)\s*problems/,
|
||||
new function Ctor(){},
|
||||
new Object(),
|
||||
new Array(),
|
||||
new Date(),
|
||||
];
|
||||
} + "());");
|
||||
|
||||
gc();
|
||||
|
||||
const allocs = dbg.memory.drainAllocationsLog();
|
||||
assertEq(allocs.length >= root.tests.length, true);
|
10
js/src/jit-test/tests/debug/Memory-drainAllocationsLog-07.js
Normal file
10
js/src/jit-test/tests/debug/Memory-drainAllocationsLog-07.js
Normal file
@ -0,0 +1,10 @@
|
||||
// Test retrieving the log when allocation tracking hasn't ever been enabled.
|
||||
|
||||
load(libdir + 'asserts.js');
|
||||
|
||||
const root = newGlobal();
|
||||
const dbg = new Debugger();
|
||||
dbg.addDebuggee(root)
|
||||
|
||||
assertThrowsInstanceOf(() => dbg.memory.drainAllocationsLog(),
|
||||
Error);
|
30
js/src/jit-test/tests/debug/Memory-drainAllocationsLog-08.js
Normal file
30
js/src/jit-test/tests/debug/Memory-drainAllocationsLog-08.js
Normal file
@ -0,0 +1,30 @@
|
||||
// Test retrieving the log multiple times.
|
||||
|
||||
const root = newGlobal();
|
||||
const dbg = new Debugger();
|
||||
dbg.addDebuggee(root)
|
||||
|
||||
root.eval([
|
||||
"this.allocs = [];",
|
||||
"this.doFirstAlloc = " + function () {
|
||||
this.allocs.push({}); this.firstAllocLine = Error().lineNumber;
|
||||
},
|
||||
"this.doSecondAlloc = " + function () {
|
||||
this.allocs.push(new Object()); this.secondAllocLine = Error().lineNumber;
|
||||
}
|
||||
].join("\n"));
|
||||
|
||||
dbg.memory.trackingAllocationSites = true;
|
||||
|
||||
root.doFirstAlloc();
|
||||
let allocs1 = dbg.memory.drainAllocationsLog();
|
||||
root.doSecondAlloc();
|
||||
let allocs2 = dbg.memory.drainAllocationsLog();
|
||||
|
||||
let allocs1Lines = allocs1.map(x => x.line);
|
||||
assertEq(allocs1Lines.indexOf(root.firstAllocLine) != -1, true);
|
||||
assertEq(allocs1Lines.indexOf(root.secondAllocLine) == -1, true);
|
||||
|
||||
let allocs2Lines = allocs2.map(x => x.line);
|
||||
assertEq(allocs2Lines.indexOf(root.secondAllocLine) != -1, true);
|
||||
assertEq(allocs2Lines.indexOf(root.firstAllocLine) == -1, true);
|
20
js/src/jit-test/tests/debug/Memory-drainAllocationsLog-09.js
Normal file
20
js/src/jit-test/tests/debug/Memory-drainAllocationsLog-09.js
Normal file
@ -0,0 +1,20 @@
|
||||
// Test logs that contain allocations from many debuggee compartments.
|
||||
|
||||
const dbg = new Debugger();
|
||||
|
||||
const root1 = newGlobal();
|
||||
const root2 = newGlobal();
|
||||
const root3 = newGlobal();
|
||||
|
||||
dbg.addDebuggee(root1);
|
||||
dbg.addDebuggee(root2);
|
||||
dbg.addDebuggee(root3);
|
||||
|
||||
dbg.memory.trackingAllocationSites = true;
|
||||
|
||||
root1.eval("this.alloc = {}");
|
||||
root2.eval("this.alloc = {}");
|
||||
root3.eval("this.alloc = {}");
|
||||
|
||||
const allocs = dbg.memory.drainAllocationsLog();
|
||||
assertEq(allocs.length >= 3, true);
|
21
js/src/jit-test/tests/debug/Memory-drainAllocationsLog-10.js
Normal file
21
js/src/jit-test/tests/debug/Memory-drainAllocationsLog-10.js
Normal file
@ -0,0 +1,21 @@
|
||||
// Test logs that contain allocations from debuggee compartments added as we are
|
||||
// logging.
|
||||
|
||||
const dbg = new Debugger();
|
||||
|
||||
dbg.memory.trackingAllocationSites = true;
|
||||
|
||||
const root1 = newGlobal();
|
||||
dbg.addDebuggee(root1);
|
||||
root1.eval("this.alloc = {}");
|
||||
|
||||
const root2 = newGlobal();
|
||||
dbg.addDebuggee(root2);
|
||||
root2.eval("this.alloc = {}");
|
||||
|
||||
const root3 = newGlobal();
|
||||
dbg.addDebuggee(root3);
|
||||
root3.eval("this.alloc = {}");
|
||||
|
||||
const allocs = dbg.memory.drainAllocationsLog();
|
||||
assertEq(allocs.length >= 3, true);
|
25
js/src/jit-test/tests/debug/Memory-drainAllocationsLog-11.js
Normal file
25
js/src/jit-test/tests/debug/Memory-drainAllocationsLog-11.js
Normal file
@ -0,0 +1,25 @@
|
||||
// Test logs that shouldn't contain allocations from debuggee compartments
|
||||
// removed as we are logging.
|
||||
|
||||
const dbg = new Debugger();
|
||||
|
||||
const root1 = newGlobal();
|
||||
dbg.addDebuggee(root1);
|
||||
const root2 = newGlobal();
|
||||
dbg.addDebuggee(root2);
|
||||
const root3 = newGlobal();
|
||||
dbg.addDebuggee(root3);
|
||||
|
||||
dbg.memory.trackingAllocationSites = true;
|
||||
|
||||
dbg.removeDebuggee(root1);
|
||||
root1.eval("this.alloc = {}");
|
||||
|
||||
dbg.removeDebuggee(root2);
|
||||
root2.eval("this.alloc = {}");
|
||||
|
||||
dbg.removeDebuggee(root3);
|
||||
root3.eval("this.alloc = {}");
|
||||
|
||||
const allocs = dbg.memory.drainAllocationsLog();
|
||||
assertEq(allocs.length, 0);
|
17
js/src/jit-test/tests/debug/Memory-drainAllocationsLog-12.js
Normal file
17
js/src/jit-test/tests/debug/Memory-drainAllocationsLog-12.js
Normal file
@ -0,0 +1,17 @@
|
||||
// Test that disabling the debugger disables allocation tracking.
|
||||
|
||||
load(libdir + "asserts.js");
|
||||
|
||||
const dbg = new Debugger();
|
||||
const root = newGlobal();
|
||||
dbg.addDebuggee(root);
|
||||
|
||||
dbg.memory.trackingAllocationSites = true;
|
||||
dbg.enabled = false;
|
||||
|
||||
root.eval("this.alloc = {}");
|
||||
|
||||
// We shouldn't accumulate allocations in our log while the debugger is
|
||||
// disabled.
|
||||
let allocs = dbg.memory.drainAllocationsLog();
|
||||
assertEq(allocs.length, 0);
|
18
js/src/jit-test/tests/debug/Memory-drainAllocationsLog-13.js
Normal file
18
js/src/jit-test/tests/debug/Memory-drainAllocationsLog-13.js
Normal file
@ -0,0 +1,18 @@
|
||||
// Test that we don't crash while logging allocations and there is
|
||||
// off-main-thread compilation. OMT compilation will allocate functions and
|
||||
// regexps, but we just punt on measuring that accurately.
|
||||
|
||||
if (helperThreadCount() === 0) {
|
||||
quit(0);
|
||||
}
|
||||
|
||||
const root = newGlobal();
|
||||
root.eval("this.dbg = new Debugger()");
|
||||
root.dbg.addDebuggee(this);
|
||||
root.dbg.memory.trackingAllocationSites = true;
|
||||
|
||||
offThreadCompileScript(
|
||||
"function foo() {\n" +
|
||||
" print('hello world');\n" +
|
||||
"}"
|
||||
);
|
@ -240,7 +240,7 @@ MSG_DEF(JSMSG_SYMBOL_TO_STRING, 186, 0, JSEXN_TYPEERR, "can't convert symb
|
||||
MSG_DEF(JSMSG_OBJECT_METADATA_CALLBACK_ALREADY_SET, 187, 0, JSEXN_ERR, "Cannot track object allocation, because other tools are already doing so")
|
||||
MSG_DEF(JSMSG_INCOMPATIBLE_METHOD, 188, 3, JSEXN_TYPEERR, "{0} {1} called on incompatible {2}")
|
||||
MSG_DEF(JSMSG_SYMBOL_TO_PRIMITIVE, 189, 0, JSEXN_TYPEERR, "can't convert symbol object to primitive")
|
||||
MSG_DEF(JSMSG_UNUSED190, 190, 0, JSEXN_NONE, "")
|
||||
MSG_DEF(JSMSG_NOT_TRACKING_ALLOCATIONS, 190, 1, JSEXN_ERR, "Cannot call {0} without setting trackingAllocationSites to true")
|
||||
MSG_DEF(JSMSG_BAD_INDEX, 191, 0, JSEXN_RANGEERR, "invalid or out-of-range index")
|
||||
MSG_DEF(JSMSG_SELFHOSTED_TOP_LEVEL_LET,192,0, JSEXN_SYNTAXERR, "self-hosted code cannot contain top-level 'let' declarations")
|
||||
MSG_DEF(JSMSG_BAD_FOR_EACH_LOOP, 193, 0, JSEXN_SYNTAXERR, "invalid for each loop")
|
||||
|
@ -6,6 +6,8 @@
|
||||
|
||||
#include "vm/Debugger-inl.h"
|
||||
|
||||
#include "mozilla/DebugOnly.h"
|
||||
|
||||
#include "jscntxt.h"
|
||||
#include "jscompartment.h"
|
||||
#include "jshashutil.h"
|
||||
@ -338,6 +340,7 @@ Breakpoint::nextInSite()
|
||||
|
||||
Debugger::Debugger(JSContext *cx, JSObject *dbg)
|
||||
: object(dbg), uncaughtExceptionHook(nullptr), enabled(true), trackingAllocationSites(false),
|
||||
allocationsLogLength(0), maxAllocationsLogLength(DEFAULT_MAX_ALLOCATIONS_LOG_LENGTH),
|
||||
frames(cx->runtime()), scripts(cx), sources(cx), objects(cx), environments(cx)
|
||||
{
|
||||
assertSameCompartment(cx, dbg);
|
||||
@ -350,6 +353,7 @@ Debugger::Debugger(JSContext *cx, JSObject *dbg)
|
||||
Debugger::~Debugger()
|
||||
{
|
||||
JS_ASSERT_IF(debuggees.initialized(), debuggees.empty());
|
||||
emptyAllocationsLog();
|
||||
|
||||
/*
|
||||
* Since the inactive state for this link is a singleton cycle, it's always
|
||||
@ -392,6 +396,19 @@ Debugger::fromChildJSObject(JSObject *obj)
|
||||
return fromJSObject(dbgobj);
|
||||
}
|
||||
|
||||
bool
|
||||
Debugger::hasMemory() const
|
||||
{
|
||||
return object->getReservedSlot(JSSLOT_DEBUG_MEMORY_INSTANCE).isObject();
|
||||
}
|
||||
|
||||
DebuggerMemory &
|
||||
Debugger::memory() const
|
||||
{
|
||||
JS_ASSERT(hasMemory());
|
||||
return object->getReservedSlot(JSSLOT_DEBUG_MEMORY_INSTANCE).toObject().as<DebuggerMemory>();
|
||||
}
|
||||
|
||||
bool
|
||||
Debugger::getScriptFrameWithIter(JSContext *cx, AbstractFramePtr frame,
|
||||
const ScriptFrameIter *maybeIter, MutableHandleValue vp)
|
||||
@ -1439,6 +1456,61 @@ Debugger::slowPathOnNewGlobalObject(JSContext *cx, Handle<GlobalObject *> global
|
||||
JS_ASSERT(!cx->isExceptionPending());
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
Debugger::slowPathOnLogAllocationSite(JSContext *cx, HandleSavedFrame frame,
|
||||
GlobalObject::DebuggerVector &dbgs)
|
||||
{
|
||||
JS_ASSERT(!dbgs.empty());
|
||||
mozilla::DebugOnly<Debugger **> begin = dbgs.begin();
|
||||
|
||||
for (Debugger **dbgp = dbgs.begin(); dbgp < dbgs.end(); dbgp++) {
|
||||
// The set of debuggers had better not change while we're iterating,
|
||||
// such that the vector gets reallocated.
|
||||
JS_ASSERT(dbgs.begin() == begin);
|
||||
|
||||
if ((*dbgp)->trackingAllocationSites &&
|
||||
(*dbgp)->enabled &&
|
||||
!(*dbgp)->appendAllocationSite(cx, frame))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
Debugger::appendAllocationSite(JSContext *cx, HandleSavedFrame frame)
|
||||
{
|
||||
AutoCompartment ac(cx, object);
|
||||
RootedObject wrapped(cx, frame);
|
||||
if (!cx->compartment()->wrap(cx, &wrapped))
|
||||
return false;
|
||||
|
||||
AllocationSite *allocSite = cx->new_<AllocationSite>(wrapped);
|
||||
if (!allocSite)
|
||||
return false;
|
||||
|
||||
allocationsLog.insertBack(allocSite);
|
||||
|
||||
if (allocationsLogLength >= maxAllocationsLogLength) {
|
||||
js_delete(allocationsLog.getFirst());
|
||||
} else {
|
||||
allocationsLogLength++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
Debugger::emptyAllocationsLog()
|
||||
{
|
||||
while (!allocationsLog.isEmpty())
|
||||
js_delete(allocationsLog.getFirst());
|
||||
allocationsLogLength = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*** Debugger JSObjects **************************************************************************/
|
||||
|
||||
@ -1634,6 +1706,12 @@ Debugger::trace(JSTracer *trc)
|
||||
MarkObject(trc, &frameobj, "live Debugger.Frame");
|
||||
}
|
||||
|
||||
/*
|
||||
* Mark every allocation site in our allocation log.
|
||||
*/
|
||||
for (AllocationSite *s = allocationsLog.getFirst(); s; s = s->getNext())
|
||||
MarkObject(trc, &s->frame, "allocation log SavedFrame");
|
||||
|
||||
/* Trace the weak map from JSScript instances to Debugger.Script objects. */
|
||||
scripts.trace(trc);
|
||||
|
||||
|
@ -14,14 +14,17 @@
|
||||
#include "jscntxt.h"
|
||||
#include "jscompartment.h"
|
||||
#include "jsweakmap.h"
|
||||
#include "jswrapper.h"
|
||||
|
||||
#include "gc/Barrier.h"
|
||||
#include "js/HashTable.h"
|
||||
#include "vm/GlobalObject.h"
|
||||
#include "vm/SavedStacks.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
class Breakpoint;
|
||||
class DebuggerMemory;
|
||||
|
||||
/*
|
||||
* A weakmap that supports the keys being in different compartments to the
|
||||
@ -161,6 +164,7 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
|
||||
friend class DebuggerMemory;
|
||||
friend class mozilla::LinkedListElement<Debugger>;
|
||||
friend bool (::JS_DefineDebuggerObject)(JSContext *cx, JS::HandleObject obj);
|
||||
friend bool SavedStacksMetadataCallback(JSContext *cx, JSObject **pmetadata);
|
||||
|
||||
public:
|
||||
enum Hook {
|
||||
@ -190,9 +194,25 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
|
||||
GlobalObjectSet debuggees; /* Debuggee globals. Cross-compartment weak references. */
|
||||
js::HeapPtrObject uncaughtExceptionHook; /* Strong reference. */
|
||||
bool enabled;
|
||||
bool trackingAllocationSites;
|
||||
JSCList breakpoints; /* Circular list of all js::Breakpoints in this debugger */
|
||||
|
||||
struct AllocationSite : public mozilla::LinkedListElement<AllocationSite>
|
||||
{
|
||||
AllocationSite(HandleObject frame) : frame(frame) {
|
||||
JS_ASSERT(UncheckedUnwrap(frame)->is<SavedFrame>());
|
||||
};
|
||||
RelocatablePtrObject frame;
|
||||
};
|
||||
typedef mozilla::LinkedList<AllocationSite> AllocationSiteList;
|
||||
|
||||
bool trackingAllocationSites;
|
||||
AllocationSiteList allocationsLog;
|
||||
size_t allocationsLogLength;
|
||||
size_t maxAllocationsLogLength;
|
||||
static const size_t DEFAULT_MAX_ALLOCATIONS_LOG_LENGTH = 5000;
|
||||
bool appendAllocationSite(JSContext *cx, HandleSavedFrame frame);
|
||||
void emptyAllocationsLog();
|
||||
|
||||
/*
|
||||
* If this Debugger is enabled, and has a onNewGlobalObject handler, then
|
||||
* this link is inserted into the circular list headed by
|
||||
@ -359,6 +379,8 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
|
||||
static void slowPathOnNewScript(JSContext *cx, HandleScript script,
|
||||
GlobalObject *compileAndGoGlobal);
|
||||
static void slowPathOnNewGlobalObject(JSContext *cx, Handle<GlobalObject *> global);
|
||||
static bool slowPathOnLogAllocationSite(JSContext *cx, HandleSavedFrame frame,
|
||||
GlobalObject::DebuggerVector &dbgs);
|
||||
static JSTrapStatus dispatchHook(JSContext *cx, MutableHandleValue vp, Hook which);
|
||||
|
||||
JSTrapStatus fireDebuggerStatement(JSContext *cx, MutableHandleValue vp);
|
||||
@ -408,6 +430,9 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
|
||||
static inline Debugger *fromJSObject(JSObject *obj);
|
||||
static Debugger *fromChildJSObject(JSObject *obj);
|
||||
|
||||
bool hasMemory() const;
|
||||
DebuggerMemory &memory() const;
|
||||
|
||||
/*********************************** Methods for interaction with the GC. */
|
||||
|
||||
/*
|
||||
@ -441,6 +466,7 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
|
||||
static inline void onNewScript(JSContext *cx, HandleScript script,
|
||||
GlobalObject *compileAndGoGlobal);
|
||||
static inline void onNewGlobalObject(JSContext *cx, Handle<GlobalObject *> global);
|
||||
static inline bool onLogAllocationSite(JSContext *cx, HandleSavedFrame frame);
|
||||
static JSTrapStatus onTrap(JSContext *cx, MutableHandleValue vp);
|
||||
static JSTrapStatus onSingleStep(JSContext *cx, MutableHandleValue vp);
|
||||
static bool handleBaselineOsr(JSContext *cx, InterpreterFrame *from, jit::BaselineFrame *to);
|
||||
@ -758,6 +784,15 @@ Debugger::onNewGlobalObject(JSContext *cx, Handle<GlobalObject *> global)
|
||||
Debugger::slowPathOnNewGlobalObject(cx, global);
|
||||
}
|
||||
|
||||
bool
|
||||
Debugger::onLogAllocationSite(JSContext *cx, HandleSavedFrame frame)
|
||||
{
|
||||
GlobalObject::DebuggerVector *dbgs = frame->global().getDebuggers();
|
||||
if (!dbgs || dbgs->empty())
|
||||
return true;
|
||||
return Debugger::slowPathOnLogAllocationSite(cx, frame, *dbgs);
|
||||
}
|
||||
|
||||
extern bool
|
||||
EvaluateInEnv(JSContext *cx, Handle<Env*> env, HandleValue thisv, AbstractFramePtr frame,
|
||||
mozilla::Range<const jschar> chars, const char *filename, unsigned lineno,
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "vm/DebuggerMemory.h"
|
||||
|
||||
#include "jscompartment.h"
|
||||
#include "gc/Marking.h"
|
||||
#include "vm/Debugger.h"
|
||||
#include "vm/GlobalObject.h"
|
||||
#include "vm/SavedStacks.h"
|
||||
@ -31,6 +32,13 @@ DebuggerMemory::create(JSContext *cx, Debugger *dbg)
|
||||
return &memory->as<DebuggerMemory>();
|
||||
}
|
||||
|
||||
Debugger *
|
||||
DebuggerMemory::getDebugger()
|
||||
{
|
||||
const Value &dbgVal = getReservedSlot(JSSLOT_DEBUGGER);
|
||||
return Debugger::fromJSObject(&dbgVal.toObject());
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
DebuggerMemory::construct(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
@ -51,12 +59,6 @@ DebuggerMemory::construct(JSContext *cx, unsigned argc, Value *vp)
|
||||
JS_EnumerateStub, // enumerate
|
||||
JS_ResolveStub, // resolve
|
||||
JS_ConvertStub, // convert
|
||||
|
||||
nullptr, // finalize
|
||||
nullptr, // call
|
||||
nullptr, // hasInstance
|
||||
nullptr, // construct
|
||||
nullptr // trace
|
||||
};
|
||||
|
||||
/* static */ DebuggerMemory *
|
||||
@ -108,12 +110,6 @@ DebuggerMemory::checkThis(JSContext *cx, CallArgs &args, const char *fnName)
|
||||
if (!memory) \
|
||||
return false
|
||||
|
||||
Debugger *
|
||||
DebuggerMemory::getDebugger()
|
||||
{
|
||||
return Debugger::fromJSObject(&getReservedSlot(JSSLOT_DEBUGGER).toObject());
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
DebuggerMemory::setTrackingAllocationSites(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
@ -149,6 +145,9 @@ DebuggerMemory::setTrackingAllocationSites(JSContext *cx, unsigned argc, Value *
|
||||
}
|
||||
}
|
||||
|
||||
if (!enabling)
|
||||
dbg->emptyAllocationsLog();
|
||||
|
||||
dbg->trackingAllocationSites = enabling;
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
@ -162,11 +161,81 @@ DebuggerMemory::getTrackingAllocationSites(JSContext *cx, unsigned argc, Value *
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
DebuggerMemory::drainAllocationsLog(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
THIS_DEBUGGER_MEMORY(cx, argc, vp, "drainAllocationsLog", args, memory);
|
||||
Debugger* dbg = memory->getDebugger();
|
||||
|
||||
if (!dbg->trackingAllocationSites) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_TRACKING_ALLOCATIONS,
|
||||
"drainAllocationsLog");
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t length = dbg->allocationsLogLength;
|
||||
|
||||
RootedObject result(cx, NewDenseAllocatedArray(cx, length));
|
||||
if (!result)
|
||||
return false;
|
||||
result->ensureDenseInitializedLength(cx, 0, length);
|
||||
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
Debugger::AllocationSite *allocSite = dbg->allocationsLog.popFirst();
|
||||
result->setDenseElement(i, ObjectValue(*allocSite->frame));
|
||||
js_delete(allocSite);
|
||||
}
|
||||
|
||||
dbg->allocationsLogLength = 0;
|
||||
args.rval().setObject(*result);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
DebuggerMemory::getMaxAllocationsLogLength(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
THIS_DEBUGGER_MEMORY(cx, argc, vp, "(get maxAllocationsLogLength)", args, memory);
|
||||
args.rval().setInt32(memory->getDebugger()->maxAllocationsLogLength);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
DebuggerMemory::setMaxAllocationsLogLength(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
THIS_DEBUGGER_MEMORY(cx, argc, vp, "(set maxAllocationsLogLength)", args, memory);
|
||||
if (!args.requireAtLeast(cx, "(set maxAllocationsLogLength)", 1))
|
||||
return false;
|
||||
|
||||
int32_t max;
|
||||
if (!ToInt32(cx, args[0], &max))
|
||||
return false;
|
||||
|
||||
if (max < 1) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
|
||||
"(set maxAllocationsLogLength)'s parameter",
|
||||
"not a positive integer");
|
||||
return false;
|
||||
}
|
||||
|
||||
Debugger *dbg = memory->getDebugger();
|
||||
dbg->maxAllocationsLogLength = max;
|
||||
|
||||
while (dbg->allocationsLogLength > dbg->maxAllocationsLogLength) {
|
||||
js_delete(dbg->allocationsLog.getFirst());
|
||||
dbg->allocationsLogLength--;
|
||||
}
|
||||
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ const JSPropertySpec DebuggerMemory::properties[] = {
|
||||
JS_PSGS("trackingAllocationSites", getTrackingAllocationSites, setTrackingAllocationSites, 0),
|
||||
JS_PSGS("maxAllocationsLogLength", getMaxAllocationsLogLength, setMaxAllocationsLogLength, 0),
|
||||
JS_PS_END
|
||||
};
|
||||
|
||||
/* static */ const JSFunctionSpec DebuggerMemory::methods[] = {
|
||||
JS_FN("drainAllocationsLog", DebuggerMemory::drainAllocationsLog, 0, 0),
|
||||
JS_FS_END
|
||||
};
|
||||
|
@ -37,6 +37,9 @@ class DebuggerMemory : public JSObject {
|
||||
|
||||
static bool setTrackingAllocationSites(JSContext *cx, unsigned argc, Value *vp);
|
||||
static bool getTrackingAllocationSites(JSContext *cx, unsigned argc, Value *vp);
|
||||
static bool drainAllocationsLog(JSContext *cx, unsigned argc, Value *vp);
|
||||
static bool setMaxAllocationsLogLength(JSContext*cx, unsigned argc, Value *vp);
|
||||
static bool getMaxAllocationsLogLength(JSContext*cx, unsigned argc, Value *vp);
|
||||
};
|
||||
|
||||
} /* namespace js */
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
#include "gc/Marking.h"
|
||||
#include "js/Vector.h"
|
||||
#include "vm/Debugger.h"
|
||||
#include "vm/GlobalObject.h"
|
||||
#include "vm/StringBuffer.h"
|
||||
|
||||
@ -734,7 +735,8 @@ SavedStacksMetadataCallback(JSContext *cx, JSObject **pmetadata)
|
||||
if (!cx->compartment()->savedStacks().saveCurrentStack(cx, &frame))
|
||||
return false;
|
||||
*pmetadata = frame;
|
||||
return true;
|
||||
|
||||
return Debugger::onLogAllocationSite(cx, frame);
|
||||
}
|
||||
|
||||
#ifdef JS_CRASH_DIAGNOSTICS
|
||||
|
Loading…
Reference in New Issue
Block a user