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:
parent
4dd71ce221
commit
abaf8347a5
16
include/v8.h
16
include/v8.h
@ -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.
|
||||
*/
|
||||
|
31
src/api.cc
31
src/api.cc
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
53
src/top.cc
53
src/top.cc
@ -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();
|
||||
|
10
src/top.h
10
src/top.h
@ -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.
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user