Bug 626743 - Set debug mode for whole runtime, avoiding compartment-less contexts, r=dmandelin
☠☠ backed out by 19574542e48f ☠ ☠
authorSteve Fink <sfink@mozilla.com>
Wed, 19 Jan 2011 15:33:34 -0800
changeset 61236 47678330818a94f0d06aa5f79d9b6bd945b683fa
parent 61235 6dcf1547ea08ef0e7c4c028fd8e7d06c701cf781
child 61237 8743def9e74889fc13c351b574487dcde7b8bf20
push id18277
push usercleary@mozilla.com
push dateTue, 25 Jan 2011 03:52:51 +0000
treeherdermozilla-central@7ee91bd90e7a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdmandelin
bugs626743
milestone2.0b10pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 626743 - Set debug mode for whole runtime, avoiding compartment-less contexts, r=dmandelin
js/jsd/jsd_xpc.cpp
js/src/jsdbgapi.cpp
js/src/jsdbgapi.h
js/src/shell/js.cpp
--- a/js/jsd/jsd_xpc.cpp
+++ b/js/jsd/jsd_xpc.cpp
@@ -2502,28 +2502,24 @@ jsdService::AsyncOn (jsdIActivationCallb
     
     return xpc->SetDebugModeWhenPossible(PR_TRUE);
 }
 
 NS_IMETHODIMP
 jsdService::RecompileForDebugMode (JSRuntime *rt, JSBool mode) {
   NS_ASSERTION(NS_IsMainThread(), "wrong thread");
 
-  JSContext *cx;
-  JSContext *iter = NULL;
-
-  jsword currentThreadId = reinterpret_cast<jsword>(js_CurrentThreadId());
-
-  while ((cx = JS_ContextIterator (rt, &iter))) {
-    if (JS_GetContextThread(cx) == currentThreadId) {
-      JS_SetDebugMode(cx, mode);
-    }
-  }
-
-  return NS_OK;
+  JSContext *cx = JS_NewContext(rt, 256);
+  if (!cx)
+      return NS_ERROR_FAILURE;
+  JS_BeginRequest(cx);
+  JSBool ok = JS_SetDebugMode(cx, mode);
+  JS_EndRequest(cx);
+  JS_DestroyContext(cx);
+  return ok ? NS_OK : NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 jsdService::ActivateDebugger (JSRuntime *rt)
 {
     if (mOn)
         return (rt == mRuntime) ? NS_OK : NS_ERROR_ALREADY_INITIALIZED;
 
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -41,16 +41,17 @@
 /*
  * JS debugging API.
  */
 #include <string.h>
 #include "jstypes.h"
 #include "jsstdint.h"
 #include "jsutil.h"
 #include "jsclist.h"
+#include "jshashtable.h"
 #include "jsapi.h"
 #include "jscntxt.h"
 #include "jsversion.h"
 #include "jsdbgapi.h"
 #include "jsemit.h"
 #include "jsfun.h"
 #include "jsgc.h"
 #include "jsinterp.h"
@@ -134,61 +135,93 @@ PurgeCallICs(JSContext *cx, JSScript *st
             script->jitNormal->nukeScriptDependentICs();
         if (script->jitCtor)
             script->jitCtor->nukeScriptDependentICs();
     }
 }
 #endif
 
 JS_FRIEND_API(JSBool)
-js_SetDebugMode(JSContext *cx, JSBool debug)
+JS_SetDebugMode(JSContext *cx, JSBool debug)
 {
-    if (!cx->compartment)
-        return JS_TRUE;
+    JSBool rv = JS_TRUE;
+    JSRuntime *rt = cx->runtime;
+    rt->debugMode = debug;
+
+    // We can only recompile scripts that are not currently live
+    // (executing in some context). Recompile all scripts that can be
+    // recompiled and set their debugMode. The remaining scripts will
+    // be left as-is.
+    //
+    // This prevents simply iterating over a per-compartment
+    // SetDebugMode, because the only way to tell whether a script is
+    // live is by traversing all stack frames, and it requires a
+    // linear scan to get from a compartment to the set of contexts
+    // using it.
+
+    // Find all live scripts
 
-    cx->compartment->debugMode = debug;
-#ifdef JS_METHODJIT
-    for (JSScript *script = (JSScript *)cx->compartment->scripts.next;
-         &script->links != &cx->compartment->scripts;
-         script = (JSScript *)script->links.next) {
-        if (script->debugMode != !!debug &&
-            script->hasJITCode() &&
-            !IsScriptLive(cx, script)) {
-            /*
-             * In the event that this fails, debug mode is left partially on,
-             * leading to a small performance overhead but no loss of
-             * correctness. We set the debug flag to false so that the caller
-             * will not later attempt to use debugging features.
-             */
-            js::mjit::Recompiler recompiler(cx, script);
-            if (!recompiler.recompile()) {
-                /*
-                 * If recompilation failed, we could be in a state where
-                 * remaining compiled scripts hold call IC references that
-                 * have been destroyed by recompilation. Clear those ICs now.
-                 */
-                PurgeCallICs(cx, script);
-                cx->compartment->debugMode = JS_FALSE;
-                return JS_FALSE;
-            }
+    JSContext *iter = NULL;
+    jsword currentThreadId = reinterpret_cast<jsword>(js_CurrentThreadId());
+    typedef HashSet<JSScript *, DefaultHasher<JSScript*>, ContextAllocPolicy> ScriptMap;
+    ScriptMap liveScripts(cx);
+    if (!liveScripts.init()) {
+        rt->debugMode = JS_FALSE;
+        return JS_FALSE;
+    }
+
+    JSContext *icx;
+    while ((icx = JS_ContextIterator(rt, &iter))) {
+        if (JS_GetContextThread(icx) != currentThreadId)
+            continue;
+            
+        for (AllFramesIter i(icx); !i.done(); ++i) {
+            JSScript *script = i.fp()->maybeScript();
+            if (script)
+                liveScripts.put(script);
         }
     }
-#endif
-    return JS_TRUE;
-}
+
+    WrapperVector &vector = rt->compartments;
+    for (JSCompartment **p = vector.begin(); p != vector.end(); ++p) {
+        JSCompartment *comp = *p;
+        comp->debugMode = debug;
+
+        JSAutoEnterCompartment ac;
+
+#ifdef JS_METHODJIT
+        for (JSScript *script = (JSScript *)comp->scripts.next;
+             &script->links != &comp->scripts;
+             script = (JSScript *)script->links.next)
+        {
+            if (!script->debugMode == !debug)
+                continue;
+            if (debug && liveScripts.has(script))
+                continue;
 
-JS_PUBLIC_API(JSBool)
-JS_SetDebugMode(JSContext *cx, JSBool debug)
-{
-#ifdef DEBUG
-    for (AllFramesIter i(cx); !i.done(); ++i)
-        JS_ASSERT(!JS_IsScriptFrame(cx, i.fp()));
+            /*
+             * If compartment entry fails, debug mode is left
+             * partially on, leading to a small performance overhead
+             * but no loss of correctness. We set the debug flags to
+             * false so that the caller will not later attempt to use
+             * debugging features.
+             */
+            if (!ac.entered() && !ac.enter(cx, script)) {
+                rt->debugMode = comp->debugMode = JS_FALSE;
+                rv = JS_FALSE;
+                break;
+            }
+
+            mjit::ReleaseScriptCode(cx, script);
+            script->debugMode = !!debug;
+        }
 #endif
+    }
 
-    return js_SetDebugMode(cx, debug);
+    return rv;
 }
 
 JS_FRIEND_API(JSBool)
 js_SetSingleStepMode(JSContext *cx, JSScript *script, JSBool singleStep)
 {
 #ifdef JS_METHODJIT
     if (!script->singleStepMode == !singleStep)
         return JS_TRUE;
--- a/js/src/jsdbgapi.h
+++ b/js/src/jsdbgapi.h
@@ -65,22 +65,18 @@ JS_SetRuntimeDebugMode(JSRuntime *rt, JS
  * before debug mode was enabled. For this reason, it is also not safe to
  * enable debug mode while frames are live.
  */
 
 /* Get current state of debugging mode. */
 extern JS_PUBLIC_API(JSBool)
 JS_GetDebugMode(JSContext *cx);
 
-/* Turn on debugging mode, ignoring the presence of live frames. */
-extern JS_FRIEND_API(JSBool)
-js_SetDebugMode(JSContext *cx, JSBool debug);
-
-/* Turn on debugging mode. */
-extern JS_PUBLIC_API(JSBool)
+/* Turn on/off debugging mode for a whole runtime. */
+JS_FRIEND_API(JSBool)
 JS_SetDebugMode(JSContext *cx, JSBool debug);
 
 /* Turn on single step mode. Requires debug mode. */
 extern JS_FRIEND_API(JSBool)
 js_SetSingleStepMode(JSContext *cx, JSScript *script, JSBool singleStep);
 
 /* Turn on single step mode. */
 extern JS_PUBLIC_API(JSBool)
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -904,17 +904,17 @@ ProcessArgs(JSContext *cx, JSObject *obj
             if (++i == argc)
                 return usage();
 
             /* Set maximum stack size. */
             gMaxStackSize = atoi(argv[i]);
             break;
 
         case 'd':
-            js_SetDebugMode(cx, JS_TRUE);
+            JS_SetDebugMode(cx, JS_TRUE);
             break;
 
         case 'z':
             obj = split_setup(cx, JS_FALSE);
             if (!obj)
                 return gExitCode;
             break;
 #ifdef MOZ_TRACEVIS
@@ -1653,17 +1653,17 @@ SetDebug(JSContext *cx, uintN argc, jsva
 {
     jsval *argv = JS_ARGV(cx, vp);
     if (argc == 0 || !JSVAL_IS_BOOLEAN(argv[0])) {
         JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
                              JSSMSG_NOT_ENOUGH_ARGS, "setDebug");
         return JS_FALSE;
     }
     
-    js_SetDebugMode(cx, JSVAL_TO_BOOLEAN(argv[0]));
+    JS_SetDebugMode(cx, JSVAL_TO_BOOLEAN(argv[0]));
     JS_SET_RVAL(cx, vp, JSVAL_VOID);
     return JS_TRUE;
 }
 
 static JSBool
 GetTrapArgs(JSContext *cx, uintN argc, jsval *argv, JSScript **scriptp,
             int32 *ip)
 {