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:
parent
561bd9861a
commit
a3a0d64305
@ -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.
|
||||
|
11
src/api.cc
11
src/api.cc
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
66
src/debug.cc
66
src/debug.cc
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
11
src/debug.h
11
src/debug.h
@ -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_;
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user