Bug 1191758 - Rework JS::FormatStackDump() to fix OOM handling r=terrence

This commit is contained in:
Jon Coppeard 2015-08-12 10:36:40 +01:00
parent 4444ba08a7
commit 94ee08d336
7 changed files with 113 additions and 52 deletions

View File

@ -483,31 +483,26 @@ SIMDObject::initClass(JSContext* cx, Handle<GlobalObject*> global)
i8x16 = CreateAndBindSimdClass<Int8x16Defn>(cx, global, SIMD, cx->names().int8x16);
if (!i8x16)
return nullptr;
global->setInt8x16TypeDescr(*i8x16);
RootedObject i16x8(cx);
i16x8 = CreateAndBindSimdClass<Int16x8Defn>(cx, global, SIMD, cx->names().int16x8);
if (!i16x8)
return nullptr;
global->setInt16x8TypeDescr(*i16x8);
RootedObject f32x4(cx);
f32x4 = CreateAndBindSimdClass<Float32x4Defn>(cx, global, SIMD, cx->names().float32x4);
if (!f32x4)
return nullptr;
global->setFloat32x4TypeDescr(*f32x4);
RootedObject i32x4(cx);
i32x4 = CreateAndBindSimdClass<Int32x4Defn>(cx, global, SIMD, cx->names().int32x4);
if (!i32x4)
return nullptr;
global->setInt32x4TypeDescr(*i32x4);
RootedObject f64x2(cx);
f64x2 = CreateAndBindSimdClass<Float64x2Defn>(cx, global, SIMD, cx->names().float64x2);
if (!f64x2)
return nullptr;
global->setFloat64x2TypeDescr(*f64x2);
// Everything is set up, install SIMD on the global object.
RootedValue SIMDValue(cx, ObjectValue(*SIMD));
@ -517,6 +512,11 @@ SIMDObject::initClass(JSContext* cx, Handle<GlobalObject*> global)
return nullptr;
}
global->setInt8x16TypeDescr(*i8x16);
global->setInt16x8TypeDescr(*i16x8);
global->setFloat32x4TypeDescr(*f32x4);
global->setInt32x4TypeDescr(*i32x4);
global->setFloat64x2TypeDescr(*f64x2);
global->setConstructor(JSProto_SIMD, SIMDValue);
return SIMD;
}

View File

@ -2005,6 +2005,9 @@ GetBacktrace(JSContext* cx, unsigned argc, Value* vp)
}
char* buf = JS::FormatStackDump(cx, nullptr, showArgs, showLocals, showThisProps);
if (!buf)
return false;
RootedString str(cx);
if (!(str = JS_NewStringCopyZ(cx, buf)))
return false;

View File

@ -0,0 +1,4 @@
// |jit-test| --no-ggc; allow-unhandlable-oom; --no-threads
load(libdir + 'oomTest.js');
oomTest(() => getBacktrace({args: true, locals: true, thisprops: true}));

View File

@ -800,8 +800,10 @@ InvalidateScriptsInZone(JSContext* cx, Zone* zone, const Vector<DebugModeOSREntr
continue;
if (script->hasIonScript()) {
if (!invalid.append(script->ionScript()->recompileInfo()))
if (!invalid.append(script->ionScript()->recompileInfo())) {
ReportOutOfMemory(cx);
return false;
}
}
// Cancel off-thread Ion compile for anything that has a

View File

@ -2906,8 +2906,10 @@ jit::IonScript::invalidate(JSContext* cx, bool resetUses, const char* reason)
{
JitSpew(JitSpew_IonInvalidate, " Invalidate IonScript %p: %s", this, reason);
RecompileInfoVector list;
if (!list.append(recompileInfo()))
if (!list.append(recompileInfo())) {
ReportOutOfMemory(cx);
return false;
}
Invalidate(cx, list, resetUses, true);
return true;
}
@ -2940,8 +2942,10 @@ jit::Invalidate(JSContext* cx, JSScript* script, bool resetUses, bool cancelOffT
RecompileInfoVector scripts;
MOZ_ASSERT(script->hasIonScript());
if (!scripts.append(script->ionScript()->recompileInfo()))
if (!scripts.append(script->ionScript()->recompileInfo())) {
ReportOutOfMemory(cx);
return false;
}
Invalidate(cx, scripts, resetUses, cancelOffThread);
return true;

View File

@ -656,6 +656,21 @@ FormatValue(JSContext* cx, const Value& vArg, JSAutoByteString& bytes)
return buf;
}
// Wrapper for JS_sprintf_append() that reports allocation failure to the
// context.
template <typename... Args>
static char*
sprintf_append(JSContext* cx, char* buf, Args&&... args)
{
char* result = JS_sprintf_append(buf, mozilla::Forward<Args>(args)...);
if (!result) {
ReportOutOfMemory(cx);
return nullptr;
}
return result;
}
static char*
FormatFrame(JSContext* cx, const ScriptFrameIter& iter, char* buf, int num,
bool showArgs, bool showLocals, bool showThisProps)
@ -682,14 +697,17 @@ FormatFrame(JSContext* cx, const ScriptFrameIter& iter, char* buf, int num,
// print the frame number and function name
if (funname) {
JSAutoByteString funbytes;
buf = JS_sprintf_append(buf, "%d %s(", num, funbytes.encodeLatin1(cx, funname));
char* str = funbytes.encodeLatin1(cx, funname);
if (!str)
return nullptr;
buf = sprintf_append(cx, buf, "%d %s(", num, str);
} else if (fun) {
buf = JS_sprintf_append(buf, "%d anonymous(", num);
buf = sprintf_append(cx, buf, "%d anonymous(", num);
} else {
buf = JS_sprintf_append(buf, "%d <TOP LEVEL>", num);
buf = sprintf_append(cx, buf, "%d <TOP LEVEL>", num);
}
if (!buf)
return buf;
return nullptr;
if (showArgs && iter.hasArgs()) {
BindingIter bi(script);
@ -714,6 +732,11 @@ FormatFrame(JSContext* cx, const ScriptFrameIter& iter, char* buf, int num,
JSAutoByteString valueBytes;
const char* value = FormatValue(cx, arg, valueBytes);
if (!value) {
if (cx->isThrowingOutOfMemory())
return nullptr;
cx->clearPendingException();
}
JSAutoByteString nameBytes;
const char* name = nullptr;
@ -721,40 +744,40 @@ FormatFrame(JSContext* cx, const ScriptFrameIter& iter, char* buf, int num,
if (i < iter.numFormalArgs()) {
MOZ_ASSERT(i == bi.argIndex());
name = nameBytes.encodeLatin1(cx, bi->name());
if (!buf)
if (!name)
return nullptr;
bi++;
}
if (value) {
buf = JS_sprintf_append(buf, "%s%s%s%s%s%s",
!first ? ", " : "",
name ? name :"",
name ? " = " : "",
arg.isString() ? "\"" : "",
value,
arg.isString() ? "\"" : "");
buf = sprintf_append(cx, buf, "%s%s%s%s%s%s",
!first ? ", " : "",
name ? name :"",
name ? " = " : "",
arg.isString() ? "\"" : "",
value,
arg.isString() ? "\"" : "");
if (!buf)
return buf;
return nullptr;
first = false;
} else {
buf = JS_sprintf_append(buf, " <Failed to get argument while inspecting stack frame>\n");
buf = sprintf_append(cx, buf,
" <Failed to get argument while inspecting stack frame>\n");
if (!buf)
return buf;
cx->clearPendingException();
return nullptr;
}
}
}
// print filename and line number
buf = JS_sprintf_append(buf, "%s [\"%s\":%d]\n",
fun ? ")" : "",
filename ? filename : "<unknown>",
lineno);
buf = sprintf_append(cx, buf, "%s [\"%s\":%d]\n",
fun ? ")" : "",
filename ? filename : "<unknown>",
lineno);
if (!buf)
return buf;
return nullptr;
// Note: Right now we don't dump the local variables anymore, because
@ -765,17 +788,21 @@ FormatFrame(JSContext* cx, const ScriptFrameIter& iter, char* buf, int num,
if (!thisVal.isUndefined()) {
JSAutoByteString thisValBytes;
RootedString thisValStr(cx, ToString<CanGC>(cx, thisVal));
const char* str = nullptr;
if (thisValStr &&
(str = thisValBytes.encodeLatin1(cx, thisValStr)))
{
buf = JS_sprintf_append(buf, " this = %s\n", str);
if (!buf)
return buf;
} else {
buf = JS_sprintf_append(buf, " <failed to get 'this' value>\n");
if (!thisValStr) {
if (cx->isThrowingOutOfMemory())
return nullptr;
cx->clearPendingException();
}
if (thisValStr) {
const char* str = thisValBytes.encodeLatin1(cx, thisValStr);
if (!str)
return nullptr;
buf = sprintf_append(cx, buf, " this = %s\n", str);
} else {
buf = sprintf_append(cx, buf, " <failed to get 'this' value>\n");
}
if (!buf)
return nullptr;
}
}
@ -784,8 +811,9 @@ FormatFrame(JSContext* cx, const ScriptFrameIter& iter, char* buf, int num,
AutoIdVector keys(cx);
if (!GetPropertyKeys(cx, obj, JSITER_OWNONLY, &keys)) {
if (cx->isThrowingOutOfMemory())
return nullptr;
cx->clearPendingException();
return buf;
}
RootedId id(cx);
@ -795,27 +823,44 @@ FormatFrame(JSContext* cx, const ScriptFrameIter& iter, char* buf, int num,
RootedValue v(cx);
if (!GetProperty(cx, obj, obj, id, &v)) {
buf = JS_sprintf_append(buf, " <Failed to fetch property while inspecting stack frame>\n");
if (cx->isThrowingOutOfMemory())
return nullptr;
cx->clearPendingException();
buf = sprintf_append(cx, buf,
" <Failed to fetch property while inspecting stack frame>\n");
if (!buf)
return nullptr;
continue;
}
JSAutoByteString nameBytes;
JSAutoByteString valueBytes;
const char* name = FormatValue(cx, key, nameBytes);
const char* value = FormatValue(cx, v, valueBytes);
if (name && value) {
buf = JS_sprintf_append(buf, " this.%s = %s%s%s\n",
name,
v.isString() ? "\"" : "",
value,
v.isString() ? "\"" : "");
if (!buf)
return buf;
} else {
buf = JS_sprintf_append(buf, " <Failed to format values while inspecting stack frame>\n");
if (!name) {
if (cx->isThrowingOutOfMemory())
return nullptr;
cx->clearPendingException();
}
JSAutoByteString valueBytes;
const char* value = FormatValue(cx, v, valueBytes);
if (!value) {
if (cx->isThrowingOutOfMemory())
return nullptr;
cx->clearPendingException();
}
if (name && value) {
buf = sprintf_append(cx, buf, " this.%s = %s%s%s\n",
name,
v.isString() ? "\"" : "",
value,
v.isString() ? "\"" : "");
} else {
buf = sprintf_append(cx, buf,
" <Failed to format values while inspecting stack frame>\n");
}
if (!buf)
return nullptr;
}
}
@ -830,6 +875,8 @@ JS::FormatStackDump(JSContext* cx, char* buf, bool showArgs, bool showLocals, bo
for (AllFramesIter i(cx); !i.done(); ++i) {
buf = FormatFrame(cx, i, buf, num, showArgs, showLocals, showThisProps);
if (!buf)
return nullptr;
num++;
}

View File

@ -31,6 +31,7 @@ ExtractWellSized(ExclusiveContext* cx, Buffer& cb)
CharT* tmp = cx->zone()->pod_realloc<CharT>(buf, capacity, length + 1);
if (!tmp) {
js_free(buf);
ReportOutOfMemory(cx);
return nullptr;
}
buf = tmp;