Instrument mjit code and script-data allocation for reporting in about:memory
authorMike Shaver <shaver@mozilla.org>
Tue, 04 Jan 2011 22:48:46 -0800
changeset 59852 532e6e72dc9ce131af508dd71d51718670f8e49e
parent 59848 d72a6aaf66a4bff4150ce34a0f6e1c390e6cddf6
child 59853 6df0d209b91c5d83fb999ea5d289f645845364d0
push id2
push usershaver@mozilla.com
push dateWed, 05 Jan 2011 09:48:28 +0000
milestone2.0b9pre
Instrument mjit code and script-data allocation for reporting in about:memory
js/src/jscntxt.h
js/src/methodjit/Compiler.cpp
js/src/methodjit/MethodJIT.cpp
js/src/methodjit/MethodJIT.h
js/src/shell/js.cpp
js/src/xpconnect/src/xpcjsruntime.cpp
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -1445,16 +1445,20 @@ struct JSRuntime {
     FunctionCountMap    unjoinedFunctionCountMap;
 #endif
 
     JSWrapObjectCallback wrapObjectCallback;
     JSPreWrapCallback    preWrapObjectCallback;
 
     JSC::ExecutableAllocator *regExpAllocator;
 
+#ifdef JS_METHODJIT
+    uint32               mjitCodeSize;
+#endif
+
     JSRuntime();
     ~JSRuntime();
 
     bool init(uint32 maxbytes);
 
     void setGCTriggerFactor(uint32 factor);
     void setGCLastBytes(size_t lastBytes);
 
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -408,16 +408,17 @@ mjit::Compiler::finishThisUp(JITScript *
 
     size_t nNmapLive = 0;
     for (size_t i = 0; i < script->length; i++) {
         analyze::Bytecode *opinfo = analysis->maybeCode(i);
         if (opinfo && opinfo->safePoint)
             nNmapLive++;
     }
 
+    /* Please keep in sync with JITScript::nonCodeBytes! */
     size_t totalBytes = sizeof(JITScript) +
                         sizeof(NativeMapEntry) * nNmapLive +
 #if defined JS_MONOIC
                         sizeof(ic::MICInfo) * mics.length() +
                         sizeof(ic::CallICInfo) * callICs.length() +
                         sizeof(ic::EqualityICInfo) * equalityICs.length() +
                         sizeof(ic::TraceICInfo) * traceICs.length() +
 #endif
@@ -784,16 +785,19 @@ mjit::Compiler::finishThisUp(JITScript *
     }
 
     JS_ASSERT(size_t(cursor - (uint8*)jit) == totalBytes);
 
     jit->nmap = nmap;
     jit->nNmapPairs = nNmapLive;
     *jitp = jit;
 
+    /* We tolerate a race in the stats. */
+    cx->runtime->mjitCodeSize += totalSize + totalBytes;
+
     return Compile_Okay;
 }
 
 #ifdef DEBUG
 #define SPEW_OPCODE()                                                         \
     JS_BEGIN_MACRO                                                            \
         if (IsJaegerSpewChannelActive(JSpew_JSOps)) {                         \
             JaegerSpew(JSpew_JSOps, "    %2d ", frame.stackDepth());          \
--- a/js/src/methodjit/MethodJIT.cpp
+++ b/js/src/methodjit/MethodJIT.cpp
@@ -829,33 +829,57 @@ mjit::JITScript::~JITScript()
         (*pExecPool)->release();
     }
     
     for (uint32 i = 0; i < nCallICs; i++)
         callICs[i].releasePools();
 #endif
 }
 
+size_t
+mjit::JITScript::nonCodeSize()
+{
+    return sizeof(JITScript) +
+        sizeof(NativeMapEntry) * nNmapPairs +
+#if defined JS_MONOIC
+        sizeof(ic::MICInfo) * nMICs +
+        sizeof(ic::CallICInfo) * nCallICs +
+        sizeof(ic::EqualityICInfo) * nEqualityICs +
+        sizeof(ic::TraceICInfo) * nTraceICs +
+#endif
+#if defined JS_POLYIC
+        sizeof(ic::PICInfo) * nPICs +
+        sizeof(ic::GetElementIC) * nGetElems +
+        sizeof(ic::SetElementIC) * nSetElems +
+#endif
+        sizeof(CallSite) * nCallSites;
+}
+
 void
 mjit::ReleaseScriptCode(JSContext *cx, JSScript *script)
 {
     // NB: The recompiler may call ReleaseScriptCode, in which case it
     // will get called again when the script is destroyed, so we
     // must protect against calling ReleaseScriptCode twice.
+    JITScript *jscr;
 
-    if (script->jitNormal) {
-        script->jitNormal->~JITScript();
-        cx->free(script->jitNormal);
+    if ((jscr = script->jitNormal)) {
+        cx->runtime->mjitCodeSize -= jscr->nonCodeSize() + jscr->codeSize();
+
+        jscr->~JITScript();
+        cx->free(jscr);
         script->jitNormal = NULL;
         script->jitArityCheckNormal = NULL;
     }
 
-    if (script->jitCtor) {
-        script->jitCtor->~JITScript();
-        cx->free(script->jitCtor);
+    if ((jscr = script->jitCtor)) {
+        cx->runtime->mjitCodeSize -= jscr->nonCodeSize() + jscr->codeSize();
+
+        jscr->~JITScript();
+        cx->free(jscr);
         script->jitCtor = NULL;
         script->jitArityCheckCtor = NULL;
     }
 }
 
 #ifdef JS_METHODJIT_PROFILE_STUBS
 void JS_FASTCALL
 mjit::ProfileStubCall(VMFrame &f)
--- a/js/src/methodjit/MethodJIT.h
+++ b/js/src/methodjit/MethodJIT.h
@@ -339,16 +339,20 @@ struct JITScript {
         char *jcheck = (char *)ptr;
         return jcheck >= jitcode && jcheck < jitcode + code.m_size;
     }
 
     void nukeScriptDependentICs();
     void sweepCallICs();
     void purgeMICs();
     void purgePICs();
+
+    size_t nonCodeSize();
+
+    size_t codeSize() { return code.m_size; } /* doesn't account for fragmentation */
 };
 
 /*
  * Execute the given mjit code. This is a low-level call and callers must
  * provide the same guarantees as JaegerShot/CheckStackAndEnterMethodJIT.
  */
 JSBool EnterMethodJIT(JSContext *cx, JSStackFrame *fp, void *code, Value *stackLimit);
 
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -4203,16 +4203,23 @@ Deserialize(JSContext *cx, uintN argc, j
     if (!JS_ReadStructuredClone(cx, (uint64 *) array->data, array->byteLength,
                                 JS_STRUCTURED_CLONE_VERSION, &v)) {
         return false;
     }
     JS_SET_RVAL(cx, vp, v);
     return true;
 }
 
+JSBool
+MJitStats(JSContext *cx, uintN argc, jsval *vp)
+{
+    JS_SET_RVAL(cx, vp, INT_TO_JSVAL(cx->runtime->mjitCodeSize));
+    return true;
+}
+
 /* We use a mix of JS_FS and JS_FN to test both kinds of natives. */
 static JSFunctionSpec shell_functions[] = {
     JS_FN("version",        Version,        0,0),
     JS_FN("revertVersion",  RevertVersion,  0,0),
     JS_FN("options",        Options,        0,0),
     JS_FN("load",           Load,           1,0),
     JS_FN("readline",       ReadLine,       0,0),
     JS_FN("print",          Print,          0,0),
@@ -4300,16 +4307,19 @@ static JSFunctionSpec shell_functions[] 
     JS_FN("compile",        Compile,        1,0),
     JS_FN("parse",          Parse,          1,0),
     JS_FN("timeout",        Timeout,        1,0),
     JS_FN("elapsed",        Elapsed,        0,0),
     JS_FN("parent",         Parent,         1,0),
     JS_FN("wrap",           Wrap,           1,0),
     JS_FN("serialize",      Serialize,      1,0),
     JS_FN("deserialize",    Deserialize,    1,0),
+#ifdef JS_METHODJIT
+    JS_FN("mjitstats",      MJitStats,      0,0),
+#endif
     JS_FS_END
 };
 
 static const char shell_help_header[] =
 "Command                  Description\n"
 "=======                  ===========\n";
 
 static const char *const shell_help_messages[] = {
@@ -4429,16 +4439,19 @@ static const char *const shell_help_mess
 "timeout([seconds])\n"
 "  Get/Set the limit in seconds for the execution time for the current context.\n"
 "  A negative value (default) means that the execution time is unlimited.",
 "elapsed()                Execution time elapsed for the current context.",
 "parent(obj)              Returns the parent of obj.\n",
 "wrap(obj)                Wrap an object into a noop wrapper.\n",
 "serialize(sd)            Serialize sd using JS_WriteStructuredClone. Returns a TypedArray.\n",
 "deserialize(a)           Deserialize data generated by serialize.\n"
+#ifdef JS_METHODJIT
+,"mjitstats()             Return stats for the method JIT.\n"
+#endif
 };
 
 /* Help messages must match shell functions. */
 JS_STATIC_ASSERT(JS_ARRAY_LENGTH(shell_help_messages) + 1 ==
                  JS_ARRAY_LENGTH(shell_functions));
 
 #ifdef DEBUG
 static void
--- a/js/src/xpconnect/src/xpcjsruntime.cpp
+++ b/js/src/xpconnect/src/xpcjsruntime.cpp
@@ -1192,16 +1192,22 @@ protected:
 static XPConnectGCChunkAllocator gXPCJSChunkAllocator;
 
 NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSRuntimeGCChunks,
                              "xpconnect/js/gcchunks",
                              "Memory in use by main JS Runtime GC chunks",
                              XPConnectGCChunkAllocator::GetGCChunkBytesInUse,
                              &gXPCJSChunkAllocator)
 
+NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSMethodJitCode,
+                             "xpconnect/js/mjit-code",
+                             "Memory in use by method-jit for compiled code",
+                             GetJSMethodJITCodeMemoryInUse,
+                             NULL)
+
 XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
  : mXPConnect(aXPConnect),
    mJSRuntime(nsnull),
    mWrappedJSMap(JSObject2WrappedJSMap::newMap(XPC_JS_MAP_SIZE)),
    mWrappedJSClassMap(IID2WrappedJSClassMap::newMap(XPC_JS_CLASS_MAP_SIZE)),
    mIID2NativeInterfaceMap(IID2NativeInterfaceMap::newMap(XPC_NATIVE_INTERFACE_MAP_SIZE)),
    mClassInfo2NativeSetMap(ClassInfo2NativeSetMap::newMap(XPC_NATIVE_SET_MAP_SIZE)),
    mNativeSetMap(NativeSetMap::newMap(XPC_NATIVE_SET_MAP_SIZE)),
@@ -1254,16 +1260,17 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* 
                                   xpc::WrapperFactory::PrepareForWrapping);
         mWatchdogWakeup = JS_NEW_CONDVAR(mJSRuntime->gcLock);
 
         mJSRuntime->setActivityCallback(ActivityCallback, this);
 
         mJSRuntime->setCustomGCChunkAllocator(&gXPCJSChunkAllocator);
 
         NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSRuntimeGCChunks));
+        NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSMethodJitCode));
     }
 
     if(!JS_DHashTableInit(&mJSHolders, JS_DHashGetStubOps(), nsnull,
                           sizeof(ObjectHolder), 512))
         mJSHolders.ops = nsnull;
 
     mCompartmentMap.Init();
     mMTCompartmentMap.Init();