Author: [email protected]
Date: Fri Mar 13 06:26:21 2009
New Revision: 1508

Modified:
    branches/bleeding_edge/src/debug.cc
    branches/bleeding_edge/src/debug.h
    branches/bleeding_edge/src/execution.cc
    branches/bleeding_edge/src/execution.h
    branches/bleeding_edge/src/flag-definitions.h
    branches/bleeding_edge/test/cctest/test-debug.cc

Log:
A new stack guard break flag DEBUGCOMMAND has been introduced. This is used  
to signal debug break due to debugger commands available in the queue for  
processing. If a stack guard break happens with this flag and not the  
DEBUGBREAK flag the no debug break event is generated and execution is  
resumed automatically when all debugger commands in the queue has been  
processed.

This makes it possible to remote debuggers to just add commands to the  
queue without having to request a break as well. As soon as any JavaScript  
executes the debugger commands will be processed and the response send to  
the remote debugger.

Currently hide this behind a flag (--debugger-auto-break) as the current  
command line debugger in Chrome is not designed for this new behaviour,  
whereas the new Chrome developer tools will use it.
Review URL: http://codereview.chromium.org/42173

Modified: branches/bleeding_edge/src/debug.cc
==============================================================================
--- branches/bleeding_edge/src/debug.cc (original)
+++ branches/bleeding_edge/src/debug.cc Fri Mar 13 06:26:21 2009
@@ -688,7 +688,7 @@
      ClearStepping();

      // Notify the debug event listeners.
-    Debugger::OnDebugBreak(break_points_hit);
+    Debugger::OnDebugBreak(break_points_hit, false);
    } else if (thread_local_.last_step_action_ != StepNone) {
      // Hold on to last step action as it is cleared by the call to
      // ClearStepping.
@@ -1508,12 +1508,13 @@
    }

    // Process debug event
-  ProcessDebugEvent(v8::Exception, event_data);
+  ProcessDebugEvent(v8::Exception, event_data, false);
    // Return to continue execution from where the exception was thrown.
  }


-void Debugger::OnDebugBreak(Handle<Object> break_points_hit) {
+void Debugger::OnDebugBreak(Handle<Object> break_points_hit,
+                            bool auto_continue) {
    HandleScope scope;

    // Debugger has already been entered by caller.
@@ -1539,7 +1540,7 @@
    }

    // Process debug event
-  ProcessDebugEvent(v8::Break, event_data);
+  ProcessDebugEvent(v8::Break, event_data, auto_continue);
  }


@@ -1564,7 +1565,7 @@
    }

    // Process debug event
-  ProcessDebugEvent(v8::BeforeCompile, event_data);
+  ProcessDebugEvent(v8::BeforeCompile, event_data, false);
  }


@@ -1625,7 +1626,7 @@
      return;
    }
    // Process debug event
-  ProcessDebugEvent(v8::AfterCompile, event_data);
+  ProcessDebugEvent(v8::AfterCompile, event_data, false);
  }


@@ -1650,12 +1651,13 @@
      return;
    }
    // Process debug event.
-  ProcessDebugEvent(v8::NewFunction, event_data);
+  ProcessDebugEvent(v8::NewFunction, event_data, false);
  }


  void Debugger::ProcessDebugEvent(v8::DebugEvent event,
-                                 Handle<Object> event_data) {
+                                 Handle<Object> event_data,
+                                 bool auto_continue) {
    HandleScope scope;

    // Create the execution state.
@@ -1666,7 +1668,7 @@
    }
    // First notify the builtin debugger.
    if (message_thread_ != NULL) {
-    message_thread_->DebugEvent(event, exec_state, event_data);
+    message_thread_->DebugEvent(event, exec_state, event_data,  
auto_continue);
    }
    // Notify registered debug event listener. This can be either a C or a
    // JavaScript function.
@@ -1773,6 +1775,15 @@
  }


+bool Debugger::HasCommands() {
+  if (message_thread_ != NULL) {
+    return message_thread_->HasCommands();
+  } else {
+    return false;
+  }
+}
+
+
  void Debugger::ProcessHostDispatch(void* dispatch) {
    if (message_thread_ != NULL) {
      message_thread_->ProcessHostDispatch(dispatch);
@@ -1904,7 +1915,8 @@
  // the VM.
  void DebugMessageThread::DebugEvent(v8::DebugEvent event,
                                      Handle<Object> exec_state,
-                                    Handle<Object> event_data) {
+                                    Handle<Object> event_data,
+                                    bool auto_continue) {
    HandleScope scope;

    if (!Debug::Load()) return;
@@ -1947,18 +1959,27 @@
    }

    // Notify the debugger that a debug event has occurred.
-  bool success = SetEventJSONFromEvent(event_data);
-  if (!success) {
-    // If failed to notify debugger just continue running.
-    return;
+  if (!auto_continue) {
+    bool success = SetEventJSONFromEvent(event_data);
+    if (!success) {
+      // If failed to notify debugger just continue running.
+      return;
+    }
    }

-  // Wait for requests from the debugger.
+  // Process requests from the debugger.
    host_running_ = false;
    while (true) {
+    // Wait for new command in the queue.
      command_received_->Wait();
-    Logger::DebugTag("Got request from command queue, in interactive  
loop.");
+
+    // The debug command interrupt flag might have been set when the  
command was
+    // added.
+    StackGuard::Continue(DEBUGCOMMAND);
+
+    // Get the command from the queue.
      Vector<uint16_t> command = command_queue_.Get();
+    Logger::DebugTag("Got request from command queue, in interactive  
loop.");
      ASSERT(!host_running_);
      if (!Debugger::debugger_active()) {
        host_running_ = true;
@@ -2031,7 +2052,7 @@
      SendMessage(str);

      // Return from debug event processing is VM should be running.
-    if (running) {
+    if (running || (auto_continue && !HasCommands())) {
        return;
      }
    }
@@ -2049,6 +2070,10 @@
    Logger::DebugTag("Put command on command_queue.");
    command_queue_.Put(command_copy);
    command_received_->Signal();
+
+  if (!Debug::InDebugger()) {
+    StackGuard::DebugCommand();
+  }
  }



Modified: branches/bleeding_edge/src/debug.h
==============================================================================
--- branches/bleeding_edge/src/debug.h  (original)
+++ branches/bleeding_edge/src/debug.h  Fri Mar 13 06:26:21 2009
@@ -418,20 +418,22 @@
    static Handle<Object> MakeCompileEvent(Handle<Script> script,
                                           bool before,
                                           bool* caught_exception);
-  static void OnDebugBreak(Handle<Object> break_points_hit);
+  static void OnDebugBreak(Handle<Object> break_points_hit, bool  
auto_continue);
    static void OnException(Handle<Object> exception, bool uncaught);
    static void OnBeforeCompile(Handle<Script> script);
    static void OnAfterCompile(Handle<Script> script,
                             Handle<JSFunction> fun);
    static void OnNewFunction(Handle<JSFunction> fun);
    static void ProcessDebugEvent(v8::DebugEvent event,
-                                Handle<Object> event_data);
+                                Handle<Object> event_data,
+                                bool auto_continue);
    static void SetEventListener(Handle<Object> callback, Handle<Object>  
data);
    static void SetMessageHandler(v8::DebugMessageHandler handler, void*  
data);
    static void SetHostDispatchHandler(v8::DebugHostDispatchHandler handler,
                                       void* data);
    static void SendMessage(Vector<uint16_t> message);
    static void ProcessCommand(Vector<const uint16_t> command);
+  static bool HasCommands();
    static void ProcessHostDispatch(void* dispatch);
    static void UpdateActiveDebugger();
    static Handle<Object> Call(Handle<JSFunction> fun,
@@ -528,7 +530,8 @@
    // when host_running_ is false.
    void DebugEvent(v8::DebugEvent,
                    Handle<Object> exec_state,
-                  Handle<Object> event_data);
+                  Handle<Object> event_data,
+                  bool auto_continue);
    // Puts event on the output queue.  Called by V8.
    // This is where V8 hands off
    // processing of the event to the DebugMessageThread thread,
@@ -546,6 +549,9 @@
    // Main function of DebugMessageThread thread.
    void Run();

+  // Check whether there are commands in the queue.
+  bool HasCommands() { return !command_queue_.IsEmpty(); }
+
    bool host_running_;  // Is the debugging host running or stopped?
    Semaphore* command_received_;  // Non-zero when command queue is  
non-empty.
    Semaphore* message_received_;  // Exactly equal to message queue length.
@@ -614,6 +620,12 @@
      if (prev_ == NULL && Debug::preemption_pending()) {
        StackGuard::Preempt();
        Debug::set_preemption_pending(false);
+    }
+
+    // If there are commands in the queue when leaving the debugger  
request that
+    // these commands are processed.
+    if (prev_ == NULL && Debugger::HasCommands()) {
+      StackGuard::DebugCommand();
      }

      // Leaving this debugger entry.

Modified: branches/bleeding_edge/src/execution.cc
==============================================================================
--- branches/bleeding_edge/src/execution.cc     (original)
+++ branches/bleeding_edge/src/execution.cc     Fri Mar 13 06:26:21 2009
@@ -318,6 +318,21 @@
  }


+bool StackGuard::IsDebugCommand() {
+  ExecutionAccess access;
+  return thread_local_.interrupt_flags_ & DEBUGCOMMAND;
+}
+
+
+void StackGuard::DebugCommand() {
+  if (FLAG_debugger_auto_break) {
+    ExecutionAccess access;
+    thread_local_.interrupt_flags_ |= DEBUGCOMMAND;
+    set_limits(kInterruptLimit, access);
+  }
+}
+
+
  void StackGuard::Continue(InterruptFlag after_what) {
    ExecutionAccess access;
    thread_local_.interrupt_flags_ &= ~static_cast<int>(after_what);
@@ -556,8 +571,18 @@
      }
    }

-  // Clear the debug request flag.
+  // Check for debug command break only.
+  bool debug_command_only =
+      StackGuard::IsDebugCommand() && !StackGuard::IsDebugBreak();
+
+  // Clear the debug request flags.
    StackGuard::Continue(DEBUGBREAK);
+  StackGuard::Continue(DEBUGCOMMAND);
+
+  // If debug command only and already in debugger ignore it.
+  if (debug_command_only && Debug::InDebugger()) {
+    return Heap::undefined_value();
+  }

    HandleScope scope;
    // Enter the debugger. Just continue if we fail to enter the debugger.
@@ -567,7 +592,7 @@
    }

    // Notify the debug event listeners.
-  Debugger::OnDebugBreak(Factory::undefined_value());
+  Debugger::OnDebugBreak(Factory::undefined_value(), debug_command_only);

    // Return to continue execution.
    return Heap::undefined_value();
@@ -575,7 +600,9 @@


  Object* Execution::HandleStackGuardInterrupt() {
-  if (StackGuard::IsDebugBreak()) DebugBreakHelper();
+  if (StackGuard::IsDebugBreak() || StackGuard::IsDebugCommand()) {
+    DebugBreakHelper();
+  }
    if (StackGuard::IsPreempted()) RuntimePreempt();
    if (StackGuard::IsInterrupted()) {
      // interrupt

Modified: branches/bleeding_edge/src/execution.h
==============================================================================
--- branches/bleeding_edge/src/execution.h      (original)
+++ branches/bleeding_edge/src/execution.h      Fri Mar 13 06:26:21 2009
@@ -35,7 +35,8 @@
  enum InterruptFlag {
    INTERRUPT = 1 << 0,
    DEBUGBREAK = 1 << 1,
-  PREEMPT = 1 << 2
+  DEBUGCOMMAND = 1 << 2,
+  PREEMPT = 1 << 3
  };

  class Execution : public AllStatic {
@@ -159,6 +160,8 @@
    static void Interrupt();
    static bool IsDebugBreak();
    static void DebugBreak();
+  static bool IsDebugCommand();
+  static void DebugCommand();
    static void Continue(InterruptFlag after_what);

   private:

Modified: branches/bleeding_edge/src/flag-definitions.h
==============================================================================
--- branches/bleeding_edge/src/flag-definitions.h       (original)
+++ branches/bleeding_edge/src/flag-definitions.h       Fri Mar 13 06:26:21 2009
@@ -136,6 +136,9 @@
  // debug.cc
  DEFINE_bool(remote_debugging, false, "enable remote debugging")
  DEFINE_bool(trace_debug_json, false, "trace debugging JSON  
request/response")
+DEFINE_bool(debugger_auto_break, false,
+            "automatically set the debug break flag when debugger commands  
are "
+            "in the queue (experimental)")

  // execution.cc
  DEFINE_bool(call_regexp, false, "allow calls to RegExp objects")

Modified: branches/bleeding_edge/test/cctest/test-debug.cc
==============================================================================
--- branches/bleeding_edge/test/cctest/test-debug.cc    (original)
+++ branches/bleeding_edge/test/cctest/test-debug.cc    Fri Mar 13 06:26:21  
2009
@@ -3244,7 +3244,6 @@
    v8::Debug::SendCommand(buffer_2, AsciiToUtf16(command_3, buffer_2));
    v8::Debug::SendCommand(buffer_2, AsciiToUtf16(command_3, buffer_2));
    v8::Debug::SendCommand(buffer_2, AsciiToUtf16(command_3, buffer_2));
-  v8::Debug::SendCommand(buffer_2, AsciiToUtf16(command_continue,  
buffer_2));
    message_queue_barriers.barrier_2.Wait();
    // Main thread compiles and runs source_2.
    // Queued commands are executed at the start of compilation of source_2.
@@ -3286,7 +3285,6 @@
    CompileRun(source_1);
    message_queue_barriers.barrier_1.Wait();
    message_queue_barriers.barrier_2.Wait();
-  v8::Debug::DebugBreak();
    CompileRun(source_2);
    message_queue_barriers.barrier_3.Wait();
    CompileRun(source_3);
@@ -3464,28 +3462,25 @@
        "\"type\":\"request\","
        "\"command\":\"setbreakpoint\","
        "\"arguments\":{\"type\":\"function\",\"target\":\"dog\",\"line\":3}}";
-  const char* command_3 = "{\"seq\":103,"
-      "\"type\":\"request\","
-      "\"command\":\"continue\"}";
-  const char* command_4 = "{\"seq\":104,"
+  const char* command_3 = "{\"seq\":104,"
        "\"type\":\"request\","
        "\"command\":\"evaluate\","
        "\"arguments\":{\"expression\":\"dog()\",\"disable_break\":false}}";
-  const char* command_5 = "{\"seq\":105,"
+  const char* command_4 = "{\"seq\":105,"
        "\"type\":\"request\","
        "\"command\":\"evaluate\","
        "\"arguments\":{\"expression\":\"x\",\"disable_break\":true}}";
-  const char* command_6 = "{\"seq\":106,"
+  const char* command_5 = "{\"seq\":106,"
        "\"type\":\"request\","
        "\"command\":\"continue\"}";
-  const char* command_7 = "{\"seq\":107,"
+  const char* command_6 = "{\"seq\":107,"
        "\"type\":\"request\","
        "\"command\":\"continue\"}";
-  const char* command_8 = "{\"seq\":108,"
+  const char* command_7 = "{\"seq\":108,"
       "\"type\":\"request\","
       "\"command\":\"evaluate\","
       "\"arguments\":{\"expression\":\"dog()\",\"disable_break\":true}}";
-  const char* command_9 = "{\"seq\":109,"
+  const char* command_8 = "{\"seq\":109,"
        "\"type\":\"request\","
        "\"command\":\"continue\"}";

@@ -3493,12 +3488,9 @@
    // v8 thread initializes, runs source_1
    breakpoints_barriers->barrier_1.Wait();
    // 1:Set breakpoint in cat().
-  v8::Debug::DebugBreak();
    v8::Debug::SendCommand(buffer, AsciiToUtf16(command_1, buffer));
    // 2:Set breakpoint in dog()
    v8::Debug::SendCommand(buffer, AsciiToUtf16(command_2, buffer));
-  // 3:Continue
-  v8::Debug::SendCommand(buffer, AsciiToUtf16(command_3, buffer));
    breakpoints_barriers->barrier_2.Wait();
    // v8 thread starts compiling source_2.
    // Automatic break happens, to run queued commands
@@ -3508,29 +3500,31 @@
    // message callback receives break event.
    breakpoints_barriers->semaphore_1->Wait();
    // 4:Evaluate dog() (which has a breakpoint).
-  v8::Debug::SendCommand(buffer, AsciiToUtf16(command_4, buffer));
+  v8::Debug::SendCommand(buffer, AsciiToUtf16(command_3, buffer));
    // v8 thread hits breakpoint in dog()
    breakpoints_barriers->semaphore_1->Wait();  // wait for break event
    // 5:Evaluate x
-  v8::Debug::SendCommand(buffer, AsciiToUtf16(command_5, buffer));
+  v8::Debug::SendCommand(buffer, AsciiToUtf16(command_4, buffer));
    // 6:Continue evaluation of dog()
-  v8::Debug::SendCommand(buffer, AsciiToUtf16(command_6, buffer));
+  v8::Debug::SendCommand(buffer, AsciiToUtf16(command_5, buffer));
    // dog() finishes.
    // 7:Continue evaluation of source_2, finish cat(17), hit breakpoint
    // in cat(19).
-  v8::Debug::SendCommand(buffer, AsciiToUtf16(command_7, buffer));
+  v8::Debug::SendCommand(buffer, AsciiToUtf16(command_6, buffer));
    // message callback gets break event
    breakpoints_barriers->semaphore_1->Wait();  // wait for break event
    // 8: Evaluate dog() with breaks disabled
-  v8::Debug::SendCommand(buffer, AsciiToUtf16(command_8, buffer));
+  v8::Debug::SendCommand(buffer, AsciiToUtf16(command_7, buffer));
    // 9: Continue evaluation of source2, reach end.
-  v8::Debug::SendCommand(buffer, AsciiToUtf16(command_9, buffer));
+  v8::Debug::SendCommand(buffer, AsciiToUtf16(command_8, buffer));
  }

  BreakpointsDebuggerThread breakpoints_debugger_thread;
  BreakpointsV8Thread breakpoints_v8_thread;

  TEST(RecursiveBreakpoints) {
+  i::FLAG_debugger_auto_break = true;
+
    // Create a V8 environment
    Barriers stack_allocated_breakpoints_barriers;
    stack_allocated_breakpoints_barriers.Initialize();
@@ -3802,13 +3796,15 @@
  // Test that clearing the debug event listener actually clears all break  
points
  // and related information.
  TEST(DebuggerHostDispatch) {
+  i::FLAG_debugger_auto_break = true;
+
    v8::HandleScope scope;
    DebugLocalContext env;

    const int kBufferSize = 1000;
    uint16_t buffer[kBufferSize];
    const char* command_continue =
-    "{\"seq\":106,"
+    "{\"seq\":0,"
       "\"type\":\"request\","
       "\"command\":\"continue\"}";

@@ -3818,10 +3814,10 @@
                                      NULL);

    // Fill a host dispatch and a continue command on the command queue  
before
-  // generating a debug break.
+  // running some code.
    v8::Debug::SendHostDispatch(NULL);
    v8::Debug::SendCommand(buffer, AsciiToUtf16(command_continue, buffer));
-  CompileRun("debugger");
+  CompileRun("void 0");

    // The host dispatch callback should be called.
    CHECK_EQ(1, host_dispatch_hit_count);

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

Reply via email to