Index: src/debug.cc |
=================================================================== |
--- src/debug.cc (revision 1626) |
+++ src/debug.cc (working copy) |
@@ -1380,6 +1380,10 @@ |
v8::DebugHostDispatchHandler Debugger::host_dispatch_handler_ = NULL; |
void* Debugger::host_dispatch_handler_data_ = NULL; |
DebuggerAgent* Debugger::agent_ = NULL; |
+LockingMessageQueue Debugger::command_queue_(kQueueInitialSize); |
+LockingMessageQueue Debugger::message_queue_(kQueueInitialSize); |
+Semaphore* Debugger::command_received_ = OS::CreateSemaphore(0); |
+Semaphore* Debugger::message_received_ = OS::CreateSemaphore(0); |
Handle<Object> Debugger::MakeJSObject(Vector<const char> constructor_name, |
@@ -1666,9 +1670,9 @@ |
if (caught_exception) { |
return; |
} |
- // First notify the builtin debugger. |
- if (message_thread_ != NULL) { |
- message_thread_->DebugEvent(event, exec_state, event_data, auto_continue); |
+ // First notify the message handler if any. |
+ if (message_handler_ != NULL) { |
+ NotifyMessageHandler(event, exec_state, event_data, auto_continue); |
} |
// Notify registered debug event listener. This can be either a C or a |
// JavaScript function. |
@@ -1706,240 +1710,7 @@ |
} |
-void Debugger::SetEventListener(Handle<Object> callback, |
- Handle<Object> data) { |
- HandleScope scope; |
- |
- // Clear the global handles for the event listener and the event listener data |
- // object. |
- if (!event_listener_.is_null()) { |
- GlobalHandles::Destroy( |
- reinterpret_cast<Object**>(event_listener_.location())); |
- event_listener_ = Handle<Object>(); |
- } |
- if (!event_listener_data_.is_null()) { |
- GlobalHandles::Destroy( |
- reinterpret_cast<Object**>(event_listener_data_.location())); |
- event_listener_data_ = Handle<Object>(); |
- } |
- |
- // If there is a new debug event listener register it together with its data |
- // object. |
- if (!callback->IsUndefined() && !callback->IsNull()) { |
- event_listener_ = Handle<Object>::cast(GlobalHandles::Create(*callback)); |
- if (data.is_null()) { |
- data = Factory::undefined_value(); |
- } |
- event_listener_data_ = Handle<Object>::cast(GlobalHandles::Create(*data)); |
- } |
- |
- UpdateActiveDebugger(); |
-} |
- |
- |
-void Debugger::TearDown() { |
- if (message_thread_ != NULL) { |
- message_thread_->Stop(); |
- delete message_thread_; |
- message_thread_ = NULL; |
- } |
-} |
- |
- |
-void Debugger::SetMessageHandler(v8::DebugMessageHandler handler, void* data) { |
- message_handler_ = handler; |
- message_handler_data_ = data; |
- if (!message_thread_) { |
- message_thread_ = new DebugMessageThread(); |
- message_thread_->Start(); |
- } |
- UpdateActiveDebugger(); |
-} |
- |
- |
-void Debugger::SetHostDispatchHandler(v8::DebugHostDispatchHandler handler, |
- void* data) { |
- host_dispatch_handler_ = handler; |
- host_dispatch_handler_data_ = data; |
-} |
- |
- |
-// Posts an output message from the debugger to the debug_message_handler |
-// callback. This callback is part of the public API. Messages are |
-// kept internally as Vector<uint16_t> strings, which are allocated in various |
-// places and deallocated by the calling function sometime after this call. |
-void Debugger::SendMessage(Vector< uint16_t> message) { |
- if (message_handler_ != NULL) { |
- message_handler_(message.start(), message.length(), message_handler_data_); |
- } |
-} |
- |
- |
-void Debugger::ProcessCommand(Vector<const uint16_t> command) { |
- if (message_thread_ != NULL) { |
- message_thread_->ProcessCommand( |
- Vector<uint16_t>(const_cast<uint16_t *>(command.start()), |
- command.length())); |
- } |
-} |
- |
- |
-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); |
- } |
-} |
- |
- |
-void Debugger::UpdateActiveDebugger() { |
- set_debugger_active((message_thread_ != NULL && |
- message_handler_ != NULL) || |
- !event_listener_.is_null()); |
- if (!debugger_active() && message_thread_) { |
- message_thread_->OnDebuggerInactive(); |
- } |
- if (!debugger_active()) { |
- Debug::Unload(); |
- } |
-} |
- |
- |
-Handle<Object> Debugger::Call(Handle<JSFunction> fun, |
- Handle<Object> data, |
- bool* pending_exception) { |
- // Enter the debugger. |
- EnterDebugger debugger; |
- if (debugger.FailedToEnter() || !debugger.HasJavaScriptFrames()) { |
- return Factory::undefined_value(); |
- } |
- |
- // Create the execution state. |
- bool caught_exception = false; |
- Handle<Object> exec_state = MakeExecutionState(&caught_exception); |
- if (caught_exception) { |
- return Factory::undefined_value(); |
- } |
- |
- static const int kArgc = 2; |
- Object** argv[kArgc] = { exec_state.location(), data.location() }; |
- Handle<Object> result = Execution::Call(fun, Factory::undefined_value(), |
- kArgc, argv, pending_exception); |
- return result; |
-} |
- |
- |
-bool Debugger::StartAgent(const char* name, int port) { |
- if (Socket::Setup()) { |
- agent_ = new DebuggerAgent(name, port); |
- agent_->Start(); |
- return true; |
- } |
- |
- return false; |
-} |
- |
- |
-void Debugger::StopAgent() { |
- if (agent_ != NULL) { |
- agent_->Shutdown(); |
- agent_->Join(); |
- delete agent_; |
- agent_ = NULL; |
- } |
-} |
- |
- |
-DebugMessageThread::DebugMessageThread() |
- : host_running_(true), |
- command_queue_(kQueueInitialSize), |
- message_queue_(kQueueInitialSize), |
- keep_running_(true) { |
- command_received_ = OS::CreateSemaphore(0); |
- message_received_ = OS::CreateSemaphore(0); |
-} |
- |
-// Should only be done after the thread is done running. |
-DebugMessageThread::~DebugMessageThread() { |
- delete command_received_; |
- delete message_received_; |
-} |
- |
-void DebugMessageThread::Stop() { |
- keep_running_ = false; |
- SendMessage(Vector<uint16_t>(NULL, 0)); |
- Join(); |
-} |
- |
-// Puts an event coming from V8 on the queue. Creates |
-// a copy of the JSON formatted event string managed by the V8. |
-// Called by the V8 thread. |
-// The new copy of the event string is destroyed in Run(). |
-void DebugMessageThread::SendMessage(Vector<uint16_t> message) { |
- Vector<uint16_t> message_copy = message.Clone(); |
- Logger::DebugTag("Put message on event message_queue."); |
- message_queue_.Put(message_copy); |
- message_received_->Signal(); |
-} |
- |
- |
-bool DebugMessageThread::SetEventJSONFromEvent(Handle<Object> event_data) { |
- v8::HandleScope scope; |
- // Call toJSONProtocol on the debug event object. |
- v8::Local<v8::Object> api_event_data = |
- v8::Utils::ToLocal(Handle<JSObject>::cast(event_data)); |
- v8::Local<v8::String> fun_name = v8::String::New("toJSONProtocol"); |
- v8::Local<v8::Function> fun = |
- v8::Function::Cast(*api_event_data->Get(fun_name)); |
- v8::TryCatch try_catch; |
- v8::Local<v8::Value> json_event = *fun->Call(api_event_data, 0, NULL); |
- v8::Local<v8::String> json_event_string; |
- if (!try_catch.HasCaught()) { |
- if (!json_event->IsUndefined()) { |
- json_event_string = json_event->ToString(); |
- if (FLAG_trace_debug_json) { |
- PrintLn(json_event_string); |
- } |
- v8::String::Value val(json_event_string); |
- Vector<uint16_t> str(reinterpret_cast<uint16_t*>(*val), |
- json_event_string->Length()); |
- SendMessage(str); |
- } else { |
- SendMessage(Vector<uint16_t>::empty()); |
- } |
- } else { |
- PrintLn(try_catch.Exception()); |
- return false; |
- } |
- return true; |
-} |
- |
- |
-void DebugMessageThread::Run() { |
- // Sends debug events to an installed debugger message callback. |
- while (keep_running_) { |
- // Wait and Get are paired so that semaphore count equals queue length. |
- message_received_->Wait(); |
- Logger::DebugTag("Get message from event message_queue."); |
- Vector<uint16_t> message = message_queue_.Get(); |
- if (message.length() > 0) { |
- Debugger::SendMessage(message); |
- } |
- } |
-} |
- |
- |
-// This method is called by the V8 thread whenever a debug event occurs in |
-// the VM. |
-void DebugMessageThread::DebugEvent(v8::DebugEvent event, |
+void Debugger::NotifyMessageHandler(v8::DebugEvent event, |
Handle<Object> exec_state, |
Handle<Object> event_data, |
bool auto_continue) { |
@@ -1987,7 +1758,7 @@ |
// Notify the debugger that a debug event has occurred unless auto continue is |
// active in which case no event is send. |
if (!auto_continue) { |
- bool success = SetEventJSONFromEvent(event_data); |
+ bool success = SendEventMessage(event_data); |
if (!success) { |
// If failed to notify debugger just continue running. |
return; |
@@ -1995,7 +1766,6 @@ |
} |
// Process requests from the debugger. |
- host_running_ = false; |
while (true) { |
// Wait for new command in the queue. |
command_received_->Wait(); |
@@ -2007,9 +1777,7 @@ |
// 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; |
return; |
} |
@@ -2023,7 +1791,7 @@ |
continue; |
} |
- // Invoke the JavaScript to process the debug request. |
+ // Invoke JavaScript to process the debug request. |
v8::Local<v8::String> fun_name; |
v8::Local<v8::Function> fun; |
v8::Local<v8::Value> request; |
@@ -2072,9 +1840,6 @@ |
Vector<uint16_t> str(reinterpret_cast<uint16_t*>(*val), |
response->Length()); |
- // Set host_running_ correctly for nested debugger evaluations. |
- host_running_ = running; |
- |
// Return the result. |
SendMessage(str); |
@@ -2088,26 +1853,142 @@ |
} |
+void Debugger::SetEventListener(Handle<Object> callback, |
+ Handle<Object> data) { |
+ HandleScope scope; |
+ |
+ // Clear the global handles for the event listener and the event listener data |
+ // object. |
+ if (!event_listener_.is_null()) { |
+ GlobalHandles::Destroy( |
+ reinterpret_cast<Object**>(event_listener_.location())); |
+ event_listener_ = Handle<Object>(); |
+ } |
+ if (!event_listener_data_.is_null()) { |
+ GlobalHandles::Destroy( |
+ reinterpret_cast<Object**>(event_listener_data_.location())); |
+ event_listener_data_ = Handle<Object>(); |
+ } |
+ |
+ // If there is a new debug event listener register it together with its data |
+ // object. |
+ if (!callback->IsUndefined() && !callback->IsNull()) { |
+ event_listener_ = Handle<Object>::cast(GlobalHandles::Create(*callback)); |
+ if (data.is_null()) { |
+ data = Factory::undefined_value(); |
+ } |
+ event_listener_data_ = Handle<Object>::cast(GlobalHandles::Create(*data)); |
+ } |
+ |
+ UpdateActiveDebugger(); |
+} |
+ |
+ |
+void Debugger::SetMessageHandler(v8::DebugMessageHandler handler, void* data, |
+ bool message_handler_thread) { |
+ message_handler_ = handler; |
+ message_handler_data_ = data; |
+ if (!message_thread_ && message_handler_thread) { |
+ message_thread_ = new DebugMessageThread(); |
+ message_thread_->Start(); |
+ } |
+ UpdateActiveDebugger(); |
+} |
+ |
+ |
+void Debugger::SetHostDispatchHandler(v8::DebugHostDispatchHandler handler, |
+ void* data) { |
+ host_dispatch_handler_ = handler; |
+ host_dispatch_handler_data_ = data; |
+} |
+ |
+ |
+// Calls the registered debug message handler. This callback is part of the |
+// public API. Messages are kept internally as Vector<uint16_t> strings, which |
+// are allocated in various places and deallocated by the calling function |
+// sometime after this call. |
+void Debugger::InvokeMessageHandler(Vector<uint16_t> message) { |
+ if (message_handler_ != NULL) { |
+ message_handler_(message.start(), message.length(), message_handler_data_); |
+ } |
+} |
+ |
+ |
+void Debugger::SendMessage(Vector<uint16_t> message) { |
+ if (message_thread_ == NULL) { |
+ // If there is no message thread just invoke the message handler from the |
+ // V8 thread. |
+ InvokeMessageHandler(message); |
+ } else { |
+ // Put a copy of the message coming from V8 on the queue. The new copy of |
+ // the event string is destroyed by the message thread. |
+ Vector<uint16_t> message_copy = message.Clone(); |
+ Logger::DebugTag("Put message on event message_queue."); |
+ message_queue_.Put(message_copy); |
+ message_received_->Signal(); |
+ } |
+} |
+ |
+ |
+bool Debugger::SendEventMessage(Handle<Object> event_data) { |
+ v8::HandleScope scope; |
+ // Call toJSONProtocol on the debug event object. |
+ v8::Local<v8::Object> api_event_data = |
+ v8::Utils::ToLocal(Handle<JSObject>::cast(event_data)); |
+ v8::Local<v8::String> fun_name = v8::String::New("toJSONProtocol"); |
+ v8::Local<v8::Function> fun = |
+ v8::Function::Cast(*api_event_data->Get(fun_name)); |
+ v8::TryCatch try_catch; |
+ v8::Local<v8::Value> json_event = *fun->Call(api_event_data, 0, NULL); |
+ v8::Local<v8::String> json_event_string; |
+ if (!try_catch.HasCaught()) { |
+ if (!json_event->IsUndefined()) { |
+ json_event_string = json_event->ToString(); |
+ if (FLAG_trace_debug_json) { |
+ PrintLn(json_event_string); |
+ } |
+ v8::String::Value val(json_event_string); |
+ Vector<uint16_t> str(reinterpret_cast<uint16_t*>(*val), |
+ json_event_string->Length()); |
+ SendMessage(str); |
+ } else { |
+ SendMessage(Vector<uint16_t>::empty()); |
+ } |
+ } else { |
+ PrintLn(try_catch.Exception()); |
+ return false; |
+ } |
+ return true; |
+} |
+ |
+ |
// Puts a command coming from the public API on the queue. Creates |
// a copy of the command string managed by the debugger. Up to this |
// point, the command data was managed by the API client. Called |
// by the API client thread. This is where the API client hands off |
// processing of the command to the DebugMessageThread thread. |
// The new copy of the command is destroyed in HandleCommand(). |
-void DebugMessageThread::ProcessCommand(Vector<uint16_t> command) { |
- Vector<uint16_t> command_copy = command.Clone(); |
+void Debugger::ProcessCommand(Vector<const uint16_t> command) { |
+ // Make a copy of the command. Need to cast away const for Clone to work. |
+ Vector<uint16_t> command_copy = |
+ Vector<uint16_t>(const_cast<uint16_t*>(command.start()), |
+ command.length()).Clone(); |
Logger::DebugTag("Put command on command_queue."); |
command_queue_.Put(command_copy); |
command_received_->Signal(); |
- |
if (!Debug::InDebugger()) { |
StackGuard::DebugCommand(); |
} |
} |
+bool Debugger::HasCommands() { |
+ return !command_queue_.IsEmpty(); |
+} |
+ |
+ |
+void Debugger::ProcessHostDispatch(void* dispatch) { |
// Puts a host dispatch comming from the public API on the queue. |
-void DebugMessageThread::ProcessHostDispatch(void* dispatch) { |
uint16_t hack[3]; |
hack[0] = 0; |
hack[1] = reinterpret_cast<uint32_t>(dispatch) >> 16; |
@@ -2118,15 +1999,96 @@ |
} |
-void DebugMessageThread::OnDebuggerInactive() { |
- // Send an empty command to the debugger if in a break to make JavaScript run |
- // again if the debugger is closed. |
- if (!host_running_) { |
- ProcessCommand(Vector<uint16_t>::empty()); |
+void Debugger::UpdateActiveDebugger() { |
+ set_debugger_active((message_thread_ != NULL && |
+ message_handler_ != NULL) || |
+ !event_listener_.is_null()); |
+ if (!debugger_active() && message_thread_) { |
+ // Send an empty command to the debugger if in a break to make JavaScript |
+ // run again if the debugger is closed. |
+ ProcessCommand(Vector<const uint16_t>::empty()); |
} |
+ if (!debugger_active()) { |
+ Debug::Unload(); |
+ } |
} |
+Handle<Object> Debugger::Call(Handle<JSFunction> fun, |
+ Handle<Object> data, |
+ bool* pending_exception) { |
+ // Enter the debugger. |
+ EnterDebugger debugger; |
+ if (debugger.FailedToEnter() || !debugger.HasJavaScriptFrames()) { |
+ return Factory::undefined_value(); |
+ } |
+ |
+ // Create the execution state. |
+ bool caught_exception = false; |
+ Handle<Object> exec_state = MakeExecutionState(&caught_exception); |
+ if (caught_exception) { |
+ return Factory::undefined_value(); |
+ } |
+ |
+ static const int kArgc = 2; |
+ Object** argv[kArgc] = { exec_state.location(), data.location() }; |
+ Handle<Object> result = Execution::Call(fun, Factory::undefined_value(), |
+ kArgc, argv, pending_exception); |
+ return result; |
+} |
+ |
+ |
+bool Debugger::StartAgent(const char* name, int port) { |
+ if (Socket::Setup()) { |
+ agent_ = new DebuggerAgent(name, port); |
+ agent_->Start(); |
+ return true; |
+ } |
+ |
+ return false; |
+} |
+ |
+ |
+void Debugger::StopAgent() { |
+ if (agent_ != NULL) { |
+ agent_->Shutdown(); |
+ agent_->Join(); |
+ delete agent_; |
+ agent_ = NULL; |
+ } |
+} |
+ |
+ |
+void Debugger::TearDown() { |
+ if (message_thread_ != NULL) { |
+ message_thread_->Stop(); |
+ delete message_thread_; |
+ message_thread_ = NULL; |
+ } |
+} |
+ |
+ |
+void DebugMessageThread::Run() { |
+ // Sends debug events to an installed debugger message callback. |
+ while (keep_running_) { |
+ // Wait and Get are paired so that semaphore count equals queue length. |
+ Debugger::message_received_->Wait(); |
+ Logger::DebugTag("Get message from event message_queue."); |
+ Vector<uint16_t> message = Debugger::message_queue_.Get(); |
+ if (message.length() > 0) { |
+ Debugger::InvokeMessageHandler(message); |
+ } |
+ } |
+} |
+ |
+ |
+void DebugMessageThread::Stop() { |
+ keep_running_ = false; |
+ Debugger::SendMessage(Vector<uint16_t>(NULL, 0)); |
+ Join(); |
+} |
+ |
+ |
MessageQueue::MessageQueue(int size) : start_(0), end_(0), size_(size) { |
messages_ = NewArray<Vector<uint16_t> >(size); |
} |