Revision: 5043
Author: [email protected]
Date: Mon Jul 12 06:17:27 2010
Log: Allow to capture stack trace for uncaught exceptions
Review URL: http://codereview.chromium.org/2961003
http://code.google.com/p/v8/source/detail?r=5043

Modified:
 /branches/bleeding_edge/include/v8.h
 /branches/bleeding_edge/src/api.cc
 /branches/bleeding_edge/src/debug.cc
 /branches/bleeding_edge/src/messages.cc
 /branches/bleeding_edge/src/messages.h
 /branches/bleeding_edge/src/messages.js
 /branches/bleeding_edge/src/top.cc
 /branches/bleeding_edge/src/top.h
 /branches/bleeding_edge/test/cctest/test-api.cc

=======================================
--- /branches/bleeding_edge/include/v8.h        Fri Jul  2 00:39:42 2010
+++ /branches/bleeding_edge/include/v8.h        Mon Jul 12 06:17:27 2010
@@ -693,6 +693,13 @@
    */
   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 @@
    */
   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.
    */
=======================================
--- /branches/bleeding_edge/src/api.cc  Thu Jun 24 06:56:35 2010
+++ /branches/bleeding_edge/src/api.cc  Mon Jul 12 06:17:27 2010
@@ -1436,6 +1436,22 @@
   i::Handle<i::Object> data(i::Script::cast(script->value())->data());
   return scope.Close(Utils::ToLocal(data));
 }
+
+
+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,
@@ -1583,7 +1599,9 @@
     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);
 }


@@ -3780,6 +3798,17 @@
     }
   }
 }
+
+
+void V8::SetCaptureStackTraceForUncaughtExceptions(
+      bool capture,
+      int frame_limit,
+      StackTrace::StackTraceOptions options) {
+  i::Top::SetCaptureStackTraceForUncaughtExceptions(
+      capture,
+      frame_limit,
+      options);
+}


 void V8::SetCounterFunction(CounterLookupCallback callback) {
=======================================
--- /branches/bleeding_edge/src/debug.cc        Tue Jul  6 05:10:49 2010
+++ /branches/bleeding_edge/src/debug.cc        Mon Jul 12 06:17:27 2010
@@ -759,7 +759,7 @@
   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;
   }
=======================================
--- /branches/bleeding_edge/src/messages.cc     Mon Mar  8 22:38:33 2010
+++ /branches/bleeding_edge/src/messages.cc     Mon Jul 12 06:17:27 2010
@@ -66,7 +66,8 @@
     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> 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
=======================================
--- /branches/bleeding_edge/src/messages.h      Mon May 25 03:05:56 2009
+++ /branches/bleeding_edge/src/messages.h      Mon Jul 12 06:17:27 2010
@@ -96,7 +96,8 @@
   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);
=======================================
--- /branches/bleeding_edge/src/messages.js     Wed Jul  7 03:28:22 2010
+++ /branches/bleeding_edge/src/messages.js     Mon Jul 12 06:17:27 2010
@@ -601,18 +601,22 @@
 }


-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);
 }


=======================================
--- /branches/bleeding_edge/src/top.cc  Tue May 25 02:18:08 2010
+++ /branches/bleeding_edge/src/top.cc  Mon Jul 12 06:17:27 2010
@@ -44,6 +44,11 @@

 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 @@
 }


-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 @@
   }

   stack_trace->set_length(Smi::FromInt(frames_seen));
-  return scope.Close(Utils::StackTraceToLocal(stack_trace));
+  return stack_trace;
 }


@@ -681,10 +685,7 @@
   // 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();
 }
@@ -776,25 +777,6 @@
     }
   }
 }
-
-
-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,
@@ -869,8 +851,15 @@
       // 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);
     }
   }

@@ -995,6 +984,16 @@
   clear_pending_exception();
   return true;
 }
+
+
+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() {
=======================================
--- /branches/bleeding_edge/src/top.h   Thu May  6 00:32:44 2010
+++ /branches/bleeding_edge/src/top.h   Mon Jul 12 06:17:27 2010
@@ -226,6 +226,11 @@
         (thread_local_.catcher_ != NULL) &&
         (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.
@@ -266,7 +271,7 @@
   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 @@
                       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.
=======================================
--- /branches/bleeding_edge/test/cctest/test-api.cc     Wed Jul  7 04:31:33 2010
+++ /branches/bleeding_edge/test/cctest/test-api.cc     Mon Jul 12 06:17:27 2010
@@ -10348,6 +10348,40 @@
   ASSERT(!detailed_result.IsEmpty());
   ASSERT(detailed_result->IsObject());
 }
+
+
+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.

--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev

Reply via email to