Bug 1154079 - Add the allocated object's [[class]] name to the allocations log. r=shu

This commit is contained in:
Nick Fitzgerald 2015-04-17 15:22:00 +02:00
parent 67e5382f15
commit 6175651f60
11 changed files with 79 additions and 26 deletions

View File

@ -1423,7 +1423,7 @@ DisplayName(JSContext* cx, unsigned argc, jsval* vp)
}
static JSObject*
ShellObjectMetadataCallback(JSContext* cx)
ShellObjectMetadataCallback(JSContext* cx, JSObject*)
{
RootedObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx));
if (!obj)

View File

@ -198,14 +198,16 @@ Function Properties of the `Debugger.Memory.prototype` Object
<pre class='language-js'><code>
{
"timestamp": <i>timestamp</i>,
"frame": <i>allocationSite</i>
"frame": <i>allocationSite</i>,
"class": <i>className</i>
}
</code></pre>
Here <i>timestamp</i> is the [timestamp][timestamps] of the allocation event and
<i>allocationSite</i> is an allocation site (as a
[captured stack][saved-frame]). <i>allocationSite</i> is `null` for objects
allocated with no JavaScript frames on the stack.
Here <i>timestamp</i> is the [timestamp][timestamps] of the allocation
event, <i>allocationSite</i> is an allocation site (as a
[captured stack][saved-frame]), and <i>className</i> is the string name of
the allocated object's internal `[[Class]]` property. <i>allocationSite</i>
is `null` for objects allocated with no JavaScript frames on the stack.
When `trackingAllocationSites` is `false`, `drainAllocationsLog()` throws an
`Error`.

View File

@ -0,0 +1,31 @@
// Test drainAllocationsLog() and [[Class]] names.
const root = newGlobal();
const dbg = new Debugger();
const wrappedRoot = dbg.addDebuggee(root)
root.eval(
`
this.tests = [
{ expected: "Object", test: () => new Object },
{ expected: "Array", test: () => [] },
{ expected: "Date", test: () => new Date },
{ expected: "RegExp", test: () => /problems/ },
{ expected: "Int8Array", test: () => new Int8Array },
{ expected: "Promise", test: makeFakePromise },
];
`
);
for (let { expected, test } of root.tests) {
print(expected);
dbg.memory.trackingAllocationSites = true;
test();
let allocs = dbg.memory.drainAllocationsLog();
dbg.memory.trackingAllocationSites = false;
assertEq(allocs.some(a => a.class === expected), true);
}

View File

@ -713,7 +713,7 @@ JSCompartment::setNewObjectMetadata(JSContext* cx, JSObject* obj)
{
MOZ_ASSERT(this == cx->compartment());
if (JSObject* metadata = objectMetadataCallback(cx)) {
if (JSObject* metadata = objectMetadataCallback(cx, obj)) {
if (!objectMetadataTable) {
objectMetadataTable = cx->new_<ObjectWeakMap>(cx);
if (!objectMetadataTable)

View File

@ -2622,7 +2622,7 @@ class JS_FRIEND_API(AutoCTypesActivityCallback) {
};
typedef JSObject*
(* ObjectMetadataCallback)(JSContext* cx);
(* ObjectMetadataCallback)(JSContext* cx, JSObject* obj);
/*
* Specify a callback to invoke when creating each JS object in the current

View File

@ -246,7 +246,8 @@ SetNewObjectMetadata(ExclusiveContext* cxArg, JSObject* obj)
// callback, and any reentering of JS via Invoke() etc.
AutoEnterAnalysis enter(cx);
cx->compartment()->setNewObjectMetadata(cx, obj);
RootedObject hobj(cx, obj);
cx->compartment()->setNewObjectMetadata(cx, hobj);
}
}
}

View File

@ -1695,8 +1695,8 @@ Debugger::slowPathOnNewGlobalObject(JSContext* cx, Handle<GlobalObject*> global)
}
/* static */ bool
Debugger::slowPathOnLogAllocationSite(JSContext* cx, HandleSavedFrame frame, int64_t when,
GlobalObject::DebuggerVector& dbgs)
Debugger::slowPathOnLogAllocationSite(JSContext* cx, HandleObject obj, HandleSavedFrame frame,
int64_t when, GlobalObject::DebuggerVector& dbgs)
{
MOZ_ASSERT(!dbgs.empty());
mozilla::DebugOnly<Debugger**> begin = dbgs.begin();
@ -1708,7 +1708,7 @@ Debugger::slowPathOnLogAllocationSite(JSContext* cx, HandleSavedFrame frame, int
if ((*dbgp)->trackingAllocationSites &&
(*dbgp)->enabled &&
!(*dbgp)->appendAllocationSite(cx, frame, when))
!(*dbgp)->appendAllocationSite(cx, obj, frame, when))
{
return false;
}
@ -1725,14 +1725,15 @@ Debugger::isDebuggee(const JSCompartment* compartment) const
}
bool
Debugger::appendAllocationSite(JSContext* cx, HandleSavedFrame frame, int64_t when)
Debugger::appendAllocationSite(JSContext* cx, HandleObject obj, HandleSavedFrame frame,
int64_t when)
{
AutoCompartment ac(cx, object);
RootedObject wrapped(cx, frame);
if (!cx->compartment()->wrap(cx, &wrapped))
RootedObject wrappedFrame(cx, frame);
if (!cx->compartment()->wrap(cx, &wrappedFrame))
return false;
AllocationSite* allocSite = cx->new_<AllocationSite>(wrapped, when);
AllocationSite* allocSite = cx->new_<AllocationSite>(wrappedFrame, when, obj->getClass()->name);
if (!allocSite)
return false;

View File

@ -255,11 +255,17 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
struct AllocationSite : public mozilla::LinkedListElement<AllocationSite>
{
AllocationSite(HandleObject frame, int64_t when) : frame(frame), when(when) {
AllocationSite(HandleObject frame, int64_t when, const char* className)
: frame(frame),
when(when),
className(className)
{
MOZ_ASSERT_IF(frame, UncheckedUnwrap(frame)->is<SavedFrame>());
};
RelocatablePtrObject frame;
int64_t when;
const char* className;
};
typedef mozilla::LinkedList<AllocationSite> AllocationSiteList;
@ -305,7 +311,8 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
static const size_t DEFAULT_MAX_ALLOCATIONS_LOG_LENGTH = 5000;
bool appendAllocationSite(JSContext* cx, HandleSavedFrame frame, int64_t when);
bool appendAllocationSite(JSContext* cx, HandleObject obj, HandleSavedFrame frame,
int64_t when);
void emptyAllocationsLog();
/*
@ -515,7 +522,7 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
static JSTrapStatus slowPathOnExceptionUnwind(JSContext* cx, AbstractFramePtr frame);
static void slowPathOnNewScript(JSContext* cx, HandleScript script);
static void slowPathOnNewGlobalObject(JSContext* cx, Handle<GlobalObject*> global);
static bool slowPathOnLogAllocationSite(JSContext* cx, HandleSavedFrame frame,
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 JSTrapStatus dispatchHook(JSContext* cx, MutableHandleValue vp, Hook which,
@ -666,7 +673,8 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
static inline void onNewScript(JSContext* cx, HandleScript script);
static inline void onNewGlobalObject(JSContext* cx, Handle<GlobalObject*> global);
static inline bool onLogAllocationSite(JSContext* cx, HandleSavedFrame frame, int64_t when);
static inline bool onLogAllocationSite(JSContext* cx, JSObject* obj, HandleSavedFrame frame,
int64_t when);
static inline void onGarbageCollection(JSRuntime* rt, const gcstats::Statistics& stats);
static JSTrapStatus onTrap(JSContext* cx, MutableHandleValue vp);
static JSTrapStatus onSingleStep(JSContext* cx, MutableHandleValue vp);
@ -975,12 +983,13 @@ Debugger::onNewGlobalObject(JSContext* cx, Handle<GlobalObject*> global)
}
/* static */ bool
Debugger::onLogAllocationSite(JSContext* cx, HandleSavedFrame frame, int64_t when)
Debugger::onLogAllocationSite(JSContext* cx, JSObject* obj, HandleSavedFrame frame, int64_t when)
{
GlobalObject::DebuggerVector* dbgs = cx->global()->getDebuggers();
if (!dbgs || dbgs->empty())
return true;
return Debugger::slowPathOnLogAllocationSite(cx, frame, when, *dbgs);
RootedObject hobj(cx, obj);
return Debugger::slowPathOnLogAllocationSite(cx, hobj, frame, when, *dbgs);
}
/* static */ void

View File

@ -209,6 +209,13 @@ DebuggerMemory::drainAllocationsLog(JSContext* cx, unsigned argc, Value* vp)
if (!DefineProperty(cx, obj, cx->names().timestamp, timestampValue))
return false;
RootedString className(cx, Atomize(cx, allocSite->className, strlen(allocSite->className)));
if (!className)
return false;
RootedValue classNameValue(cx, StringValue(className));
if (!DefineProperty(cx, obj, cx->names().class_, classNameValue))
return false;
result->setDenseElement(i, ObjectValue(*obj));
// Pop the front queue entry, and delete it immediately, so that

View File

@ -1180,8 +1180,10 @@ SavedStacks::chooseSamplingProbability(JSContext* cx)
}
JSObject*
SavedStacksMetadataCallback(JSContext* cx)
SavedStacksMetadataCallback(JSContext* cx, JSObject* target)
{
RootedObject obj(cx, target);
SavedStacks& stacks = cx->compartment()->savedStacks();
if (stacks.allocationSkipCount > 0) {
stacks.allocationSkipCount--;
@ -1221,7 +1223,7 @@ SavedStacksMetadataCallback(JSContext* cx)
if (!stacks.saveCurrentStack(cx, &frame))
CrashAtUnhandlableOOM("SavedStacksMetadataCallback");
if (!Debugger::onLogAllocationSite(cx, frame, PRMJ_Now()))
if (!Debugger::onLogAllocationSite(cx, obj, frame, PRMJ_Now()))
CrashAtUnhandlableOOM("SavedStacksMetadataCallback");
return frame;

View File

@ -260,7 +260,7 @@ struct SavedFrame::HashPolicy
inline void AssertObjectIsSavedFrameOrWrapper(JSContext* cx, HandleObject stack);
class SavedStacks {
friend JSObject* SavedStacksMetadataCallback(JSContext* cx);
friend JSObject* SavedStacksMetadataCallback(JSContext* cx, JSObject* target);
public:
SavedStacks()
@ -400,7 +400,7 @@ class SavedStacks {
bool getLocation(JSContext* cx, const FrameIter& iter, MutableHandleLocationValue locationp);
};
JSObject* SavedStacksMetadataCallback(JSContext* cx);
JSObject* SavedStacksMetadataCallback(JSContext* cx, JSObject* target);
} /* namespace js */