1 /* 2 * Unit tests for the debugger facility 3 * 4 * Copyright (c) 2007 Francois Gouget for CodeWeavers 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 19 */ 20 21 #include <stdio.h> 22 #include <assert.h> 23 24 #include <windows.h> 25 #include <winternl.h> 26 #include <winreg.h> 27 #include "wine/test.h" 28 29 #ifndef STATUS_DEBUGGER_INACTIVE 30 #define STATUS_DEBUGGER_INACTIVE ((NTSTATUS) 0xC0000354) 31 #endif 32 33 #define child_ok (winetest_set_location(__FILE__, __LINE__), 0) ? (void)0 : test_child_ok 34 35 static int myARGC; 36 static char** myARGV; 37 38 static BOOL (WINAPI *pCheckRemoteDebuggerPresent)(HANDLE,PBOOL); 39 static BOOL (WINAPI *pDebugActiveProcessStop)(DWORD); 40 static BOOL (WINAPI *pDebugSetProcessKillOnExit)(BOOL); 41 static BOOL (WINAPI *pIsDebuggerPresent)(void); 42 43 static LONG child_failures; 44 45 static void WINETEST_PRINTF_ATTR(2, 3) test_child_ok(int condition, const char *msg, ...) 46 { 47 va_list valist; 48 49 va_start(valist, msg); 50 winetest_vok(condition, msg, valist); 51 va_end(valist); 52 if (!condition) ++child_failures; 53 } 54 55 /* Copied from the process test */ 56 static void get_file_name(char* buf) 57 { 58 char path[MAX_PATH]; 59 60 buf[0] = '\0'; 61 GetTempPathA(sizeof(path), path); 62 GetTempFileNameA(path, "wt", 0, buf); 63 } 64 65 typedef struct tag_reg_save_value 66 { 67 const char *name; 68 DWORD type; 69 BYTE *data; 70 DWORD size; 71 } reg_save_value; 72 73 static DWORD save_value(HKEY hkey, const char *value, reg_save_value *saved) 74 { 75 DWORD ret; 76 saved->name=value; 77 saved->data=0; 78 saved->size=0; 79 ret=RegQueryValueExA(hkey, value, NULL, &saved->type, NULL, &saved->size); 80 if (ret == ERROR_SUCCESS) 81 { 82 saved->data=HeapAlloc(GetProcessHeap(), 0, saved->size); 83 RegQueryValueExA(hkey, value, NULL, &saved->type, saved->data, &saved->size); 84 } 85 return ret; 86 } 87 88 static void restore_value(HKEY hkey, reg_save_value *saved) 89 { 90 if (saved->data) 91 { 92 RegSetValueExA(hkey, saved->name, 0, saved->type, saved->data, saved->size); 93 HeapFree(GetProcessHeap(), 0, saved->data); 94 } 95 else 96 RegDeleteValueA(hkey, saved->name); 97 } 98 99 static void get_events(const char* name, HANDLE *start_event, HANDLE *done_event) 100 { 101 const char* basename; 102 char* event_name; 103 104 basename=strrchr(name, '\\'); 105 basename=(basename ? basename+1 : name); 106 event_name=HeapAlloc(GetProcessHeap(), 0, 6+strlen(basename)+1); 107 108 sprintf(event_name, "start_%s", basename); 109 *start_event=CreateEventA(NULL, 0,0, event_name); 110 sprintf(event_name, "done_%s", basename); 111 *done_event=CreateEventA(NULL, 0,0, event_name); 112 HeapFree(GetProcessHeap(), 0, event_name); 113 } 114 115 static void save_blackbox(const char* logfile, void* blackbox, int size) 116 { 117 HANDLE hFile; 118 DWORD written; 119 120 hFile=CreateFileA(logfile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0); 121 if (hFile == INVALID_HANDLE_VALUE) 122 return; 123 WriteFile(hFile, blackbox, size, &written, NULL); 124 CloseHandle(hFile); 125 } 126 127 static int load_blackbox(const char* logfile, void* blackbox, int size) 128 { 129 HANDLE hFile; 130 DWORD read; 131 BOOL ret; 132 133 hFile=CreateFileA(logfile, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, 0); 134 if (hFile == INVALID_HANDLE_VALUE) 135 { 136 ok(0, "unable to open '%s'\n", logfile); 137 return 0; 138 } 139 SetLastError(0xdeadbeef); 140 ret=ReadFile(hFile, blackbox, size, &read, NULL); 141 ok(ret, "ReadFile failed: %d\n", GetLastError()); 142 ok(read == size, "wrong size for '%s': read=%d\n", logfile, read); 143 CloseHandle(hFile); 144 return 1; 145 } 146 147 typedef struct 148 { 149 DWORD pid; 150 } crash_blackbox_t; 151 152 static void doCrash(int argc, char** argv) 153 { 154 volatile char* p; 155 156 /* make sure the exception gets to the debugger */ 157 SetErrorMode( 0 ); 158 SetUnhandledExceptionFilter( NULL ); 159 160 if (argc >= 4) 161 { 162 crash_blackbox_t blackbox; 163 blackbox.pid=GetCurrentProcessId(); 164 save_blackbox(argv[3], &blackbox, sizeof(blackbox)); 165 } 166 167 /* Just crash */ 168 trace("child: crashing...\n"); 169 p=NULL; 170 *p=0; 171 } 172 173 typedef struct 174 { 175 int argc; 176 DWORD pid; 177 BOOL debug_rc; 178 DWORD debug_err; 179 BOOL attach_rc; 180 DWORD attach_err; 181 BOOL nokill_rc; 182 DWORD nokill_err; 183 BOOL detach_rc; 184 DWORD detach_err; 185 } debugger_blackbox_t; 186 187 static void doDebugger(int argc, char** argv) 188 { 189 const char* logfile; 190 debugger_blackbox_t blackbox; 191 HANDLE start_event = 0, done_event = 0, debug_event; 192 193 blackbox.argc=argc; 194 logfile=(argc >= 4 ? argv[3] : NULL); 195 blackbox.pid=(argc >= 5 ? atol(argv[4]) : 0); 196 197 blackbox.attach_err=0; 198 if (strstr(myARGV[2], "attach")) 199 { 200 blackbox.attach_rc=DebugActiveProcess(blackbox.pid); 201 if (!blackbox.attach_rc) 202 blackbox.attach_err=GetLastError(); 203 } 204 else 205 blackbox.attach_rc=TRUE; 206 207 debug_event=(argc >= 6 ? (HANDLE)(INT_PTR)atol(argv[5]) : NULL); 208 blackbox.debug_err=0; 209 if (debug_event && strstr(myARGV[2], "event")) 210 { 211 blackbox.debug_rc=SetEvent(debug_event); 212 if (!blackbox.debug_rc) 213 blackbox.debug_err=GetLastError(); 214 } 215 else 216 blackbox.debug_rc=TRUE; 217 218 if (logfile) 219 { 220 get_events(logfile, &start_event, &done_event); 221 } 222 223 if (strstr(myARGV[2], "order")) 224 { 225 trace("debugger: waiting for the start signal...\n"); 226 WaitForSingleObject(start_event, INFINITE); 227 } 228 229 blackbox.nokill_err=0; 230 if (strstr(myARGV[2], "nokill")) 231 { 232 blackbox.nokill_rc=pDebugSetProcessKillOnExit(FALSE); 233 if (!blackbox.nokill_rc) 234 blackbox.nokill_err=GetLastError(); 235 } 236 else 237 blackbox.nokill_rc=TRUE; 238 239 blackbox.detach_err=0; 240 if (strstr(myARGV[2], "detach")) 241 { 242 blackbox.detach_rc=pDebugActiveProcessStop(blackbox.pid); 243 if (!blackbox.detach_rc) 244 blackbox.detach_err=GetLastError(); 245 } 246 else 247 blackbox.detach_rc=TRUE; 248 249 if (logfile) 250 { 251 save_blackbox(logfile, &blackbox, sizeof(blackbox)); 252 } 253 trace("debugger: done debugging...\n"); 254 SetEvent(done_event); 255 256 /* Just exit with a known value */ 257 ExitProcess(0xdeadbeef); 258 } 259 260 static void crash_and_debug(HKEY hkey, const char* argv0, const char* dbgtasks) 261 { 262 static BOOL skip_crash_and_debug = FALSE; 263 BOOL bRet; 264 DWORD ret; 265 HANDLE start_event, done_event; 266 char* cmd; 267 char dbglog[MAX_PATH]; 268 char childlog[MAX_PATH]; 269 PROCESS_INFORMATION info; 270 STARTUPINFOA startup; 271 DWORD exit_code; 272 crash_blackbox_t crash_blackbox; 273 debugger_blackbox_t dbg_blackbox; 274 DWORD wait_code; 275 276 if (skip_crash_and_debug) 277 { 278 win_skip("Skipping crash_and_debug\n"); 279 return; 280 } 281 282 ret=RegSetValueExA(hkey, "auto", 0, REG_SZ, (BYTE*)"1", 2); 283 if (ret == ERROR_ACCESS_DENIED) 284 { 285 skip_crash_and_debug = TRUE; 286 skip("No write access to change the debugger\n"); 287 return; 288 } 289 290 ok(ret == ERROR_SUCCESS, "unable to set AeDebug/auto: ret=%d\n", ret); 291 292 get_file_name(dbglog); 293 get_events(dbglog, &start_event, &done_event); 294 cmd=HeapAlloc(GetProcessHeap(), 0, strlen(argv0)+10+strlen(dbgtasks)+1+strlen(dbglog)+2+34+1); 295 sprintf(cmd, "%s debugger %s \"%s\" %%ld %%ld", argv0, dbgtasks, dbglog); 296 ret=RegSetValueExA(hkey, "debugger", 0, REG_SZ, (BYTE*)cmd, strlen(cmd)+1); 297 ok(ret == ERROR_SUCCESS, "unable to set AeDebug/debugger: ret=%d\n", ret); 298 HeapFree(GetProcessHeap(), 0, cmd); 299 300 get_file_name(childlog); 301 cmd=HeapAlloc(GetProcessHeap(), 0, strlen(argv0)+16+strlen(dbglog)+2+1); 302 sprintf(cmd, "%s debugger crash \"%s\"", argv0, childlog); 303 304 memset(&startup, 0, sizeof(startup)); 305 startup.cb = sizeof(startup); 306 startup.dwFlags = STARTF_USESHOWWINDOW; 307 startup.wShowWindow = SW_SHOWNORMAL; 308 ret=CreateProcessA(NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info); 309 ok(ret, "CreateProcess: err=%d\n", GetLastError()); 310 HeapFree(GetProcessHeap(), 0, cmd); 311 CloseHandle(info.hThread); 312 313 /* The process exits... */ 314 trace("waiting for child exit...\n"); 315 wait_code = WaitForSingleObject(info.hProcess, 30000); 316 #if defined(_WIN64) && defined(__MINGW32__) 317 /* Mingw x64 doesn't output proper unwind info */ 318 skip_crash_and_debug = broken(wait_code == WAIT_TIMEOUT); 319 if (skip_crash_and_debug) 320 { 321 TerminateProcess(info.hProcess, WAIT_TIMEOUT); 322 WaitForSingleObject(info.hProcess, 5000); 323 CloseHandle(info.hProcess); 324 DeleteFileA(dbglog); 325 DeleteFileA(childlog); 326 win_skip("Giving up on child process\n"); 327 return; 328 } 329 #endif 330 ok(wait_code == WAIT_OBJECT_0, "Timed out waiting for the child to crash\n"); 331 bRet = GetExitCodeProcess(info.hProcess, &exit_code); 332 ok(bRet, "GetExitCodeProcess failed: err=%d\n", GetLastError()); 333 if (strstr(dbgtasks, "code2")) 334 { 335 /* If, after attaching to the debuggee, the debugger exits without 336 * detaching, then the debuggee gets a special exit code. 337 */ 338 ok(exit_code == STATUS_DEBUGGER_INACTIVE || 339 broken(exit_code == STATUS_ACCESS_VIOLATION) || /* Intermittent Vista+ */ 340 broken(exit_code == WAIT_ABANDONED), /* NT4, W2K */ 341 "wrong exit code : %08x\n", exit_code); 342 } 343 else 344 ok(exit_code == STATUS_ACCESS_VIOLATION || 345 broken(exit_code == WAIT_ABANDONED), /* NT4, W2K, W2K3 */ 346 "wrong exit code : %08x\n", exit_code); 347 CloseHandle(info.hProcess); 348 349 /* ...before the debugger */ 350 if (strstr(dbgtasks, "order")) 351 ok(SetEvent(start_event), "SetEvent(start_event) failed\n"); 352 353 trace("waiting for the debugger...\n"); 354 wait_code = WaitForSingleObject(done_event, 5000); 355 #if defined(_WIN64) && defined(__MINGW32__) 356 /* Mingw x64 doesn't output proper unwind info */ 357 skip_crash_and_debug = broken(wait_code == WAIT_TIMEOUT); 358 if (skip_crash_and_debug) 359 { 360 DeleteFileA(dbglog); 361 DeleteFileA(childlog); 362 win_skip("Giving up on debugger\n"); 363 return; 364 } 365 #endif 366 ok(wait_code == WAIT_OBJECT_0, "Timed out waiting for the debugger\n"); 367 368 ok(load_blackbox(childlog, &crash_blackbox, sizeof(crash_blackbox)), "failed to open: %s\n", childlog); 369 ok(load_blackbox(dbglog, &dbg_blackbox, sizeof(dbg_blackbox)), "failed to open: %s\n", dbglog); 370 371 ok(dbg_blackbox.argc == 6, "wrong debugger argument count: %d\n", dbg_blackbox.argc); 372 ok(dbg_blackbox.pid == crash_blackbox.pid, "the child and debugged pids don't match: %d != %d\n", crash_blackbox.pid, dbg_blackbox.pid); 373 ok(dbg_blackbox.debug_rc, "debugger: SetEvent(debug_event) failed err=%d\n", dbg_blackbox.debug_err); 374 ok(dbg_blackbox.attach_rc, "DebugActiveProcess(%d) failed err=%d\n", dbg_blackbox.pid, dbg_blackbox.attach_err); 375 ok(dbg_blackbox.nokill_rc, "DebugSetProcessKillOnExit(FALSE) failed err=%d\n", dbg_blackbox.nokill_err); 376 ok(dbg_blackbox.detach_rc, "DebugActiveProcessStop(%d) failed err=%d\n", dbg_blackbox.pid, dbg_blackbox.detach_err); 377 378 DeleteFileA(dbglog); 379 DeleteFileA(childlog); 380 } 381 382 static void crash_and_winedbg(HKEY hkey, const char* argv0) 383 { 384 BOOL bRet; 385 DWORD ret; 386 char* cmd; 387 PROCESS_INFORMATION info; 388 STARTUPINFOA startup; 389 DWORD exit_code; 390 391 ret=RegSetValueExA(hkey, "auto", 0, REG_SZ, (BYTE*)"1", 2); 392 ok(ret == ERROR_SUCCESS, "unable to set AeDebug/auto: ret=%d\n", ret); 393 394 cmd=HeapAlloc(GetProcessHeap(), 0, strlen(argv0)+15+1); 395 sprintf(cmd, "%s debugger crash", argv0); 396 397 memset(&startup, 0, sizeof(startup)); 398 startup.cb = sizeof(startup); 399 startup.dwFlags = STARTF_USESHOWWINDOW; 400 startup.wShowWindow = SW_SHOWNORMAL; 401 ret=CreateProcessA(NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info); 402 ok(ret, "CreateProcess: err=%d\n", GetLastError()); 403 HeapFree(GetProcessHeap(), 0, cmd); 404 CloseHandle(info.hThread); 405 406 trace("waiting for child exit...\n"); 407 ok(WaitForSingleObject(info.hProcess, 60000) == WAIT_OBJECT_0, "Timed out waiting for the child to crash\n"); 408 bRet = GetExitCodeProcess(info.hProcess, &exit_code); 409 ok(bRet, "GetExitCodeProcess failed: err=%d\n", GetLastError()); 410 ok(exit_code == STATUS_ACCESS_VIOLATION, "exit code = %08x\n", exit_code); 411 CloseHandle(info.hProcess); 412 } 413 414 static void test_ExitCode(void) 415 { 416 static const char* AeDebug="Software\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug"; 417 static const char* WineDbg="Software\\Wine\\WineDbg"; 418 char test_exe[MAX_PATH]; 419 DWORD ret; 420 HKEY hkey; 421 DWORD disposition; 422 reg_save_value auto_value; 423 reg_save_value debugger_value; 424 425 GetModuleFileNameA(GetModuleHandleA(NULL), test_exe, sizeof(test_exe)); 426 if (GetFileAttributesA(test_exe) == INVALID_FILE_ATTRIBUTES) 427 strcat(test_exe, ".so"); 428 if (GetFileAttributesA(test_exe) == INVALID_FILE_ATTRIBUTES) 429 { 430 ok(0, "could not find the test executable '%s'\n", test_exe); 431 return; 432 } 433 434 ret=RegCreateKeyExA(HKEY_LOCAL_MACHINE, AeDebug, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, &disposition); 435 if (ret == ERROR_SUCCESS) 436 { 437 save_value(hkey, "auto", &auto_value); 438 save_value(hkey, "debugger", &debugger_value); 439 trace("HKLM\\%s\\debugger is set to '%s'\n", AeDebug, debugger_value.data); 440 } 441 else if (ret == ERROR_ACCESS_DENIED) 442 { 443 skip("not enough privileges to change the debugger\n"); 444 return; 445 } 446 else if (ret != ERROR_FILE_NOT_FOUND) 447 { 448 ok(0, "could not open the AeDebug key: %d\n", ret); 449 return; 450 } 451 else debugger_value.data = NULL; 452 453 if (debugger_value.data && debugger_value.type == REG_SZ && 454 strstr((char*)debugger_value.data, "winedbg --auto")) 455 { 456 HKEY hkeyWinedbg; 457 ret=RegCreateKeyA(HKEY_CURRENT_USER, WineDbg, &hkeyWinedbg); 458 if (ret == ERROR_SUCCESS) 459 { 460 static DWORD zero; 461 reg_save_value crash_dlg_value; 462 save_value(hkeyWinedbg, "ShowCrashDialog", &crash_dlg_value); 463 RegSetValueExA(hkeyWinedbg, "ShowCrashDialog", 0, REG_DWORD, (BYTE *)&zero, sizeof(DWORD)); 464 crash_and_winedbg(hkey, test_exe); 465 restore_value(hkeyWinedbg, &crash_dlg_value); 466 RegCloseKey(hkeyWinedbg); 467 } 468 else 469 ok(0, "Couldn't access WineDbg Key - error %u\n", ret); 470 } 471 472 if (winetest_interactive) 473 /* Since the debugging process never sets the debug event, it isn't recognized 474 as a valid debugger and, after the debugger exits, Windows will show a dialog box 475 asking the user what to do */ 476 crash_and_debug(hkey, test_exe, "dbg,none"); 477 else 478 skip("\"none\" debugger test needs user interaction\n"); 479 ok(disposition == REG_OPENED_EXISTING_KEY, "expected REG_OPENED_EXISTING_KEY, got %d\n", disposition); 480 crash_and_debug(hkey, test_exe, "dbg,event,order"); 481 crash_and_debug(hkey, test_exe, "dbg,attach,event,code2"); 482 if (pDebugSetProcessKillOnExit) 483 crash_and_debug(hkey, test_exe, "dbg,attach,event,nokill"); 484 else 485 win_skip("DebugSetProcessKillOnExit is not available\n"); 486 if (pDebugActiveProcessStop) 487 crash_and_debug(hkey, test_exe, "dbg,attach,event,detach"); 488 else 489 win_skip("DebugActiveProcessStop is not available\n"); 490 491 if (disposition == REG_CREATED_NEW_KEY) 492 { 493 RegCloseKey(hkey); 494 RegDeleteKeyA(HKEY_LOCAL_MACHINE, AeDebug); 495 } 496 else 497 { 498 restore_value(hkey, &auto_value); 499 restore_value(hkey, &debugger_value); 500 RegCloseKey(hkey); 501 } 502 } 503 504 static void test_RemoteDebugger(void) 505 { 506 BOOL bret, present; 507 if(!pCheckRemoteDebuggerPresent) 508 { 509 win_skip("CheckRemoteDebuggerPresent is not available\n"); 510 return; 511 } 512 present = TRUE; 513 SetLastError(0xdeadbeef); 514 bret = pCheckRemoteDebuggerPresent(GetCurrentProcess(),&present); 515 ok(bret , "expected CheckRemoteDebuggerPresent to succeed\n"); 516 ok(0xdeadbeef == GetLastError(), 517 "expected error to be unchanged, got %d/%x\n",GetLastError(), GetLastError()); 518 519 present = TRUE; 520 SetLastError(0xdeadbeef); 521 bret = pCheckRemoteDebuggerPresent(NULL,&present); 522 ok(!bret , "expected CheckRemoteDebuggerPresent to fail\n"); 523 ok(present, "expected parameter to be unchanged\n"); 524 ok(ERROR_INVALID_PARAMETER == GetLastError(), 525 "expected error ERROR_INVALID_PARAMETER, got %d/%x\n",GetLastError(), GetLastError()); 526 527 SetLastError(0xdeadbeef); 528 bret = pCheckRemoteDebuggerPresent(GetCurrentProcess(),NULL); 529 ok(!bret , "expected CheckRemoteDebuggerPresent to fail\n"); 530 ok(ERROR_INVALID_PARAMETER == GetLastError(), 531 "expected error ERROR_INVALID_PARAMETER, got %d/%x\n",GetLastError(), GetLastError()); 532 } 533 534 struct child_blackbox 535 { 536 LONG failures; 537 }; 538 539 static void doChild(int argc, char **argv) 540 { 541 struct child_blackbox blackbox; 542 const char *blackbox_file; 543 HANDLE parent; 544 DWORD ppid; 545 BOOL debug; 546 BOOL ret; 547 548 blackbox_file = argv[4]; 549 sscanf(argv[3], "%08x", &ppid); 550 551 parent = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, ppid); 552 child_ok(!!parent, "OpenProcess failed, last error %#x.\n", GetLastError()); 553 554 ret = pCheckRemoteDebuggerPresent(parent, &debug); 555 child_ok(ret, "CheckRemoteDebuggerPresent failed, last error %#x.\n", GetLastError()); 556 child_ok(!debug, "Expected debug == 0, got %#x.\n", debug); 557 558 ret = DebugActiveProcess(ppid); 559 child_ok(ret, "DebugActiveProcess failed, last error %#x.\n", GetLastError()); 560 561 ret = pCheckRemoteDebuggerPresent(parent, &debug); 562 child_ok(ret, "CheckRemoteDebuggerPresent failed, last error %#x.\n", GetLastError()); 563 child_ok(debug, "Expected debug != 0, got %#x.\n", debug); 564 565 ret = pDebugActiveProcessStop(ppid); 566 child_ok(ret, "DebugActiveProcessStop failed, last error %#x.\n", GetLastError()); 567 568 ret = pCheckRemoteDebuggerPresent(parent, &debug); 569 child_ok(ret, "CheckRemoteDebuggerPresent failed, last error %#x.\n", GetLastError()); 570 child_ok(!debug, "Expected debug == 0, got %#x.\n", debug); 571 572 ret = CloseHandle(parent); 573 child_ok(ret, "CloseHandle failed, last error %#x.\n", GetLastError()); 574 575 ret = pIsDebuggerPresent(); 576 child_ok(ret, "Expected ret != 0, got %#x.\n", ret); 577 ret = pCheckRemoteDebuggerPresent(GetCurrentProcess(), &debug); 578 child_ok(ret, "CheckRemoteDebuggerPresent failed, last error %#x.\n", GetLastError()); 579 child_ok(debug, "Expected debug != 0, got %#x.\n", debug); 580 581 NtCurrentTeb()->Peb->BeingDebugged = FALSE; 582 583 ret = pIsDebuggerPresent(); 584 child_ok(!ret, "Expected ret != 0, got %#x.\n", ret); 585 ret = pCheckRemoteDebuggerPresent(GetCurrentProcess(), &debug); 586 child_ok(ret, "CheckRemoteDebuggerPresent failed, last error %#x.\n", GetLastError()); 587 child_ok(debug, "Expected debug != 0, got %#x.\n", debug); 588 589 NtCurrentTeb()->Peb->BeingDebugged = TRUE; 590 591 blackbox.failures = child_failures; 592 save_blackbox(blackbox_file, &blackbox, sizeof(blackbox)); 593 } 594 595 static void test_debug_loop(int argc, char **argv) 596 { 597 const char *arguments = " debugger child "; 598 struct child_blackbox blackbox; 599 char blackbox_file[MAX_PATH]; 600 PROCESS_INFORMATION pi; 601 STARTUPINFOA si; 602 BOOL debug; 603 DWORD pid; 604 char *cmd; 605 BOOL ret; 606 607 if (!pDebugActiveProcessStop || !pCheckRemoteDebuggerPresent) 608 { 609 win_skip("DebugActiveProcessStop or CheckRemoteDebuggerPresent not available, skipping test.\n"); 610 return; 611 } 612 613 pid = GetCurrentProcessId(); 614 ret = DebugActiveProcess(pid); 615 ok(!ret, "DebugActiveProcess() succeeded on own process.\n"); 616 617 get_file_name(blackbox_file); 618 cmd = HeapAlloc(GetProcessHeap(), 0, strlen(argv[0]) + strlen(arguments) + strlen(blackbox_file) + 2 + 10); 619 sprintf(cmd, "%s%s%08x \"%s\"", argv[0], arguments, pid, blackbox_file); 620 621 memset(&si, 0, sizeof(si)); 622 si.cb = sizeof(si); 623 ret = CreateProcessA(NULL, cmd, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &si, &pi); 624 ok(ret, "CreateProcess failed, last error %#x.\n", GetLastError()); 625 626 HeapFree(GetProcessHeap(), 0, cmd); 627 628 ret = pCheckRemoteDebuggerPresent(pi.hProcess, &debug); 629 ok(ret, "CheckRemoteDebuggerPresent failed, last error %#x.\n", GetLastError()); 630 ok(debug, "Expected debug != 0, got %#x.\n", debug); 631 632 for (;;) 633 { 634 DEBUG_EVENT ev; 635 636 ret = WaitForDebugEvent(&ev, INFINITE); 637 ok(ret, "WaitForDebugEvent failed, last error %#x.\n", GetLastError()); 638 if (!ret) break; 639 640 if (ev.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT) break; 641 642 ret = ContinueDebugEvent(ev.dwProcessId, ev.dwThreadId, DBG_CONTINUE); 643 ok(ret, "ContinueDebugEvent failed, last error %#x.\n", GetLastError()); 644 if (!ret) break; 645 } 646 647 ret = CloseHandle(pi.hThread); 648 ok(ret, "CloseHandle failed, last error %#x.\n", GetLastError()); 649 ret = CloseHandle(pi.hProcess); 650 ok(ret, "CloseHandle failed, last error %#x.\n", GetLastError()); 651 652 load_blackbox(blackbox_file, &blackbox, sizeof(blackbox)); 653 ok(!blackbox.failures, "Got %d failures from child process.\n", blackbox.failures); 654 655 ret = DeleteFileA(blackbox_file); 656 ok(ret, "DeleteFileA failed, last error %#x.\n", GetLastError()); 657 } 658 659 static void doChildren(int argc, char **argv) 660 { 661 const char *arguments = "debugger children last"; 662 struct child_blackbox blackbox; 663 const char *blackbox_file, *p; 664 char event_name[MAX_PATH]; 665 PROCESS_INFORMATION pi; 666 STARTUPINFOA si; 667 HANDLE event; 668 char *cmd; 669 BOOL ret; 670 671 if (!strcmp(argv[3], "last")) return; 672 673 blackbox_file = argv[3]; 674 675 p = strrchr(blackbox_file, '\\'); 676 p = p ? p+1 : blackbox_file; 677 strcpy(event_name, p); 678 strcat(event_name, "_init"); 679 event = OpenEventA(EVENT_ALL_ACCESS, FALSE, event_name); 680 child_ok(event != NULL, "OpenEvent failed, last error %d.\n", GetLastError()); 681 SetEvent(event); 682 CloseHandle(event); 683 684 p = strrchr(blackbox_file, '\\'); 685 p = p ? p+1 : blackbox_file; 686 strcpy(event_name, p); 687 strcat(event_name, "_attach"); 688 event = OpenEventA(EVENT_ALL_ACCESS, FALSE, event_name); 689 child_ok(event != NULL, "OpenEvent failed, last error %d.\n", GetLastError()); 690 WaitForSingleObject(event, INFINITE); 691 CloseHandle(event); 692 693 cmd = HeapAlloc(GetProcessHeap(), 0, strlen(argv[0]) + strlen(arguments) + 2); 694 sprintf(cmd, "%s %s", argv[0], arguments); 695 696 memset(&si, 0, sizeof(si)); 697 si.cb = sizeof(si); 698 ret = CreateProcessA(NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); 699 child_ok(ret, "CreateProcess failed, last error %d.\n", GetLastError()); 700 701 child_ok(WaitForSingleObject(pi.hProcess, 10000) == WAIT_OBJECT_0, 702 "Timed out waiting for the child to exit\n"); 703 704 ret = CloseHandle(pi.hThread); 705 child_ok(ret, "CloseHandle failed, last error %d.\n", GetLastError()); 706 ret = CloseHandle(pi.hProcess); 707 child_ok(ret, "CloseHandle failed, last error %d.\n", GetLastError()); 708 709 blackbox.failures = child_failures; 710 save_blackbox(blackbox_file, &blackbox, sizeof(blackbox)); 711 712 HeapFree(GetProcessHeap(), 0, cmd); 713 } 714 715 static void test_debug_children(char *name, DWORD flag, BOOL debug_child) 716 { 717 const char *arguments = "debugger children"; 718 struct child_blackbox blackbox; 719 char blackbox_file[MAX_PATH], *p; 720 char event_name[MAX_PATH]; 721 PROCESS_INFORMATION pi; 722 STARTUPINFOA si; 723 HANDLE event_init, event_attach; 724 char *cmd; 725 BOOL debug, ret; 726 BOOL got_child_event = FALSE; 727 728 if (!pDebugActiveProcessStop || !pCheckRemoteDebuggerPresent) 729 { 730 win_skip("DebugActiveProcessStop or CheckRemoteDebuggerPresent not available, skipping test.\n"); 731 return; 732 } 733 734 get_file_name(blackbox_file); 735 cmd = HeapAlloc(GetProcessHeap(), 0, strlen(name) + strlen(arguments) + strlen(blackbox_file) + 5); 736 sprintf(cmd, "%s %s \"%s\"", name, arguments, blackbox_file); 737 738 p = strrchr(blackbox_file, '\\'); 739 p = p ? p+1 : blackbox_file; 740 strcpy(event_name, p); 741 strcat(event_name, "_init"); 742 event_init = CreateEventA(NULL, FALSE, FALSE, event_name); 743 ok(event_init != NULL, "OpenEvent failed, last error %d.\n", GetLastError()); 744 745 p = strrchr(blackbox_file, '\\'); 746 p = p ? p+1 : blackbox_file; 747 strcpy(event_name, p); 748 strcat(event_name, "_attach"); 749 event_attach = CreateEventA(NULL, FALSE, flag!=0, event_name); 750 ok(event_attach != NULL, "CreateEvent failed, last error %d.\n", GetLastError()); 751 752 memset(&si, 0, sizeof(si)); 753 si.cb = sizeof(si); 754 755 ret = CreateProcessA(NULL, cmd, NULL, NULL, FALSE, flag, NULL, NULL, &si, &pi); 756 ok(ret, "CreateProcess failed, last error %d.\n", GetLastError()); 757 HeapFree(GetProcessHeap(), 0, cmd); 758 if (!flag) 759 { 760 WaitForSingleObject(event_init, INFINITE); 761 ret = DebugActiveProcess(pi.dwProcessId); 762 ok(ret, "DebugActiveProcess failed, last error %d.\n", GetLastError()); 763 ret = SetEvent(event_attach); 764 ok(ret, "SetEvent failed, last error %d.\n", GetLastError()); 765 } 766 767 ret = pCheckRemoteDebuggerPresent(pi.hProcess, &debug); 768 ok(ret, "CheckRemoteDebuggerPresent failed, last error %d.\n", GetLastError()); 769 ok(debug, "Expected debug != 0, got %x.\n", debug); 770 771 for (;;) 772 { 773 DEBUG_EVENT ev; 774 775 ret = WaitForDebugEvent(&ev, INFINITE); 776 ok(ret, "WaitForDebugEvent failed, last error %d.\n", GetLastError()); 777 if (!ret) break; 778 779 if (ev.dwDebugEventCode==EXIT_PROCESS_DEBUG_EVENT && ev.dwProcessId==pi.dwProcessId) break; 780 else if (ev.dwProcessId != pi.dwProcessId) got_child_event = TRUE; 781 782 ret = ContinueDebugEvent(ev.dwProcessId, ev.dwThreadId, DBG_CONTINUE); 783 ok(ret, "ContinueDebugEvent failed, last error %d.\n", GetLastError()); 784 if (!ret) break; 785 } 786 if(debug_child) 787 ok(got_child_event, "didn't get any child events (flag: %x).\n", flag); 788 else 789 ok(!got_child_event, "got child event (flag: %x).\n", flag); 790 CloseHandle(event_init); 791 CloseHandle(event_attach); 792 793 ret = CloseHandle(pi.hThread); 794 ok(ret, "CloseHandle failed, last error %d.\n", GetLastError()); 795 ret = CloseHandle(pi.hProcess); 796 ok(ret, "CloseHandle failed, last error %d.\n", GetLastError()); 797 798 load_blackbox(blackbox_file, &blackbox, sizeof(blackbox)); 799 ok(!blackbox.failures, "Got %d failures from child process.\n", blackbox.failures); 800 801 ret = DeleteFileA(blackbox_file); 802 ok(ret, "DeleteFileA failed, last error %d.\n", GetLastError()); 803 } 804 805 START_TEST(debugger) 806 { 807 HMODULE hdll; 808 809 hdll=GetModuleHandleA("kernel32.dll"); 810 pCheckRemoteDebuggerPresent=(void*)GetProcAddress(hdll, "CheckRemoteDebuggerPresent"); 811 pDebugActiveProcessStop=(void*)GetProcAddress(hdll, "DebugActiveProcessStop"); 812 pDebugSetProcessKillOnExit=(void*)GetProcAddress(hdll, "DebugSetProcessKillOnExit"); 813 pIsDebuggerPresent=(void*)GetProcAddress(hdll, "IsDebuggerPresent"); 814 815 myARGC=winetest_get_mainargs(&myARGV); 816 if (myARGC >= 3 && strcmp(myARGV[2], "crash") == 0) 817 { 818 doCrash(myARGC, myARGV); 819 } 820 else if (myARGC >= 3 && strncmp(myARGV[2], "dbg,", 4) == 0) 821 { 822 doDebugger(myARGC, myARGV); 823 } 824 else if (myARGC >= 5 && !strcmp(myARGV[2], "child")) 825 { 826 doChild(myARGC, myARGV); 827 } 828 else if (myARGC >= 4 && !strcmp(myARGV[2], "children")) 829 { 830 doChildren(myARGC, myARGV); 831 } 832 else 833 { 834 test_ExitCode(); 835 test_RemoteDebugger(); 836 test_debug_loop(myARGC, myARGV); 837 test_debug_children(myARGV[0], DEBUG_PROCESS, TRUE); 838 test_debug_children(myARGV[0], DEBUG_ONLY_THIS_PROCESS, FALSE); 839 test_debug_children(myARGV[0], DEBUG_PROCESS|DEBUG_ONLY_THIS_PROCESS, FALSE); 840 test_debug_children(myARGV[0], 0, FALSE); 841 } 842 } 843