From: mikhail.naganov@gmail.com Date: Wed, 14 Jul 2010 08:23:35 +0000 (+0000) Subject: Debugger: introduce parametrized debug break, the parameter is passed X-Git-Tag: upstream/4.7.83~21495 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=84ffa5610b0e44e51c349b9a17e0f69f5900dfff;p=platform%2Fupstream%2Fv8.git Debugger: introduce parametrized debug break, the parameter is passed back to EventListener to be able to dynamically specify behavior on asynchronously enforced VM breakouts. Review URL: http://codereview.chromium.org/2962007/show git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@5063 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- diff --git a/include/v8-debug.h b/include/v8-debug.h index c53b634..414fd86 100644 --- a/include/v8-debug.h +++ b/include/v8-debug.h @@ -76,7 +76,8 @@ enum DebugEvent { NewFunction = 3, BeforeCompile = 4, AfterCompile = 5, - ScriptCollected = 6 + ScriptCollected = 6, + BreakForCommand = 7 }; @@ -172,6 +173,13 @@ class EXPORT Debug { */ virtual Handle GetCallbackData() const = 0; + /** + * Client data passed to DebugBreakForCommand function. The + * debugger takes ownership of the data and will delete it even if + * there is no message handler. + */ + virtual ClientData* GetClientData() const = 0; + virtual ~EventDetails() {} }; @@ -248,6 +256,12 @@ class EXPORT Debug { // Break execution of JavaScript. static void DebugBreak(); + // Break execution of JavaScript (this method can be invoked from a + // non-VM thread) for further client command execution on a VM + // thread. Client data is then passed in EventDetails to + // EventCallback at the moment when the VM actually stops. + static void DebugBreakForCommand(ClientData* data = NULL); + // Message based interface. The message protocol is JSON. NOTE the message // handler thread is not supported any more parameter must be false. static void SetMessageHandler(MessageHandler handler, diff --git a/src/api.cc b/src/api.cc index 07d9eb0..b3c77d8 100644 --- a/src/api.cc +++ b/src/api.cc @@ -4213,6 +4213,12 @@ void Debug::DebugBreak() { } +void Debug::DebugBreakForCommand(ClientData* data) { + if (!i::V8::IsRunning()) return; + i::Debugger::EnqueueDebugCommand(data); +} + + static v8::Debug::MessageHandler message_handler = NULL; static void MessageHandlerWrapper(const v8::Debug::Message& message) { diff --git a/src/debug.cc b/src/debug.cc index 4922a62..7288135 100644 --- a/src/debug.cc +++ b/src/debug.cc @@ -1882,6 +1882,7 @@ int Debugger::host_dispatch_micros_ = 100 * 1000; DebuggerAgent* Debugger::agent_ = NULL; LockingCommandMessageQueue Debugger::command_queue_(kQueueInitialSize); Semaphore* Debugger::command_received_ = OS::CreateSemaphore(0); +LockingCommandMessageQueue Debugger::event_command_queue_(kQueueInitialSize); Handle Debugger::MakeJSObject(Vector constructor_name, @@ -2207,39 +2208,75 @@ void Debugger::ProcessDebugEvent(v8::DebugEvent event, event_data, auto_continue); } - // Notify registered debug event listener. This can be either a C or a - // JavaScript function. - if (!event_listener_.is_null()) { - if (event_listener_->IsProxy()) { - // C debug event listener. - Handle callback_obj(Handle::cast(event_listener_)); - v8::Debug::EventCallback2 callback = - FUNCTION_CAST(callback_obj->proxy()); - EventDetailsImpl event_details( - event, - Handle::cast(exec_state), - event_data, - event_listener_data_); - callback(event_details); - } else { - // JavaScript debug event listener. - ASSERT(event_listener_->IsJSFunction()); - Handle fun(Handle::cast(event_listener_)); - - // Invoke the JavaScript debug event listener. - const int argc = 4; - Object** argv[argc] = { Handle(Smi::FromInt(event)).location(), - exec_state.location(), - Handle::cast(event_data).location(), - event_listener_data_.location() }; - Handle result = Execution::TryCall(fun, Top::global(), - argc, argv, &caught_exception); - // Silently ignore exceptions from debug event listeners. + // Notify registered debug event listener. This can be either a C or + // a JavaScript function. Don't call event listener for v8::Break + // here, if it's only a debug command -- they will be processed later. + if ((event != v8::Break || !auto_continue) && !event_listener_.is_null()) { + CallEventCallback(event, exec_state, event_data, NULL); + } + // Process pending debug commands. + if (event == v8::Break) { + while (!event_command_queue_.IsEmpty()) { + CommandMessage command = event_command_queue_.Get(); + if (!event_listener_.is_null()) { + CallEventCallback(v8::BreakForCommand, + exec_state, + event_data, + command.client_data()); + } + command.Dispose(); } } } +void Debugger::CallEventCallback(v8::DebugEvent event, + Handle exec_state, + Handle event_data, + v8::Debug::ClientData* client_data) { + if (event_listener_->IsProxy()) { + CallCEventCallback(event, exec_state, event_data, client_data); + } else { + CallJSEventCallback(event, exec_state, event_data); + } +} + + +void Debugger::CallCEventCallback(v8::DebugEvent event, + Handle exec_state, + Handle event_data, + v8::Debug::ClientData* client_data) { + Handle callback_obj(Handle::cast(event_listener_)); + v8::Debug::EventCallback2 callback = + FUNCTION_CAST(callback_obj->proxy()); + EventDetailsImpl event_details( + event, + Handle::cast(exec_state), + Handle::cast(event_data), + event_listener_data_, + client_data); + callback(event_details); +} + + +void Debugger::CallJSEventCallback(v8::DebugEvent event, + Handle exec_state, + Handle event_data) { + ASSERT(event_listener_->IsJSFunction()); + Handle fun(Handle::cast(event_listener_)); + + // Invoke the JavaScript debug event listener. + const int argc = 4; + Object** argv[argc] = { Handle(Smi::FromInt(event)).location(), + exec_state.location(), + Handle::cast(event_data).location(), + event_listener_data_.location() }; + bool caught_exception = false; + Execution::TryCall(fun, Top::global(), argc, argv, &caught_exception); + // Silently ignore exceptions from debug event listeners. +} + + Handle Debugger::GetDebugContext() { never_unload_debugger_ = true; EnterDebugger debugger; @@ -2273,6 +2310,7 @@ void Debugger::NotifyMessageHandler(v8::DebugEvent event, bool sendEventMessage = false; switch (event) { case v8::Break: + case v8::BreakForCommand: sendEventMessage = !auto_continue; break; case v8::Exception: @@ -2560,6 +2598,17 @@ bool Debugger::HasCommands() { } +void Debugger::EnqueueDebugCommand(v8::Debug::ClientData* client_data) { + CommandMessage message = CommandMessage::New(Vector(), client_data); + event_command_queue_.Put(message); + + // Set the debug command break flag to have the command processed. + if (!Debug::InDebugger()) { + StackGuard::DebugCommand(); + } +} + + bool Debugger::IsDebuggerActive() { ScopedLock with(debugger_access_); @@ -2761,11 +2810,13 @@ v8::Debug::ClientData* MessageImpl::GetClientData() const { EventDetailsImpl::EventDetailsImpl(DebugEvent event, Handle exec_state, Handle event_data, - Handle callback_data) + Handle callback_data, + v8::Debug::ClientData* client_data) : event_(event), exec_state_(exec_state), event_data_(event_data), - callback_data_(callback_data) {} + callback_data_(callback_data), + client_data_(client_data) {} DebugEvent EventDetailsImpl::GetEvent() const { @@ -2793,6 +2844,11 @@ v8::Handle EventDetailsImpl::GetCallbackData() const { } +v8::Debug::ClientData* EventDetailsImpl::GetClientData() const { + return client_data_; +} + + CommandMessage::CommandMessage() : text_(Vector::empty()), client_data_(NULL) { } diff --git a/src/debug.h b/src/debug.h index fb92692..7bb4a42 100644 --- a/src/debug.h +++ b/src/debug.h @@ -566,18 +566,21 @@ class EventDetailsImpl : public v8::Debug::EventDetails { EventDetailsImpl(DebugEvent event, Handle exec_state, Handle event_data, - Handle callback_data); + Handle callback_data, + v8::Debug::ClientData* client_data); virtual DebugEvent GetEvent() const; virtual v8::Handle GetExecutionState() const; virtual v8::Handle GetEventData() const; virtual v8::Handle GetEventContext() const; virtual v8::Handle GetCallbackData() const; + virtual v8::Debug::ClientData* GetClientData() const; private: DebugEvent event_; // Debug event causing the break. - Handle exec_state_; // Current execution state. - Handle event_data_; // Data associated with the event. - Handle callback_data_; // User data passed with the callback when - // it was registered. + Handle exec_state_; // Current execution state. + Handle event_data_; // Data associated with the event. + Handle callback_data_; // User data passed with the callback + // when it was registered. + v8::Debug::ClientData* client_data_; // Data passed to DebugBreakForCommand. }; @@ -706,6 +709,9 @@ class Debugger { // Check whether there are commands in the command queue. static bool HasCommands(); + // Enqueue a debugger command to the command queue for event listeners. + static void EnqueueDebugCommand(v8::Debug::ClientData* client_data = NULL); + static Handle Call(Handle fun, Handle data, bool* pending_exception); @@ -753,6 +759,17 @@ class Debugger { static bool IsDebuggerActive(); private: + static void CallEventCallback(v8::DebugEvent event, + Handle exec_state, + Handle event_data, + v8::Debug::ClientData* client_data); + static void CallCEventCallback(v8::DebugEvent event, + Handle exec_state, + Handle event_data, + v8::Debug::ClientData* client_data); + static void CallJSEventCallback(v8::DebugEvent event, + Handle exec_state, + Handle event_data); static void ListenersChanged(); static Mutex* debugger_access_; // Mutex guarding debugger variables. @@ -775,6 +792,8 @@ class Debugger { static LockingCommandMessageQueue command_queue_; static Semaphore* command_received_; // Signaled for each command received. + static LockingCommandMessageQueue event_command_queue_; + friend class EnterDebugger; }; diff --git a/test/cctest/test-debug.cc b/test/cctest/test-debug.cc index 8ebf752..6a94bed 100644 --- a/test/cctest/test-debug.cc +++ b/test/cctest/test-debug.cc @@ -6650,4 +6650,69 @@ TEST(DebugEventContext) { CheckDebuggerUnloaded(); } + +static void* expected_break_data; +static bool was_debug_break_called; +static bool was_debug_event_called; +static void DebugEventBreakDataChecker(const v8::Debug::EventDetails& details) { + if (details.GetEvent() == v8::BreakForCommand) { + CHECK_EQ(expected_break_data, details.GetClientData()); + was_debug_event_called = true; + } else if (details.GetEvent() == v8::Break) { + was_debug_break_called = true; + } +} + +// Check that event details contain context where debug event occured. +TEST(DebugEventBreakData) { + v8::HandleScope scope; + DebugLocalContext env; + v8::Debug::SetDebugEventListener2(DebugEventBreakDataChecker); + + TestClientData::constructor_call_counter = 0; + TestClientData::destructor_call_counter = 0; + + expected_break_data = NULL; + was_debug_event_called = false; + was_debug_break_called = false; + v8::Debug::DebugBreakForCommand(); + v8::Script::Compile(v8::String::New("(function(x){return x;})(1);"))->Run(); + CHECK(was_debug_event_called); + CHECK(!was_debug_break_called); + + TestClientData* data1 = new TestClientData(); + expected_break_data = data1; + was_debug_event_called = false; + was_debug_break_called = false; + v8::Debug::DebugBreakForCommand(data1); + v8::Script::Compile(v8::String::New("(function(x){return x+1;})(1);"))->Run(); + CHECK(was_debug_event_called); + CHECK(!was_debug_break_called); + + expected_break_data = NULL; + was_debug_event_called = false; + was_debug_break_called = false; + v8::Debug::DebugBreak(); + v8::Script::Compile(v8::String::New("(function(x){return x+2;})(1);"))->Run(); + CHECK(!was_debug_event_called); + CHECK(was_debug_break_called); + + TestClientData* data2 = new TestClientData(); + expected_break_data = data2; + was_debug_event_called = false; + was_debug_break_called = false; + v8::Debug::DebugBreak(); + v8::Debug::DebugBreakForCommand(data2); + v8::Script::Compile(v8::String::New("(function(x){return x+3;})(1);"))->Run(); + CHECK(was_debug_event_called); + CHECK(was_debug_break_called); + + CHECK_EQ(2, TestClientData::constructor_call_counter); + CHECK_EQ(TestClientData::constructor_call_counter, + TestClientData::destructor_call_counter); + + v8::Debug::SetDebugEventListener(NULL); + CheckDebuggerUnloaded(); +} + #endif // ENABLE_DEBUGGER_SUPPORT