Commit a3a0d643 authored by pfeldman@chromium.org's avatar pfeldman@chromium.org

DevTools: Add support for eventless host message dispatching.

Review URL: http://codereview.chromium.org/87026

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@1773 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 561bd986
...@@ -124,12 +124,8 @@ class EXPORT Debug { ...@@ -124,12 +124,8 @@ class EXPORT Debug {
/** /**
* Debug host dispatch callback function. * Debug host dispatch callback function.
*
* \param dispatch the dispatch value
* \param data the data value passed when registering the dispatch handler
*/ */
typedef void (*HostDispatchHandler)(ClientData* dispatch); typedef void (*HostDispatchHandler)();
// Set a C debug event listener. // Set a C debug event listener.
static bool SetDebugEventListener(EventCallback that, static bool SetDebugEventListener(EventCallback that,
...@@ -149,8 +145,8 @@ class EXPORT Debug { ...@@ -149,8 +145,8 @@ class EXPORT Debug {
ClientData* client_data = NULL); ClientData* client_data = NULL);
// Dispatch interface. // Dispatch interface.
static void SetHostDispatchHandler(HostDispatchHandler handler); static void SetHostDispatchHandler(HostDispatchHandler handler,
static void SendHostDispatch(ClientData* dispatch); int period = 100);
/** /**
* Run a JavaScript function in the debugger. * Run a JavaScript function in the debugger.
......
...@@ -3276,16 +3276,11 @@ void Debug::SendCommand(const uint16_t* command, int length, ...@@ -3276,16 +3276,11 @@ void Debug::SendCommand(const uint16_t* command, int length,
} }
void Debug::SetHostDispatchHandler(HostDispatchHandler handler) { void Debug::SetHostDispatchHandler(HostDispatchHandler handler,
int period) {
EnsureInitialized("v8::Debug::SetHostDispatchHandler"); EnsureInitialized("v8::Debug::SetHostDispatchHandler");
ENTER_V8; ENTER_V8;
i::Debugger::SetHostDispatchHandler(handler); i::Debugger::SetHostDispatchHandler(handler, period);
}
void Debug::SendHostDispatch(ClientData* dispatch) {
if (!i::V8::HasBeenSetup()) return;
i::Debugger::ProcessHostDispatch(dispatch);
} }
......
...@@ -1424,6 +1424,7 @@ DebugMessageThread* Debugger::message_thread_ = NULL; ...@@ -1424,6 +1424,7 @@ DebugMessageThread* Debugger::message_thread_ = NULL;
v8::Debug::MessageHandler Debugger::message_handler_ = NULL; v8::Debug::MessageHandler Debugger::message_handler_ = NULL;
bool Debugger::message_handler_cleared_ = false; bool Debugger::message_handler_cleared_ = false;
v8::Debug::HostDispatchHandler Debugger::host_dispatch_handler_ = NULL; v8::Debug::HostDispatchHandler Debugger::host_dispatch_handler_ = NULL;
int Debugger::host_dispatch_micros_ = 100 * 1000;
DebuggerAgent* Debugger::agent_ = NULL; DebuggerAgent* Debugger::agent_ = NULL;
LockingMessageQueue Debugger::command_queue_(kQueueInitialSize); LockingMessageQueue Debugger::command_queue_(kQueueInitialSize);
LockingMessageQueue Debugger::message_queue_(kQueueInitialSize); LockingMessageQueue Debugger::message_queue_(kQueueInitialSize);
...@@ -1827,7 +1828,17 @@ void Debugger::NotifyMessageHandler(v8::DebugEvent event, ...@@ -1827,7 +1828,17 @@ void Debugger::NotifyMessageHandler(v8::DebugEvent event,
// Process requests from the debugger. // Process requests from the debugger.
while (true) { while (true) {
// Wait for new command in the queue. // Wait for new command in the queue.
command_received_->Wait(); if (Debugger::host_dispatch_handler_) {
// In case there is a host dispatch - do periodic dispatches.
if (!command_received_->Wait(host_dispatch_micros_)) {
// Timout expired, do the dispatch.
Debugger::host_dispatch_handler_();
continue;
}
} else {
// In case there is no host dispatch - just wait.
command_received_->Wait();
}
// The debug command interrupt flag might have been set when the command was // The debug command interrupt flag might have been set when the command was
// added. // added.
...@@ -1842,19 +1853,6 @@ void Debugger::NotifyMessageHandler(v8::DebugEvent event, ...@@ -1842,19 +1853,6 @@ void Debugger::NotifyMessageHandler(v8::DebugEvent event,
return; return;
} }
// Check if the command is a host dispatch.
if (command.IsHostDispatch()) {
if (Debugger::host_dispatch_handler_) {
Debugger::host_dispatch_handler_(command.client_data());
// Delete the dispatch.
command.Dispose();
}
if (auto_continue && !HasCommands()) {
return;
}
continue;
}
// Invoke JavaScript to process the debug request. // Invoke JavaScript to process the debug request.
v8::Local<v8::String> fun_name; v8::Local<v8::String> fun_name;
v8::Local<v8::Function> fun; v8::Local<v8::Function> fun;
...@@ -1971,8 +1969,10 @@ void Debugger::SetMessageHandler(v8::Debug::MessageHandler handler, ...@@ -1971,8 +1969,10 @@ void Debugger::SetMessageHandler(v8::Debug::MessageHandler handler,
} }
void Debugger::SetHostDispatchHandler(v8::Debug::HostDispatchHandler handler) { void Debugger::SetHostDispatchHandler(v8::Debug::HostDispatchHandler handler,
int period) {
host_dispatch_handler_ = handler; host_dispatch_handler_ = handler;
host_dispatch_micros_ = period * 1000;
} }
...@@ -2067,19 +2067,6 @@ bool Debugger::HasCommands() { ...@@ -2067,19 +2067,6 @@ bool Debugger::HasCommands() {
} }
void Debugger::ProcessHostDispatch(v8::Debug::ClientData* dispatch) {
// Puts a host dispatch comming from the public API on the queue.
Logger::DebugTag("Put dispatch on command_queue.");
command_queue_.Put(Message::NewHostDispatch(dispatch));
command_received_->Signal();
// Set the debug command break flag to have the host dispatch processed.
if (!Debug::InDebugger()) {
StackGuard::DebugCommand();
}
}
bool Debugger::IsDebuggerActive() { bool Debugger::IsDebuggerActive() {
ScopedLock with(debugger_access_); ScopedLock with(debugger_access_);
...@@ -2168,17 +2155,14 @@ void DebugMessageThread::Stop() { ...@@ -2168,17 +2155,14 @@ void DebugMessageThread::Stop() {
Message::Message() : text_(Vector<uint16_t>::empty()), Message::Message() : text_(Vector<uint16_t>::empty()),
client_data_(NULL), client_data_(NULL) {
is_host_dispatch_(false) {
} }
Message::Message(const Vector<uint16_t>& text, Message::Message(const Vector<uint16_t>& text,
v8::Debug::ClientData* data, v8::Debug::ClientData* data)
bool is_host_dispatch)
: text_(text), : text_(text),
client_data_(data), client_data_(data) {
is_host_dispatch_(is_host_dispatch) {
} }
...@@ -2193,19 +2177,9 @@ void Message::Dispose() { ...@@ -2193,19 +2177,9 @@ void Message::Dispose() {
} }
bool Message::IsHostDispatch() const {
return is_host_dispatch_;
}
Message Message::NewCommand(const Vector<uint16_t>& command, Message Message::NewCommand(const Vector<uint16_t>& command,
v8::Debug::ClientData* data) { v8::Debug::ClientData* data) {
return Message(command.Clone(), data, false); return Message(command.Clone(), data);
}
Message Message::NewHostDispatch(v8::Debug::ClientData* dispatch) {
return Message(Vector<uint16_t>::empty(), dispatch, true);
} }
...@@ -2217,7 +2191,7 @@ Message Message::NewOutput(v8::Handle<v8::String> output, ...@@ -2217,7 +2191,7 @@ Message Message::NewOutput(v8::Handle<v8::String> output,
text = Vector<uint16_t>::New(output->Length()); text = Vector<uint16_t>::New(output->Length());
output->Write(text.start(), 0, output->Length()); output->Write(text.start(), 0, output->Length());
} }
return Message(text, data, false); return Message(text, data);
} }
......
...@@ -409,7 +409,6 @@ class Message { ...@@ -409,7 +409,6 @@ class Message {
public: public:
static Message NewCommand(const Vector<uint16_t>& command, static Message NewCommand(const Vector<uint16_t>& command,
v8::Debug::ClientData* data); v8::Debug::ClientData* data);
static Message NewHostDispatch(v8::Debug::ClientData* dispatch);
static Message NewOutput(v8::Handle<v8::String> output, static Message NewOutput(v8::Handle<v8::String> output,
v8::Debug::ClientData* data); v8::Debug::ClientData* data);
static Message NewEmptyMessage(); static Message NewEmptyMessage();
...@@ -418,17 +417,14 @@ class Message { ...@@ -418,17 +417,14 @@ class Message {
// Deletes user data and disposes of the text. // Deletes user data and disposes of the text.
void Dispose(); void Dispose();
bool IsHostDispatch() const;
Vector<uint16_t> text() const { return text_; } Vector<uint16_t> text() const { return text_; }
v8::Debug::ClientData* client_data() const { return client_data_; } v8::Debug::ClientData* client_data() const { return client_data_; }
private: private:
Message(const Vector<uint16_t>& text, Message(const Vector<uint16_t>& text,
v8::Debug::ClientData* data, v8::Debug::ClientData* data);
bool is_host_dispatch);
Vector<uint16_t> text_; Vector<uint16_t> text_;
v8::Debug::ClientData* client_data_; v8::Debug::ClientData* client_data_;
bool is_host_dispatch_;
}; };
// A Queue of Vector<uint16_t> objects. A thread-safe version is // A Queue of Vector<uint16_t> objects. A thread-safe version is
...@@ -510,7 +506,8 @@ class Debugger { ...@@ -510,7 +506,8 @@ class Debugger {
static void SetMessageHandler(v8::Debug::MessageHandler handler, static void SetMessageHandler(v8::Debug::MessageHandler handler,
bool message_handler_thread); bool message_handler_thread);
static void TearDown(); static void TearDown();
static void SetHostDispatchHandler(v8::Debug::HostDispatchHandler handler); static void SetHostDispatchHandler(v8::Debug::HostDispatchHandler handler,
int period);
// Invoke the message handler function. // Invoke the message handler function.
static void InvokeMessageHandler(Message message); static void InvokeMessageHandler(Message message);
...@@ -529,7 +526,6 @@ class Debugger { ...@@ -529,7 +526,6 @@ 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();
static void ProcessHostDispatch(v8::Debug::ClientData* dispatch);
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);
...@@ -576,6 +572,7 @@ class Debugger { ...@@ -576,6 +572,7 @@ class Debugger {
static v8::Debug::MessageHandler message_handler_; static v8::Debug::MessageHandler message_handler_;
static bool message_handler_cleared_; // Was message handler cleared? static bool message_handler_cleared_; // Was message handler cleared?
static v8::Debug::HostDispatchHandler host_dispatch_handler_; static v8::Debug::HostDispatchHandler host_dispatch_handler_;
static int host_dispatch_micros_;
static DebuggerAgent* agent_; static DebuggerAgent* agent_;
......
...@@ -3564,13 +3564,17 @@ TEST(MessageQueueExpandAndDestroy) { ...@@ -3564,13 +3564,17 @@ TEST(MessageQueueExpandAndDestroy) {
new TestClientData())); new TestClientData()));
queue.Put(Message::NewCommand(Vector<uint16_t>::empty(), queue.Put(Message::NewCommand(Vector<uint16_t>::empty(),
new TestClientData())); new TestClientData()));
queue.Put(Message::NewHostDispatch(new TestClientData())); queue.Put(Message::NewCommand(Vector<uint16_t>::empty(),
new TestClientData()));
ASSERT_EQ(0, TestClientData::destructor_call_counter); ASSERT_EQ(0, TestClientData::destructor_call_counter);
queue.Get().Dispose(); queue.Get().Dispose();
ASSERT_EQ(1, TestClientData::destructor_call_counter); ASSERT_EQ(1, TestClientData::destructor_call_counter);
queue.Put(Message::NewHostDispatch(new TestClientData())); queue.Put(Message::NewCommand(Vector<uint16_t>::empty(),
queue.Put(Message::NewHostDispatch(new TestClientData())); new TestClientData()));
queue.Put(Message::NewHostDispatch(new TestClientData())); queue.Put(Message::NewCommand(Vector<uint16_t>::empty(),
new TestClientData()));
queue.Put(Message::NewCommand(Vector<uint16_t>::empty(),
new TestClientData()));
queue.Put(Message::NewOutput(v8::Handle<v8::String>(), queue.Put(Message::NewOutput(v8::Handle<v8::String>(),
new TestClientData())); new TestClientData()));
queue.Put(Message::NewEmptyMessage()); queue.Put(Message::NewEmptyMessage());
...@@ -4219,53 +4223,107 @@ TEST(DebuggerClearMessageHandlerWhileActive) { ...@@ -4219,53 +4223,107 @@ TEST(DebuggerClearMessageHandlerWhileActive) {
} }
int host_dispatch_hit_count = 0; /* Test DebuggerHostDispatch */
static void HostDispatchHandlerHitCount(v8::Debug::ClientData* dispatch) { /* In this test, the debugger waits for a command on a breakpoint
host_dispatch_hit_count++; * and is dispatching host commands while in the infinite loop.
*/
class HostDispatchV8Thread : public v8::internal::Thread {
public:
void Run();
};
class HostDispatchDebuggerThread : public v8::internal::Thread {
public:
void Run();
};
Barriers* host_dispatch_barriers;
static void HostDispatchMessageHandler(const uint16_t* message,
int length,
v8::Debug::ClientData* client_data) {
static char print_buffer[1000];
Utf16ToAscii(message, length, print_buffer);
printf("%s\n", print_buffer);
fflush(stdout);
}
static void HostDispatchDispatchHandler() {
host_dispatch_barriers->semaphore_1->Signal();
} }
// Test that clearing the debug event listener actually clears all break points void HostDispatchV8Thread::Run() {
// and related information. const char* source_1 = "var y_global = 3;\n"
TEST(DebuggerHostDispatch) { "function cat( new_value ) {\n"
i::FLAG_debugger_auto_break = true; " var x = new_value;\n"
" y_global = 4;\n"
" x = 3 * x + 1;\n"
" y_global = 5;\n"
" return x;\n"
"}\n"
"\n";
const char* source_2 = "cat(17);\n";
v8::HandleScope scope; v8::HandleScope scope;
DebugLocalContext env; DebugLocalContext env;
const int kBufferSize = 1000; // Setup message and host dispatch handlers.
uint16_t buffer[kBufferSize]; v8::Debug::SetMessageHandler(HostDispatchMessageHandler);
const char* command_continue = v8::Debug::SetHostDispatchHandler(HostDispatchDispatchHandler, 10 /* ms */);
"{\"seq\":0,"
"\"type\":\"request\","
"\"command\":\"continue\"}";
// Create an empty function to call for processing debug commands CompileRun(source_1);
v8::Local<v8::Function> empty = host_dispatch_barriers->barrier_1.Wait();
CompileFunction(&env, "function empty(){}", "empty"); host_dispatch_barriers->barrier_2.Wait();
CompileRun(source_2);
}
// Setup message and host dispatch handlers.
v8::Debug::SetMessageHandler(DummyMessageHandler);
v8::Debug::SetHostDispatchHandler(HostDispatchHandlerHitCount);
// Send a host dispatch by itself. void HostDispatchDebuggerThread::Run() {
v8::Debug::SendHostDispatch(NULL); const int kBufSize = 1000;
empty->Call(env->Global(), 0, NULL); // Run JavaScript to activate debugger. uint16_t buffer[kBufSize];
CHECK_EQ(1, host_dispatch_hit_count);
// Fill a host dispatch and a continue command on the command queue. const char* command_1 = "{\"seq\":101,"
v8::Debug::SendHostDispatch(NULL); "\"type\":\"request\","
v8::Debug::SendCommand(buffer, AsciiToUtf16(command_continue, buffer)); "\"command\":\"setbreakpoint\","
empty->Call(env->Global(), 0, NULL); // Run JavaScript to activate debugger. "\"arguments\":{\"type\":\"function\",\"target\":\"cat\",\"line\":3}}";
const char* command_2 = "{\"seq\":102,"
"\"type\":\"request\","
"\"command\":\"continue\"}";
// Fill a continue command and a host dispatch on the command queue. // v8 thread initializes, runs source_1
v8::Debug::SendCommand(buffer, AsciiToUtf16(command_continue, buffer)); host_dispatch_barriers->barrier_1.Wait();
v8::Debug::SendHostDispatch(NULL); // 1: Set breakpoint in cat().
empty->Call(env->Global(), 0, NULL); // Run JavaScript to activate debugger. v8::Debug::SendCommand(buffer, AsciiToUtf16(command_1, buffer));
empty->Call(env->Global(), 0, NULL); // Run JavaScript to activate debugger.
host_dispatch_barriers->barrier_2.Wait();
// v8 thread starts compiling source_2.
// Break happens, to run queued commands and host dispatches.
// Wait for host dispatch to be processed.
host_dispatch_barriers->semaphore_1->Wait();
// 2: Continue evaluation
v8::Debug::SendCommand(buffer, AsciiToUtf16(command_2, buffer));
}
HostDispatchDebuggerThread host_dispatch_debugger_thread;
HostDispatchV8Thread host_dispatch_v8_thread;
TEST(DebuggerHostDispatch) {
i::FLAG_debugger_auto_break = true;
// Create a V8 environment
Barriers stack_allocated_host_dispatch_barriers;
stack_allocated_host_dispatch_barriers.Initialize();
host_dispatch_barriers = &stack_allocated_host_dispatch_barriers;
host_dispatch_v8_thread.Start();
host_dispatch_debugger_thread.Start();
// All the host dispatch callback should be called. host_dispatch_v8_thread.Join();
CHECK_EQ(3, host_dispatch_hit_count); host_dispatch_debugger_thread.Join();
} }
......
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