-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Handle SIGUSR1 #26053
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Handle SIGUSR1 #26053
Changes from 66 commits
05ea1a2
91bfb9f
f0f5d17
3e15ddc
5e504db
0a40bb5
e474a1d
a859227
997c776
d1924b8
6aeadbf
1130675
8752473
1366c69
c25572e
a028ee9
60b7424
19fa3d3
1a70d18
1d2becb
93de1c3
d607391
0ae67c7
cc6704d
856eda2
f188b93
d28affd
d662557
57efbd0
c06ef30
ebcfbd2
aec9e81
b848ac2
0869bd7
3b67f3f
d9b396a
8990dfc
21fb83e
f758a5f
ef5b11c
e8f40c2
c83254a
e7ef32e
3466088
75a7ee5
f2a6c7c
9195e68
098bcfa
1f70115
6bd0cf3
83a78f3
e4cd00d
88b19a8
a461b72
51e26fd
e3baed5
4c10814
32ead03
75c127f
82bf72a
1c88d40
d780bf1
f6189c7
f2a1350
82eb3f3
37cbb0c
3800722
ad01185
084f565
d819416
cf53710
5024e20
a2ba9cb
b97f3f3
1a6b804
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1346,6 +1346,9 @@ extern "C" bool Bun__shouldIgnoreOneDisconnectEventListener(JSC::JSGlobalObject* | |
| extern "C" void Bun__ensureSignalHandler(); | ||
| extern "C" bool Bun__isMainThreadVM(); | ||
| extern "C" void Bun__onPosixSignal(int signalNumber); | ||
| #ifdef SIGUSR1 | ||
| extern "C" void Bun__Sigusr1Handler__uninstall(); | ||
| #endif | ||
|
|
||
| __attribute__((noinline)) static void forwardSignal(int signalNumber) | ||
| { | ||
|
|
@@ -1504,6 +1507,14 @@ static void onDidChangeListeners(EventEmitter& eventEmitter, const Identifier& e | |
| action.sa_flags = SA_RESTART; | ||
|
|
||
| sigaction(signalNumber, &action, nullptr); | ||
|
|
||
| #ifdef SIGUSR1 | ||
| // When user adds a SIGUSR1 listener, uninstall the automatic | ||
| // inspector activation handler. User handlers take precedence. | ||
| if (signalNumber == SIGUSR1) { | ||
| Bun__Sigusr1Handler__uninstall(); | ||
| } | ||
| #endif | ||
| #else | ||
| signal_handle.handle = Bun__UVSignalHandle__init( | ||
| eventEmitter.scriptExecutionContext()->jsGlobalObject(), | ||
|
|
@@ -3834,6 +3845,71 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionReallyKill, (JSC::JSGlobalObject * glob | |
| RELEASE_AND_RETURN(scope, JSValue::encode(jsNumber(result))); | ||
| } | ||
|
|
||
| JSC_DEFINE_HOST_FUNCTION(Process_functionDebugProcess, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) | ||
| { | ||
| auto scope = DECLARE_THROW_SCOPE(JSC::getVM(globalObject)); | ||
|
|
||
| if (callFrame->argumentCount() < 1) { | ||
| throwVMError(globalObject, scope, "process._debugProcess requires a pid argument"_s); | ||
| return {}; | ||
| } | ||
|
|
||
| int pid = callFrame->argument(0).toInt32(globalObject); | ||
| RETURN_IF_EXCEPTION(scope, {}); | ||
|
|
||
| // posix we can just send SIGUSR1, on windows we map a file to `bun-debug-handler-<pid>` and send to that | ||
| #if !OS(WINDOWS) | ||
| int result = kill(pid, SIGUSR1); | ||
| if (result < 0) { | ||
| throwVMError(globalObject, scope, makeString("Failed to send SIGUSR1 to process "_s, pid, ": process may not exist or permission denied"_s)); | ||
| return {}; | ||
| } | ||
| #else | ||
| wchar_t mappingName[64]; | ||
| swprintf(mappingName, 64, L"bun-debug-handler-%d", pid); | ||
|
|
||
| HANDLE hMapping = OpenFileMappingW(FILE_MAP_READ, FALSE, mappingName); | ||
| if (!hMapping) { | ||
| // Match Node.js error message for compatibility | ||
| throwVMError(globalObject, scope, "The system cannot find the file specified."_s); | ||
| return {}; | ||
| } | ||
|
|
||
| void* pFunc = MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, sizeof(void*)); | ||
| if (!pFunc) { | ||
| CloseHandle(hMapping); | ||
| throwVMError(globalObject, scope, makeString("Failed to map debug handler for process "_s, pid)); | ||
| return {}; | ||
| } | ||
|
|
||
| LPTHREAD_START_ROUTINE threadProc = *reinterpret_cast<LPTHREAD_START_ROUTINE*>(pFunc); | ||
| UnmapViewOfFile(pFunc); | ||
|
Comment on lines
+3873
to
+3876
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add null check for After reading from the mapped memory, 🔧 Suggested fix LPTHREAD_START_ROUTINE threadProc = *reinterpret_cast<LPTHREAD_START_ROUTINE*>(pFunc);
UnmapViewOfFile(pFunc);
CloseHandle(hMapping);
+ if (!threadProc) {
+ throwVMError(globalObject, scope, makeString("Invalid debug handler for process "_s, pid));
+ return {};
+ }
+
HANDLE hProcess = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, FALSE, pid);🤖 Prompt for AI Agents |
||
| CloseHandle(hMapping); | ||
|
|
||
| HANDLE hProcess = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, FALSE, pid); | ||
| if (!hProcess) { | ||
| throwVMError(globalObject, scope, makeString("Failed to open process "_s, pid, ": access denied or process not found"_s)); | ||
| return {}; | ||
| } | ||
|
|
||
| HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, threadProc, NULL, 0, NULL); | ||
| if (!hThread) { | ||
| CloseHandle(hProcess); | ||
| throwVMError(globalObject, scope, makeString("Failed to create remote thread in process "_s, pid)); | ||
| return {}; | ||
| } | ||
|
|
||
| // Wait briefly for the thread to complete because closing the handles | ||
| // immediately could terminate the remote thread before it finishes | ||
| // triggering the inspector in the target process. | ||
| WaitForSingleObject(hThread, 1000); | ||
| CloseHandle(hThread); | ||
| CloseHandle(hProcess); | ||
| #endif | ||
|
|
||
| return JSValue::encode(jsUndefined()); | ||
| } | ||
|
|
||
| JSC_DEFINE_HOST_FUNCTION(Process_functionKill, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) | ||
| { | ||
| auto scope = DECLARE_THROW_SCOPE(JSC::getVM(globalObject)); | ||
|
|
@@ -3973,7 +4049,7 @@ extern "C" void Process__emitErrorEvent(Zig::GlobalObject* global, EncodedJSValu | |
| /* Source for Process.lut.h | ||
| @begin processObjectTable | ||
| _debugEnd Process_stubEmptyFunction Function 0 | ||
| _debugProcess Process_stubEmptyFunction Function 0 | ||
| _debugProcess Process_functionDebugProcess Function 1 | ||
| _eval processGetEval CustomAccessor | ||
| _fatalException Process_stubEmptyFunction Function 1 | ||
| _getActiveHandles Process_stubFunctionReturningArray Function 0 | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| /// Zig bindings for JSC::VMManager | ||
| /// | ||
| /// VMManager coordinates multiple VMs (workers) and provides the StopTheWorld | ||
| /// mechanism for safely interrupting JavaScript execution at safe points. | ||
| /// | ||
| /// Note: StopReason values are bitmasks (1 << bit_position), not sequential. | ||
| /// This matches the C++ enum in VMManager.h which uses: | ||
| /// enum class StopReason : StopRequestBits { None = 0, GC = 1, WasmDebugger = 2, MemoryDebugger = 4, JSDebugger = 8 } | ||
| pub const StopReason = enum(u32) { | ||
| None = 0, | ||
| GC = 1 << 0, // 1 | ||
| WasmDebugger = 1 << 1, // 2 | ||
| MemoryDebugger = 1 << 2, // 4 | ||
| JSDebugger = 1 << 3, // 8 | ||
| }; | ||
|
|
||
| extern fn VMManager__requestStopAll(reason: StopReason) void; | ||
| extern fn VMManager__requestResumeAll(reason: StopReason) void; | ||
|
|
||
| /// Request all VMs to stop at their next safe point. | ||
| /// The registered StopTheWorld callback for the given reason will be called | ||
| /// on the main thread once all VMs have stopped. | ||
| pub fn requestStopAll(reason: StopReason) void { | ||
| VMManager__requestStopAll(reason); | ||
| } | ||
|
|
||
| /// Clear the pending stop request and resume all VMs. | ||
| pub fn requestResumeAll(reason: StopReason) void { | ||
| VMManager__requestResumeAll(reason); | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.