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
parent 6744793c
...@@ -76,7 +76,8 @@ enum DebugEvent { ...@@ -76,7 +76,8 @@ enum DebugEvent {
NewFunction = 3, NewFunction = 3,
BeforeCompile = 4, BeforeCompile = 4,
AfterCompile = 5, AfterCompile = 5,
ScriptCollected = 6 ScriptCollected = 6,
BreakForCommand = 7
}; };
...@@ -172,6 +173,13 @@ class EXPORT Debug { ...@@ -172,6 +173,13 @@ class EXPORT Debug {
*/ */
virtual Handle<Value> GetCallbackData() const = 0; virtual Handle<Value> 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() {} virtual ~EventDetails() {}
}; };
...@@ -248,6 +256,12 @@ class EXPORT Debug { ...@@ -248,6 +256,12 @@ class EXPORT Debug {
// Break execution of JavaScript. // Break execution of JavaScript.
static void DebugBreak(); 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 // Message based interface. The message protocol is JSON. NOTE the message
// handler thread is not supported any more parameter must be false. // handler thread is not supported any more parameter must be false.
static void SetMessageHandler(MessageHandler handler, static void SetMessageHandler(MessageHandler handler,
......
...@@ -4213,6 +4213,12 @@ void Debug::DebugBreak() { ...@@ -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 v8::Debug::MessageHandler message_handler = NULL;
static void MessageHandlerWrapper(const v8::Debug::Message& message) { static void MessageHandlerWrapper(const v8::Debug::Message& message) {
......
...@@ -1882,6 +1882,7 @@ int Debugger::host_dispatch_micros_ = 100 * 1000; ...@@ -1882,6 +1882,7 @@ int Debugger::host_dispatch_micros_ = 100 * 1000;
DebuggerAgent* Debugger::agent_ = NULL; DebuggerAgent* Debugger::agent_ = NULL;
LockingCommandMessageQueue Debugger::command_queue_(kQueueInitialSize); LockingCommandMessageQueue Debugger::command_queue_(kQueueInitialSize);
Semaphore* Debugger::command_received_ = OS::CreateSemaphore(0); Semaphore* Debugger::command_received_ = OS::CreateSemaphore(0);
LockingCommandMessageQueue Debugger::event_command_queue_(kQueueInitialSize);
Handle<Object> Debugger::MakeJSObject(Vector<const char> constructor_name, Handle<Object> Debugger::MakeJSObject(Vector<const char> constructor_name,
...@@ -2207,39 +2208,75 @@ void Debugger::ProcessDebugEvent(v8::DebugEvent event, ...@@ -2207,39 +2208,75 @@ void Debugger::ProcessDebugEvent(v8::DebugEvent event,
event_data, event_data,
auto_continue); auto_continue);
} }
// Notify registered debug event listener. This can be either a C or a // Notify registered debug event listener. This can be either a C or
// JavaScript function. // a JavaScript function. Don't call event listener for v8::Break
if (!event_listener_.is_null()) { // here, if it's only a debug command -- they will be processed later.
if (event_listener_->IsProxy()) { if ((event != v8::Break || !auto_continue) && !event_listener_.is_null()) {
// C debug event listener. CallEventCallback(event, exec_state, event_data, NULL);
Handle<Proxy> callback_obj(Handle<Proxy>::cast(event_listener_)); }
v8::Debug::EventCallback2 callback = // Process pending debug commands.
FUNCTION_CAST<v8::Debug::EventCallback2>(callback_obj->proxy()); if (event == v8::Break) {
EventDetailsImpl event_details( while (!event_command_queue_.IsEmpty()) {
event, CommandMessage command = event_command_queue_.Get();
Handle<JSObject>::cast(exec_state), if (!event_listener_.is_null()) {
event_data, CallEventCallback(v8::BreakForCommand,
event_listener_data_); exec_state,
callback(event_details); event_data,
} else { command.client_data());
// JavaScript debug event listener. }
ASSERT(event_listener_->IsJSFunction()); command.Dispose();
Handle<JSFunction> fun(Handle<JSFunction>::cast(event_listener_));
// Invoke the JavaScript debug event listener.
const int argc = 4;
Object** argv[argc] = { Handle<Object>(Smi::FromInt(event)).location(),
exec_state.location(),
Handle<Object>::cast(event_data).location(),
event_listener_data_.location() };
Handle<Object> result = Execution::TryCall(fun, Top::global(),
argc, argv, &caught_exception);
// Silently ignore exceptions from debug event listeners.
} }
} }
} }
void Debugger::CallEventCallback(v8::DebugEvent event,
Handle<Object> exec_state,
Handle<Object> 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<Object> exec_state,
Handle<Object> event_data,
v8::Debug::ClientData* client_data) {
Handle<Proxy> callback_obj(Handle<Proxy>::cast(event_listener_));
v8::Debug::EventCallback2 callback =
FUNCTION_CAST<v8::Debug::EventCallback2>(callback_obj->proxy());
EventDetailsImpl event_details(
event,
Handle<JSObject>::cast(exec_state),
Handle<JSObject>::cast(event_data),
event_listener_data_,
client_data);
callback(event_details);
}
void Debugger::CallJSEventCallback(v8::DebugEvent event,
Handle<Object> exec_state,
Handle<Object> event_data) {
ASSERT(event_listener_->IsJSFunction());
Handle<JSFunction> fun(Handle<JSFunction>::cast(event_listener_));
// Invoke the JavaScript debug event listener.
const int argc = 4;
Object** argv[argc] = { Handle<Object>(Smi::FromInt(event)).location(),
exec_state.location(),
Handle<Object>::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<Context> Debugger::GetDebugContext() { Handle<Context> Debugger::GetDebugContext() {
never_unload_debugger_ = true; never_unload_debugger_ = true;
EnterDebugger debugger; EnterDebugger debugger;
...@@ -2273,6 +2310,7 @@ void Debugger::NotifyMessageHandler(v8::DebugEvent event, ...@@ -2273,6 +2310,7 @@ void Debugger::NotifyMessageHandler(v8::DebugEvent event,
bool sendEventMessage = false; bool sendEventMessage = false;
switch (event) { switch (event) {
case v8::Break: case v8::Break:
case v8::BreakForCommand:
sendEventMessage = !auto_continue; sendEventMessage = !auto_continue;
break; break;
case v8::Exception: case v8::Exception:
...@@ -2560,6 +2598,17 @@ bool Debugger::HasCommands() { ...@@ -2560,6 +2598,17 @@ bool Debugger::HasCommands() {
} }
void Debugger::EnqueueDebugCommand(v8::Debug::ClientData* client_data) {
CommandMessage message = CommandMessage::New(Vector<uint16_t>(), 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() { bool Debugger::IsDebuggerActive() {
ScopedLock with(debugger_access_); ScopedLock with(debugger_access_);
...@@ -2761,11 +2810,13 @@ v8::Debug::ClientData* MessageImpl::GetClientData() const { ...@@ -2761,11 +2810,13 @@ v8::Debug::ClientData* MessageImpl::GetClientData() const {
EventDetailsImpl::EventDetailsImpl(DebugEvent event, EventDetailsImpl::EventDetailsImpl(DebugEvent event,
Handle<JSObject> exec_state, Handle<JSObject> exec_state,
Handle<JSObject> event_data, Handle<JSObject> event_data,
Handle<Object> callback_data) Handle<Object> callback_data,
v8::Debug::ClientData* client_data)
: event_(event), : event_(event),
exec_state_(exec_state), exec_state_(exec_state),
event_data_(event_data), event_data_(event_data),
callback_data_(callback_data) {} callback_data_(callback_data),
client_data_(client_data) {}
DebugEvent EventDetailsImpl::GetEvent() const { DebugEvent EventDetailsImpl::GetEvent() const {
...@@ -2793,6 +2844,11 @@ v8::Handle<v8::Value> EventDetailsImpl::GetCallbackData() const { ...@@ -2793,6 +2844,11 @@ v8::Handle<v8::Value> EventDetailsImpl::GetCallbackData() const {
} }
v8::Debug::ClientData* EventDetailsImpl::GetClientData() const {
return client_data_;
}
CommandMessage::CommandMessage() : text_(Vector<uint16_t>::empty()), CommandMessage::CommandMessage() : text_(Vector<uint16_t>::empty()),
client_data_(NULL) { client_data_(NULL) {
} }
......
...@@ -566,18 +566,21 @@ class EventDetailsImpl : public v8::Debug::EventDetails { ...@@ -566,18 +566,21 @@ class EventDetailsImpl : public v8::Debug::EventDetails {
EventDetailsImpl(DebugEvent event, EventDetailsImpl(DebugEvent event,
Handle<JSObject> exec_state, Handle<JSObject> exec_state,
Handle<JSObject> event_data, Handle<JSObject> event_data,
Handle<Object> callback_data); Handle<Object> callback_data,
v8::Debug::ClientData* client_data);
virtual DebugEvent GetEvent() const; virtual DebugEvent GetEvent() const;
virtual v8::Handle<v8::Object> GetExecutionState() const; virtual v8::Handle<v8::Object> GetExecutionState() const;
virtual v8::Handle<v8::Object> GetEventData() const; virtual v8::Handle<v8::Object> GetEventData() const;
virtual v8::Handle<v8::Context> GetEventContext() const; virtual v8::Handle<v8::Context> GetEventContext() const;
virtual v8::Handle<v8::Value> GetCallbackData() const; virtual v8::Handle<v8::Value> GetCallbackData() const;
virtual v8::Debug::ClientData* GetClientData() const;
private: private:
DebugEvent event_; // Debug event causing the break. DebugEvent event_; // Debug event causing the break.
Handle<JSObject> exec_state_; // Current execution state. Handle<JSObject> exec_state_; // Current execution state.
Handle<JSObject> event_data_; // Data associated with the event. Handle<JSObject> event_data_; // Data associated with the event.
Handle<Object> callback_data_; // User data passed with the callback when Handle<Object> callback_data_; // User data passed with the callback
// it was registered. // when it was registered.
v8::Debug::ClientData* client_data_; // Data passed to DebugBreakForCommand.
}; };
...@@ -706,6 +709,9 @@ class Debugger { ...@@ -706,6 +709,9 @@ class Debugger {
// Check whether there are commands in the command queue. // Check whether there are commands in the command queue.
static bool HasCommands(); 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<Object> Call(Handle<JSFunction> fun, static Handle<Object> Call(Handle<JSFunction> fun,
Handle<Object> data, Handle<Object> data,
bool* pending_exception); bool* pending_exception);
...@@ -753,6 +759,17 @@ class Debugger { ...@@ -753,6 +759,17 @@ class Debugger {
static bool IsDebuggerActive(); static bool IsDebuggerActive();
private: private:
static void CallEventCallback(v8::DebugEvent event,
Handle<Object> exec_state,
Handle<Object> event_data,
v8::Debug::ClientData* client_data);
static void CallCEventCallback(v8::DebugEvent event,
Handle<Object> exec_state,
Handle<Object> event_data,
v8::Debug::ClientData* client_data);
static void CallJSEventCallback(v8::DebugEvent event,
Handle<Object> exec_state,
Handle<Object> event_data);
static void ListenersChanged(); static void ListenersChanged();
static Mutex* debugger_access_; // Mutex guarding debugger variables. static Mutex* debugger_access_; // Mutex guarding debugger variables.
...@@ -775,6 +792,8 @@ class Debugger { ...@@ -775,6 +792,8 @@ class Debugger {
static LockingCommandMessageQueue command_queue_; static LockingCommandMessageQueue command_queue_;
static Semaphore* command_received_; // Signaled for each command received. static Semaphore* command_received_; // Signaled for each command received.
static LockingCommandMessageQueue event_command_queue_;
friend class EnterDebugger; friend class EnterDebugger;
}; };
......
...@@ -6650,4 +6650,69 @@ TEST(DebugEventContext) { ...@@ -6650,4 +6650,69 @@ TEST(DebugEventContext) {
CheckDebuggerUnloaded(); 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 #endif // ENABLE_DEBUGGER_SUPPORT
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