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
This commit is contained in:
pfeldman@chromium.org 2009-04-22 14:16:50 +00:00
parent 561bd9861a
commit a3a0d64305
5 changed files with 127 additions and 107 deletions

View File

@ -124,12 +124,8 @@ class EXPORT Debug {
/**
* 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.
static bool SetDebugEventListener(EventCallback that,
@ -149,8 +145,8 @@ class EXPORT Debug {
ClientData* client_data = NULL);
// Dispatch interface.
static void SetHostDispatchHandler(HostDispatchHandler handler);
static void SendHostDispatch(ClientData* dispatch);
static void SetHostDispatchHandler(HostDispatchHandler handler,
int period = 100);
/**
* Run a JavaScript function in the debugger.

View File

@ -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");
ENTER_V8;
i::Debugger::SetHostDispatchHandler(handler);
}
void Debug::SendHostDispatch(ClientData* dispatch) {
if (!i::V8::HasBeenSetup()) return;
i::Debugger::ProcessHostDispatch(dispatch);
i::Debugger::SetHostDispatchHandler(handler, period);
}

View File

@ -1424,6 +1424,7 @@ DebugMessageThread* Debugger::message_thread_ = NULL;
v8::Debug::MessageHandler Debugger::message_handler_ = NULL;
bool Debugger::message_handler_cleared_ = false;
v8::Debug::HostDispatchHandler Debugger::host_dispatch_handler_ = NULL;
int Debugger::host_dispatch_micros_ = 100 * 1000;
DebuggerAgent* Debugger::agent_ = NULL;
LockingMessageQueue Debugger::command_queue_(kQueueInitialSize);
LockingMessageQueue Debugger::message_queue_(kQueueInitialSize);
@ -1827,7 +1828,17 @@ void Debugger::NotifyMessageHandler(v8::DebugEvent event,
// Process requests from the debugger.
while (true) {
// 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
// added.
@ -1842,19 +1853,6 @@ void Debugger::NotifyMessageHandler(v8::DebugEvent event,
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.
v8::Local<v8::String> fun_name;
v8::Local<v8::Function> fun;
@ -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_micros_ = period * 1000;
}
@ -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() {
ScopedLock with(debugger_access_);
@ -2168,17 +2155,14 @@ void DebugMessageThread::Stop() {
Message::Message() : text_(Vector<uint16_t>::empty()),
client_data_(NULL),
is_host_dispatch_(false) {
client_data_(NULL) {
}
Message::Message(const Vector<uint16_t>& text,
v8::Debug::ClientData* data,
bool is_host_dispatch)
v8::Debug::ClientData* data)
: text_(text),
client_data_(data),
is_host_dispatch_(is_host_dispatch) {
client_data_(data) {
}
@ -2193,19 +2177,9 @@ void Message::Dispose() {
}
bool Message::IsHostDispatch() const {
return is_host_dispatch_;
}
Message Message::NewCommand(const Vector<uint16_t>& command,
v8::Debug::ClientData* data) {
return Message(command.Clone(), data, false);
}
Message Message::NewHostDispatch(v8::Debug::ClientData* dispatch) {
return Message(Vector<uint16_t>::empty(), dispatch, true);
return Message(command.Clone(), data);
}
@ -2217,7 +2191,7 @@ Message Message::NewOutput(v8::Handle<v8::String> output,
text = Vector<uint16_t>::New(output->Length());
output->Write(text.start(), 0, output->Length());
}
return Message(text, data, false);
return Message(text, data);
}

View File

@ -409,7 +409,6 @@ class Message {
public:
static Message NewCommand(const Vector<uint16_t>& command,
v8::Debug::ClientData* data);
static Message NewHostDispatch(v8::Debug::ClientData* dispatch);
static Message NewOutput(v8::Handle<v8::String> output,
v8::Debug::ClientData* data);
static Message NewEmptyMessage();
@ -418,17 +417,14 @@ class Message {
// Deletes user data and disposes of the text.
void Dispose();
bool IsHostDispatch() const;
Vector<uint16_t> text() const { return text_; }
v8::Debug::ClientData* client_data() const { return client_data_; }
private:
Message(const Vector<uint16_t>& text,
v8::Debug::ClientData* data,
bool is_host_dispatch);
v8::Debug::ClientData* data);
Vector<uint16_t> text_;
v8::Debug::ClientData* client_data_;
bool is_host_dispatch_;
};
// A Queue of Vector<uint16_t> objects. A thread-safe version is
@ -510,7 +506,8 @@ class Debugger {
static void SetMessageHandler(v8::Debug::MessageHandler handler,
bool message_handler_thread);
static void TearDown();
static void SetHostDispatchHandler(v8::Debug::HostDispatchHandler handler);
static void SetHostDispatchHandler(v8::Debug::HostDispatchHandler handler,
int period);
// Invoke the message handler function.
static void InvokeMessageHandler(Message message);
@ -529,7 +526,6 @@ class Debugger {
// Check whether there are commands in the command queue.
static bool HasCommands();
static void ProcessHostDispatch(v8::Debug::ClientData* dispatch);
static Handle<Object> Call(Handle<JSFunction> fun,
Handle<Object> data,
bool* pending_exception);
@ -576,6 +572,7 @@ class Debugger {
static v8::Debug::MessageHandler message_handler_;
static bool message_handler_cleared_; // Was message handler cleared?
static v8::Debug::HostDispatchHandler host_dispatch_handler_;
static int host_dispatch_micros_;
static DebuggerAgent* agent_;

View File

@ -3564,13 +3564,17 @@ TEST(MessageQueueExpandAndDestroy) {
new TestClientData()));
queue.Put(Message::NewCommand(Vector<uint16_t>::empty(),
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);
queue.Get().Dispose();
ASSERT_EQ(1, TestClientData::destructor_call_counter);
queue.Put(Message::NewHostDispatch(new TestClientData()));
queue.Put(Message::NewHostDispatch(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::NewCommand(Vector<uint16_t>::empty(),
new TestClientData()));
queue.Put(Message::NewOutput(v8::Handle<v8::String>(),
new TestClientData()));
queue.Put(Message::NewEmptyMessage());
@ -4219,53 +4223,107 @@ TEST(DebuggerClearMessageHandlerWhileActive) {
}
int host_dispatch_hit_count = 0;
static void HostDispatchHandlerHitCount(v8::Debug::ClientData* dispatch) {
host_dispatch_hit_count++;
/* Test DebuggerHostDispatch */
/* In this test, the debugger waits for a command on a breakpoint
* 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);
}
// Test that clearing the debug event listener actually clears all break points
// and related information.
TEST(DebuggerHostDispatch) {
i::FLAG_debugger_auto_break = true;
static void HostDispatchDispatchHandler() {
host_dispatch_barriers->semaphore_1->Signal();
}
void HostDispatchV8Thread::Run() {
const char* source_1 = "var y_global = 3;\n"
"function cat( new_value ) {\n"
" 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;
DebugLocalContext env;
const int kBufferSize = 1000;
uint16_t buffer[kBufferSize];
const char* command_continue =
"{\"seq\":0,"
"\"type\":\"request\","
"\"command\":\"continue\"}";
// Create an empty function to call for processing debug commands
v8::Local<v8::Function> empty =
CompileFunction(&env, "function empty(){}", "empty");
// Setup message and host dispatch handlers.
v8::Debug::SetMessageHandler(DummyMessageHandler);
v8::Debug::SetHostDispatchHandler(HostDispatchHandlerHitCount);
v8::Debug::SetMessageHandler(HostDispatchMessageHandler);
v8::Debug::SetHostDispatchHandler(HostDispatchDispatchHandler, 10 /* ms */);
// Send a host dispatch by itself.
v8::Debug::SendHostDispatch(NULL);
empty->Call(env->Global(), 0, NULL); // Run JavaScript to activate debugger.
CHECK_EQ(1, host_dispatch_hit_count);
CompileRun(source_1);
host_dispatch_barriers->barrier_1.Wait();
host_dispatch_barriers->barrier_2.Wait();
CompileRun(source_2);
}
// Fill a host dispatch and a continue command on the command queue.
v8::Debug::SendHostDispatch(NULL);
v8::Debug::SendCommand(buffer, AsciiToUtf16(command_continue, buffer));
empty->Call(env->Global(), 0, NULL); // Run JavaScript to activate debugger.
// Fill a continue command and a host dispatch on the command queue.
v8::Debug::SendCommand(buffer, AsciiToUtf16(command_continue, buffer));
v8::Debug::SendHostDispatch(NULL);
empty->Call(env->Global(), 0, NULL); // Run JavaScript to activate debugger.
empty->Call(env->Global(), 0, NULL); // Run JavaScript to activate debugger.
void HostDispatchDebuggerThread::Run() {
const int kBufSize = 1000;
uint16_t buffer[kBufSize];
// All the host dispatch callback should be called.
CHECK_EQ(3, host_dispatch_hit_count);
const char* command_1 = "{\"seq\":101,"
"\"type\":\"request\","
"\"command\":\"setbreakpoint\","
"\"arguments\":{\"type\":\"function\",\"target\":\"cat\",\"line\":3}}";
const char* command_2 = "{\"seq\":102,"
"\"type\":\"request\","
"\"command\":\"continue\"}";
// v8 thread initializes, runs source_1
host_dispatch_barriers->barrier_1.Wait();
// 1: Set breakpoint in cat().
v8::Debug::SendCommand(buffer, AsciiToUtf16(command_1, buffer));
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();
host_dispatch_v8_thread.Join();
host_dispatch_debugger_thread.Join();
}