Bug 588537 - Inject probe points all over the JS tree (r=gal)
authorSteve Fink <sfink@mozilla.com>
Mon, 20 Sep 2010 12:43:53 -0700
changeset 72698 3774d9ba026540b2d044c2fad7c059a42eb736ce
parent 72697 d5e9f04a9f4486640431479ae00838478317f168
child 72699 5562d165fe2bc952acaae8281b5a341ec7e165a8
push idunknown
push userunknown
push dateunknown
reviewersgal
bugs588537
milestone8.0a1
Bug 588537 - Inject probe points all over the JS tree (r=gal)
js/src/jsapi.cpp
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jsgc.cpp
js/src/jsinterp.cpp
js/src/jsobj.cpp
js/src/jsobjinlines.h
js/src/jsparse.cpp
js/src/jsstr.cpp
js/src/jstracer.cpp
js/src/jstracer.h
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -71,16 +71,17 @@
 #include "jsiter.h"
 #include "jslock.h"
 #include "jsmath.h"
 #include "jsnum.h"
 #include "json.h"
 #include "jsobj.h"
 #include "jsopcode.h"
 #include "jsparse.h"
+#include "jsprobes.h"
 #include "jsproxy.h"
 #include "jsregexp.h"
 #include "jsscope.h"
 #include "jsscript.h"
 #include "jsstr.h"
 #include "jstracer.h"
 #include "prmjtime.h"
 #include "jsstaticcheck.h"
@@ -2712,17 +2713,19 @@ JS_RemoveExternalStringFinalizer(JSStrin
 {
     return JSExternalString::changeFinalizer(finalizer, NULL);
 }
 
 JS_PUBLIC_API(JSString *)
 JS_NewExternalString(JSContext *cx, const jschar *chars, size_t length, intN type)
 {
     CHECK_REQUEST(cx);
-    return JSExternalString::new_(cx, chars, length, type, NULL);
+    JSString *s = JSExternalString::new_(cx, chars, length, type, NULL);
+    Probes::createString(cx, s, length);
+    return s;
 }
 
 extern JS_PUBLIC_API(JSString *)
 JS_NewExternalStringWithClosure(JSContext *cx, const jschar *chars, size_t length,
                                 intN type, void *closure)
 {
     CHECK_REQUEST(cx);
     return JSExternalString::new_(cx, chars, length, type, closure);
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -144,16 +144,30 @@ ThreadData::triggerOperationCallback(JSR
     /* rt->interruptCounter does not reflect suspended threads. */
     if (requestDepth != 0)
         JS_ATOMIC_INCREMENT(&rt->interruptCounter);
 #endif
 }
 
 } /* namespace js */
 
+JSScript *
+js_GetCurrentScript(JSContext *cx)
+{
+#ifdef JS_TRACER
+    VOUCH_DOES_NOT_REQUIRE_STACK();
+    if (JS_ON_TRACE(cx)) {
+        VMSideExit *bailExit = JS_TRACE_MONITOR_ON_TRACE(cx)->bailExit;
+        return bailExit ? bailExit->script : NULL;
+    }
+#endif
+    return cx->hasfp() ? cx->fp()->maybeScript() : NULL;
+}
+
+
 #ifdef JS_THREADSAFE
 
 JSThread *
 js_CurrentThreadAndLockGC(JSRuntime *rt)
 {
     void *id = js_CurrentThreadId();
     JS_LOCK_GC(rt);
 
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -2278,16 +2278,19 @@ TriggerAllOperationCallbacks(JSRuntime *
 } /* namespace js */
 
 extern js::StackFrame *
 js_GetScriptedCaller(JSContext *cx, js::StackFrame *fp);
 
 extern jsbytecode*
 js_GetCurrentBytecodePC(JSContext* cx);
 
+extern JSScript *
+js_GetCurrentScript(JSContext* cx);
+
 extern bool
 js_CurrentPCIsInImacro(JSContext *cx);
 
 namespace js {
 
 extern JS_FORCES_STACK JS_FRIEND_API(void)
 LeaveTrace(JSContext *cx);
 
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -364,16 +364,17 @@ Chunk::allocateArena(JSContext *cx, unsi
     JSCompartment *comp = cx->compartment;
     JS_ASSERT(hasAvailableArenas());
     ArenaHeader *aheader = info.emptyArenaListHead;
     info.emptyArenaListHead = aheader->next;
     aheader->init(comp, thingKind, thingSize);
     --info.numFree;
 
     JSRuntime *rt = info.runtime;
+    Probes::resizeHeap(comp, rt->gcBytes, rt->gcBytes + ArenaSize);
     JS_ATOMIC_ADD(&rt->gcBytes, ArenaSize);
     JS_ATOMIC_ADD(&comp->gcBytes, ArenaSize);
     if (comp->gcBytes >= comp->gcTriggerBytes)
         TriggerCompartmentGC(comp);
 
     return aheader;
 }
 
@@ -383,16 +384,17 @@ Chunk::releaseArena(ArenaHeader *aheader
     JSRuntime *rt = info.runtime;
 #ifdef JS_THREADSAFE
     Maybe<AutoLockGC> maybeLock;
     if (rt->gcHelperThread.sweeping)
         maybeLock.construct(info.runtime);
 #endif
     JSCompartment *comp = aheader->compartment;
 
+    Probes::resizeHeap(comp, rt->gcBytes, rt->gcBytes - ArenaSize);
     JS_ASSERT(size_t(rt->gcBytes) >= ArenaSize);
     JS_ASSERT(size_t(comp->gcBytes) >= ArenaSize);
 #ifdef JS_THREADSAFE
     if (rt->gcHelperThread.sweeping) {
         rt->reduceGCTriggerBytes(GC_HEAP_GROWTH_FACTOR * ArenaSize);
         comp->reduceGCTriggerBytes(GC_HEAP_GROWTH_FACTOR * ArenaSize);
     }
 #endif
@@ -2194,16 +2196,17 @@ SweepCompartments(JSContext *cx, JSGCInv
 
     while (read < end) {
         JSCompartment *compartment = *read++;
 
         if (!compartment->hold &&
             (compartment->arenaListsAreEmpty() || gckind == GC_LAST_CONTEXT))
         {
             compartment->freeLists.checkEmpty();
+            Probes::GCEndSweepPhase(compartment);
             if (callback)
                 JS_ALWAYS_TRUE(callback(cx, compartment, JSCOMPARTMENT_DESTROY));
             if (compartment->principals)
                 JSPRINCIPALS_DROP(cx, compartment->principals);
             cx->delete_(compartment);
             continue;
         }
         *write++ = compartment;
@@ -2346,29 +2349,39 @@ MarkAndSweep(JSContext *cx, JSCompartmen
         comp->sweep(cx, 0);
         comp->finalizeObjectArenaLists(cx);
         GCTIMESTAMP(sweepObjectEnd);
         comp->finalizeStringArenaLists(cx);
         GCTIMESTAMP(sweepStringEnd);
         comp->finalizeShapeArenaLists(cx);
         GCTIMESTAMP(sweepShapeEnd);
     } else {
+        /*
+         * Some sweeping is not compartment-specific. Start a NULL-compartment
+         * phase to demarcate all of that. (The compartment sweeps will nest
+         * within.)
+         */
+        Probes::GCStartSweepPhase(NULL);
         SweepCrossCompartmentWrappers(cx);
-        for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); c++)
+        for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); c++) {
+            Probes::GCStartSweepPhase(*c);
             (*c)->finalizeObjectArenaLists(cx);
+        }
 
         GCTIMESTAMP(sweepObjectEnd);
 
         for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); c++)
             (*c)->finalizeStringArenaLists(cx);
 
         GCTIMESTAMP(sweepStringEnd);
 
-        for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); c++)
+        for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); c++) {
             (*c)->finalizeShapeArenaLists(cx);
+            Probes::GCEndSweepPhase(*c);
+        }
 
         GCTIMESTAMP(sweepShapeEnd);
     }
 
 #ifdef DEBUG
      PropertyTree::dumpShapes(cx);
 #endif
 
@@ -2377,16 +2390,19 @@ MarkAndSweep(JSContext *cx, JSCompartmen
 
         /*
          * Sweep script filenames after sweeping functions in the generic loop
          * above. In this way when a scripted function's finalizer destroys the
          * script and calls rt->destroyScriptHook, the hook can still access the
          * script's filename. See bug 323267.
          */
         js_SweepScriptFilenames(rt);
+
+        /* non-compartmental sweep pieces */
+        Probes::GCEndSweepPhase(NULL);
     }
 
 #ifndef JS_THREADSAFE
     /*
      * Destroy arenas after we finished the sweeping so finalizers can safely
      * use IsAboutToBeFinalized().
      * This is done on the GCHelperThread if JS_THREADSAFE is defined.
      */
@@ -2691,16 +2707,26 @@ js_GC(JSContext *cx, JSCompartment *comp
 
     GCCrashData crashData;
     crashData.isRegen = rt->shapeGen & SHAPE_OVERFLOW_BIT;
     crashData.isCompartment = !!comp;
     js_SaveCrashData(crash::JS_CRASH_TAG_GC, &crashData, sizeof(crashData));
 
     GCTIMER_BEGIN(rt, comp);
 
+    struct AutoGCProbe {
+        JSCompartment *comp;
+        AutoGCProbe(JSCompartment *comp) : comp(comp) {
+            Probes::GCStart(comp);
+        }
+        ~AutoGCProbe() {
+            Probes::GCEnd(comp); /* background thread may still be sweeping */
+        }
+    } autoGCProbe(comp);
+
     do {
         /*
          * Let the API user decide to defer a GC if it wants to (unless this
          * is the last context).  Invoke the callback regardless. Sample the
          * callback in case we are freely racing with a JS_SetGCCallback{,RT}
          * on another thread.
          */
         if (JSGCCallback callback = rt->gcCallback) {
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -1196,17 +1196,20 @@ InvokeConstructor(JSContext *cx, const C
     if (args.calleev().isObject()) {
         JSObject *callee = &args.callee();
         Class *clasp = callee->getClass();
         if (clasp == &js_FunctionClass) {
             JSFunction *fun = callee->getFunctionPrivate();
 
             if (fun->isConstructor()) {
                 args.thisv().setMagicWithObjectOrNullPayload(NULL);
-                return CallJSNativeConstructor(cx, fun->u.n.native, args);
+            Probes::calloutBegin(cx, fun);
+            bool ok = CallJSNativeConstructor(cx, fun->u.n.native, args);
+            Probes::calloutEnd(cx, fun);
+            return ok;
             }
 
             if (!fun->isInterpretedConstructor())
                 goto error;
 
             if (!Invoke(cx, args, CONSTRUCT))
                 return false;
 
@@ -1240,17 +1243,19 @@ InvokeConstructorWithGivenThis(JSContext
 
     /* Handle the fast-constructor cases before calling the general case. */
     JSObject &callee = fval.toObject();
     Class *clasp = callee.getClass();
     JSFunction *fun;
     bool ok;
     if (clasp == &js_FunctionClass && (fun = callee.getFunctionPrivate())->isConstructor()) {
         args.thisv().setMagicWithObjectOrNullPayload(thisobj);
+        Probes::calloutBegin(cx, fun);
         ok = CallJSNativeConstructor(cx, fun->u.n.native, args);
+        Probes::calloutEnd(cx, fun);
     } else if (clasp->construct) {
         args.thisv().setMagicWithObjectOrNullPayload(thisobj);
         ok = CallJSNativeConstructor(cx, clasp->construct, args);
     } else {
         args.thisv().setObjectOrNull(thisobj);
         ok = Invoke(cx, args, CONSTRUCT);
     }
 
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -65,16 +65,17 @@
 #include "jsinterp.h"
 #include "jsiter.h"
 #include "jslock.h"
 #include "jsnum.h"
 #include "jsobj.h"
 #include "jsonparser.h"
 #include "jsopcode.h"
 #include "jsparse.h"
+#include "jsprobes.h"
 #include "jsproxy.h"
 #include "jsscope.h"
 #include "jsscript.h"
 #include "jsstaticcheck.h"
 #include "jsstdint.h"
 #include "jsstr.h"
 #include "jstracer.h"
 #include "jsdbgapi.h"
@@ -95,17 +96,16 @@
 #if JS_HAS_XML_SUPPORT
 #include "jsxml.h"
 #endif
 
 #if JS_HAS_XDR
 #include "jsxdrapi.h"
 #endif
 
-#include "jsprobes.h"
 #include "jsatominlines.h"
 #include "jsobjinlines.h"
 #include "jsscriptinlines.h"
 
 #include "jsautooplen.h"
 
 using namespace js;
 using namespace js::gc;
@@ -3956,16 +3956,17 @@ js_InitClass(JSContext *cx, JSObject *ob
     return DefineConstructorAndPrototype(cx, obj, key, atom, protoProto, clasp, constructor, nargs,
                                          ps, fs, static_ps, static_fs);
 }
 
 bool
 JSObject::allocSlots(JSContext *cx, size_t newcap)
 {
     uint32 oldcap = numSlots();
+    size_t oldSize = slotsAndStructSize();
 
     JS_ASSERT(newcap >= oldcap && !hasSlotsArray());
 
     if (newcap > NSLOTS_LIMIT) {
         if (!JS_ON_TRACE(cx))
             js_ReportAllocationOverflow(cx);
         return false;
     }
@@ -3974,16 +3975,18 @@ JSObject::allocSlots(JSContext *cx, size
     if (!tmpslots)
         return false;  /* Leave slots at inline buffer. */
     slots = tmpslots;
     capacity = newcap;
 
     /* Copy over anything from the inline buffer. */
     memcpy(slots, fixedSlots(), oldcap * sizeof(Value));
     ClearValueRange(slots + oldcap, newcap - oldcap, isDenseArray());
+    Probes::resizeObject(cx, this, oldSize, slotsAndStructSize());
+
     return true;
 }
 
 bool
 JSObject::growSlots(JSContext *cx, size_t newcap)
 {
     /*
      * When an object with CAPACITY_DOUBLING_MAX or fewer slots needs to
@@ -4016,21 +4019,24 @@ JSObject::growSlots(JSContext *cx, size_
 
     /* If nothing was allocated yet, treat it as initial allocation. */
     if (!hasSlotsArray())
         return allocSlots(cx, actualCapacity);
 
     Value *tmpslots = (Value*) cx->realloc_(slots, oldcap * sizeof(Value), actualCapacity * sizeof(Value));
     if (!tmpslots)
         return false;    /* Leave dslots as its old size. */
+    size_t oldSize = slotsAndStructSize();
     slots = tmpslots;
     capacity = actualCapacity;
 
     /* Initialize the additional slots we added. */
     ClearValueRange(slots + oldcap, actualCapacity - oldcap, isDenseArray());
+    Probes::resizeObject(cx, this, oldSize, slotsAndStructSize());
+
     return true;
 }
 
 void
 JSObject::shrinkSlots(JSContext *cx, size_t newcap)
 {
     uint32 oldcap = numSlots();
     JS_ASSERT(newcap <= oldcap);
@@ -4046,23 +4052,26 @@ JSObject::shrinkSlots(JSContext *cx, siz
     if (newcap < SLOT_CAPACITY_MIN)
         newcap = SLOT_CAPACITY_MIN;
     if (newcap < numFixedSlots())
         newcap = numFixedSlots();
 
     Value *tmpslots = (Value*) cx->realloc_(slots, newcap * sizeof(Value));
     if (!tmpslots)
         return;  /* Leave slots at its old size. */
+    size_t oldSize = slotsAndStructSize();
     slots = tmpslots;
     capacity = newcap;
 
     if (fill < newcap) {
         /* Clear any excess holes if we tried to shrink below SLOT_CAPACITY_MIN. */
         ClearValueRange(slots + fill, newcap - fill, isDenseArray());
     }
+
+    Probes::resizeObject(cx, this, oldSize, slotsAndStructSize());
 }
 
 bool
 JSObject::ensureInstanceReservedSlots(JSContext *cx, size_t nreserved)
 {
     JS_ASSERT_IF(isNative(),
                  isBlock() || isCall() || (isFunction() && isBoundFunction()));
 
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -130,23 +130,23 @@ JSObject::syncSpecialEquality()
 
 inline void
 JSObject::finalize(JSContext *cx)
 {
     /* Cope with stillborn objects that have no map. */
     if (isNewborn())
         return;
 
+    js::Probes::finalizeObject(this);
+
     /* Finalize obj first, in case it needs map and slots. */
     js::Class *clasp = getClass();
     if (clasp->finalize)
         clasp->finalize(cx, this);
 
-    js::Probes::finalizeObject(this);
-
     finish(cx);
 }
 
 /* 
  * Initializer for Call objects for functions and eval frames. Set class,
  * parent, map, and shape, and allocate slots.
  */
 inline void
--- a/js/src/jsparse.cpp
+++ b/js/src/jsparse.cpp
@@ -69,16 +69,17 @@
 #include "jsgcmark.h"
 #include "jsinterp.h"
 #include "jsiter.h"
 #include "jslock.h"
 #include "jsnum.h"
 #include "jsobj.h"
 #include "jsopcode.h"
 #include "jsparse.h"
+#include "jsprobes.h"
 #include "jsscan.h"
 #include "jsscope.h"
 #include "jsscript.h"
 #include "jsstr.h"
 #include "jsstaticcheck.h"
 #include "jslibmath.h"
 #include "jsvector.h"
 
@@ -924,16 +925,18 @@ Compiler::compileScript(JSContext *cx, J
 
     Parser &parser = compiler.parser;
     TokenStream &tokenStream = parser.tokenStream;
 
     JSCodeGenerator cg(&parser, &codePool, &notePool, tokenStream.getLineno());
     if (!cg.init(cx, JSTreeContext::USED_AS_TREE_CONTEXT))
         return NULL;
 
+    Probes::compileScriptBegin(cx, filename, lineno);
+
     MUST_FLOW_THROUGH("out");
 
     // We can specialize a bit for the given scope chain if that scope chain is the global object.
     JSObject *globalObj = scopeChain && scopeChain == scopeChain->getGlobal()
                         ? scopeChain->getGlobal()
                         : NULL;
 
     JS_ASSERT_IF(globalObj, globalObj->isNative());
@@ -1118,16 +1121,17 @@ Compiler::compileScript(JSContext *cx, J
         AutoShapeRooter shapeRoot(cx, script->bindings.lastShape());
         if (!defineGlobals(cx, globalScope, script))
             goto late_error;
     }
 
   out:
     JS_FinishArenaPool(&codePool);
     JS_FinishArenaPool(&notePool);
+    Probes::compileScriptEnd(cx, script, filename, lineno);
     return script;
 
   too_many_slots:
     parser.reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_TOO_MANY_LOCALS);
     /* Fall through. */
 
   late_error:
     if (script) {
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -62,16 +62,17 @@
 #include "jsbuiltins.h"
 #include "jscntxt.h"
 #include "jsgc.h"
 #include "jsinterp.h"
 #include "jslock.h"
 #include "jsnum.h"
 #include "jsobj.h"
 #include "jsopcode.h"
+#include "jsprobes.h"
 #include "jsregexp.h"
 #include "jsscope.h"
 #include "jsstaticcheck.h"
 #include "jsstr.h"
 #include "jsbit.h"
 #include "jsvector.h"
 #include "jsversion.h"
 
@@ -3218,17 +3219,19 @@ js_InitStringClass(JSContext *cx, JSObje
 }
 
 JSFixedString *
 js_NewString(JSContext *cx, jschar *chars, size_t length)
 {
     if (!CheckStringLength(cx, length))
         return NULL;
 
-    return JSFixedString::new_(cx, chars, length);
+    JSFixedString *s = JSFixedString::new_(cx, chars, length);
+    Probes::createString(cx, s, length);
+    return s;
 }
 
 static JS_ALWAYS_INLINE JSFixedString *
 NewShortString(JSContext *cx, const jschar *chars, size_t length)
 {
     /*
      * Don't bother trying to find a static atom; measurement shows that not
      * many get here (for one, Atomize is catching them).
@@ -3239,16 +3242,17 @@ NewShortString(JSContext *cx, const jsch
                           ? JSInlineString::new_(cx)
                           : JSShortString::new_(cx);
     if (!str)
         return NULL;
 
     jschar *storage = str->init(length);
     PodCopy(storage, chars, length);
     storage[length] = 0;
+    Probes::createString(cx, str, length);
     return str;
 }
 
 static JSInlineString *
 NewShortString(JSContext *cx, const char *chars, size_t length)
 {
     JS_ASSERT(JSShortString::lengthFits(length));
     JSInlineString *str = JSInlineString::lengthFits(length)
@@ -3269,16 +3273,17 @@ NewShortString(JSContext *cx, const char
         str->resetLength(length);
     } else {
         size_t n = length;
         jschar *p = storage;
         while (n--)
             *p++ = (unsigned char)*chars++;
         *p = 0;
     }
+    Probes::createString(cx, str, length);
     return str;
 }
 
 jschar *
 StringBuffer::extractWellSized()
 {
     size_t capacity = cb.capacity();
     size_t length = cb.length();
@@ -3359,17 +3364,19 @@ js_NewDependentString(JSContext *cx, JSS
     if (start == 0 && length == base->length())
         return base;
 
     const jschar *chars = base->chars() + start;
 
     if (JSLinearString *staticStr = JSAtom::lookupStatic(chars, length))
         return staticStr;
 
-    return JSDependentString::new_(cx, base, chars, length);
+    JSLinearString *s = JSDependentString::new_(cx, base, chars, length);
+    Probes::createString(cx, s, length);
+    return s;
 }
 
 JSFixedString *
 js_NewStringCopyN(JSContext *cx, const jschar *s, size_t n)
 {
     if (JSShortString::lengthFits(n))
         return NewShortString(cx, s, n);
 
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -4400,16 +4400,17 @@ TraceRecorder::snapshot(ExitType exitTyp
     exit->calldepth = callDepth;
     exit->numGlobalSlots = ngslots;
     exit->numStackSlots = stackSlots;
     exit->numStackSlotsBelowCurrentFrame = cx->fp()->isFunctionFrame() ?
                                            nativeStackOffset(&cx->fp()->calleev()) / sizeof(double) :
                                            0;
     exit->exitType = exitType;
     exit->pc = pc;
+    exit->script = fp->maybeScript();
     exit->imacpc = fp->maybeImacropc();
     exit->sp_adj = (stackSlots * sizeof(double)) - tree->nativeStackBase;
     exit->rp_adj = exit->calldepth * sizeof(FrameInfo*);
     exit->lookupFlags = js_InferFlags(cx, 0);
     memcpy(exit->fullTypeMap(), typemap, typemap_size);
 
 #if defined JS_JIT_SPEW
     TreevisLogExit(cx, exit);
--- a/js/src/jstracer.h
+++ b/js/src/jstracer.h
@@ -359,16 +359,17 @@ enum ExitType {
     #undef MAKE_EXIT_CODE
     TOTAL_EXIT_TYPES
 };
 
 struct FrameInfo;
 
 struct VMSideExit : public nanojit::SideExit
 {
+    JSScript* script;
     jsbytecode* pc;
     jsbytecode* imacpc;
     intptr_t sp_adj;
     intptr_t rp_adj;
     int32_t calldepth;
     uint32 numGlobalSlots;
     uint32 numStackSlots;
     uint32 numStackSlotsBelowCurrentFrame;