Skip to content

Commit e09b534

Browse files
committed
[Windows] Write out minidumps on reception of SIGQUIT
This commit adds the ability to dump core when sending the `SIGQUIT` signal on Windows. The change reads in the current registry setting for local dumps, and attempts to write out to that location before killing the process. See [0] for registry and pathing details. This behavior mimics that of the dumps created by the typical Windows Error Reporting mechanism. [0] https://learn.microsoft.com/en-us/windows/win32/wer/collecting-user-mode-dumps
1 parent 23270d3 commit e09b534

File tree

4 files changed

+81
-2
lines changed

4 files changed

+81
-2
lines changed

configure

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15138,7 +15138,7 @@ fi
1513815138

1513915139
case $host_os in
1514015140
mingw*)
15141-
LIBS="$LIBS -lws2_32 -lpsapi -liphlpapi -lshell32 -lsecur32 -luserenv -luser32"
15141+
LIBS="$LIBS -lws2_32 -lpsapi -liphlpapi -lshell32 -lsecur32 -luserenv -luser32 -ldbghelp -lole32 -luuid"
1514215142
;;
1514315143
esac
1514415144
case $host_os in

configure.ac

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ AM_CONDITIONAL([OS400], [AS_CASE([$host_os],[os400], [true], [false])
7474
AM_CONDITIONAL([SUNOS], [AS_CASE([$host_os],[solaris*], [true], [false])])
7575
AM_CONDITIONAL([WINNT], [AS_CASE([$host_os],[mingw*], [true], [false])])
7676
AS_CASE([$host_os],[mingw*], [
77-
LIBS="$LIBS -lws2_32 -lpsapi -liphlpapi -lshell32 -lsecur32 -luserenv -luser32"
77+
LIBS="$LIBS -lws2_32 -lpsapi -liphlpapi -lshell32 -lsecur32 -luserenv -luser32 -ldbghelp -lole32 -luuid"
7878
])
7979
AS_CASE([$host_os], [netbsd*], [AC_CHECK_LIB([kvm], [kvm_open])])
8080
AS_CASE([$host_os], [kfreebsd*], [

include/uv/win.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ typedef intptr_t ssize_t;
5757
/* Signals supported by uv_signal and or uv_kill */
5858
#define SIGHUP 1
5959
#define SIGINT 2
60+
#define SIGQUIT 3
6061
#define SIGILL 4
6162
#define SIGABRT_COMPAT 6
6263
#define SIGFPE 8

src/win/process.c

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@
3030
#include "internal.h"
3131
#include "handle-inl.h"
3232
#include "req-inl.h"
33+
#include <dbghelp.h>
34+
#include <shlobj.h>
35+
#include <psapi.h> /* GetModuleBaseNameW */
3336

3437

3538
typedef struct env_var {
@@ -1250,7 +1253,82 @@ static int uv__kill(HANDLE process_handle, int signum) {
12501253
return UV_EINVAL;
12511254
}
12521255

1256+
/*
1257+
* Create a dump file for the targeted process, if the registry key
1258+
* `HKLM:Software\Microsoft\Windows\Windows Error Reporting\LocalDumps\DumpFolder`
1259+
* points to a valid folder. The default value if the registry key does not
1260+
* exist is `%LOCALAPPDATA%\CrashDumps`, see [0] for more detail.
1261+
* [0]: https://learn.microsoft.com/en-us/windows/win32/wer/collecting-user-mode-dumps
1262+
*/
1263+
if (signum == SIGQUIT) {
1264+
/* Get target process name. */
1265+
WCHAR basename[MAX_PATH];
1266+
GetModuleBaseNameW(process_handle, NULL, &basename[0], sizeof(basename));
1267+
1268+
/* Get PID of target process. */
1269+
DWORD pid = GetProcessId(process_handle);
1270+
1271+
/* Get LocalDumps directory path. */
1272+
HKEY registry_key;
1273+
WCHAR dump_folder[MAX_PATH];
1274+
DWORD ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
1275+
L"SOFTWARE\\Microsoft\\Windows\\Windows Error Reporting\\LocalDumps",
1276+
0,
1277+
KEY_QUERY_VALUE,
1278+
&registry_key);
1279+
if (ret == ERROR_SUCCESS) {
1280+
DWORD dump_folder_len = sizeof(dump_folder);
1281+
DWORD key_type = 0;
1282+
ret = RegGetValueW(registry_key,
1283+
NULL,
1284+
L"DumpFolder",
1285+
RRF_RT_ANY,
1286+
&key_type,
1287+
(PVOID) dump_folder,
1288+
&dump_folder_len);
1289+
if (ret != ERROR_SUCCESS) {
1290+
/* Default value for `dump_folder` is `%LOCALAPPDATA%\CrashDumps`. */
1291+
WCHAR * localappdata;
1292+
SHGetKnownFolderPath(&FOLDERID_LocalAppData, 0, NULL, &localappdata);
1293+
_snwprintf_s(dump_folder, sizeof(dump_folder), _TRUNCATE, L"%ls\\CrashDumps", localappdata);
1294+
CoTaskMemFree(localappdata);
1295+
}
1296+
RegCloseKey(registry_key);
1297+
}
1298+
1299+
/* Construct dump filename. */
1300+
WCHAR dump_name[MAX_PATH];
1301+
_snwprintf_s(dump_name, sizeof(dump_name), _TRUNCATE, L"%ls\\%ls.%d.dmp", dump_folder, basename, pid);
1302+
1303+
HANDLE hDumpFile = CreateFileW(dump_name, GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
1304+
if (hDumpFile != INVALID_HANDLE_VALUE) {
1305+
/* If something goes wrong while writing it out, delete the file to prevent clutter accumulation. */
1306+
FILE_DISPOSITION_INFO DeleteOnClose = { TRUE };
1307+
SetFileInformationByHandle(hDumpFile, FileDispositionInfo, &DeleteOnClose, sizeof(DeleteOnClose));
1308+
1309+
/* Tell wine to dump ELF modules as well. */
1310+
DWORD sym_options = SymGetOptions();
1311+
SymSetOptions(sym_options | 0x40000000);
1312+
1313+
if (MiniDumpWriteDump(process_handle, pid, hDumpFile,
1314+
/*
1315+
* We default to a rather large dump, so as to be more useful for debugging.
1316+
* In the future, we may want to allow customization of what kinds of dumps
1317+
* to create, as not all clients may wish to create such a large dump.
1318+
*/
1319+
MiniDumpWithFullMemory | MiniDumpIgnoreInaccessibleMemory | MiniDumpWithAvxXStateContext,
1320+
NULL, NULL, NULL) == TRUE) {
1321+
/* Don't delete the file on close if we successfully wrote it out. */
1322+
FILE_DISPOSITION_INFO DeleteOnClose = { FALSE };
1323+
SetFileInformationByHandle(hDumpFile, FileDispositionInfo, &DeleteOnClose, sizeof(DeleteOnClose));
1324+
}
1325+
SymSetOptions(sym_options);
1326+
CloseHandle(hDumpFile);
1327+
}
1328+
}
1329+
12531330
switch (signum) {
1331+
case SIGQUIT:
12541332
case SIGTERM:
12551333
case SIGKILL:
12561334
case SIGINT: {

0 commit comments

Comments
 (0)