Bug 961288 - Add an object metadata callback for tracking object allocation sites. r=ejpbruel
authorNick Fitzgerald <fitzgen@mozilla.com>
Wed, 30 Apr 2014 11:59:00 -0400
changeset 181616 887b927cce10d2007a6ed06ece9ecdb62166a60d
parent 181615 3dc0e538254ddc9bbce41511e677abec06e3a28a
child 181617 2b6ce664867550950407ce0d27e249d668c877a0
push id272
push userpvanderbeken@mozilla.com
push dateMon, 05 May 2014 16:31:18 +0000
reviewersejpbruel
bugs961288
milestone32.0a1
Bug 961288 - Add an object metadata callback for tracking object allocation sites. r=ejpbruel
js/src/builtin/TestingFunctions.cpp
js/src/jit-test/tests/basic/track-allocation-sites.js
js/src/vm/SavedStacks.cpp
js/src/vm/SavedStacks.h
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -865,16 +865,30 @@ SaveStack(JSContext *cx, unsigned argc, 
     CallArgs args = CallArgsFromVp(argc, vp);
     Rooted<SavedFrame*> frame(cx);
     if (!cx->compartment()->savedStacks().saveCurrentStack(cx, &frame))
         return false;
     args.rval().setObject(*frame.get());
     return true;
 }
 
+static bool
+EnableTrackAllocations(JSContext *cx, unsigned argc, jsval *vp)
+{
+    SetObjectMetadataCallback(cx, SavedStacksMetadataCallback);
+    return true;
+}
+
+static bool
+DisableTrackAllocations(JSContext *cx, unsigned argc, jsval *vp)
+{
+    SetObjectMetadataCallback(cx, nullptr);
+    return true;
+}
+
 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
 static bool
 OOMAfterAllocations(JSContext *cx, unsigned argc, jsval *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() != 1) {
         JS_ReportError(cx, "count argument required");
         return false;
@@ -1626,16 +1640,26 @@ static const JSFunctionSpecWithHelp Test
 "getSavedFrameCount()",
 "  Return the number of SavedFrame instances stored in this compartment's\n"
 "  SavedStacks cache."),
 
     JS_FN_HELP("saveStack", SaveStack, 0, 0,
 "saveStack()",
 "  Capture a stack.\n"),
 
+    JS_FN_HELP("enableTrackAllocations", EnableTrackAllocations, 0, 0,
+"enableTrackAllocations()",
+"  Start capturing the JS stack at every allocation. Note that this sets an "
+"  object metadata callback that will override any other object metadata "
+"  callback that may be set."),
+
+    JS_FN_HELP("disableTrackAllocations", DisableTrackAllocations, 0, 0,
+"disableTrackAllocations()",
+"  Stop capturing the JS stack at every allocation."),
+
 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
     JS_FN_HELP("oomAfterAllocations", OOMAfterAllocations, 1, 0,
 "oomAfterAllocations(count)",
 "  After 'count' js_malloc memory allocations, fail every following allocation\n"
 "  (return NULL)."),
 #endif
 
     JS_FN_HELP("makeFinalizeObserver", MakeFinalizeObserver, 0, 0,
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/track-allocation-sites.js
@@ -0,0 +1,24 @@
+// Test that we can track allocation sites.
+
+enableTrackAllocations();
+
+const tests = [
+  { name: "object literal",  object: {},                    line: Error().lineNumber },
+  { name: "array literal",   object: [],                    line: Error().lineNumber },
+  { name: "regexp literal",  object: /(two|2)\s*problems/,  line: Error().lineNumber },
+  { name: "new constructor", object: new function Ctor(){}, line: Error().lineNumber },
+  { name: "new Object",      object: new Object(),          line: Error().lineNumber },
+  { name: "new Array",       object: new Array(),           line: Error().lineNumber },
+  { name: "new Date",        object: new Date(),            line: Error().lineNumber }
+];
+
+disableTrackAllocations();
+
+for (let { name, object, line } of tests) {
+  print("Entering test: " + name);
+
+  let allocationSite = getObjectMetadata(object);
+  print(allocationSite);
+
+  assertEq(allocationSite.line, line);
+}
--- a/js/src/vm/SavedStacks.cpp
+++ b/js/src/vm/SavedStacks.cpp
@@ -504,9 +504,19 @@ SavedStacks::createFrameFromLookup(JSCon
         return nullptr;
 
     SavedFrame &f = frameObj->as<SavedFrame>();
     f.initFromLookup(lookup);
 
     return &f;
 }
 
+bool
+SavedStacksMetadataCallback(JSContext *cx, JSObject **pmetadata)
+{
+    Rooted<SavedFrame *> frame(cx);
+    if (!cx->compartment()->savedStacks().saveCurrentStack(cx, &frame))
+        return false;
+    *pmetadata = frame;
+    return true;
+}
+
 } /* namespace js */
--- a/js/src/vm/SavedStacks.h
+++ b/js/src/vm/SavedStacks.h
@@ -134,11 +134,13 @@ class SavedStacks {
     bool       insertFrames(JSContext *cx, ScriptFrameIter &iter, MutableHandle<SavedFrame*> frame);
     SavedFrame *getOrCreateSavedFrame(JSContext *cx, SavedFrame::Lookup &lookup);
     // |SavedFrame.prototype| is created lazily and held weakly. It should only
     // be accessed through this method.
     JSObject   *getOrCreateSavedFramePrototype(JSContext *cx);
     SavedFrame *createFrameFromLookup(JSContext *cx, SavedFrame::Lookup &lookup);
 };
 
+bool SavedStacksMetadataCallback(JSContext *cx, JSObject **pmetadata);
+
 } /* namespace js */
 
 #endif /* vm_SavedStacks_h */