static void crash_and_debug(HKEY hkey, const char* argv0, const char* dbgtasks) { DWORD ret; HANDLE start_event, done_event; char* cmd; char dbglog[MAX_PATH]; char childlog[MAX_PATH]; PROCESS_INFORMATION info; STARTUPINFOA startup; DWORD exit_code; crash_blackbox_t crash_blackbox; debugger_blackbox_t dbg_blackbox; ret=RegSetValueExA(hkey, "auto", 0, REG_SZ, (BYTE*)"1", 2); ok(ret == ERROR_SUCCESS, "unable to set AeDebug/auto: ret=%d\n", ret); get_file_name(dbglog); get_events(dbglog, &start_event, &done_event); cmd=HeapAlloc(GetProcessHeap(), 0, strlen(argv0)+10+strlen(dbgtasks)+1+strlen(dbglog)+34+1); sprintf(cmd, "%s debugger %s %s %%ld %%ld", argv0, dbgtasks, dbglog); ret=RegSetValueExA(hkey, "debugger", 0, REG_SZ, (BYTE*)cmd, strlen(cmd)+1); ok(ret == ERROR_SUCCESS, "unable to set AeDebug/debugger: ret=%d\n", ret); HeapFree(GetProcessHeap(), 0, cmd); get_file_name(childlog); cmd=HeapAlloc(GetProcessHeap(), 0, strlen(argv0)+16+strlen(dbglog)+1); sprintf(cmd, "%s debugger crash %s", argv0, childlog); memset(&startup, 0, sizeof(startup)); startup.cb = sizeof(startup); startup.dwFlags = STARTF_USESHOWWINDOW; startup.wShowWindow = SW_SHOWNORMAL; ret=CreateProcessA(NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info); ok(ret, "CreateProcess: err=%d\n", GetLastError()); HeapFree(GetProcessHeap(), 0, cmd); CloseHandle(info.hThread); /* The process exits... */ trace("waiting for child exit...\n"); ok(WaitForSingleObject(info.hProcess, 60000) == WAIT_OBJECT_0, "Timed out waiting for the child to crash\n"); ok(GetExitCodeProcess(info.hProcess, &exit_code), "GetExitCodeProcess failed: err=%d\n", GetLastError()); if (strstr(dbgtasks, "code2")) { /* If, after attaching to the debuggee, the debugger exits without * detaching, then the debuggee gets a special exit code. */ ok(exit_code == 0xffffffff || /* Win 9x */ exit_code == 0x80 || /* NT4 */ exit_code == STATUS_DEBUGGER_INACTIVE, /* Win >= XP */ "wrong exit code : %08x\n", exit_code); } else ok(exit_code == STATUS_ACCESS_VIOLATION || exit_code == WAIT_ABANDONED, /* win2k3 */ "exit code = %08x instead of STATUS_ACCESS_VIOLATION or WAIT_ABANDONED\n", exit_code); CloseHandle(info.hProcess); /* ...before the debugger */ if (strstr(dbgtasks, "order")) ok(SetEvent(start_event), "SetEvent(start_event) failed\n"); trace("waiting for the debugger...\n"); ok(WaitForSingleObject(done_event, 60000) == WAIT_OBJECT_0, "Timed out waiting for the debugger\n"); assert(load_blackbox(childlog, &crash_blackbox, sizeof(crash_blackbox))); assert(load_blackbox(dbglog, &dbg_blackbox, sizeof(dbg_blackbox))); ok(dbg_blackbox.argc == 6, "wrong debugger argument count: %d\n", dbg_blackbox.argc); ok(dbg_blackbox.pid == crash_blackbox.pid, "the child and debugged pids don't match: %d != %d\n", crash_blackbox.pid, dbg_blackbox.pid); ok(dbg_blackbox.debug_rc, "debugger: SetEvent(debug_event) failed err=%d\n", dbg_blackbox.debug_err); ok(dbg_blackbox.attach_rc, "DebugActiveProcess(%d) failed err=%d\n", dbg_blackbox.pid, dbg_blackbox.attach_err); ok(dbg_blackbox.nokill_rc, "DebugSetProcessKillOnExit(FALSE) failed err=%d\n", dbg_blackbox.nokill_err); ok(dbg_blackbox.detach_rc, "DebugActiveProcessStop(%d) failed err=%d\n", dbg_blackbox.pid, dbg_blackbox.detach_err); assert(DeleteFileA(dbglog) != 0); assert(DeleteFileA(childlog) != 0); }
static void test_debug_children(char *name, DWORD flag, BOOL debug_child) { const char *arguments = "debugger children"; struct child_blackbox blackbox; char blackbox_file[MAX_PATH], *p; char event_name[MAX_PATH]; PROCESS_INFORMATION pi; STARTUPINFOA si; HANDLE event_init, event_attach; char *cmd; BOOL debug, ret; BOOL got_child_event = FALSE; if (!pDebugActiveProcessStop || !pCheckRemoteDebuggerPresent) { win_skip("DebugActiveProcessStop or CheckRemoteDebuggerPresent not available, skipping test.\n"); return; } get_file_name(blackbox_file); cmd = HeapAlloc(GetProcessHeap(), 0, strlen(name) + strlen(arguments) + strlen(blackbox_file) + 5); sprintf(cmd, "%s %s \"%s\"", name, arguments, blackbox_file); p = strrchr(blackbox_file, '\\'); p = p ? p+1 : blackbox_file; strcpy(event_name, p); strcat(event_name, "_init"); event_init = CreateEventA(NULL, FALSE, FALSE, event_name); ok(event_init != NULL, "OpenEvent failed, last error %d.\n", GetLastError()); p = strrchr(blackbox_file, '\\'); p = p ? p+1 : blackbox_file; strcpy(event_name, p); strcat(event_name, "_attach"); event_attach = CreateEventA(NULL, FALSE, flag!=0, event_name); ok(event_attach != NULL, "CreateEvent failed, last error %d.\n", GetLastError()); memset(&si, 0, sizeof(si)); si.cb = sizeof(si); ret = CreateProcessA(NULL, cmd, NULL, NULL, FALSE, flag, NULL, NULL, &si, &pi); ok(ret, "CreateProcess failed, last error %d.\n", GetLastError()); HeapFree(GetProcessHeap(), 0, cmd); if (!flag) { WaitForSingleObject(event_init, INFINITE); ret = DebugActiveProcess(pi.dwProcessId); ok(ret, "DebugActiveProcess failed, last error %d.\n", GetLastError()); ret = SetEvent(event_attach); ok(ret, "SetEvent failed, last error %d.\n", GetLastError()); } ret = pCheckRemoteDebuggerPresent(pi.hProcess, &debug); ok(ret, "CheckRemoteDebuggerPresent failed, last error %d.\n", GetLastError()); ok(debug, "Expected debug != 0, got %x.\n", debug); for (;;) { DEBUG_EVENT ev; ret = WaitForDebugEvent(&ev, INFINITE); ok(ret, "WaitForDebugEvent failed, last error %d.\n", GetLastError()); if (!ret) break; if (ev.dwDebugEventCode==EXIT_PROCESS_DEBUG_EVENT && ev.dwProcessId==pi.dwProcessId) break; else if (ev.dwProcessId != pi.dwProcessId) got_child_event = TRUE; ret = ContinueDebugEvent(ev.dwProcessId, ev.dwThreadId, DBG_CONTINUE); ok(ret, "ContinueDebugEvent failed, last error %d.\n", GetLastError()); if (!ret) break; } if(debug_child) ok(got_child_event, "didn't get any child events (flag: %x).\n", flag); else ok(!got_child_event, "got child event (flag: %x).\n", flag); CloseHandle(event_init); CloseHandle(event_attach); ret = CloseHandle(pi.hThread); ok(ret, "CloseHandle failed, last error %d.\n", GetLastError()); ret = CloseHandle(pi.hProcess); ok(ret, "CloseHandle failed, last error %d.\n", GetLastError()); load_blackbox(blackbox_file, &blackbox, sizeof(blackbox)); ok(!blackbox.failures, "Got %d failures from child process.\n", blackbox.failures); ret = DeleteFileA(blackbox_file); ok(ret, "DeleteFileA failed, last error %d.\n", GetLastError()); }
static void test_debug_loop(int argc, char **argv) { const char *arguments = " debugger child "; struct child_blackbox blackbox; char blackbox_file[MAX_PATH]; PROCESS_INFORMATION pi; STARTUPINFOA si; BOOL debug; DWORD pid; char *cmd; BOOL ret; if (!pDebugActiveProcessStop || !pCheckRemoteDebuggerPresent) { win_skip("DebugActiveProcessStop or CheckRemoteDebuggerPresent not available, skipping test.\n"); return; } pid = GetCurrentProcessId(); ret = DebugActiveProcess(pid); ok(!ret, "DebugActiveProcess() succeeded on own process.\n"); get_file_name(blackbox_file); cmd = HeapAlloc(GetProcessHeap(), 0, strlen(argv[0]) + strlen(arguments) + strlen(blackbox_file) + 10); sprintf(cmd, "%s%s%08x %s", argv[0], arguments, pid, blackbox_file); memset(&si, 0, sizeof(si)); si.cb = sizeof(si); ret = CreateProcessA(NULL, cmd, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &si, &pi); ok(ret, "CreateProcess failed, last error %#x.\n", GetLastError()); HeapFree(GetProcessHeap(), 0, cmd); ret = pCheckRemoteDebuggerPresent(pi.hProcess, &debug); ok(ret, "CheckRemoteDebuggerPresent failed, last error %#x.\n", GetLastError()); ok(debug, "Expected debug != 0, got %#x.\n", debug); for (;;) { DEBUG_EVENT ev; ret = WaitForDebugEvent(&ev, INFINITE); ok(ret, "WaitForDebugEvent failed, last error %#x.\n", GetLastError()); if (!ret) break; if (ev.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT) break; ret = ContinueDebugEvent(ev.dwProcessId, ev.dwThreadId, DBG_CONTINUE); ok(ret, "ContinueDebugEvent failed, last error %#x.\n", GetLastError()); if (!ret) break; } ret = CloseHandle(pi.hThread); ok(ret, "CloseHandle failed, last error %#x.\n", GetLastError()); ret = CloseHandle(pi.hProcess); ok(ret, "CloseHandle failed, last error %#x.\n", GetLastError()); load_blackbox(blackbox_file, &blackbox, sizeof(blackbox)); ok(!blackbox.failures, "Got %d failures from child process.\n", blackbox.failures); ret = DeleteFileA(blackbox_file); ok(ret, "DeleteFileA failed, last error %#x.\n", GetLastError()); }
static void crash_and_debug(HKEY hkey, const char* argv0, const char* dbgtasks) { static BOOL skip_crash_and_debug = FALSE; BOOL bRet; DWORD ret; HANDLE start_event, done_event; char* cmd; char dbglog[MAX_PATH]; char childlog[MAX_PATH]; PROCESS_INFORMATION info; STARTUPINFOA startup; DWORD exit_code; crash_blackbox_t crash_blackbox; debugger_blackbox_t dbg_blackbox; DWORD wait_code; if (skip_crash_and_debug) { win_skip("Skipping crash_and_debug\n"); return; } ret=RegSetValueExA(hkey, "auto", 0, REG_SZ, (BYTE*)"1", 2); if (ret == ERROR_ACCESS_DENIED) { skip_crash_and_debug = TRUE; skip("No write access to change the debugger\n"); return; } ok(ret == ERROR_SUCCESS, "unable to set AeDebug/auto: ret=%d\n", ret); get_file_name(dbglog); get_events(dbglog, &start_event, &done_event); cmd=HeapAlloc(GetProcessHeap(), 0, strlen(argv0)+10+strlen(dbgtasks)+1+strlen(dbglog)+2+34+1); sprintf(cmd, "%s debugger %s \"%s\" %%ld %%ld", argv0, dbgtasks, dbglog); ret=RegSetValueExA(hkey, "debugger", 0, REG_SZ, (BYTE*)cmd, strlen(cmd)+1); ok(ret == ERROR_SUCCESS, "unable to set AeDebug/debugger: ret=%d\n", ret); HeapFree(GetProcessHeap(), 0, cmd); get_file_name(childlog); cmd=HeapAlloc(GetProcessHeap(), 0, strlen(argv0)+16+strlen(dbglog)+2+1); sprintf(cmd, "%s debugger crash \"%s\"", argv0, childlog); memset(&startup, 0, sizeof(startup)); startup.cb = sizeof(startup); startup.dwFlags = STARTF_USESHOWWINDOW; startup.wShowWindow = SW_SHOWNORMAL; ret=CreateProcessA(NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info); ok(ret, "CreateProcess: err=%d\n", GetLastError()); HeapFree(GetProcessHeap(), 0, cmd); CloseHandle(info.hThread); /* The process exits... */ trace("waiting for child exit...\n"); wait_code = WaitForSingleObject(info.hProcess, 30000); #if defined(_WIN64) && defined(__MINGW32__) /* Mingw x64 doesn't output proper unwind info */ skip_crash_and_debug = broken(wait_code == WAIT_TIMEOUT); if (skip_crash_and_debug) { TerminateProcess(info.hProcess, WAIT_TIMEOUT); WaitForSingleObject(info.hProcess, 5000); CloseHandle(info.hProcess); assert(DeleteFileA(dbglog) != 0); assert(DeleteFileA(childlog) != 0); win_skip("Giving up on child process\n"); return; } #endif ok(wait_code == WAIT_OBJECT_0, "Timed out waiting for the child to crash\n"); bRet = GetExitCodeProcess(info.hProcess, &exit_code); ok(bRet, "GetExitCodeProcess failed: err=%d\n", GetLastError()); if (strstr(dbgtasks, "code2")) { /* If, after attaching to the debuggee, the debugger exits without * detaching, then the debuggee gets a special exit code. */ ok(exit_code == STATUS_DEBUGGER_INACTIVE || broken(exit_code == STATUS_ACCESS_VIOLATION) || /* Intermittent Vista+ */ broken(exit_code == WAIT_ABANDONED), /* NT4, W2K */ "wrong exit code : %08x\n", exit_code); } else ok(exit_code == STATUS_ACCESS_VIOLATION || broken(exit_code == WAIT_ABANDONED), /* NT4, W2K, W2K3 */ "wrong exit code : %08x\n", exit_code); CloseHandle(info.hProcess); /* ...before the debugger */ if (strstr(dbgtasks, "order")) ok(SetEvent(start_event), "SetEvent(start_event) failed\n"); trace("waiting for the debugger...\n"); wait_code = WaitForSingleObject(done_event, 5000); #if defined(_WIN64) && defined(__MINGW32__) /* Mingw x64 doesn't output proper unwind info */ skip_crash_and_debug = broken(wait_code == WAIT_TIMEOUT); if (skip_crash_and_debug) { assert(DeleteFileA(dbglog) != 0); assert(DeleteFileA(childlog) != 0); win_skip("Giving up on debugger\n"); return; } #endif ok(wait_code == WAIT_OBJECT_0, "Timed out waiting for the debugger\n"); assert(load_blackbox(childlog, &crash_blackbox, sizeof(crash_blackbox))); assert(load_blackbox(dbglog, &dbg_blackbox, sizeof(dbg_blackbox))); ok(dbg_blackbox.argc == 6, "wrong debugger argument count: %d\n", dbg_blackbox.argc); ok(dbg_blackbox.pid == crash_blackbox.pid, "the child and debugged pids don't match: %d != %d\n", crash_blackbox.pid, dbg_blackbox.pid); ok(dbg_blackbox.debug_rc, "debugger: SetEvent(debug_event) failed err=%d\n", dbg_blackbox.debug_err); ok(dbg_blackbox.attach_rc, "DebugActiveProcess(%d) failed err=%d\n", dbg_blackbox.pid, dbg_blackbox.attach_err); ok(dbg_blackbox.nokill_rc, "DebugSetProcessKillOnExit(FALSE) failed err=%d\n", dbg_blackbox.nokill_err); ok(dbg_blackbox.detach_rc, "DebugActiveProcessStop(%d) failed err=%d\n", dbg_blackbox.pid, dbg_blackbox.detach_err); assert(DeleteFileA(dbglog) != 0); assert(DeleteFileA(childlog) != 0); }