|
30 | 30 | #include "internal.h" |
31 | 31 | #include "handle-inl.h" |
32 | 32 | #include "req-inl.h" |
| 33 | +#include <dbghelp.h> |
| 34 | +#include <shlobj.h> |
| 35 | +#include <psapi.h> /* GetModuleBaseNameW */ |
33 | 36 |
|
34 | 37 |
|
35 | 38 | typedef struct env_var { |
@@ -1250,7 +1253,87 @@ static int uv__kill(HANDLE process_handle, int signum) { |
1250 | 1253 | return UV_EINVAL; |
1251 | 1254 | } |
1252 | 1255 |
|
| 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 | + ®istry_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 | + |
1253 | 1335 | switch (signum) { |
| 1336 | + case SIGQUIT: |
1254 | 1337 | case SIGTERM: |
1255 | 1338 | case SIGKILL: |
1256 | 1339 | case SIGINT: { |
|
0 commit comments