diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 1163c07a58d..c11e0d58d39 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -3208,6 +3208,18 @@ StackDump(JSContext *cx, unsigned argc, Value *vp) } #endif +static bool +StackPointerInfo(JSContext *cx, unsigned argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + // Copy the truncated stack pointer to the result. This value is not used + // as a pointer but as a way to measure frame-size from JS. + args.rval().setInt32(int32_t(reinterpret_cast(&args) & 0xfffffff)); + return true; +} + + static bool Elapsed(JSContext *cx, unsigned argc, jsval *vp) { @@ -4897,6 +4909,11 @@ static const JSFunctionSpecWithHelp shell_functions[] = { "isLatin1(s)", " Return true iff the string's characters are stored as Latin1."), + JS_FN_HELP("stackPointerInfo", StackPointerInfo, 0, 0, +"stackPointerInfo()", +" Return an int32 value which corresponds to the offset of the latest stack\n" +" pointer, such that one can take the differences of 2 to estimate a frame-size."), + JS_FS_HELP_END }; diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index 97d9843d6d8..00e14499e93 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -3325,8 +3325,8 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect) // OSX 64-bit Debug: 7MB stack, 636 stack frames => ~11.3k per stack frame // OSX64 Opt: 7MB stack, 2440 stack frames => ~3k per stack frame // - // Linux 32-bit Debug: 2MB stack, 447 stack frames => ~4.6k per stack frame - // Linux 64-bit Debug: 4MB stack, 501 stack frames => ~8.2k per stack frame + // Linux 32-bit Debug: 2MB stack, 426 stack frames => ~4.8k per stack frame + // Linux 64-bit Debug: 4MB stack, 455 stack frames => ~9.0k per stack frame // // Windows (Opt+Debug): 900K stack, 235 stack frames => ~3.4k per stack frame // @@ -3358,9 +3358,9 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect) // were not taken at the time of this writing, so we hazard a guess that // ASAN builds have roughly thrice the stack overhead as normal builds. // On normal builds, the largest stack frame size we might encounter is - // 8.2k, so let's use a buffer of 8.2 * 3 * 10 = 246k. + // 9.0k (see above), so let's use a buffer of 9.0 * 3 * 10 = 270k. const size_t kStackQuota = 2 * kDefaultStackQuota; - const size_t kTrustedScriptBuffer = 246 * 1024; + const size_t kTrustedScriptBuffer = 270 * 1024; #elif defined(XP_WIN) // 1MB is the default stack size on Windows, so use 900k. // Windows PGO stack frames have unfortunately gotten pretty large lately. :-( diff --git a/js/xpconnect/tests/chrome/test_bug732665.xul b/js/xpconnect/tests/chrome/test_bug732665.xul index 3a77918987c..f295ac6e1f1 100644 --- a/js/xpconnect/tests/chrome/test_bug732665.xul +++ b/js/xpconnect/tests/chrome/test_bug732665.xul @@ -67,6 +67,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=732665 // enough to make the cross-compartment call. So rather than exhausting the // stack entirely and then checking for 10 chrome frames, we leave ourselves // one frame's worth, and check for 11. + // + // If this assertion fails, the current work-around so far is to measure + // again the worst frame size, by using the JS Shell to run + // test_bug732665_meta.js . This script will output numbers to update + // XPCJSRuntime.cpp comment, as well as the kTrustedScriptBuffer constant. contentSb.nnslChrome = chromeSb.nearNativeStackLimit; var nestedLimit = Cu.evalInSandbox("nearNativeStackLimit(1, function() { nestedLimit = nnslChrome(0);}); nestedLimit;", contentSb); ok(nestedLimit >= 11, "Chrome should be invokable from content script with an exhausted stack: " + nestedLimit); diff --git a/js/xpconnect/tests/chrome/test_bug732665_meta.js b/js/xpconnect/tests/chrome/test_bug732665_meta.js new file mode 100644 index 00000000000..a2afddd98f7 --- /dev/null +++ b/js/xpconnect/tests/chrome/test_bug732665_meta.js @@ -0,0 +1,26 @@ +var bottom = stackPointerInfo(); +var top = bottom; + +function nearNativeStackLimit() { + function inner() { + try { + with ({}) { // keep things predictable -- stay in the interpreter + top = stackPointerInfo(); + var stepsFromLimit = eval("inner()"); // Use eval to force a number of native stackframes to be created. + } + return stepsFromLimit + 1; + } catch(e) { + // It would be nice to check here that the exception is actually an + // over-recursion here. But doing so would require toString()ing the + // exception, which we may not have the stack space to do. + return 1; + } + } + return inner(); +} + +var nbFrames = nearNativeStackLimit(); +var frameSize = bottom - top; +print("Max stack size:", frameSize, "bytes", + "\nMaximum number of frames:", nbFrames, + "\nAverage frame size:", Math.ceil(frameSize / nbFrames), "bytes");