Bug 1244280 - Use system stack limitation instead of hardcoded smaller value on linux. r=nbp
authorTooru Fujisawa <arai_a@mac.com>
Wed, 08 Nov 2017 00:30:31 +0900
changeset 443783 04d539bc9bca98f22ca51de00a40eceb516405e3
parent 443782 0d5c2d176a454b25ee8aa893165d9e95f1d63f92
child 443784 e15196e25f9e783b2c04689936074990908170b0
push id1618
push userCallek@gmail.com
push dateThu, 11 Jan 2018 17:45:48 +0000
treeherdermozilla-release@882ca853e05a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnbp
bugs1244280
milestone58.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 1244280 - Use system stack limitation instead of hardcoded smaller value on linux. r=nbp Great thanks to krzysztof.modras@gmail.com who initially wrote this patch.
js/rust/src/rust.rs
js/xpconnect/src/XPCJSContext.cpp
--- a/js/rust/src/rust.rs
+++ b/js/rust/src/rust.rs
@@ -30,37 +30,17 @@ use panic;
 const DEFAULT_HEAPSIZE: u32 = 32_u32 * 1024_u32 * 1024_u32;
 
 // From Gecko:
 // Our "default" stack is what we use in configurations where we don't have a compelling reason to
 // do things differently. This is effectively 1MB on 64-bit platforms.
 const STACK_QUOTA: usize = 128 * 8 * 1024;
 
 // From Gecko:
-// The JS engine permits us to set different stack limits for system code,
-// trusted script, and untrusted script. We have tests that ensure that
-// we can always execute 10 "heavy" (eval+with) stack frames deeper in
-// privileged code. Our stack sizes vary greatly in different configurations,
-// so satisfying those tests requires some care. Manual measurements of the
-// number of heavy stack frames achievable gives us the following rough data,
-// ordered by the effective categories in which they are grouped in the
-// JS_SetNativeStackQuota call (which predates this analysis).
-//
-// (NB: These numbers may have drifted recently - see bug 938429)
-// 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, 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
-//
-// Linux 32-bit Opt: 1MB stack, 272 stack frames => ~3.8k per stack frame
-// Linux 64-bit Opt: 2MB stack, 316 stack frames => ~6.5k per stack frame
-//
+// (See js/xpconnect/src/XPCJSContext.cpp)
 // We tune the trusted/untrusted quotas for each configuration to achieve our
 // invariants while attempting to minimize overhead. In contrast, our buffer
 // between system code and trusted script is a very unscientific 10k.
 const SYSTEM_CODE_BUFFER: usize = 10 * 1024;
 
 // Gecko's value on 64-bit.
 const TRUSTED_SCRIPT_BUFFER: usize = 8 * 12800;
 
--- a/js/xpconnect/src/XPCJSContext.cpp
+++ b/js/xpconnect/src/XPCJSContext.cpp
@@ -60,16 +60,22 @@
 
 #include "GeckoProfiler.h"
 #include "nsIInputStream.h"
 #include "nsIXULRuntime.h"
 #include "nsJSPrincipals.h"
 #include "ExpandedPrincipal.h"
 #include "SystemPrincipal.h"
 
+#if defined(XP_LINUX) && !defined(ANDROID)
+// For getrlimit and min/max.
+#include <algorithm>
+#include <sys/resource.h>
+#endif
+
 #ifdef XP_WIN
 #include <windows.h>
 #endif
 
 static MOZ_THREAD_LOCAL(XPCJSContext*) gTlsContext;
 
 using namespace mozilla;
 using namespace xpc;
@@ -1013,27 +1019,36 @@ XPCJSContext::Initialize(XPCJSContext* a
     // trusted script, and untrusted script. We have tests that ensure that
     // we can always execute 10 "heavy" (eval+with) stack frames deeper in
     // privileged code. Our stack sizes vary greatly in different configurations,
     // so satisfying those tests requires some care. Manual measurements of the
     // number of heavy stack frames achievable gives us the following rough data,
     // ordered by the effective categories in which they are grouped in the
     // JS_SetNativeStackQuota call (which predates this analysis).
     //
-    // (NB: These numbers may have drifted recently - see bug 938429)
-    // 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
+    // The following "Stack Frames" numbers come from `chromeLimit` in
+    // js/xpconnect/tests/chrome/test_bug732665.xul
     //
-    // 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
-    //
-    // Linux 32-bit Opt: 1MB stack, 272 stack frames => ~3.8k per stack frame
-    // Linux 64-bit Opt: 2MB stack, 316 stack frames => ~6.5k per stack frame
+    //  Platform   | Build | Stack Quota | Stack Frames | Stack Frame Size
+    // ------------+-------+-------------+--------------+------------------
+    //  OSX 64     | Opt   | 7MB         | 1331         | ~5.4k
+    //  OSX 64     | Debug | 7MB         | 1202         | ~6.0k
+    // ------------+-------+-------------+--------------+------------------
+    //  Linux 32   | Opt   | 7.875MB     | 2513         | ~3.2k
+    //  Linux 32   | Debug | 7.875MB     | 2146         | ~3.8k
+    // ------------+-------+-------------+--------------+------------------
+    //  Linux 64   | Opt   | 7.875MB     | 1360         | ~5.9k
+    //  Linux 64   | Debug | 7.875MB     | 1180         | ~6.8k
+    //  Linux 64   | ASan  | 7.875MB     | 473          | ~17.0k
+    // ------------+-------+-------------+--------------+------------------
+    //  Windows 32 | Opt   | 984k        | 188          | ~5.2k
+    //  Windows 32 | Debug | 984k        | 208          | ~4.7k
+    // ------------+-------+-------------+--------------+------------------
+    //  Windows 64 | Opt   | 1.922MB     | 189          | ~10.4k
+    //  Windows 64 | Debug | 1.922MB     | 175          | ~11.2k
     //
     // We tune the trusted/untrusted quotas for each configuration to achieve our
     // invariants while attempting to minimize overhead. In contrast, our buffer
     // between system code and trusted script is a very unscientific 10k.
     const size_t kSystemCodeBuffer = 10 * 1024;
 
     // Our "default" stack is what we use in configurations where we don't have
     // a compelling reason to do things differently. This is effectively 512KB
@@ -1044,47 +1059,79 @@ XPCJSContext::Initialize(XPCJSContext* a
     // the web to base this decision primarily on the default stack size that the
     // underlying platform makes available, but that seems to be what we do. :-(
 
 #if defined(XP_MACOSX) || defined(DARWIN)
     // MacOS has a gargantuan default stack size of 8MB. Go wild with 7MB,
     // and give trusted script 180k extra. The stack is huge on mac anyway.
     const size_t kStackQuota = 7 * 1024 * 1024;
     const size_t kTrustedScriptBuffer = 180 * 1024;
+#elif defined(XP_LINUX) && !defined(ANDROID)
+    // Most Linux distributions set default stack size to 8MB.  Use it as the
+    // maximum value.
+    const size_t kStackQuotaMax = 8 * 1024 * 1024;
+#  if defined(MOZ_ASAN) || defined(DEBUG)
+    // Bug 803182: account for the 4x difference in the size of js::Interpret
+    // between optimized and debug builds.  We use 2x since the JIT part
+    // doesn't increase much.
+    // See the standalone MOZ_ASAN branch below for the ASan case.
+    const size_t kStackQuotaMin = 2 * kDefaultStackQuota;
+#  else
+    const size_t kStackQuotaMin = kDefaultStackQuota;
+#  endif
+    // Allocate 128kB margin for the safe space.
+    const size_t kStackSafeMargin = 128 * 1024;
+
+    struct rlimit rlim;
+    const size_t kStackQuota =
+        getrlimit(RLIMIT_STACK, &rlim) == 0
+        ? std::max(std::min(size_t(rlim.rlim_cur - kStackSafeMargin),
+                            kStackQuotaMax - kStackSafeMargin),
+                   kStackQuotaMin)
+        : kStackQuotaMin;
+#  if defined(MOZ_ASAN)
+    // See the standalone MOZ_ASAN branch below for the ASan case.
+    const size_t kTrustedScriptBuffer = 450 * 1024;
+#  else
+    const size_t kTrustedScriptBuffer = 180 * 1024;
+#  endif
 #elif defined(MOZ_ASAN)
     // ASan requires more stack space due to red-zones, so give it double the
     // default (1MB on 32-bit, 2MB on 64-bit). ASAN stack frame measurements
     // 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
     // 9.0k (see above), so let's use a buffer of 9.0 * 5 * 10 = 450k.
+    //
+    // FIXME: Does this branch make sense for Windows and Android?
+    // (See bug 1415195)
     const size_t kStackQuota =  2 * kDefaultStackQuota;
     const size_t kTrustedScriptBuffer = 450 * 1024;
 #elif defined(XP_WIN)
-    // 1MB is the default stack size on Windows. We use the /STACK linker flag
-    // to request a larger stack, so we determine the stack size at runtime.
+    // 1MB is the default stack size on Windows. We use the -STACK linker flag
+    // (see WIN32_EXE_LDFLAGS in config/config.mk) to request a larger stack,
+    // so we determine the stack size at runtime.
     const size_t kStackQuota = GetWindowsStackSize();
     const size_t kTrustedScriptBuffer = (sizeof(size_t) == 8) ? 180 * 1024   //win64
                                                               : 120 * 1024;  //win32
-    // The following two configurations are linux-only. Given the numbers above,
-    // we use 50k and 100k trusted buffers on 32-bit and 64-bit respectively.
 #elif defined(ANDROID)
     // Android appears to have 1MB stacks. Allow the use of 3/4 of that size
     // (768KB on 32-bit), since otherwise we can crash with a stack overflow
     // when nearing the 1MB limit.
     const size_t kStackQuota = kDefaultStackQuota + kDefaultStackQuota / 2;
     const size_t kTrustedScriptBuffer = sizeof(size_t) * 12800;
-#elif defined(DEBUG)
-    // Bug 803182: account for the 4x difference in the size of js::Interpret
-    // between optimized and debug builds.
-    // XXXbholley - Then why do we only account for 2x of difference?
+#else
+    // Catch-all configuration for other environments.
+#  if defined(DEBUG)
     const size_t kStackQuota = 2 * kDefaultStackQuota;
-    const size_t kTrustedScriptBuffer = sizeof(size_t) * 12800;
-#else
+#  else
     const size_t kStackQuota = kDefaultStackQuota;
+#  endif
+    // Given the numbers above, we use 50k and 100k trusted buffers on 32-bit
+    // and 64-bit respectively.
     const size_t kTrustedScriptBuffer = sizeof(size_t) * 12800;
 #endif
 
     // Avoid an unused variable warning on platforms where we don't use the
     // default.
     (void) kDefaultStackQuota;
 
     JS_SetNativeStackQuota(cx,