DevTools: Add support for eventless host message dispatching.
authorpfeldman@chromium.org <pfeldman@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 22 Apr 2009 14:16:50 +0000 (14:16 +0000)
committerpfeldman@chromium.org <pfeldman@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 22 Apr 2009 14:16:50 +0000 (14:16 +0000)
Review URL: http://codereview.chromium.org/87026

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@1773 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

include/v8-debug.h
src/api.cc
src/debug.cc
src/debug.h
test/cctest/test-debug.cc

index fc93d76ddf1bfdb9fbe8a6ce63bdeba636e62c37..debeac15c08c4bb570c76eafb1edda9694260470 100644 (file)
@@ -124,12 +124,8 @@ class EXPORT Debug {
 
   /**
    * Debug host dispatch callback function.
-   *
-   * \param dispatch the dispatch value
-   * \param data the data value passed when registering the dispatch handler
    */
-  typedef void (*HostDispatchHandler)(ClientData* dispatch);
-
+  typedef void (*HostDispatchHandler)();
 
   // Set a C debug event listener.
   static bool SetDebugEventListener(EventCallback that,
@@ -149,8 +145,8 @@ class EXPORT Debug {
                           ClientData* client_data = NULL);
 
   // Dispatch interface.
-  static void SetHostDispatchHandler(HostDispatchHandler handler);
-  static void SendHostDispatch(ClientData* dispatch);
+  static void SetHostDispatchHandler(HostDispatchHandler handler,
+                                     int period = 100);
 
  /**
   * Run a JavaScript function in the debugger.
index 7845525da74440ec147fec77376efcb4efc225c4..9b681260bdd661a94ab4c81226c00f8d32a605bc 100644 (file)
@@ -3276,16 +3276,11 @@ void Debug::SendCommand(const uint16_t* command, int length,
 }
 
 
-void Debug::SetHostDispatchHandler(HostDispatchHandler handler) {
+void Debug::SetHostDispatchHandler(HostDispatchHandler handler,
+                                   int period) {
   EnsureInitialized("v8::Debug::SetHostDispatchHandler");
   ENTER_V8;
-  i::Debugger::SetHostDispatchHandler(handler);
-}
-
-
-void Debug::SendHostDispatch(ClientData* dispatch) {
-  if (!i::V8::HasBeenSetup()) return;
-  i::Debugger::ProcessHostDispatch(dispatch);
+  i::Debugger::SetHostDispatchHandler(handler, period);
 }
 
 
index da5952ba0d2b3079f7eab0409f3db23a3a80cb61..32a96a83a5be4c981e30b3801686d98b7a0bac4d 100644 (file)
@@ -1424,6 +1424,7 @@ DebugMessageThread* Debugger::message_thread_ = NULL;
 v8::Debug::MessageHandler Debugger::message_handler_ = NULL;
 bool Debugger::message_handler_cleared_ = false;
 v8::Debug::HostDispatchHandler Debugger::host_dispatch_handler_ = NULL;
+int Debugger::host_dispatch_micros_ = 100 * 1000;
 DebuggerAgent* Debugger::agent_ = NULL;
 LockingMessageQueue Debugger::command_queue_(kQueueInitialSize);
 LockingMessageQueue Debugger::message_queue_(kQueueInitialSize);
@@ -1827,7 +1828,17 @@ void Debugger::NotifyMessageHandler(v8::DebugEvent event,
   // Process requests from the debugger.
   while (true) {
     // Wait for new command in the queue.
-    command_received_->Wait();
+    if (Debugger::host_dispatch_handler_) {
+      // In case there is a host dispatch - do periodic dispatches.
+      if (!command_received_->Wait(host_dispatch_micros_)) {
+        // Timout expired, do the dispatch.
+        Debugger::host_dispatch_handler_();
+        continue;
+      }
+    } else {
+      // In case there is no host dispatch - just wait.
+      command_received_->Wait();
+    }
 
     // The debug command interrupt flag might have been set when the command was
     // added.
@@ -1842,19 +1853,6 @@ void Debugger::NotifyMessageHandler(v8::DebugEvent event,
       return;
     }
 
-    // Check if the command is a host dispatch.
-    if (command.IsHostDispatch()) {
-      if (Debugger::host_dispatch_handler_) {
-        Debugger::host_dispatch_handler_(command.client_data());
-        // Delete the dispatch.
-        command.Dispose();
-      }
-      if (auto_continue && !HasCommands()) {
-        return;
-      }
-      continue;
-    }
-
     // Invoke JavaScript to process the debug request.
     v8::Local<v8::String> fun_name;
     v8::Local<v8::Function> fun;
@@ -1971,8 +1969,10 @@ void Debugger::SetMessageHandler(v8::Debug::MessageHandler handler,
 }
 
 
-void Debugger::SetHostDispatchHandler(v8::Debug::HostDispatchHandler handler) {
+void Debugger::SetHostDispatchHandler(v8::Debug::HostDispatchHandler handler,
+                                      int period) {
   host_dispatch_handler_ = handler;
+  host_dispatch_micros_ = period * 1000;
 }
 
 
@@ -2067,19 +2067,6 @@ bool Debugger::HasCommands() {
 }
 
 
-void Debugger::ProcessHostDispatch(v8::Debug::ClientData* dispatch) {
-  // Puts a host dispatch comming from the public API on the queue.
-  Logger::DebugTag("Put dispatch on command_queue.");
-  command_queue_.Put(Message::NewHostDispatch(dispatch));
-  command_received_->Signal();
-
-  // Set the debug command break flag to have the host dispatch processed.
-  if (!Debug::InDebugger()) {
-    StackGuard::DebugCommand();
-  }
-}
-
-
 bool Debugger::IsDebuggerActive() {
   ScopedLock with(debugger_access_);
 
@@ -2168,17 +2155,14 @@ void DebugMessageThread::Stop() {
 
 
 Message::Message() : text_(Vector<uint16_t>::empty()),
-                     client_data_(NULL),
-                     is_host_dispatch_(false) {
+                     client_data_(NULL) {
 }
 
 
 Message::Message(const Vector<uint16_t>& text,
-                 v8::Debug::ClientData* data,
-                 bool is_host_dispatch)
+                 v8::Debug::ClientData* data)
     : text_(text),
-      client_data_(data),
-      is_host_dispatch_(is_host_dispatch) {
+      client_data_(data) {
 }
 
 
@@ -2193,19 +2177,9 @@ void Message::Dispose() {
 }
 
 
-bool Message::IsHostDispatch() const {
-  return is_host_dispatch_;
-}
-
-
 Message Message::NewCommand(const Vector<uint16_t>& command,
                             v8::Debug::ClientData* data) {
-  return Message(command.Clone(), data, false);
-}
-
-
-Message Message::NewHostDispatch(v8::Debug::ClientData* dispatch) {
-  return Message(Vector<uint16_t>::empty(), dispatch, true);
+  return Message(command.Clone(), data);
 }
 
 
@@ -2217,7 +2191,7 @@ Message Message::NewOutput(v8::Handle<v8::String> output,
     text = Vector<uint16_t>::New(output->Length());
     output->Write(text.start(), 0, output->Length());
   }
-  return Message(text, data, false);
+  return Message(text, data);
 }
 
 
index 45de9ffe8d4d41d68422f4ecb306eb6b35fc6339..ffd3da9b4762aff9277c767cf73024cc12defe4f 100644 (file)
@@ -409,7 +409,6 @@ class Message {
  public:
   static Message NewCommand(const Vector<uint16_t>& command,
                             v8::Debug::ClientData* data);
-  static Message NewHostDispatch(v8::Debug::ClientData* dispatch);
   static Message NewOutput(v8::Handle<v8::String> output,
                            v8::Debug::ClientData* data);
   static Message NewEmptyMessage();
@@ -418,17 +417,14 @@ class Message {
 
   // Deletes user data and disposes of the text.
   void Dispose();
-  bool IsHostDispatch() const;
   Vector<uint16_t> text() const { return text_; }
   v8::Debug::ClientData* client_data() const { return client_data_; }
  private:
   Message(const Vector<uint16_t>& text,
-          v8::Debug::ClientData* data,
-          bool is_host_dispatch);
+          v8::Debug::ClientData* data);
 
   Vector<uint16_t> text_;
   v8::Debug::ClientData* client_data_;
-  bool is_host_dispatch_;
 };
 
 // A Queue of Vector<uint16_t> objects.  A thread-safe version is
@@ -510,7 +506,8 @@ class Debugger {
   static void SetMessageHandler(v8::Debug::MessageHandler handler,
                                 bool message_handler_thread);
   static void TearDown();
-  static void SetHostDispatchHandler(v8::Debug::HostDispatchHandler handler);
+  static void SetHostDispatchHandler(v8::Debug::HostDispatchHandler handler,
+                                     int period);
 
   // Invoke the message handler function.
   static void InvokeMessageHandler(Message message);
@@ -529,7 +526,6 @@ class Debugger {
   // Check whether there are commands in the command queue.
   static bool HasCommands();
 
-  static void ProcessHostDispatch(v8::Debug::ClientData* dispatch);
   static Handle<Object> Call(Handle<JSFunction> fun,
                              Handle<Object> data,
                              bool* pending_exception);
@@ -576,6 +572,7 @@ class Debugger {
   static v8::Debug::MessageHandler message_handler_;
   static bool message_handler_cleared_;  // Was message handler cleared?
   static v8::Debug::HostDispatchHandler host_dispatch_handler_;
+  static int host_dispatch_micros_;
 
   static DebuggerAgent* agent_;
 
index bfbd54bd2abe12e056b288f4a16572c041bea9fa..fea07c564696d03c62538da2cd34616faf834d45 100644 (file)
@@ -3564,13 +3564,17 @@ TEST(MessageQueueExpandAndDestroy) {
                                   new TestClientData()));
     queue.Put(Message::NewCommand(Vector<uint16_t>::empty(),
                                   new TestClientData()));
-    queue.Put(Message::NewHostDispatch(new TestClientData()));
+    queue.Put(Message::NewCommand(Vector<uint16_t>::empty(),
+                                  new TestClientData()));
     ASSERT_EQ(0, TestClientData::destructor_call_counter);
     queue.Get().Dispose();
     ASSERT_EQ(1, TestClientData::destructor_call_counter);
-    queue.Put(Message::NewHostDispatch(new TestClientData()));
-    queue.Put(Message::NewHostDispatch(new TestClientData()));
-    queue.Put(Message::NewHostDispatch(new TestClientData()));
+    queue.Put(Message::NewCommand(Vector<uint16_t>::empty(),
+                                  new TestClientData()));
+    queue.Put(Message::NewCommand(Vector<uint16_t>::empty(),
+                                  new TestClientData()));
+    queue.Put(Message::NewCommand(Vector<uint16_t>::empty(),
+                                  new TestClientData()));
     queue.Put(Message::NewOutput(v8::Handle<v8::String>(),
                                  new TestClientData()));
     queue.Put(Message::NewEmptyMessage());
@@ -4219,53 +4223,107 @@ TEST(DebuggerClearMessageHandlerWhileActive) {
 }
 
 
-int host_dispatch_hit_count = 0;
-static void HostDispatchHandlerHitCount(v8::Debug::ClientData* dispatch) {
-  host_dispatch_hit_count++;
+/* Test DebuggerHostDispatch */
+/* In this test, the debugger waits for a command on a breakpoint
+ * and is dispatching host commands while in the infinite loop.
+ */
+
+class HostDispatchV8Thread : public v8::internal::Thread {
+ public:
+  void Run();
+};
+
+class HostDispatchDebuggerThread : public v8::internal::Thread {
+ public:
+  void Run();
+};
+
+Barriers* host_dispatch_barriers;
+
+static void HostDispatchMessageHandler(const uint16_t* message,
+                                       int length,
+                                       v8::Debug::ClientData* client_data) {
+  static char print_buffer[1000];
+  Utf16ToAscii(message, length, print_buffer);
+  printf("%s\n", print_buffer);
+  fflush(stdout);
+}
+
+
+static void HostDispatchDispatchHandler() {
+  host_dispatch_barriers->semaphore_1->Signal();
 }
 
 
-// Test that clearing the debug event listener actually clears all break points
-// and related information.
-TEST(DebuggerHostDispatch) {
-  i::FLAG_debugger_auto_break = true;
+void HostDispatchV8Thread::Run() {
+  const char* source_1 = "var y_global = 3;\n"
+    "function cat( new_value ) {\n"
+    "  var x = new_value;\n"
+    "  y_global = 4;\n"
+    "  x = 3 * x + 1;\n"
+    "  y_global = 5;\n"
+    "  return x;\n"
+    "}\n"
+    "\n";
+  const char* source_2 = "cat(17);\n";
 
   v8::HandleScope scope;
   DebugLocalContext env;
 
-  const int kBufferSize = 1000;
-  uint16_t buffer[kBufferSize];
-  const char* command_continue =
-    "{\"seq\":0,"
-     "\"type\":\"request\","
-     "\"command\":\"continue\"}";
+  // Setup message and host dispatch handlers.
+  v8::Debug::SetMessageHandler(HostDispatchMessageHandler);
+  v8::Debug::SetHostDispatchHandler(HostDispatchDispatchHandler, 10 /* ms */);
 
-  // Create an empty function to call for processing debug commands
-  v8::Local<v8::Function> empty =
-      CompileFunction(&env, "function empty(){}", "empty");
+  CompileRun(source_1);
+  host_dispatch_barriers->barrier_1.Wait();
+  host_dispatch_barriers->barrier_2.Wait();
+  CompileRun(source_2);
+}
 
-  // Setup message and host dispatch handlers.
-  v8::Debug::SetMessageHandler(DummyMessageHandler);
-  v8::Debug::SetHostDispatchHandler(HostDispatchHandlerHitCount);
 
-  // Send a host dispatch by itself.
-  v8::Debug::SendHostDispatch(NULL);
-  empty->Call(env->Global(), 0, NULL);  // Run JavaScript to activate debugger.
-  CHECK_EQ(1, host_dispatch_hit_count);
+void HostDispatchDebuggerThread::Run() {
+  const int kBufSize = 1000;
+  uint16_t buffer[kBufSize];
 
-  // Fill a host dispatch and a continue command on the command queue.
-  v8::Debug::SendHostDispatch(NULL);
-  v8::Debug::SendCommand(buffer, AsciiToUtf16(command_continue, buffer));
-  empty->Call(env->Global(), 0, NULL);  // Run JavaScript to activate debugger.
+  const char* command_1 = "{\"seq\":101,"
+      "\"type\":\"request\","
+      "\"command\":\"setbreakpoint\","
+      "\"arguments\":{\"type\":\"function\",\"target\":\"cat\",\"line\":3}}";
+  const char* command_2 = "{\"seq\":102,"
+      "\"type\":\"request\","
+      "\"command\":\"continue\"}";
 
-  // Fill a continue command and a host dispatch on the command queue.
-  v8::Debug::SendCommand(buffer, AsciiToUtf16(command_continue, buffer));
-  v8::Debug::SendHostDispatch(NULL);
-  empty->Call(env->Global(), 0, NULL);  // Run JavaScript to activate debugger.
-  empty->Call(env->Global(), 0, NULL);  // Run JavaScript to activate debugger.
+  // v8 thread initializes, runs source_1
+  host_dispatch_barriers->barrier_1.Wait();
+  // 1: Set breakpoint in cat().
+  v8::Debug::SendCommand(buffer, AsciiToUtf16(command_1, buffer));
+
+  host_dispatch_barriers->barrier_2.Wait();
+  // v8 thread starts compiling source_2.
+  // Break happens, to run queued commands and host dispatches.
+  // Wait for host dispatch to be processed.
+  host_dispatch_barriers->semaphore_1->Wait();
+  // 2: Continue evaluation
+  v8::Debug::SendCommand(buffer, AsciiToUtf16(command_2, buffer));
+}
+
+HostDispatchDebuggerThread host_dispatch_debugger_thread;
+HostDispatchV8Thread host_dispatch_v8_thread;
+
+
+TEST(DebuggerHostDispatch) {
+  i::FLAG_debugger_auto_break = true;
+
+  // Create a V8 environment
+  Barriers stack_allocated_host_dispatch_barriers;
+  stack_allocated_host_dispatch_barriers.Initialize();
+  host_dispatch_barriers = &stack_allocated_host_dispatch_barriers;
+
+  host_dispatch_v8_thread.Start();
+  host_dispatch_debugger_thread.Start();
 
-  // All the host dispatch callback should be called.
-  CHECK_EQ(3, host_dispatch_hit_count);
+  host_dispatch_v8_thread.Join();
+  host_dispatch_debugger_thread.Join();
 }