diff options
-rw-r--r-- | lib/_debugger.js | 2 | ||||
-rw-r--r-- | src/node.cc | 225 | ||||
-rw-r--r-- | test/simple/test-debugger-client.js | 4 |
3 files changed, 199 insertions, 32 deletions
diff --git a/lib/_debugger.js b/lib/_debugger.js index a6bc213e06..7c4db460b2 100644 --- a/lib/_debugger.js +++ b/lib/_debugger.js @@ -1538,7 +1538,7 @@ Interface.prototype.trySpawn = function(cb) { // TODO Do we really need to handle it? } }; - process.kill(parseInt(this.args[2], 10), 'SIGUSR1'); + process._debugProcess(parseInt(this.args[2], 10)); } } diff --git a/src/node.cc b/src/node.cc index 25d1127673..4664287d0d 100644 --- a/src/node.cc +++ b/src/node.cc @@ -801,11 +801,11 @@ Local<Value> ErrnoException(int errorno, #ifdef _WIN32 Local<Value> WinapiErrnoException(int errorno, - const char *syscall, - const char *msg, - const char *path) { + const char* syscall, + const char* msg, + const char* path) { Local<Value> e; - if (!msg[0]) { + if (!msg || !msg[0]) { msg = winapi_strerror(errorno); } Local<String> message = String::NewSymbol(msg); @@ -1823,6 +1823,8 @@ static Handle<Object> GetFeatures() { } +static Handle<Value> DebugProcess(const Arguments& args); + Handle<Object> SetupProcessObject(int argc, char *argv[]) { HandleScope scope; @@ -1941,6 +1943,8 @@ Handle<Object> SetupProcessObject(int argc, char *argv[]) { NODE_SET_METHOD(process, "_kill", Kill); + NODE_SET_METHOD(process, "_debugProcess", DebugProcess); + NODE_SET_METHOD(process, "dlopen", DLOpen); NODE_SET_METHOD(process, "uptime", Uptime); @@ -2104,9 +2108,15 @@ static void ParseArgs(int argc, char **argv) { option_end_index = i; } + +static Isolate* node_isolate = NULL; static volatile bool debugger_running = false; static void EnableDebug(bool wait_connect) { + // If we're called from another thread, make sure to enter the right + // v8 isolate. + node_isolate->Enter(); + // Start the debug thread and it's associated TCP server on port 5858. bool r = Debug::EnableAgent("node " NODE_VERSION, debug_port); @@ -2123,55 +2133,209 @@ static void EnableDebug(bool wait_connect) { // Print out some information. fprintf(stderr, "debugger listening on port %d\n", debug_port); + fflush(stderr); debugger_running = true; + + node_isolate->Exit(); } #ifdef __POSIX__ static void EnableDebugSignalHandler(int signal) { // Break once process will return execution to v8 - v8::Debug::DebugBreak(); + v8::Debug::DebugBreak(node_isolate); if (!debugger_running) { fprintf(stderr, "Hit SIGUSR1 - starting debugger agent.\n"); EnableDebug(false); } } + + +static void RegisterSignalHandler(int signal, void (*handler)(int)) { + struct sigaction sa; + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = handler; + sigfillset(&sa.sa_mask); + sigaction(signal, &sa, NULL); +} + + +Handle<Value> DebugProcess(const Arguments& args) { + HandleScope scope; + + if (args.Length() != 1) { + return ThrowException(Exception::Error( + String::New("Invalid number of arguments."))); + } + + pid_t pid; + int r; + + pid = args[0]->IntegerValue(); + r = kill(pid, SIGUSR1); + if (r != 0) { + return ThrowException(ErrnoException(errno, "kill")); + } + + return Undefined(); +} #endif // __POSIX__ -#if defined(__MINGW32__) || defined(_MSC_VER) -static bool EnableDebugSignalHandler(DWORD signal) { - if (signal == CTRL_C_EVENT) exit(1); - if (signal != CTRL_BREAK_EVENT) return false; +#ifdef _WIN32 +DWORD WINAPI EnableDebugThreadProc(void* arg) { // Break once process will return execution to v8 + if (!debugger_running) { + for (int i = 0; i < 1; i++) { + fprintf(stderr, "Starting debugger agent.\r\n"); + fflush(stderr); + EnableDebug(false); + } + } + v8::Debug::DebugBreak(); - if (!debugger_running) { - fprintf(stderr, "Hit Ctrl+Break - starting debugger agent.\n"); - EnableDebug(false); - return true; - } else { - // Run default system action (terminate) - return false; + return 0; +} + + +static int GetDebugSignalHandlerMappingName(DWORD pid, char* buf, size_t buf_len) { + return snprintf(buf, buf_len, "node-debug-handler-%u", pid); +} + + +static int RegisterDebugSignalHandler() { + char mapping_name[32]; + HANDLE mapping_handle; + DWORD pid; + LPTHREAD_START_ROUTINE* handler; + + pid = GetCurrentProcessId(); + + if (GetDebugSignalHandlerMappingName(pid, + mapping_name, + sizeof mapping_name) < 0) { + return -1; + } + + mapping_handle = CreateFileMappingA(INVALID_HANDLE_VALUE, + NULL, + PAGE_READWRITE, + 0, + sizeof *handler, + mapping_name); + if (mapping_handle == NULL) { + return -1; } + handler = (LPTHREAD_START_ROUTINE*) MapViewOfFile(mapping_handle, + FILE_MAP_ALL_ACCESS, + 0, + 0, + sizeof *handler); + if (handler == NULL) { + CloseHandle(mapping_handle); + return -1; + } + + *handler = EnableDebugThreadProc; + + UnmapViewOfFile((void*) handler); + + return 0; } -#endif -#ifdef __POSIX__ +static Handle<Value> DebugProcess(const Arguments& args) { + HandleScope scope; + Handle<Value> rv = Undefined(); + DWORD pid; + HANDLE process = NULL; + HANDLE thread = NULL; + HANDLE mapping = NULL; + char mapping_name[32]; + LPTHREAD_START_ROUTINE* handler = NULL; -static int RegisterSignalHandler(int signal, void (*handler)(int)) { - struct sigaction sa; + if (args.Length() != 1) { + rv = ThrowException(Exception::Error(String::New("Invalid number of arguments."))); + goto out; + } - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = handler; - sigfillset(&sa.sa_mask); - return sigaction(signal, &sa, NULL); + pid = (DWORD) args[0]->IntegerValue(); + + process = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | + PROCESS_VM_OPERATION | PROCESS_VM_WRITE | + PROCESS_VM_READ, + FALSE, + pid); + if (process == NULL) { + rv = ThrowException(WinapiErrnoException(GetLastError(), "OpenProcess")); + goto out; + } + + if (GetDebugSignalHandlerMappingName(pid, + mapping_name, + sizeof mapping_name) < 0) { + rv = ThrowException(ErrnoException(errno, "sprintf")); + goto out; + } + + mapping = OpenFileMapping(FILE_MAP_READ, FALSE, mapping_name); + if (mapping == NULL) { + rv = ThrowException(WinapiErrnoException(GetLastError(), "sprintf")); + goto out; + } + + handler = (LPTHREAD_START_ROUTINE*) MapViewOfFile(mapping, + FILE_MAP_READ, + 0, + 0, + sizeof *handler); + if (handler == NULL || *handler == NULL) { + rv = ThrowException(WinapiErrnoException(GetLastError(), "MapViewOfFile")); + goto out; + } + + thread = CreateRemoteThread(process, + NULL, + 0, + *handler, + NULL, + 0, + NULL); + if (thread == NULL) { + rv = ThrowException(WinapiErrnoException(GetLastError(), + "CreateRemoteThread")); + goto out; + } + + // Wait for the thread to terminate + if (WaitForSingleObject(thread, INFINITE) != WAIT_OBJECT_0) { + rv = ThrowException(WinapiErrnoException(GetLastError(), + "WaitForSingleObject")); + goto out; + } + + out: + if (process != NULL) { + CloseHandle(process); + } + if (thread != NULL) { + CloseHandle(thread); + } + if (handler != NULL) { + UnmapViewOfFile(handler); + } + if (mapping != NULL) { + CloseHandle(mapping); + } + + return Undefined(); } -#endif // __POSIX__ +#endif // _WIN32 char** Init(int argc, char *argv[]) { @@ -2245,6 +2409,7 @@ char** Init(int argc, char *argv[]) { // Set the callback DebugMessageDispatch which is called from the debug // thread. Debug::SetDebugMessageDispatchHandler(node::DebugMessageDispatch); + // Initialize the async watcher. DebugMessageCallback() is called from the // main thread to execute a random bit of javascript - which will give V8 // control so it can handle whatever new message had been received on the @@ -2254,17 +2419,19 @@ char** Init(int argc, char *argv[]) { // unref it so that we exit the event loop despite it being active. uv_unref(uv_default_loop()); + // Fetch a reference to the main isolate, so we have a reference to it + // even when we need it to access it from another (debugger) thread. + node_isolate = Isolate::GetCurrent(); // If the --debug flag was specified then initialize the debug thread. if (node::use_debug_agent) { EnableDebug(debug_wait_connect); } else { -#ifdef __POSIX__ +#ifdef _WIN32 + RegisterDebugSignalHandler(); +#else // Posix RegisterSignalHandler(SIGUSR1, EnableDebugSignalHandler); #endif // __POSIX__ -#if defined(__MINGW32__) || defined(_MSC_VER) - SetConsoleCtrlHandler((PHANDLER_ROUTINE) EnableDebugSignalHandler, TRUE); -#endif } return argv; diff --git a/test/simple/test-debugger-client.js b/test/simple/test-debugger-client.js index 5f138a8c7d..832e27fc31 100644 --- a/test/simple/test-debugger-client.js +++ b/test/simple/test-debugger-client.js @@ -147,8 +147,8 @@ function doTest(cb, done) { nodeProcess.stdout.once('data', function() { console.log('>>> new node process: %d', nodeProcess.pid); - process.kill(nodeProcess.pid, 'SIGUSR1'); - console.log('>>> signaling it with SIGUSR1'); + process._debugProcess(nodeProcess.pid); + console.log('>>> starting debugger session'); }); var didTryConnect = false; |