Bug 1199203 - Add support for per-thread OOM testing. r=jonco

This commit is contained in:
Christian Holler 2015-09-11 16:11:56 +02:00
Родитель fd1ffff272
Коммит 3f7b44f628
5 изменённых файлов: 143 добавлений и 22 удалений

Просмотреть файл

@ -62,6 +62,46 @@ JS_Assert(const char* s, const char* file, int ln);
#if defined JS_USE_CUSTOM_ALLOCATOR
# include "jscustomallocator.h"
#else
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
};
/*
* Getter/Setter functions to encapsulate mozilla::ThreadLocal,
* implementation is in jsutil.cpp.
*/
# if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
extern bool InitThreadType(void);
extern void SetThreadType(ThreadType);
extern uint32_t GetThreadType(void);
# else
inline bool InitThreadType(void) { return true; }
inline void SetThreadType(ThreadType t) {};
inline uint32_t GetThreadType(void) { return 0; }
# endif
} /* namespace oom */
} /* namespace js */
# if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
/*
@ -83,16 +123,28 @@ static MOZ_NEVER_INLINE void js_failedAllocBreakpoint() { asm(""); }
namespace js {
namespace oom {
extern JS_PUBLIC_DATA(uint32_t) targetThread;
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();

Просмотреть файл

@ -987,15 +987,30 @@ 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;
}
uint32_t count;
if (!JS::ToUint32(cx, args[0], &count))
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.get(0), &count))
return false;
js::oom::targetThread = targetThread;
OOM_maxAllocations = OOM_counter + count;
OOM_failAlways = true;
return true;
@ -1005,15 +1020,30 @@ 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;
}
uint32_t count;
if (!JS::ToUint32(cx, args[0], &count))
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.get(0), &count))
return false;
js::oom::targetThread = targetThread;
OOM_maxAllocations = OOM_counter + count;
OOM_failAlways = false;
return true;
@ -2832,15 +2862,17 @@ static const JSFunctionSpecWithHelp TestingFunctions[] = {
" 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)."),
" (return nullptr). The optional thread type limits the effect to the\n"
" specified type of helper thread."),
JS_FN_HELP("oomAtAllocation", OOMAtAllocation, 1, 0,
"oomAtAllocation(count)",
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()",

Просмотреть файл

@ -590,6 +590,12 @@ JS_Init(void)
if (!TlsPerThreadData.initialized() && !TlsPerThreadData.init())
return false;
#if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
if (!js::oom::InitThreadType())
return false;
js::oom::SetThreadType(js::oom::THREAD_TYPE_MAIN);
#endif
jit::ExecutableAllocator::initStatic();
if (!jit::InitializeIon())

Просмотреть файл

@ -11,6 +11,7 @@
#include "mozilla/Assertions.h"
#include "mozilla/MathAlgorithms.h"
#include "mozilla/PodOperations.h"
#include "mozilla/ThreadLocal.h"
#include <stdio.h>
@ -32,7 +33,30 @@ using mozilla::PodArrayZero;
JS_PUBLIC_DATA(uint32_t) OOM_maxAllocations = UINT32_MAX;
JS_PUBLIC_DATA(uint32_t) OOM_counter = 0;
JS_PUBLIC_DATA(bool) OOM_failAlways = true;
#endif
namespace js {
namespace oom {
JS_PUBLIC_DATA(uint32_t) targetThread = 0;
JS_PUBLIC_DATA(mozilla::ThreadLocal<uint32_t>) threadType;
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 // defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
JS_PUBLIC_API(void)
JS_Assert(const char* s, const char* file, int ln)

Просмотреть файл

@ -1451,19 +1451,26 @@ HelperThread::threadLoop()
}
// 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");
}
}
}