Commit 44b83d3e authored by sgjesse@chromium.org's avatar sgjesse@chromium.org

Add host callback for debug break.

Add the ability to have the host embedding V8 receive a callback in the V8 thread while V8 is processing a debug callback. When V8 is processing a debug callback the thread where V8 is executing is sitting in a tight loop processing debug commands until the continue command has been executed. In some embedding situations it is beneficial to be able to call back into the embedding host from the thread where V8 is sitting. The might have functions which needs to be called to complement the JavaScript debugging.

Using the uint16_t array to pass a void* is a temporary hack.
Review URL: http://codereview.chromium.org/20491

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@1318 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent b1fbed8c
......@@ -99,12 +99,23 @@ typedef void (*DebugEventCallback)(DebugEvent event,
*
* \param message the debug message
* \param length length of the message
* \param data the data value passed when registering the message handler
* A DebugMessageHandler does not take posession of the message string,
* and must not rely on the data persisting after the handler returns.
*/
typedef void (*DebugMessageHandler)(const uint16_t* message, int length,
void* data);
/**
* Debug host dispatch callback function.
*
* \param dispatch the dispatch value
* \param data the data value passed when registering the dispatch handler
*/
typedef void (*DebugHostDispatchHandler)(void* dispatch,
void* data);
class EXPORT Debug {
public:
......@@ -123,6 +134,11 @@ class EXPORT Debug {
static void SetMessageHandler(DebugMessageHandler handler, void* data = NULL);
static void SendCommand(const uint16_t* command, int length);
// Dispatch interface.
static void SetHostDispatchHandler(DebugHostDispatchHandler handler,
void* data = NULL);
static void SendHostDispatch(void* dispatch);
/**
* Run a JavaScript function in the debugger.
* \param fun the function to call
......
......@@ -2924,6 +2924,19 @@ void Debug::SendCommand(const uint16_t* command, int length) {
}
void Debug::SetHostDispatchHandler(v8::DebugHostDispatchHandler handler,
void* data) {
EnsureInitialized("v8::Debug::SetHostDispatchHandler");
i::Debugger::SetHostDispatchHandler(handler, data);
}
void Debug::SendHostDispatch(void* dispatch) {
if (!i::V8::HasBeenSetup()) return;
i::Debugger::ProcessHostDispatch(dispatch);
}
Handle<Value> Debug::Call(v8::Handle<v8::Function> fun,
v8::Handle<v8::Value> data) {
if (!i::V8::HasBeenSetup()) return Handle<Value>();
......
......@@ -1351,8 +1351,10 @@ bool Debugger::debugger_active_ = false;
bool Debugger::compiling_natives_ = false;
bool Debugger::is_loading_debugger_ = false;
DebugMessageThread* Debugger::message_thread_ = NULL;
v8::DebugMessageHandler Debugger::debug_message_handler_ = NULL;
void* Debugger::debug_message_handler_data_ = NULL;
v8::DebugMessageHandler Debugger::message_handler_ = NULL;
void* Debugger::message_handler_data_ = NULL;
v8::DebugHostDispatchHandler Debugger::host_dispatch_handler_ = NULL;
void* Debugger::host_dispatch_handler_data_ = NULL;
Handle<Object> Debugger::MakeJSObject(Vector<const char> constructor_name,
......@@ -1709,8 +1711,8 @@ void Debugger::SetEventListener(Handle<Object> callback,
void Debugger::SetMessageHandler(v8::DebugMessageHandler handler, void* data) {
debug_message_handler_ = handler;
debug_message_handler_data_ = data;
message_handler_ = handler;
message_handler_data_ = data;
if (!message_thread_) {
message_thread_ = new DebugMessageThread();
message_thread_->Start();
......@@ -1719,14 +1721,20 @@ void Debugger::SetMessageHandler(v8::DebugMessageHandler handler, void* data) {
}
void Debugger::SetHostDispatchHandler(v8::DebugHostDispatchHandler handler,
void* data) {
host_dispatch_handler_ = handler;
host_dispatch_handler_data_ = data;
}
// Posts an output message from the debugger to the debug_message_handler
// callback. This callback is part of the public API. Messages are
// kept internally as Vector<uint16_t> strings, which are allocated in various
// places and deallocated by the calling function sometime after this call.
void Debugger::SendMessage(Vector< uint16_t> message) {
if (debug_message_handler_ != NULL) {
debug_message_handler_(message.start(), message.length(),
debug_message_handler_data_);
if (message_handler_ != NULL) {
message_handler_(message.start(), message.length(), message_handler_data_);
}
}
......@@ -1740,9 +1748,16 @@ void Debugger::ProcessCommand(Vector<const uint16_t> command) {
}
void Debugger::ProcessHostDispatch(void* dispatch) {
if (message_thread_ != NULL) {
message_thread_->ProcessHostDispatch(dispatch);
}
}
void Debugger::UpdateActiveDebugger() {
set_debugger_active((message_thread_ != NULL &&
debug_message_handler_ != NULL) ||
message_handler_ != NULL) ||
!event_listener_.is_null());
if (!debugger_active() && message_thread_) {
message_thread_->OnDebuggerInactive();
......@@ -1914,6 +1929,16 @@ void DebugMessageThread::DebugEvent(v8::DebugEvent event,
return;
}
// Check if the command is a host dispatch.
if (command[0] == 0) {
if (Debugger::host_dispatch_handler_) {
int32_t dispatch = (command[1] << 16) | command[2];
Debugger::host_dispatch_handler_(reinterpret_cast<void*>(dispatch),
Debugger::host_dispatch_handler_data_);
}
continue;
}
// Invoke the JavaScript to process the debug request.
v8::Local<v8::String> fun_name;
v8::Local<v8::Function> fun;
......@@ -1991,6 +2016,18 @@ void DebugMessageThread::ProcessCommand(Vector<uint16_t> command) {
}
// Puts a host dispatch comming from the public API on the queue.
void DebugMessageThread::ProcessHostDispatch(void* dispatch) {
uint16_t hack[3];
hack[0] = 0;
hack[1] = reinterpret_cast<uint32_t>(dispatch) >> 16;
hack[2] = reinterpret_cast<uint32_t>(dispatch) & 0xFFFF;
Logger::DebugTag("Put dispatch on command_queue.");
command_queue_.Put(Vector<uint16_t>(hack, 3).Clone());
command_received_->Signal();
}
void DebugMessageThread::OnDebuggerInactive() {
// Send an empty command to the debugger if in a break to make JavaScript run
// again if the debugger is closed.
......
......@@ -381,8 +381,11 @@ class Debugger {
Handle<Object> event_data);
static void SetEventListener(Handle<Object> callback, Handle<Object> data);
static void SetMessageHandler(v8::DebugMessageHandler handler, void* data);
static void SetHostDispatchHandler(v8::DebugHostDispatchHandler handler,
void* data);
static void SendMessage(Vector<uint16_t> message);
static void ProcessCommand(Vector<const uint16_t> command);
static void ProcessHostDispatch(void* dispatch);
static void UpdateActiveDebugger();
static Handle<Object> Call(Handle<JSFunction> fun,
Handle<Object> data,
......@@ -411,8 +414,12 @@ class Debugger {
static bool compiling_natives_; // Are we compiling natives?
static bool is_loading_debugger_; // Are we loading the debugger?
static DebugMessageThread* message_thread_;
static v8::DebugMessageHandler debug_message_handler_;
static void* debug_message_handler_data_;
static v8::DebugMessageHandler message_handler_;
static void* message_handler_data_;
static v8::DebugHostDispatchHandler host_dispatch_handler_;
static void* host_dispatch_handler_data_;
friend class DebugMessageThread;
};
......@@ -481,6 +488,7 @@ class DebugMessageThread: public Thread {
// by the API client thread. This is where the API client hands off
// processing of the command to the DebugMessageThread thread.
void ProcessCommand(Vector<uint16_t> command);
void ProcessHostDispatch(void* dispatch);
void OnDebuggerInactive();
// Main function of DebugMessageThread thread.
......
......@@ -3660,3 +3660,40 @@ TEST(DebuggerUnload) {
v8::Debug::SetDebugEventListener(NULL);
CheckDebuggerUnloaded(true);
}
int host_dispatch_hit_count = 0;
static void HostDispatchHandlerHitCount(void* dispatch, void *data) {
CHECK_EQ(dispatch, &HostDispatchHandlerHitCount);
CHECK_EQ(data, &HostDispatchHandlerHitCount);
host_dispatch_hit_count++;
}
// Test that clearing the debug event listener actually clears all break points
// and related information.
TEST(DebuggerHostDispatch) {
v8::HandleScope scope;
DebugLocalContext env;
const int kBufferSize = 1000;
uint16_t buffer[kBufferSize];
const char* command_continue =
"{\"seq\":106,"
"\"type\":\"request\","
"\"command\":\"continue\"}";
// Setup message and host dispatch handlers.
v8::Debug::SetMessageHandler(DummyMessageHandler);
v8::Debug::SetHostDispatchHandler(HostDispatchHandlerHitCount,
&HostDispatchHandlerHitCount);
// Fill a host dispatch and a continue command on the command queue before
// generating a debug break.
v8::Debug::SendHostDispatch(&HostDispatchHandlerHitCount);
v8::Debug::SendCommand(buffer, AsciiToUtf16(command_continue, buffer));
CompileRun("debugger");
// The host dispatch callback should be called.
CHECK_EQ(1, host_dispatch_hit_count);
}
\ No newline at end of file
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