Allow to capture stack trace for uncaught exceptions

Review URL: http://codereview.chromium.org/2961003

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@5043 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
yurys@chromium.org 2010-07-12 13:17:27 +00:00
parent 4dd71ce221
commit abaf8347a5
9 changed files with 130 additions and 40 deletions

View File

@ -693,6 +693,13 @@ class V8EXPORT Message {
*/
Handle<Value> GetScriptData() const;
/**
* Exception stack trace. By default stack traces are not captured for
* uncaught exceptions. SetCaptureStackTraceForUncaughtExceptions allows
* to change this option.
*/
Handle<StackTrace> GetStackTrace() const;
/**
* Returns the number, 1-based, of the line where the error occurred.
*/
@ -2458,6 +2465,15 @@ class V8EXPORT V8 {
*/
static void RemoveMessageListeners(MessageCallback that);
/**
* Tells V8 to capture current stack trace when uncaught exception occurs
* and report it to the message listeners. The option is off by default.
*/
static void SetCaptureStackTraceForUncaughtExceptions(
bool capture,
int frame_limit = 10,
StackTrace::StackTraceOptions options = StackTrace::kOverview);
/**
* Sets V8 flags from a string.
*/

View File

@ -1438,6 +1438,22 @@ v8::Handle<Value> Message::GetScriptData() const {
}
v8::Handle<v8::StackTrace> Message::GetStackTrace() const {
if (IsDeadCheck("v8::Message::GetStackTrace()")) {
return Local<v8::StackTrace>();
}
ENTER_V8;
HandleScope scope;
i::Handle<i::JSObject> obj =
i::Handle<i::JSObject>::cast(Utils::OpenHandle(this));
i::Handle<i::Object> stackFramesObj = GetProperty(obj, "stackFrames");
if (!stackFramesObj->IsJSArray()) return v8::Handle<v8::StackTrace>();
i::Handle<i::JSArray> stackTrace =
i::Handle<i::JSArray>::cast(stackFramesObj);
return scope.Close(Utils::StackTraceToLocal(stackTrace));
}
static i::Handle<i::Object> CallV8HeapFunction(const char* name,
i::Handle<i::Object> recv,
int argc,
@ -1583,7 +1599,9 @@ Local<StackTrace> StackTrace::CurrentStackTrace(int frame_limit,
StackTraceOptions options) {
if (IsDeadCheck("v8::StackTrace::CurrentStackTrace()")) Local<StackTrace>();
ENTER_V8;
return i::Top::CaptureCurrentStackTrace(frame_limit, options);
i::Handle<i::JSArray> stackTrace =
i::Top::CaptureCurrentStackTrace(frame_limit, options);
return Utils::StackTraceToLocal(stackTrace);
}
@ -3782,6 +3800,17 @@ void V8::RemoveMessageListeners(MessageCallback that) {
}
void V8::SetCaptureStackTraceForUncaughtExceptions(
bool capture,
int frame_limit,
StackTrace::StackTraceOptions options) {
i::Top::SetCaptureStackTraceForUncaughtExceptions(
capture,
frame_limit,
options);
}
void V8::SetCounterFunction(CounterLookupCallback callback) {
if (IsDeadCheck("v8::V8::SetCounterFunction()")) return;
i::StatsTable::SetCounterFunction(callback);

View File

@ -759,7 +759,7 @@ bool Debug::CompileDebuggerScript(int index) {
if (caught_exception) {
Handle<Object> message = MessageHandler::MakeMessageObject(
"error_loading_debugger", NULL, Vector<Handle<Object> >::empty(),
Handle<String>());
Handle<String>(), Handle<JSArray>());
MessageHandler::ReportMessage(NULL, message);
return false;
}

View File

@ -66,7 +66,8 @@ Handle<Object> MessageHandler::MakeMessageObject(
const char* type,
MessageLocation* loc,
Vector< Handle<Object> > args,
Handle<String> stack_trace) {
Handle<String> stack_trace,
Handle<JSArray> stack_frames) {
// Build error message object
v8::HandleScope scope; // Instantiate a closeable HandleScope for EscapeFrom.
Handle<Object> type_str = Factory::LookupAsciiSymbol(type);
@ -90,13 +91,17 @@ Handle<Object> MessageHandler::MakeMessageObject(
Handle<Object> stack_trace_val = stack_trace.is_null()
? Factory::undefined_value()
: Handle<Object>::cast(stack_trace);
const int argc = 6;
Handle<Object> stack_frames_val = stack_frames.is_null()
? Factory::undefined_value()
: Handle<Object>::cast(stack_frames);
const int argc = 7;
Object** argv[argc] = { type_str.location(),
array.location(),
start_handle.location(),
end_handle.location(),
script.location(),
stack_trace_val.location() };
stack_trace_val.location(),
stack_frames_val.location() };
// Setup a catch handler to catch exceptions in creating the message. This
// handler is non-verbose to avoid calling MakeMessage recursively in case of

View File

@ -96,7 +96,8 @@ class MessageHandler {
static Handle<Object> MakeMessageObject(const char* type,
MessageLocation* loc,
Vector< Handle<Object> > args,
Handle<String> stack_trace);
Handle<String> stack_trace,
Handle<JSArray> stack_frames);
// Report a formatted message (needs JS allocation).
static void ReportMessage(MessageLocation* loc, Handle<Object> message);

View File

@ -601,18 +601,22 @@ function GetPositionInLine(message) {
}
function ErrorMessage(type, args, startPos, endPos, script, stackTrace) {
function ErrorMessage(type, args, startPos, endPos, script, stackTrace,
stackFrames) {
this.startPos = startPos;
this.endPos = endPos;
this.type = type;
this.args = args;
this.script = script;
this.stackTrace = stackTrace;
this.stackFrames = stackFrames;
}
function MakeMessage(type, args, startPos, endPos, script, stackTrace) {
return new ErrorMessage(type, args, startPos, endPos, script, stackTrace);
function MakeMessage(type, args, startPos, endPos, script, stackTrace,
stackFrames) {
return new ErrorMessage(type, args, startPos, endPos, script, stackTrace,
stackFrames);
}

View File

@ -44,6 +44,11 @@ Mutex* Top::break_access_ = OS::CreateMutex();
NoAllocationStringAllocator* preallocated_message_space = NULL;
bool capture_stack_trace_for_uncaught_exceptions = false;
int stack_trace_for_uncaught_exceptions_frame_limit = 0;
StackTrace::StackTraceOptions stack_trace_for_uncaught_exceptions_options =
StackTrace::kOverview;
Address top_addresses[] = {
#define C(name) reinterpret_cast<Address>(Top::name()),
TOP_ADDRESS_LIST(C)
@ -365,9 +370,8 @@ Handle<String> Top::StackTraceString() {
}
Local<StackTrace> Top::CaptureCurrentStackTrace(
Handle<JSArray> Top::CaptureCurrentStackTrace(
int frame_limit, StackTrace::StackTraceOptions options) {
v8::HandleScope scope;
// Ensure no negative values.
int limit = Max(frame_limit, 0);
Handle<JSArray> stack_trace = Factory::NewJSArray(frame_limit);
@ -443,7 +447,7 @@ Local<StackTrace> Top::CaptureCurrentStackTrace(
}
stack_trace->set_length(Smi::FromInt(frames_seen));
return scope.Close(Utils::StackTraceToLocal(stack_trace));
return stack_trace;
}
@ -681,10 +685,7 @@ Failure* Top::StackOverflow() {
// TODO(1240995): To avoid having to call JavaScript code to compute
// the message for stack overflow exceptions which is very likely to
// double fault with another stack overflow exception, we use a
// precomputed message. This is somewhat problematic in that it
// doesn't use ReportUncaughtException to determine the location
// from where the exception occurred. It should probably be
// reworked.
// precomputed message.
DoThrow(*exception, NULL, kStackOverflowMessage);
return Failure::Exception();
}
@ -778,25 +779,6 @@ void Top::ComputeLocation(MessageLocation* target) {
}
void Top::ReportUncaughtException(Handle<Object> exception,
MessageLocation* location,
Handle<String> stack_trace) {
Handle<Object> message;
if (!Bootstrapper::IsActive()) {
// It's not safe to try to make message objects while the bootstrapper
// is active since the infrastructure may not have been properly
// initialized.
message =
MessageHandler::MakeMessageObject("uncaught_exception",
location,
HandleVector<Object>(&exception, 1),
stack_trace);
}
// Report the uncaught exception.
MessageHandler::ReportMessage(location, message);
}
bool Top::ShouldReturnException(bool* is_caught_externally,
bool catchable_by_javascript) {
// Find the top-most try-catch handler.
@ -869,8 +851,15 @@ void Top::DoThrow(Object* exception,
// may not have been properly initialized.
Handle<String> stack_trace;
if (FLAG_trace_exception) stack_trace = StackTraceString();
Handle<JSArray> stack_trace_object;
if (report_exception && capture_stack_trace_for_uncaught_exceptions) {
stack_trace_object = Top::CaptureCurrentStackTrace(
stack_trace_for_uncaught_exceptions_frame_limit,
stack_trace_for_uncaught_exceptions_options);
}
message_obj = MessageHandler::MakeMessageObject("uncaught_exception",
location, HandleVector<Object>(&exception_handle, 1), stack_trace);
location, HandleVector<Object>(&exception_handle, 1), stack_trace,
stack_trace_object);
}
}
@ -997,6 +986,16 @@ bool Top::OptionalRescheduleException(bool is_bottom_call) {
}
void Top::SetCaptureStackTraceForUncaughtExceptions(
bool capture,
int frame_limit,
StackTrace::StackTraceOptions options) {
capture_stack_trace_for_uncaught_exceptions = capture;
stack_trace_for_uncaught_exceptions_frame_limit = frame_limit;
stack_trace_for_uncaught_exceptions_options = options;
}
bool Top::is_out_of_memory() {
if (has_pending_exception()) {
Object* e = pending_exception();

View File

@ -227,6 +227,11 @@ class Top {
(try_catch_handler() == thread_local_.catcher_);
}
static void SetCaptureStackTraceForUncaughtExceptions(
bool capture,
int frame_limit,
StackTrace::StackTraceOptions options);
// Tells whether the current context has experienced an out of memory
// exception.
static bool is_out_of_memory();
@ -266,7 +271,7 @@ class Top {
static void PrintStack(StringStream* accumulator);
static void PrintStack();
static Handle<String> StackTraceString();
static Local<StackTrace> CaptureCurrentStackTrace(
static Handle<JSArray> CaptureCurrentStackTrace(
int frame_limit,
StackTrace::StackTraceOptions options);
@ -302,9 +307,6 @@ class Top {
const char* message);
static bool ShouldReturnException(bool* is_caught_externally,
bool catchable_by_javascript);
static void ReportUncaughtException(Handle<Object> exception,
MessageLocation* location,
Handle<String> stack_trace);
// Attempts to compute the current source location, storing the
// result in the target out parameter.

View File

@ -10350,6 +10350,40 @@ THREADED_TEST(CaptureStackTrace) {
}
static void StackTraceForUncaughtExceptionListener(
v8::Handle<v8::Message> message,
v8::Handle<Value>) {
v8::Handle<v8::StackTrace> stack_trace = message->GetStackTrace();
CHECK_EQ(2, stack_trace->GetFrameCount());
checkStackFrame("origin", "foo", 2, 3, false, false,
stack_trace->GetFrame(0));
checkStackFrame("origin", "bar", 5, 3, false, false,
stack_trace->GetFrame(1));
}
TEST(CaptureStackTraceForUncaughtException) {
report_count = 0;
v8::HandleScope scope;
LocalContext env;
v8::V8::AddMessageListener(StackTraceForUncaughtExceptionListener);
v8::V8::SetCaptureStackTraceForUncaughtExceptions(true);
Script::Compile(v8_str("function foo() {\n"
" throw 1;\n"
"};\n"
"function bar() {\n"
" foo();\n"
"};"),
v8_str("origin"))->Run();
v8::Local<v8::Object> global = env->Global();
Local<Value> trouble = global->Get(v8_str("bar"));
CHECK(trouble->IsFunction());
Function::Cast(*trouble)->Call(global, 0, NULL);
v8::V8::SetCaptureStackTraceForUncaughtExceptions(false);
v8::V8::RemoveMessageListeners(StackTraceForUncaughtExceptionListener);
}
// Test that idle notification can be handled and eventually returns true.
THREADED_TEST(IdleNotification) {
bool rv = false;