Skip to content

Commit 6fe5518

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 e592899 commit 6fe5518

File tree

4 files changed

+86
-2
lines changed

4 files changed

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

0 commit comments

Comments
 (0)