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);
// 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.
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;
}
-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;
}
}
-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_);
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) {
}
}
-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);
}
text = Vector<uint16_t>::New(output->Length());
output->Write(text.start(), 0, output->Length());
}
- return Message(text, data, false);
+ return Message(text, data);
}
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();
// 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
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);
// 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);
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_;
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());
}
-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);
+}
+
+
+static void HostDispatchDispatchHandler() {
+ host_dispatch_barriers->semaphore_1->Signal();
}
-// Test that clearing the debug event listener actually clears all break points
-// and related information.
-TEST(DebuggerHostDispatch) {
- i::FLAG_debugger_auto_break = true;
+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\"}";
+ // Setup message and host dispatch handlers.
+ v8::Debug::SetMessageHandler(HostDispatchMessageHandler);
+ v8::Debug::SetHostDispatchHandler(HostDispatchDispatchHandler, 10 /* ms */);
- // Create an empty function to call for processing debug commands
- v8::Local<v8::Function> empty =
- CompileFunction(&env, "function empty(){}", "empty");
+ CompileRun(source_1);
+ host_dispatch_barriers->barrier_1.Wait();
+ 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.
- v8::Debug::SendHostDispatch(NULL);
- empty->Call(env->Global(), 0, NULL); // Run JavaScript to activate debugger.
- CHECK_EQ(1, host_dispatch_hit_count);
+void HostDispatchDebuggerThread::Run() {
+ const int kBufSize = 1000;
+ uint16_t buffer[kBufSize];
- // 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.
+ 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\"}";
- // 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.
+ // 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();
- // All the host dispatch callback should be called.
- CHECK_EQ(3, host_dispatch_hit_count);
+ host_dispatch_v8_thread.Join();
+ host_dispatch_debugger_thread.Join();
}