win,process: write minidumps when sending SIGQUIT (#3840)

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 [collecting-user-mode-dumps] for registry and pathing
details. This behavior mimics that of the dumps created by the typical
Windows Error Reporting mechanism.

[collecting-user-mode-dumps]: https://learn.microsoft.com/en-us/windows/win32/wer/collecting-user-mode-dumps
This commit is contained in:
Elliot Saba 2023-05-12 11:28:35 -07:00 committed by GitHub
parent f70027cbdc
commit 748d894e82
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 118 additions and 2 deletions

View File

@ -183,7 +183,10 @@ if(WIN32)
advapi32
iphlpapi
userenv
ws2_32)
ws2_32
dbghelp
ole32
uuid)
list(APPEND uv_sources
src/win/async.c
src/win/core.c

View File

@ -73,7 +73,7 @@ AM_CONDITIONAL([OS400], [AS_CASE([$host_os],[os400], [true], [false])
AM_CONDITIONAL([SUNOS], [AS_CASE([$host_os],[solaris*], [true], [false])])
AM_CONDITIONAL([WINNT], [AS_CASE([$host_os],[mingw*], [true], [false])])
AS_CASE([$host_os],[mingw*], [
LIBS="$LIBS -lws2_32 -lpsapi -liphlpapi -lshell32 -luserenv -luser32"
LIBS="$LIBS -lws2_32 -lpsapi -liphlpapi -lshell32 -luserenv -luser32 -ldbghelp -lole32 -luuid"
])
AS_CASE([$host_os], [solaris2.10], [
CFLAGS="$CFLAGS -DSUNOS_NO_IFADDRS"

View File

@ -91,6 +91,7 @@ typedef struct pollfd {
* variants (Linux and Darwin)
*/
#define SIGHUP 1
#define SIGQUIT 3
#define SIGKILL 9
#define SIGWINCH 28

View File

@ -32,6 +32,9 @@
#include "internal.h"
#include "handle-inl.h"
#include "req-inl.h"
#include <dbghelp.h>
#include <shlobj.h>
#include <psapi.h> /* GetModuleBaseNameW */
#define SIGKILL 9
@ -1194,7 +1197,116 @@ static int uv__kill(HANDLE process_handle, int signum) {
return UV_EINVAL;
}
/* Create a dump file for the targeted process, if the registry key
* `HKLM:Software\Microsoft\Windows\Windows Error Reporting\LocalDumps`
* exists. The location of the dumps can be influenced by the `DumpFolder`
* sub-key, which has a default value of `%LOCALAPPDATA%\CrashDumps`, see [0]
* for more detail. Note that if the dump folder does not exist, we attempt
* to create it, to match behavior with WER itself.
* [0]: https://learn.microsoft.com/en-us/windows/win32/wer/collecting-user-mode-dumps */
if (signum == SIGQUIT) {
HKEY registry_key;
DWORD pid, ret;
WCHAR basename[MAX_PATH];
/* Get target process name. */
GetModuleBaseNameW(process_handle, NULL, &basename[0], sizeof(basename));
/* Get PID of target process. */
pid = GetProcessId(process_handle);
/* Get LocalDumps directory path. */
ret = RegOpenKeyExW(
HKEY_LOCAL_MACHINE,
L"SOFTWARE\\Microsoft\\Windows\\Windows Error Reporting\\LocalDumps",
0,
KEY_QUERY_VALUE,
&registry_key);
if (ret == ERROR_SUCCESS) {
HANDLE hDumpFile = NULL;
WCHAR dump_folder[MAX_PATH], dump_name[MAX_PATH];
DWORD dump_folder_len = sizeof(dump_folder), key_type = 0;
ret = RegGetValueW(registry_key,
NULL,
L"DumpFolder",
RRF_RT_ANY,
&key_type,
(PVOID) dump_folder,
&dump_folder_len);
if (ret != ERROR_SUCCESS) {
/* Default value for `dump_folder` is `%LOCALAPPDATA%\CrashDumps`. */
WCHAR* localappdata;
SHGetKnownFolderPath(&FOLDERID_LocalAppData, 0, NULL, &localappdata);
_snwprintf_s(dump_folder,
sizeof(dump_folder),
_TRUNCATE,
L"%ls\\CrashDumps",
localappdata);
CoTaskMemFree(localappdata);
}
RegCloseKey(registry_key);
/* Create dump folder if it doesn't already exist. */
CreateDirectoryW(dump_folder, NULL);
/* Construct dump filename from process name and PID. */
_snwprintf_s(dump_name,
sizeof(dump_name),
_TRUNCATE,
L"%ls\\%ls.%d.dmp",
dump_folder,
basename,
pid);
hDumpFile = CreateFileW(dump_name,
GENERIC_WRITE,
0,
NULL,
CREATE_NEW,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hDumpFile != INVALID_HANDLE_VALUE) {
DWORD dump_options, sym_options;
FILE_DISPOSITION_INFO DeleteOnClose = { TRUE };
/* If something goes wrong while writing it out, delete the file. */
SetFileInformationByHandle(hDumpFile,
FileDispositionInfo,
&DeleteOnClose,
sizeof(DeleteOnClose));
/* Tell wine to dump ELF modules as well. */
sym_options = SymGetOptions();
SymSetOptions(sym_options | 0x40000000);
/* We default to a fairly complete dump. In the future, we may want to
* allow clients to customize what kind of dump to create. */
dump_options = MiniDumpWithFullMemory |
MiniDumpIgnoreInaccessibleMemory |
MiniDumpWithAvxXStateContext;
if (MiniDumpWriteDump(process_handle,
pid,
hDumpFile,
dump_options,
NULL,
NULL,
NULL)) {
/* Don't delete the file on close if we successfully wrote it out. */
FILE_DISPOSITION_INFO DontDeleteOnClose = { FALSE };
SetFileInformationByHandle(hDumpFile,
FileDispositionInfo,
&DontDeleteOnClose,
sizeof(DontDeleteOnClose));
}
SymSetOptions(sym_options);
CloseHandle(hDumpFile);
}
}
}
switch (signum) {
case SIGQUIT:
case SIGTERM:
case SIGKILL:
case SIGINT: {