Commit 17b498c9 authored by sgjesse@chromium.org's avatar sgjesse@chromium.org

A new stack guard break flag DEBUGCOMMAND has been introduced. This is used to...

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@1508 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 74722e19
......@@ -688,7 +688,7 @@ Object* Debug::Break(Arguments args) {
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 @@ void Debugger::OnException(Handle<Object> exception, bool uncaught) {
}
// 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 @@ void Debugger::OnDebugBreak(Handle<Object> break_points_hit) {
}
// Process debug event
ProcessDebugEvent(v8::Break, event_data);
ProcessDebugEvent(v8::Break, event_data, auto_continue);
}
......@@ -1564,7 +1565,7 @@ void Debugger::OnBeforeCompile(Handle<Script> script) {
}
// Process debug event
ProcessDebugEvent(v8::BeforeCompile, event_data);
ProcessDebugEvent(v8::BeforeCompile, event_data, false);
}
......@@ -1625,7 +1626,7 @@ void Debugger::OnAfterCompile(Handle<Script> script, Handle<JSFunction> fun) {
return;
}
// Process debug event
ProcessDebugEvent(v8::AfterCompile, event_data);
ProcessDebugEvent(v8::AfterCompile, event_data, false);
}
......@@ -1650,12 +1651,13 @@ void Debugger::OnNewFunction(Handle<JSFunction> function) {
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 @@ void Debugger::ProcessDebugEvent(v8::DebugEvent event,
}
// 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 @@ void Debugger::ProcessCommand(Vector<const uint16_t> command) {
}
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 @@ void DebugMessageThread::Run() {
// 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 @@ void DebugMessageThread::DebugEvent(v8::DebugEvent event,
}
// 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 @@ void DebugMessageThread::DebugEvent(v8::DebugEvent event,
SendMessage(str);
// Return from debug event processing is VM should be running.
if (running) {
if (running || (auto_continue && !HasCommands())) {
return;
}
}
......@@ -2049,6 +2070,10 @@ void DebugMessageThread::ProcessCommand(Vector<uint16_t> command) {
Logger::DebugTag("Put command on command_queue.");
command_queue_.Put(command_copy);
command_received_->Signal();
if (!Debug::InDebugger()) {
StackGuard::DebugCommand();
}
}
......
......@@ -418,20 +418,22 @@ class Debugger {
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 @@ class DebugMessageThread: public Thread {
// 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 @@ class DebugMessageThread: public Thread {
// 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.
......@@ -616,6 +622,12 @@ class EnterDebugger BASE_EMBEDDED {
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.
Debug::set_debugger_entry(prev_);
}
......
......@@ -318,6 +318,21 @@ void StackGuard::DebugBreak() {
}
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 @@ Object* Execution::DebugBreakHelper() {
}
}
// 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 @@ Object* Execution::DebugBreakHelper() {
}
// 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::DebugBreakHelper() {
Object* Execution::HandleStackGuardInterrupt() {
if (StackGuard::IsDebugBreak()) DebugBreakHelper();
if (StackGuard::IsDebugBreak() || StackGuard::IsDebugCommand()) {
DebugBreakHelper();
}
if (StackGuard::IsPreempted()) RuntimePreempt();
if (StackGuard::IsInterrupted()) {
// interrupt
......
......@@ -35,7 +35,8 @@ namespace v8 { namespace internal {
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 @@ class StackGuard BASE_EMBEDDED {
static void Interrupt();
static bool IsDebugBreak();
static void DebugBreak();
static bool IsDebugCommand();
static void DebugCommand();
static void Continue(InterruptFlag after_what);
private:
......
......@@ -136,6 +136,9 @@ DEFINE_int(min_preparse_length, 1024,
// 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")
......
......@@ -3244,7 +3244,6 @@ void MessageQueueDebuggerThread::Run() {
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 @@ TEST(MessageQueues) {
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 @@ void BreakpointsDebuggerThread::Run() {
"\"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 @@ void BreakpointsDebuggerThread::Run() {
// 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 @@ void BreakpointsDebuggerThread::Run() {
// 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 @@ static void HostDispatchHandlerHitCount(void* dispatch, void *data) {
// 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 @@ TEST(DebuggerHostDispatch) {
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);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment