Commit 1827b521 authored by Camillo Bruni's avatar Camillo Bruni Committed by Commit Bot

[errors] Encapsulate error message in stack allocated objects

This CL introduces the FailureMessage and StackTraceFailureMessage objects.
They are force to be stack allocated and their first and last member contain
marker values. With the help of these markers we can easily extract the stored
information in external tools such as grokdump and crash.

Change-Id: Iec4f5195eec5a2bf08e1f674c9ced13d2345f030
Reviewed-on: https://chromium-review.googlesource.com/915067Reviewed-by: 's avatarAdam Klein <adamk@chromium.org>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Commit-Queue: Camillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#51295}
parent 4b145148
......@@ -119,16 +119,50 @@ DEFINE_CHECK_OP_IMPL(GT)
} // namespace base
} // namespace v8
namespace {
// FailureMessage is a stack allocated object which has a special marker field
// at the start and at the end. This makes it possible to retrieve the embedded
// message from the stack.
//
class FailureMessage {
public:
explicit FailureMessage(const char* format, va_list arguments) {
memset(&message_, 0, arraysize(message_));
v8::base::OS::VSNPrintF(&message_[0], arraysize(message_), format,
arguments);
}
static const uintptr_t kStartMarker = 0xdecade10;
static const uintptr_t kEndMarker = 0xdecade11;
static const int kMessageBufferSize = 1024;
uintptr_t start_marker_ = kStartMarker;
char message_[kMessageBufferSize];
uintptr_t end_marker_ = kEndMarker;
};
} // namespace
void V8_Fatal(const char* file, int line, const char* format, ...) {
va_list arguments;
va_start(arguments, format);
// Format the error message into a stack object for later retrieveal by the
// crash processor.
FailureMessage message(format, arguments);
va_end(arguments);
fflush(stdout);
fflush(stderr);
v8::base::OS::PrintError("\n\n#\n# Fatal error in %s, line %d\n# ", file,
line);
va_list arguments;
// Print the error message.
va_start(arguments, format);
v8::base::OS::VPrintError(format, arguments);
va_end(arguments);
v8::base::OS::PrintError("\n#\n");
// Print the message object's address to force stack allocation.
v8::base::OS::PrintError("\n#\n#\n#\n#FailureMessage Object: %p", &message);
if (v8::base::g_print_stack_trace) v8::base::g_print_stack_trace();
......
......@@ -315,63 +315,46 @@ Handle<String> Isolate::StackTraceString() {
}
}
void Isolate::PushStackTraceAndDie(unsigned int magic1, void* ptr1, void* ptr2,
unsigned int magic2) {
PushStackTraceAndDie(magic1, ptr1, ptr2, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, magic2);
}
void Isolate::PushStackTraceAndDie(unsigned int magic1, void* ptr1, void* ptr2,
void* ptr3, void* ptr4, void* ptr5,
void* ptr6, void* ptr7, void* ptr8,
unsigned int magic2) {
const int kMaxStackTraceSize = 32 * KB;
Handle<String> trace = StackTraceString();
uint8_t buffer[kMaxStackTraceSize];
int length = Min(kMaxStackTraceSize - 1, trace->length());
String::WriteToFlat(*trace, buffer, 0, length);
buffer[length] = '\0';
// TODO(dcarney): convert buffer to utf8?
base::OS::PrintError(
"Stacktrace:"
"\n magic1=%x magic2=%x ptr1=%p ptr2=%p ptr3=%p ptr4=%p ptr5=%p "
"ptr6=%p ptr7=%p ptr8=%p\n\n%s",
magic1, magic2, ptr1, ptr2, ptr3, ptr4, ptr5, ptr6, ptr7, ptr8,
reinterpret_cast<char*>(buffer));
PushCodeObjectsAndDie(0xDEADC0DE, ptr1, ptr2, ptr3, ptr4, ptr5, ptr6, ptr7,
ptr8, 0xDEADC0DE);
}
void Isolate::PushCodeObjectsAndDie(unsigned int magic1, void* ptr1, void* ptr2,
void* ptr3, void* ptr4, void* ptr5,
void* ptr6, void* ptr7, void* ptr8,
unsigned int magic2) {
const int kMaxCodeObjects = 16;
// Mark as volatile to lower the probability of optimizing code_objects
// away. The first and last entries are set to the magic markers, making it
// easier to spot the array on the stack.
void* volatile code_objects[kMaxCodeObjects + 2];
code_objects[0] = reinterpret_cast<void*>(magic1);
code_objects[kMaxCodeObjects + 1] = reinterpret_cast<void*>(magic2);
StackFrameIterator it(this);
int numCodeObjects = 0;
for (; !it.done() && numCodeObjects < kMaxCodeObjects; it.Advance()) {
code_objects[1 + numCodeObjects++] = it.frame()->unchecked_code();
}
// Keep the top raw code object pointers on the stack in the hope that the
// corresponding pages end up more frequently in the minidump.
base::OS::PrintError(
"\nCodeObjects (%p length=%i): 1:%p 2:%p 3:%p 4:%p..."
"\n magic1=%x magic2=%x ptr1=%p ptr2=%p ptr3=%p ptr4=%p ptr5=%p "
"ptr6=%p ptr7=%p ptr8=%p\n\n",
static_cast<void*>(code_objects[0]), numCodeObjects,
static_cast<void*>(code_objects[1]), static_cast<void*>(code_objects[2]),
static_cast<void*>(code_objects[3]), static_cast<void*>(code_objects[4]),
magic1, magic2, ptr1, ptr2, ptr3, ptr4, ptr5, ptr6, ptr7, ptr8);
void Isolate::PushStackTraceAndDie(void* ptr1, void* ptr2, void* ptr3,
void* ptr4) {
StackTraceFailureMessage message(this, ptr1, ptr2, ptr3, ptr4);
message.Print();
base::OS::Abort();
}
void StackTraceFailureMessage::Print() volatile {
// Print the details of this failure message object, including its own address
// to force stack allocation.
base::OS::PrintError(
"Stacktrace:\n ptr1=%p\n ptr2=%p\n ptr3=%p\n ptr4=%p\n "
"failure_message_object=%p\n%s",
ptr1_, ptr2_, ptr3_, ptr4_, this, &js_stack_trace_[0]);
}
StackTraceFailureMessage::StackTraceFailureMessage(Isolate* isolate, void* ptr1,
void* ptr2, void* ptr3,
void* ptr4) {
isolate_ = isolate;
ptr1_ = ptr1;
ptr2_ = ptr2;
ptr3_ = ptr3;
ptr4_ = ptr4;
// Write a stracktrace into the {js_stack_trace_} buffer.
const size_t buffer_length = arraysize(js_stack_trace_);
memset(&js_stack_trace_, 0, buffer_length);
FixedStringAllocator fixed(&js_stack_trace_[0], buffer_length - 1);
StringStream accumulator(&fixed, StringStream::kPrintObjectConcise);
isolate->PrintStack(&accumulator, Isolate::kPrintStackVerbose);
// Keeping a reference to the last code objects to increase likelyhood that
// they get included in the minidump.
const size_t code_objects_length = arraysize(code_objects_);
size_t i = 0;
StackFrameIterator it(isolate);
for (; !it.done() && i < code_objects_length; it.Advance()) {
code_objects_[i++] = it.frame()->unchecked_code();
}
}
namespace {
class FrameArrayBuilder {
......
......@@ -716,16 +716,10 @@ class Isolate {
Handle<String> StackTraceString();
// Stores a stack trace in a stack-allocated temporary buffer which will
// end up in the minidump for debugging purposes.
NO_INLINE(void PushStackTraceAndDie(unsigned int magic1, void* ptr1,
void* ptr2, unsigned int magic2));
NO_INLINE(void PushStackTraceAndDie(unsigned int magic1, void* ptr1,
void* ptr2, void* ptr3, void* ptr4,
void* ptr5, void* ptr6, void* ptr7,
void* ptr8, unsigned int magic2));
NO_INLINE(void PushCodeObjectsAndDie(unsigned int magic, void* ptr1,
void* ptr2, void* ptr3, void* ptr4,
void* ptr5, void* ptr6, void* ptr7,
void* ptr8, unsigned int magic2));
NO_INLINE(void PushStackTraceAndDie(void* ptr1 = nullptr,
void* ptr2 = nullptr,
void* ptr3 = nullptr,
void* ptr4 = nullptr));
Handle<FixedArray> CaptureCurrentStackTrace(
int frame_limit, StackTrace::StackTraceOptions options);
Handle<Object> CaptureSimpleStackTrace(Handle<JSReceiver> error_object,
......@@ -1874,6 +1868,29 @@ class CodeTracer final : public Malloced {
int scope_depth_;
};
class StackTraceFailureMessage {
public:
explicit StackTraceFailureMessage(Isolate* isolate, void* ptr1 = nullptr,
void* ptr2 = nullptr, void* ptr3 = nullptr,
void* ptr4 = nullptr);
V8_NOINLINE void Print() volatile;
static const uintptr_t kStartMarker = 0xdecade30;
static const uintptr_t kEndMarker = 0xdecade31;
static const int kStacktraceBufferSize = 32 * KB;
uintptr_t start_marker_ = kStartMarker;
void* isolate_;
void* ptr1_;
void* ptr2_;
void* ptr3_;
void* ptr4_;
void* code_objects_[4];
char js_stack_trace_[kStacktraceBufferSize];
uintptr_t end_marker_ = kEndMarker;
};
} // namespace internal
} // namespace v8
......
......@@ -211,8 +211,7 @@ Handle<JSReceiver> LookupIterator::GetRootForNonJSReceiver(
auto root =
handle(receiver->GetPrototypeChainRootMap(isolate)->prototype(), isolate);
if (root->IsNull(isolate)) {
unsigned int magic = 0xBBBBBBBB;
isolate->PushStackTraceAndDie(magic, *receiver, nullptr, magic);
isolate->PushStackTraceAndDie(*receiver);
}
return Handle<JSReceiver>::cast(root);
}
......
......@@ -571,8 +571,7 @@ void ConsumedPreParsedScopeData::RestoreData(Scope* scope) {
if (scope_data_->RemainingBytes() < kUint8Size) {
// Temporary debugging code for detecting inconsistent data. Write debug
// information on the stack, then crash.
data_->GetIsolate()->PushStackTraceAndDie(0xC0DEFEE, nullptr, nullptr,
0xC0DEFEE);
data_->GetIsolate()->PushStackTraceAndDie();
}
// scope_type is stored only in debug mode.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment