Bug 663547 - Make ecma_5/Object/* not dog-slow any more by disabling a particularly expensive assertion when they run. r=luke
authorJeff Walden <jwalden@mit.edu>
Tue, 14 Jun 2011 18:39:53 -0700
changeset 71364 8e030595916317d42a7982be2ded49a88f489d0f
parent 71363 81c343a150a4bc7be1bfa665b41f877a4074bfbd
child 71365 ce077af8195c82557525c5b4aada944f4add9629
push id20538
push usercleary@mozilla.com
push dateMon, 20 Jun 2011 23:59:42 +0000
treeherdermozilla-central@a285146675dc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs663547
milestone7.0a1
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 663547 - Make ecma_5/Object/* not dog-slow any more by disabling a particularly expensive assertion when they run. r=luke
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/shell/js.cpp
js/src/tests/ecma_5/Object/defineProperty-setup.js
js/src/vm/Stack.cpp
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -1456,16 +1456,19 @@ DSTOffsetCache::DSTOffsetCache()
 }
 
 JSContext::JSContext(JSRuntime *rt)
   : hasVersionOverride(false),
     runtime(rt),
     compartment(NULL),
     stack(thisDuringConstruction()),
     busyArrays()
+#ifdef DEBUG
+    , stackIterAssertionEnabled(true)
+#endif
 {}
 
 JSContext::~JSContext()
 {
 #ifdef JS_THREADSAFE
     JS_ASSERT(!thread_);
 #endif
 
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -1415,16 +1415,24 @@ struct JSContext
 
     void setPendingException(js::Value v);
 
     void clearPendingException() {
         this->throwing = false;
         this->exception.setUndefined();
     }
 
+#ifdef DEBUG
+    /*
+     * Controls whether a quadratic-complexity assertion is performed during
+     * stack iteration, defaults to true.
+     */
+    bool stackIterAssertionEnabled;
+#endif
+
   private:
     /*
      * The allocation code calls the function to indicate either OOM failure
      * when p is null or that a memory pressure counter has reached some
      * threshold when p is not null. The function takes the pointer and not
      * a boolean flag to minimize the amount of code in its inlined callers.
      */
     JS_FRIEND_API(void) checkMallocGCPressure(void *p);
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -4845,16 +4845,33 @@ ParseLegacyJSON(JSContext *cx, uintN arg
 
     size_t length;
     const jschar *chars = JS_GetStringCharsAndLength(cx, str, &length);
     if (!chars)
         return false;
     return js::ParseJSONWithReviver(cx, chars, length, js::NullValue(), js::Valueify(vp), LEGACY);
 }
 
+static JSBool
+EnableStackWalkingAssertion(JSContext *cx, uintN argc, jsval *vp)
+{
+    if (argc == 0 || !JSVAL_IS_BOOLEAN(JS_ARGV(cx, vp)[0])) {
+        JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_INVALID_ARGS,
+                             "enableStackWalkingAssertion");
+        return false;
+    }
+
+#ifdef DEBUG
+    cx->stackIterAssertionEnabled = JSVAL_TO_BOOLEAN(JS_ARGV(cx, vp)[0]);
+#endif
+
+    JS_SET_RVAL(cx, vp, JSVAL_VOID);
+    return true;
+}
+
 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("evaluate",       Evaluate,       1,0),
     JS_FN("run",            Run,            1,0),
     JS_FN("readline",       ReadLine,       0,0),
@@ -4948,16 +4965,17 @@ static JSFunctionSpec shell_functions[] 
     JS_FN("deserialize",    Deserialize,    1,0),
 #ifdef JS_METHODJIT
     JS_FN("mjitcodestats",  MJitCodeStats,  0,0),
     JS_FN("mjitdatastats",  MJitDataStats,  0,0),
 #endif
     JS_FN("stringstats",    StringStats,    0,0),
     JS_FN("newGlobal",      NewGlobal,      1,0),
     JS_FN("parseLegacyJSON",ParseLegacyJSON,1,0),
+    JS_FN("enableStackWalkingAssertion",EnableStackWalkingAssertion,1,0),
     JS_FS_END
 };
 
 static const char shell_help_header[] =
 "Command                  Description\n"
 "=======                  ===========\n";
 
 static const char *const shell_help_messages[] = {
@@ -5093,16 +5111,21 @@ static const char *const shell_help_mess
 "mjitdatastats()          Return stats on mjit data memory usage.",
 #endif
 "stringstats()            Return stats on string memory usage.",
 "newGlobal(kind)          Return a new global object, in the current\n"
 "                         compartment if kind === 'same-compartment' or in a\n"
 "                         new compartment if kind === 'new-compartment'",
 "parseLegacyJSON(str)     Parse str as legacy JSON, returning the result if the\n"
 "                         parse succeeded and throwing a SyntaxError if not.",
+"enableStackWalkingAssertion(enabled)\n"
+"  Enables or disables a particularly expensive assertion in stack-walking\n"
+"  code.  If your test isn't ridiculously thorough, such that performing this\n"
+"  assertion increases test duration by an order of magnitude, you shouldn't\n"
+"  use this.",
 
 /* Keep these last: see the static assertion below. */
 #ifdef MOZ_PROFILING
 "startProfiling()         Start a profiling session.\n"
 "                         Profiler must be running with programatic sampling",
 "stopProfiling()          Stop a running profiling session\n"
 #endif
 };
--- a/js/src/tests/ecma_5/Object/defineProperty-setup.js
+++ b/js/src/tests/ecma_5/Object/defineProperty-setup.js
@@ -1,14 +1,21 @@
 // Any copyright is dedicated to the Public Domain.
 // http://creativecommons.org/licenses/publicdomain/
 
 assertEq("defineProperty" in Object, true);
 assertEq(Object.defineProperty.length, 3);
 
+/*
+ * Disable an assertion that is pathologically slow given the exhaustiveness of
+ * these tests.
+ */
+if (typeof enableStackWalkingAssertion === "function")
+  enableStackWalkingAssertion(false);
+
 if (!Object.prototype.toSource)
 {
   Object.defineProperty(Object.prototype, "toSource",
   {
     value: function toSource()
     {
       if (this instanceof RegExp)
       {
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -933,18 +933,22 @@ StackIter::settleOnNewState()
              *
              * Function.prototype.call will however appear, hence the debugger
              * can, by inspecting 'args.thisv', give some useful information.
              */
             JSOp op = js_GetOpcode(cx_, fp_->script(), pc_);
             if (op == JSOP_CALL || op == JSOP_FUNCALL) {
                 uintN argc = GET_ARGC(pc_);
                 DebugOnly<uintN> spoff = sp_ - fp_->base();
-                JS_ASSERT_IF(!fp_->hasImacropc(),
-                             spoff == js_ReconstructStackDepth(cx_, fp_->script(), pc_));
+#ifdef DEBUG
+                if (cx_->stackIterAssertionEnabled) {
+                    JS_ASSERT_IF(!fp_->hasImacropc(),
+                                 spoff == js_ReconstructStackDepth(cx_, fp_->script(), pc_));
+                }
+#endif
                 Value *vp = sp_ - (2 + argc);
 
                 if (IsNativeFunction(*vp)) {
                     state_ = IMPLICIT_NATIVE;
                     args_ = CallArgsFromVp(argc, vp);
                     return;
                 }
             } else if (op == JSOP_FUNAPPLY) {