Bug 1199203 - Add support for per-thread OOM testing. r=jonco
☠☠ backed out by da3b85de906a ☠ ☠
authorChristian Holler <choller@mozilla.com>
Thu, 10 Sep 2015 03:46:45 +0200
changeset 294307 ad5849c18324162a77a7ed14c761d60d655e3c35
parent 294306 672a3f59ed55d59cf71dbfa2610f5d47adc8cf84
child 294308 da3b85de906aa7e15c2ca329c108cc0234e79a7c
push id5245
push userraliiev@mozilla.com
push dateThu, 29 Oct 2015 11:30:51 +0000
treeherdermozilla-beta@dac831dc1bd0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjonco
bugs1199203
milestone43.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 1199203 - Add support for per-thread OOM testing. r=jonco
js/public/Utility.h
js/src/builtin/TestingFunctions.cpp
js/src/jsapi.cpp
js/src/jsutil.cpp
js/src/vm/HelperThreads.cpp
--- a/js/public/Utility.h
+++ b/js/public/Utility.h
@@ -78,26 +78,63 @@ static MOZ_NEVER_INLINE void js_failedAl
 #define JS_OOM_CALL_BP_FUNC() js_failedAllocBreakpoint()
 #else
 #define JS_OOM_CALL_BP_FUNC() do {} while(0)
 #endif
 
 namespace js {
 namespace oom {
 
+/*
+ * To make testing OOM in certain helper threads more effective,
+ * allow restricting the OOM testing to a certain helper thread
+ * type. This allows us to fail e.g. in off-thread script parsing
+ * without causing an OOM in the main thread first.
+ */
+enum ThreadType {
+    THREAD_TYPE_NONE,           // 0
+    THREAD_TYPE_MAIN,           // 1
+    THREAD_TYPE_ASMJS,          // 2
+    THREAD_TYPE_ION,            // 3
+    THREAD_TYPE_PARSE,          // 4
+    THREAD_TYPE_COMPRESS,       // 5
+    THREAD_TYPE_GCHELPER,       // 6
+    THREAD_TYPE_GCPARALLEL,     // 7
+    THREAD_TYPE_MAX             // Used to check shell function arguments
+};
+extern JS_PUBLIC_DATA(uint32_t) targetThread;
+
+/*
+ * Getter/Setter functions to encapsulate mozilla::ThreadLocal,
+ * implementation is in jsutil.cpp.
+ */
+extern bool InitThreadType(void);
+extern void SetThreadType(ThreadType);
+extern uint32_t GetThreadType(void);
+
+static inline bool
+OOMThreadCheck()
+{
+    return (!js::oom::targetThread 
+            || js::oom::targetThread == js::oom::GetThreadType());
+}
+
 static inline bool
 IsSimulatedOOMAllocation()
 {
-    return OOM_counter == OOM_maxAllocations ||
-           (OOM_counter > OOM_maxAllocations && OOM_failAlways);
+    return OOMThreadCheck() && (OOM_counter == OOM_maxAllocations ||
+           (OOM_counter > OOM_maxAllocations && OOM_failAlways));
 }
 
 static inline bool
 ShouldFailWithOOM()
 {
+    if (!OOMThreadCheck())
+        return false;
+
     OOM_counter++;
     if (IsSimulatedOOMAllocation()) {
         JS_OOM_CALL_BP_FUNC();
         return true;
     }
     return false;
 }
 
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -982,43 +982,73 @@ DisableTrackAllocations(JSContext* cx, u
     return true;
 }
 
 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
 static bool
 OOMAfterAllocations(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    if (args.length() != 1) {
+    if (args.length() < 1) {
         JS_ReportError(cx, "count argument required");
         return false;
     }
 
+    if (args.length() > 2) {
+        JS_ReportError(cx, "too many arguments");
+        return false;
+    }
+
+    uint32_t targetThread = 0;
+    if (!ToUint32(cx, args.get(1), &targetThread))
+        return false;
+
+    if (targetThread >= js::oom::THREAD_TYPE_MAX) {
+        JS_ReportError(cx, "invalid thread type specified");
+        return false;
+    }
+
     uint32_t count;
-    if (!JS::ToUint32(cx, args[0], &count))
+    if (!JS::ToUint32(cx, args.get(0), &count))
         return false;
 
+    js::oom::targetThread = targetThread;
     OOM_maxAllocations = OOM_counter + count;
     OOM_failAlways = true;
     return true;
 }
 
 static bool
 OOMAtAllocation(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    if (args.length() != 1) {
+    if (args.length() < 1) {
         JS_ReportError(cx, "count argument required");
         return false;
     }
 
+    if (args.length() > 2) {
+        JS_ReportError(cx, "too many arguments");
+        return false;
+    }
+
+    uint32_t targetThread = 0;
+    if (!ToUint32(cx, args.get(1), &targetThread))
+        return false;
+
+    if (targetThread >= js::oom::THREAD_TYPE_MAX) {
+        JS_ReportError(cx, "invalid thread type specified");
+        return false;
+    }
+
     uint32_t count;
-    if (!JS::ToUint32(cx, args[0], &count))
+    if (!JS::ToUint32(cx, args.get(0), &count))
         return false;
 
+    js::oom::targetThread = targetThread;
     OOM_maxAllocations = OOM_counter + count;
     OOM_failAlways = false;
     return true;
 }
 
 static bool
 ResetOOMFailure(JSContext* cx, unsigned argc, Value* vp)
 {
@@ -2827,25 +2857,27 @@ static const JSFunctionSpecWithHelp Test
 "  object metadata callback that will override any other object metadata\n"
 "  callback that may be set."),
 
     JS_FN_HELP("disableTrackAllocations", DisableTrackAllocations, 0, 0,
 "disableTrackAllocations()",
 "  Stop capturing the JS stack at every allocation."),
 
 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
-    JS_FN_HELP("oomAfterAllocations", OOMAfterAllocations, 1, 0,
-"oomAfterAllocations(count)",
+    JS_FN_HELP("oomAfterAllocations", OOMAfterAllocations, 2, 0,
+"oomAfterAllocations(count [,threadType])",
 "  After 'count' js_malloc memory allocations, fail every following allocation\n"
-"  (return NULL)."),
-
-    JS_FN_HELP("oomAtAllocation", OOMAtAllocation, 1, 0,
-"oomAtAllocation(count)",
+"  (return nullptr). The optional thread type limits the effect to the\n"
+"  specified type of helper thread."),
+
+    JS_FN_HELP("oomAtAllocation", OOMAtAllocation, 2, 0,
+"oomAtAllocation(count [,threadType])",
 "  After 'count' js_malloc memory allocations, fail the next allocation\n"
-"  (return NULL)."),
+"  (return nullptr). The optional thread type limits the effect to the\n"
+"  specified type of helper thread."),
 
     JS_FN_HELP("resetOOMFailure", ResetOOMFailure, 0, 0,
 "resetOOMFailure()",
 "  Remove the allocation failure scheduled by either oomAfterAllocations() or\n"
 "  oomAtAllocation() and return whether any allocation had been caused to fail."),
 #endif
 
     JS_FN_HELP("makeFakePromise", MakeFakePromise, 0, 0,
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -585,16 +585,20 @@ JS_Init(void)
 #ifdef DEBUG
     CheckMessageParameterCounts();
 #endif
 
     using js::TlsPerThreadData;
     if (!TlsPerThreadData.initialized() && !TlsPerThreadData.init())
         return false;
 
+    if (!js::oom::InitThreadType())
+        return false;
+    js::oom::SetThreadType(js::oom::THREAD_TYPE_MAIN);
+
     jit::ExecutableAllocator::initStatic();
 
     if (!jit::InitializeIon())
         return false;
 
 #if EXPOSE_INTL_API
     UErrorCode err = U_ZERO_ERROR;
     u_init(&err);
--- a/js/src/jsutil.cpp
+++ b/js/src/jsutil.cpp
@@ -6,16 +6,17 @@
 
 /* Various JS utility functions. */
 
 #include "jsutil.h"
 
 #include "mozilla/Assertions.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/PodOperations.h"
+#include "mozilla/ThreadLocal.h"
 
 #include <stdio.h>
 
 #include "jstypes.h"
 
 #ifdef WIN32
 #    include "jswin.h"
 #endif
@@ -27,16 +28,22 @@ using namespace js;
 using mozilla::CeilingLog2Size;
 using mozilla::PodArrayZero;
 
 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
 /* For JS_OOM_POSSIBLY_FAIL in jsutil.h. */
 JS_PUBLIC_DATA(uint32_t) OOM_maxAllocations = UINT32_MAX;
 JS_PUBLIC_DATA(uint32_t) OOM_counter = 0;
 JS_PUBLIC_DATA(bool) OOM_failAlways = true;
+namespace js {
+namespace oom {
+    JS_PUBLIC_DATA(uint32_t) targetThread = 0;
+    JS_PUBLIC_DATA(mozilla::ThreadLocal<uint32_t>) threadType;
+}
+}
 #endif
 
 JS_PUBLIC_API(void)
 JS_Assert(const char* s, const char* file, int ln)
 {
     MOZ_ReportAssertionFailure(s, file, ln);
     MOZ_CRASH();
 }
@@ -76,16 +83,35 @@ AllTheNonBasicVanillaNewAllocations()
 
     printf("%u\n", uint32_t(p));  // make sure |p| is not optimized away
 
     free((int*)p);      // this would crash if ever actually called
 
     MOZ_CRASH();
 }
 
+namespace oom {
+
+bool
+InitThreadType(void) {
+    return threadType.initialized() || threadType.init();
+}
+
+void
+SetThreadType(ThreadType type) {
+    threadType.set(type);
+}
+
+uint32_t
+GetThreadType(void) {
+    return threadType.get();
+}
+
+} // namespace oom
+
 } // namespace js
 
 #endif // __linux__
 
 #ifdef JS_BASIC_STATS
 
 #include <math.h>
 
@@ -226,8 +252,9 @@ JS_DumpHistogram(JSBasicStats* bs, FILE*
             for (unsigned i = 0; i < cnt; i++)
                 putc('*', fp);
         }
         putc('\n', fp);
     }
 }
 
 #endif /* JS_BASIC_STATS */
+
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -1446,24 +1446,31 @@ HelperThread::threadLoop()
                 HelperThreadState().canStartGCParallelTask())
             {
                 break;
             }
             HelperThreadState().wait(GlobalHelperThreadState::PRODUCER);
         }
 
         // Dispatch tasks, prioritizing AsmJS work.
-        if (HelperThreadState().canStartAsmJSCompile())
+        if (HelperThreadState().canStartAsmJSCompile()) {
+            js::oom::SetThreadType(js::oom::THREAD_TYPE_ASMJS);
             handleAsmJSWorkload();
-        else if (ionCompile)
+        } else if (ionCompile) {
+            js::oom::SetThreadType(js::oom::THREAD_TYPE_ION);
             handleIonWorkload();
-        else if (HelperThreadState().canStartParseTask())
+        } else if (HelperThreadState().canStartParseTask()) {
+            js::oom::SetThreadType(js::oom::THREAD_TYPE_PARSE);
             handleParseWorkload();
-        else if (HelperThreadState().canStartCompressionTask())
+        } else if (HelperThreadState().canStartCompressionTask()) {
+            js::oom::SetThreadType(js::oom::THREAD_TYPE_COMPRESS);
             handleCompressionWorkload();
-        else if (HelperThreadState().canStartGCHelperTask())
+        } else if (HelperThreadState().canStartGCHelperTask()) {
+            js::oom::SetThreadType(js::oom::THREAD_TYPE_GCHELPER);
             handleGCHelperWorkload();
-        else if (HelperThreadState().canStartGCParallelTask())
+        } else if (HelperThreadState().canStartGCParallelTask()) {
+            js::oom::SetThreadType(js::oom::THREAD_TYPE_GCPARALLEL);
             handleGCParallelWorkload();
-        else
+        } else {
             MOZ_CRASH("No task to perform");
+        }
     }
 }