2 * Unit tests for the debugger facility
4 * Copyright (c) 2007 Francois Gouget for CodeWeavers
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.
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.
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
27 #include "wine/test.h"
29 #ifndef STATUS_DEBUGGER_INACTIVE
30 #define STATUS_DEBUGGER_INACTIVE ((NTSTATUS) 0xC0000354)
33 #define child_ok (winetest_set_location(__FILE__, __LINE__), 0) ? (void)0 : test_child_ok
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 static struct _TEB
* (WINAPI
*pNtCurrentTeb
)(void);
44 static LONG child_failures
;
46 static void WINETEST_PRINTF_ATTR(2, 3) test_child_ok(int condition
, const char *msg
, ...)
50 va_start(valist
, msg
);
51 winetest_vok(condition
, msg
, valist
);
53 if (!condition
) ++child_failures
;
56 /* Copied from the process test */
57 static void get_file_name(char* buf
)
62 GetTempPathA(sizeof(path
), path
);
63 GetTempFileNameA(path
, "wt", 0, buf
);
66 typedef struct tag_reg_save_value
74 static DWORD
save_value(HKEY hkey
, const char *value
, reg_save_value
*saved
)
80 ret
=RegQueryValueExA(hkey
, value
, NULL
, &saved
->type
, NULL
, &saved
->size
);
81 if (ret
== ERROR_SUCCESS
)
83 saved
->data
=HeapAlloc(GetProcessHeap(), 0, saved
->size
);
84 RegQueryValueExA(hkey
, value
, NULL
, &saved
->type
, saved
->data
, &saved
->size
);
89 static void restore_value(HKEY hkey
, reg_save_value
*saved
)
93 RegSetValueExA(hkey
, saved
->name
, 0, saved
->type
, saved
->data
, saved
->size
);
94 HeapFree(GetProcessHeap(), 0, saved
->data
);
97 RegDeleteValueA(hkey
, saved
->name
);
100 static void get_events(const char* name
, HANDLE
*start_event
, HANDLE
*done_event
)
102 const char* basename
;
105 basename
=strrchr(name
, '\\');
106 basename
=(basename
? basename
+1 : name
);
107 event_name
=HeapAlloc(GetProcessHeap(), 0, 6+strlen(basename
)+1);
109 sprintf(event_name
, "start_%s", basename
);
110 *start_event
=CreateEventA(NULL
, 0,0, event_name
);
111 sprintf(event_name
, "done_%s", basename
);
112 *done_event
=CreateEventA(NULL
, 0,0, event_name
);
113 HeapFree(GetProcessHeap(), 0, event_name
);
116 static void save_blackbox(const char* logfile
, void* blackbox
, int size
)
121 hFile
=CreateFileA(logfile
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, 0, 0);
122 if (hFile
== INVALID_HANDLE_VALUE
)
124 WriteFile(hFile
, blackbox
, size
, &written
, NULL
);
128 static int load_blackbox(const char* logfile
, void* blackbox
, int size
)
134 hFile
=CreateFileA(logfile
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, 0, 0);
135 if (hFile
== INVALID_HANDLE_VALUE
)
137 ok(0, "unable to open '%s'\n", logfile
);
140 SetLastError(0xdeadbeef);
141 ret
=ReadFile(hFile
, blackbox
, size
, &read
, NULL
);
142 ok(ret
, "ReadFile failed: %d\n", GetLastError());
143 ok(read
== size
, "wrong size for '%s': read=%d\n", logfile
, read
);
153 static void doCrash(int argc
, char** argv
)
157 /* make sure the exception gets to the debugger */
159 SetUnhandledExceptionFilter( NULL
);
163 crash_blackbox_t blackbox
;
164 blackbox
.pid
=GetCurrentProcessId();
165 save_blackbox(argv
[3], &blackbox
, sizeof(blackbox
));
169 trace("child: crashing...\n");
186 } debugger_blackbox_t
;
188 static void doDebugger(int argc
, char** argv
)
191 debugger_blackbox_t blackbox
;
192 HANDLE start_event
= 0, done_event
= 0, debug_event
;
195 logfile
=(argc
>= 4 ? argv
[3] : NULL
);
196 blackbox
.pid
=(argc
>= 5 ? atol(argv
[4]) : 0);
198 blackbox
.attach_err
=0;
199 if (strstr(myARGV
[2], "attach"))
201 blackbox
.attach_rc
=DebugActiveProcess(blackbox
.pid
);
202 if (!blackbox
.attach_rc
)
203 blackbox
.attach_err
=GetLastError();
206 blackbox
.attach_rc
=TRUE
;
208 debug_event
=(argc
>= 6 ? (HANDLE
)(INT_PTR
)atol(argv
[5]) : NULL
);
209 blackbox
.debug_err
=0;
210 if (debug_event
&& strstr(myARGV
[2], "event"))
212 blackbox
.debug_rc
=SetEvent(debug_event
);
213 if (!blackbox
.debug_rc
)
214 blackbox
.debug_err
=GetLastError();
217 blackbox
.debug_rc
=TRUE
;
221 get_events(logfile
, &start_event
, &done_event
);
224 if (strstr(myARGV
[2], "order"))
226 trace("debugger: waiting for the start signal...\n");
227 WaitForSingleObject(start_event
, INFINITE
);
230 blackbox
.nokill_err
=0;
231 if (strstr(myARGV
[2], "nokill"))
233 blackbox
.nokill_rc
=pDebugSetProcessKillOnExit(FALSE
);
234 if (!blackbox
.nokill_rc
)
235 blackbox
.nokill_err
=GetLastError();
238 blackbox
.nokill_rc
=TRUE
;
240 blackbox
.detach_err
=0;
241 if (strstr(myARGV
[2], "detach"))
243 blackbox
.detach_rc
=pDebugActiveProcessStop(blackbox
.pid
);
244 if (!blackbox
.detach_rc
)
245 blackbox
.detach_err
=GetLastError();
248 blackbox
.detach_rc
=TRUE
;
252 save_blackbox(logfile
, &blackbox
, sizeof(blackbox
));
254 trace("debugger: done debugging...\n");
255 SetEvent(done_event
);
257 /* Just exit with a known value */
258 ExitProcess(0xdeadbeef);
261 static void crash_and_debug(HKEY hkey
, const char* argv0
, const char* dbgtasks
)
263 static BOOL skip_crash_and_debug
= FALSE
;
266 HANDLE start_event
, done_event
;
268 char dbglog
[MAX_PATH
];
269 char childlog
[MAX_PATH
];
270 PROCESS_INFORMATION info
;
271 STARTUPINFOA startup
;
273 crash_blackbox_t crash_blackbox
;
274 debugger_blackbox_t dbg_blackbox
;
277 if (skip_crash_and_debug
)
279 win_skip("Skipping crash_and_debug\n");
283 ret
=RegSetValueExA(hkey
, "auto", 0, REG_SZ
, (BYTE
*)"1", 2);
284 if (ret
== ERROR_ACCESS_DENIED
)
286 skip_crash_and_debug
= TRUE
;
287 skip("No write access to change the debugger\n");
291 ok(ret
== ERROR_SUCCESS
, "unable to set AeDebug/auto: ret=%d\n", ret
);
293 get_file_name(dbglog
);
294 get_events(dbglog
, &start_event
, &done_event
);
295 cmd
=HeapAlloc(GetProcessHeap(), 0, strlen(argv0
)+10+strlen(dbgtasks
)+1+strlen(dbglog
)+2+34+1);
296 sprintf(cmd
, "%s debugger %s \"%s\" %%ld %%ld", argv0
, dbgtasks
, dbglog
);
297 ret
=RegSetValueExA(hkey
, "debugger", 0, REG_SZ
, (BYTE
*)cmd
, strlen(cmd
)+1);
298 ok(ret
== ERROR_SUCCESS
, "unable to set AeDebug/debugger: ret=%d\n", ret
);
299 HeapFree(GetProcessHeap(), 0, cmd
);
301 get_file_name(childlog
);
302 cmd
=HeapAlloc(GetProcessHeap(), 0, strlen(argv0
)+16+strlen(dbglog
)+2+1);
303 sprintf(cmd
, "%s debugger crash \"%s\"", argv0
, childlog
);
305 memset(&startup
, 0, sizeof(startup
));
306 startup
.cb
= sizeof(startup
);
307 startup
.dwFlags
= STARTF_USESHOWWINDOW
;
308 startup
.wShowWindow
= SW_SHOWNORMAL
;
309 ret
=CreateProcessA(NULL
, cmd
, NULL
, NULL
, FALSE
, 0, NULL
, NULL
, &startup
, &info
);
310 ok(ret
, "CreateProcess: err=%d\n", GetLastError());
311 HeapFree(GetProcessHeap(), 0, cmd
);
312 CloseHandle(info
.hThread
);
314 /* The process exits... */
315 trace("waiting for child exit...\n");
316 wait_code
= WaitForSingleObject(info
.hProcess
, 30000);
317 #if defined(_WIN64) && defined(__MINGW32__)
318 /* Mingw x64 doesn't output proper unwind info */
319 skip_crash_and_debug
= broken(wait_code
== WAIT_TIMEOUT
);
320 if (skip_crash_and_debug
)
322 TerminateProcess(info
.hProcess
, WAIT_TIMEOUT
);
323 WaitForSingleObject(info
.hProcess
, 5000);
324 CloseHandle(info
.hProcess
);
325 assert(DeleteFileA(dbglog
) != 0);
326 assert(DeleteFileA(childlog
) != 0);
327 win_skip("Giving up on child process\n");
331 ok(wait_code
== WAIT_OBJECT_0
, "Timed out waiting for the child to crash\n");
332 bRet
= GetExitCodeProcess(info
.hProcess
, &exit_code
);
333 ok(bRet
, "GetExitCodeProcess failed: err=%d\n", GetLastError());
334 if (strstr(dbgtasks
, "code2"))
336 /* If, after attaching to the debuggee, the debugger exits without
337 * detaching, then the debuggee gets a special exit code.
339 ok(exit_code
== STATUS_DEBUGGER_INACTIVE
||
340 broken(exit_code
== STATUS_ACCESS_VIOLATION
) || /* Intermittent Vista+ */
341 broken(exit_code
== WAIT_ABANDONED
), /* NT4, W2K */
342 "wrong exit code : %08x\n", exit_code
);
345 ok(exit_code
== STATUS_ACCESS_VIOLATION
||
346 broken(exit_code
== WAIT_ABANDONED
), /* NT4, W2K, W2K3 */
347 "wrong exit code : %08x\n", exit_code
);
348 CloseHandle(info
.hProcess
);
350 /* ...before the debugger */
351 if (strstr(dbgtasks
, "order"))
352 ok(SetEvent(start_event
), "SetEvent(start_event) failed\n");
354 trace("waiting for the debugger...\n");
355 wait_code
= WaitForSingleObject(done_event
, 5000);
356 #if defined(_WIN64) && defined(__MINGW32__)
357 /* Mingw x64 doesn't output proper unwind info */
358 skip_crash_and_debug
= broken(wait_code
== WAIT_TIMEOUT
);
359 if (skip_crash_and_debug
)
361 assert(DeleteFileA(dbglog
) != 0);
362 assert(DeleteFileA(childlog
) != 0);
363 win_skip("Giving up on debugger\n");
367 ok(wait_code
== WAIT_OBJECT_0
, "Timed out waiting for the debugger\n");
369 assert(load_blackbox(childlog
, &crash_blackbox
, sizeof(crash_blackbox
)));
370 assert(load_blackbox(dbglog
, &dbg_blackbox
, sizeof(dbg_blackbox
)));
372 ok(dbg_blackbox
.argc
== 6, "wrong debugger argument count: %d\n", dbg_blackbox
.argc
);
373 ok(dbg_blackbox
.pid
== crash_blackbox
.pid
, "the child and debugged pids don't match: %d != %d\n", crash_blackbox
.pid
, dbg_blackbox
.pid
);
374 ok(dbg_blackbox
.debug_rc
, "debugger: SetEvent(debug_event) failed err=%d\n", dbg_blackbox
.debug_err
);
375 ok(dbg_blackbox
.attach_rc
, "DebugActiveProcess(%d) failed err=%d\n", dbg_blackbox
.pid
, dbg_blackbox
.attach_err
);
376 ok(dbg_blackbox
.nokill_rc
, "DebugSetProcessKillOnExit(FALSE) failed err=%d\n", dbg_blackbox
.nokill_err
);
377 ok(dbg_blackbox
.detach_rc
, "DebugActiveProcessStop(%d) failed err=%d\n", dbg_blackbox
.pid
, dbg_blackbox
.detach_err
);
379 assert(DeleteFileA(dbglog
) != 0);
380 assert(DeleteFileA(childlog
) != 0);
383 static void crash_and_winedbg(HKEY hkey
, const char* argv0
)
388 PROCESS_INFORMATION info
;
389 STARTUPINFOA startup
;
392 ret
=RegSetValueExA(hkey
, "auto", 0, REG_SZ
, (BYTE
*)"1", 2);
393 ok(ret
== ERROR_SUCCESS
, "unable to set AeDebug/auto: ret=%d\n", ret
);
395 cmd
=HeapAlloc(GetProcessHeap(), 0, strlen(argv0
)+15+1);
396 sprintf(cmd
, "%s debugger crash", argv0
);
398 memset(&startup
, 0, sizeof(startup
));
399 startup
.cb
= sizeof(startup
);
400 startup
.dwFlags
= STARTF_USESHOWWINDOW
;
401 startup
.wShowWindow
= SW_SHOWNORMAL
;
402 ret
=CreateProcessA(NULL
, cmd
, NULL
, NULL
, FALSE
, 0, NULL
, NULL
, &startup
, &info
);
403 ok(ret
, "CreateProcess: err=%d\n", GetLastError());
404 HeapFree(GetProcessHeap(), 0, cmd
);
405 CloseHandle(info
.hThread
);
407 trace("waiting for child exit...\n");
408 ok(WaitForSingleObject(info
.hProcess
, 60000) == WAIT_OBJECT_0
, "Timed out waiting for the child to crash\n");
409 bRet
= GetExitCodeProcess(info
.hProcess
, &exit_code
);
410 ok(bRet
, "GetExitCodeProcess failed: err=%d\n", GetLastError());
411 ok(exit_code
== STATUS_ACCESS_VIOLATION
, "exit code = %08x\n", exit_code
);
412 CloseHandle(info
.hProcess
);
415 static void test_ExitCode(void)
417 static const char* AeDebug
="Software\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug";
418 static const char* WineDbg
="Software\\Wine\\WineDbg";
419 char test_exe
[MAX_PATH
];
423 reg_save_value auto_value
;
424 reg_save_value debugger_value
;
426 GetModuleFileNameA(GetModuleHandleA(NULL
), test_exe
, sizeof(test_exe
));
427 if (GetFileAttributesA(test_exe
) == INVALID_FILE_ATTRIBUTES
)
428 strcat(test_exe
, ".so");
429 if (GetFileAttributesA(test_exe
) == INVALID_FILE_ATTRIBUTES
)
431 ok(0, "could not find the test executable '%s'\n", test_exe
);
435 ret
=RegCreateKeyExA(HKEY_LOCAL_MACHINE
, AeDebug
, 0, NULL
, REG_OPTION_NON_VOLATILE
, KEY_ALL_ACCESS
, NULL
, &hkey
, &disposition
);
436 if (ret
== ERROR_SUCCESS
)
438 save_value(hkey
, "auto", &auto_value
);
439 save_value(hkey
, "debugger", &debugger_value
);
440 trace("HKLM\\%s\\debugger is set to '%s'\n", AeDebug
, debugger_value
.data
);
442 else if (ret
== ERROR_ACCESS_DENIED
)
444 skip("not enough privileges to change the debugger\n");
447 else if (ret
!= ERROR_FILE_NOT_FOUND
)
449 ok(0, "could not open the AeDebug key: %d\n", ret
);
452 else debugger_value
.data
= NULL
;
454 if (debugger_value
.data
&& debugger_value
.type
== REG_SZ
&&
455 strstr((char*)debugger_value
.data
, "winedbg --auto"))
458 ret
=RegCreateKeyA(HKEY_CURRENT_USER
, WineDbg
, &hkeyWinedbg
);
459 if (ret
== ERROR_SUCCESS
)
462 reg_save_value crash_dlg_value
;
463 save_value(hkeyWinedbg
, "ShowCrashDialog", &crash_dlg_value
);
464 RegSetValueExA(hkeyWinedbg
, "ShowCrashDialog", 0, REG_DWORD
, (BYTE
*)&zero
, sizeof(DWORD
));
465 crash_and_winedbg(hkey
, test_exe
);
466 restore_value(hkeyWinedbg
, &crash_dlg_value
);
467 RegCloseKey(hkeyWinedbg
);
470 ok(0, "Couldn't access WineDbg Key - error %u\n", ret
);
473 if (winetest_interactive
)
474 /* Since the debugging process never sets the debug event, it isn't recognized
475 as a valid debugger and, after the debugger exits, Windows will show a dialog box
476 asking the user what to do */
477 crash_and_debug(hkey
, test_exe
, "dbg,none");
479 skip("\"none\" debugger test needs user interaction\n");
480 ok(disposition
== REG_OPENED_EXISTING_KEY
, "expected REG_OPENED_EXISTING_KEY, got %d\n", disposition
);
481 crash_and_debug(hkey
, test_exe
, "dbg,event,order");
482 crash_and_debug(hkey
, test_exe
, "dbg,attach,event,code2");
483 if (pDebugSetProcessKillOnExit
)
484 crash_and_debug(hkey
, test_exe
, "dbg,attach,event,nokill");
486 win_skip("DebugSetProcessKillOnExit is not available\n");
487 if (pDebugActiveProcessStop
)
488 crash_and_debug(hkey
, test_exe
, "dbg,attach,event,detach");
490 win_skip("DebugActiveProcessStop is not available\n");
492 if (disposition
== REG_CREATED_NEW_KEY
)
495 RegDeleteKeyA(HKEY_LOCAL_MACHINE
, AeDebug
);
499 restore_value(hkey
, &auto_value
);
500 restore_value(hkey
, &debugger_value
);
505 static void test_RemoteDebugger(void)
508 if(!pCheckRemoteDebuggerPresent
)
510 win_skip("CheckRemoteDebuggerPresent is not available\n");
514 SetLastError(0xdeadbeef);
515 bret
= pCheckRemoteDebuggerPresent(GetCurrentProcess(),&present
);
516 ok(bret
, "expected CheckRemoteDebuggerPresent to succeed\n");
517 ok(0xdeadbeef == GetLastError(),
518 "expected error to be unchanged, got %d/%x\n",GetLastError(), GetLastError());
521 SetLastError(0xdeadbeef);
522 bret
= pCheckRemoteDebuggerPresent(NULL
,&present
);
523 ok(!bret
, "expected CheckRemoteDebuggerPresent to fail\n");
524 ok(present
, "expected parameter to be unchanged\n");
525 ok(ERROR_INVALID_PARAMETER
== GetLastError(),
526 "expected error ERROR_INVALID_PARAMETER, got %d/%x\n",GetLastError(), GetLastError());
528 SetLastError(0xdeadbeef);
529 bret
= pCheckRemoteDebuggerPresent(GetCurrentProcess(),NULL
);
530 ok(!bret
, "expected CheckRemoteDebuggerPresent to fail\n");
531 ok(ERROR_INVALID_PARAMETER
== GetLastError(),
532 "expected error ERROR_INVALID_PARAMETER, got %d/%x\n",GetLastError(), GetLastError());
535 struct child_blackbox
540 static void doChild(int argc
, char **argv
)
542 struct child_blackbox blackbox
;
543 const char *blackbox_file
;
549 blackbox_file
= argv
[4];
550 sscanf(argv
[3], "%08x", &ppid
);
552 parent
= OpenProcess(PROCESS_QUERY_INFORMATION
, FALSE
, ppid
);
553 child_ok(!!parent
, "OpenProcess failed, last error %#x.\n", GetLastError());
555 ret
= pCheckRemoteDebuggerPresent(parent
, &debug
);
556 child_ok(ret
, "CheckRemoteDebuggerPresent failed, last error %#x.\n", GetLastError());
557 child_ok(!debug
, "Expected debug == 0, got %#x.\n", debug
);
559 ret
= DebugActiveProcess(ppid
);
560 child_ok(ret
, "DebugActiveProcess failed, last error %#x.\n", GetLastError());
562 ret
= pCheckRemoteDebuggerPresent(parent
, &debug
);
563 child_ok(ret
, "CheckRemoteDebuggerPresent failed, last error %#x.\n", GetLastError());
564 child_ok(debug
, "Expected debug != 0, got %#x.\n", debug
);
566 ret
= pDebugActiveProcessStop(ppid
);
567 child_ok(ret
, "DebugActiveProcessStop failed, last error %#x.\n", GetLastError());
569 ret
= pCheckRemoteDebuggerPresent(parent
, &debug
);
570 child_ok(ret
, "CheckRemoteDebuggerPresent failed, last error %#x.\n", GetLastError());
571 child_ok(!debug
, "Expected debug == 0, got %#x.\n", debug
);
573 ret
= CloseHandle(parent
);
574 child_ok(ret
, "CloseHandle failed, last error %#x.\n", GetLastError());
576 ret
= pIsDebuggerPresent();
577 child_ok(ret
, "Expected ret != 0, got %#x.\n", ret
);
578 ret
= pCheckRemoteDebuggerPresent(GetCurrentProcess(), &debug
);
579 child_ok(ret
, "CheckRemoteDebuggerPresent failed, last error %#x.\n", GetLastError());
580 child_ok(debug
, "Expected debug != 0, got %#x.\n", debug
);
584 pNtCurrentTeb()->Peb
->BeingDebugged
= FALSE
;
586 ret
= pIsDebuggerPresent();
587 child_ok(!ret
, "Expected ret != 0, got %#x.\n", ret
);
588 ret
= pCheckRemoteDebuggerPresent(GetCurrentProcess(), &debug
);
589 child_ok(ret
, "CheckRemoteDebuggerPresent failed, last error %#x.\n", GetLastError());
590 child_ok(debug
, "Expected debug != 0, got %#x.\n", debug
);
592 pNtCurrentTeb()->Peb
->BeingDebugged
= TRUE
;
595 blackbox
.failures
= child_failures
;
596 save_blackbox(blackbox_file
, &blackbox
, sizeof(blackbox
));
599 static void test_debug_loop(int argc
, char **argv
)
601 const char *arguments
= " debugger child ";
602 struct child_blackbox blackbox
;
603 char blackbox_file
[MAX_PATH
];
604 PROCESS_INFORMATION pi
;
611 if (!pDebugActiveProcessStop
|| !pCheckRemoteDebuggerPresent
)
613 win_skip("DebugActiveProcessStop or CheckRemoteDebuggerPresent not available, skipping test.\n");
617 pid
= GetCurrentProcessId();
618 ret
= DebugActiveProcess(pid
);
619 ok(!ret
, "DebugActiveProcess() succeeded on own process.\n");
621 get_file_name(blackbox_file
);
622 cmd
= HeapAlloc(GetProcessHeap(), 0, strlen(argv
[0]) + strlen(arguments
) + strlen(blackbox_file
) + 2 + 10);
623 sprintf(cmd
, "%s%s%08x \"%s\"", argv
[0], arguments
, pid
, blackbox_file
);
625 memset(&si
, 0, sizeof(si
));
627 ret
= CreateProcessA(NULL
, cmd
, NULL
, NULL
, FALSE
, DEBUG_PROCESS
, NULL
, NULL
, &si
, &pi
);
628 ok(ret
, "CreateProcess failed, last error %#x.\n", GetLastError());
630 HeapFree(GetProcessHeap(), 0, cmd
);
632 ret
= pCheckRemoteDebuggerPresent(pi
.hProcess
, &debug
);
633 ok(ret
, "CheckRemoteDebuggerPresent failed, last error %#x.\n", GetLastError());
634 ok(debug
, "Expected debug != 0, got %#x.\n", debug
);
640 ret
= WaitForDebugEvent(&ev
, INFINITE
);
641 ok(ret
, "WaitForDebugEvent failed, last error %#x.\n", GetLastError());
644 if (ev
.dwDebugEventCode
== EXIT_PROCESS_DEBUG_EVENT
) break;
646 ret
= ContinueDebugEvent(ev
.dwProcessId
, ev
.dwThreadId
, DBG_CONTINUE
);
647 ok(ret
, "ContinueDebugEvent failed, last error %#x.\n", GetLastError());
651 ret
= CloseHandle(pi
.hThread
);
652 ok(ret
, "CloseHandle failed, last error %#x.\n", GetLastError());
653 ret
= CloseHandle(pi
.hProcess
);
654 ok(ret
, "CloseHandle failed, last error %#x.\n", GetLastError());
656 load_blackbox(blackbox_file
, &blackbox
, sizeof(blackbox
));
657 ok(!blackbox
.failures
, "Got %d failures from child process.\n", blackbox
.failures
);
659 ret
= DeleteFileA(blackbox_file
);
660 ok(ret
, "DeleteFileA failed, last error %#x.\n", GetLastError());
663 static void doChildren(int argc
, char **argv
)
665 const char *arguments
= "debugger children last";
666 struct child_blackbox blackbox
;
667 const char *blackbox_file
, *p
;
668 char event_name
[MAX_PATH
];
669 PROCESS_INFORMATION pi
;
675 if (!strcmp(argv
[3], "last")) return;
677 blackbox_file
= argv
[3];
679 p
= strrchr(blackbox_file
, '\\');
680 p
= p
? p
+1 : blackbox_file
;
681 strcpy(event_name
, p
);
682 strcat(event_name
, "_init");
683 event
= OpenEventA(EVENT_ALL_ACCESS
, FALSE
, event_name
);
684 child_ok(event
!= NULL
, "OpenEvent failed, last error %d.\n", GetLastError());
688 p
= strrchr(blackbox_file
, '\\');
689 p
= p
? p
+1 : blackbox_file
;
690 strcpy(event_name
, p
);
691 strcat(event_name
, "_attach");
692 event
= OpenEventA(EVENT_ALL_ACCESS
, FALSE
, event_name
);
693 child_ok(event
!= NULL
, "OpenEvent failed, last error %d.\n", GetLastError());
694 WaitForSingleObject(event
, INFINITE
);
697 cmd
= HeapAlloc(GetProcessHeap(), 0, strlen(argv
[0]) + strlen(arguments
) + 2);
698 sprintf(cmd
, "%s %s", argv
[0], arguments
);
700 memset(&si
, 0, sizeof(si
));
702 ret
= CreateProcessA(NULL
, cmd
, NULL
, NULL
, FALSE
, 0, NULL
, NULL
, &si
, &pi
);
703 child_ok(ret
, "CreateProcess failed, last error %d.\n", GetLastError());
705 child_ok(WaitForSingleObject(pi
.hProcess
, 10000) == WAIT_OBJECT_0
,
706 "Timed out waiting for the child to exit\n");
708 ret
= CloseHandle(pi
.hThread
);
709 child_ok(ret
, "CloseHandle failed, last error %d.\n", GetLastError());
710 ret
= CloseHandle(pi
.hProcess
);
711 child_ok(ret
, "CloseHandle failed, last error %d.\n", GetLastError());
713 blackbox
.failures
= child_failures
;
714 save_blackbox(blackbox_file
, &blackbox
, sizeof(blackbox
));
716 HeapFree(GetProcessHeap(), 0, cmd
);
719 static void test_debug_children(char *name
, DWORD flag
, BOOL debug_child
)
721 const char *arguments
= "debugger children";
722 struct child_blackbox blackbox
;
723 char blackbox_file
[MAX_PATH
], *p
;
724 char event_name
[MAX_PATH
];
725 PROCESS_INFORMATION pi
;
727 HANDLE event_init
, event_attach
;
730 BOOL got_child_event
= FALSE
;
732 if (!pDebugActiveProcessStop
|| !pCheckRemoteDebuggerPresent
)
734 win_skip("DebugActiveProcessStop or CheckRemoteDebuggerPresent not available, skipping test.\n");
738 get_file_name(blackbox_file
);
739 cmd
= HeapAlloc(GetProcessHeap(), 0, strlen(name
) + strlen(arguments
) + strlen(blackbox_file
) + 5);
740 sprintf(cmd
, "%s %s \"%s\"", name
, arguments
, blackbox_file
);
742 p
= strrchr(blackbox_file
, '\\');
743 p
= p
? p
+1 : blackbox_file
;
744 strcpy(event_name
, p
);
745 strcat(event_name
, "_init");
746 event_init
= CreateEventA(NULL
, FALSE
, FALSE
, event_name
);
747 ok(event_init
!= NULL
, "OpenEvent failed, last error %d.\n", GetLastError());
749 p
= strrchr(blackbox_file
, '\\');
750 p
= p
? p
+1 : blackbox_file
;
751 strcpy(event_name
, p
);
752 strcat(event_name
, "_attach");
753 event_attach
= CreateEventA(NULL
, FALSE
, flag
!=0, event_name
);
754 ok(event_attach
!= NULL
, "CreateEvent failed, last error %d.\n", GetLastError());
756 memset(&si
, 0, sizeof(si
));
759 ret
= CreateProcessA(NULL
, cmd
, NULL
, NULL
, FALSE
, flag
, NULL
, NULL
, &si
, &pi
);
760 ok(ret
, "CreateProcess failed, last error %d.\n", GetLastError());
761 HeapFree(GetProcessHeap(), 0, cmd
);
764 WaitForSingleObject(event_init
, INFINITE
);
765 ret
= DebugActiveProcess(pi
.dwProcessId
);
766 ok(ret
, "DebugActiveProcess failed, last error %d.\n", GetLastError());
767 ret
= SetEvent(event_attach
);
768 ok(ret
, "SetEvent failed, last error %d.\n", GetLastError());
771 ret
= pCheckRemoteDebuggerPresent(pi
.hProcess
, &debug
);
772 ok(ret
, "CheckRemoteDebuggerPresent failed, last error %d.\n", GetLastError());
773 ok(debug
, "Expected debug != 0, got %x.\n", debug
);
779 ret
= WaitForDebugEvent(&ev
, INFINITE
);
780 ok(ret
, "WaitForDebugEvent failed, last error %d.\n", GetLastError());
783 if (ev
.dwDebugEventCode
==EXIT_PROCESS_DEBUG_EVENT
&& ev
.dwProcessId
==pi
.dwProcessId
) break;
784 else if (ev
.dwProcessId
!= pi
.dwProcessId
) got_child_event
= TRUE
;
786 ret
= ContinueDebugEvent(ev
.dwProcessId
, ev
.dwThreadId
, DBG_CONTINUE
);
787 ok(ret
, "ContinueDebugEvent failed, last error %d.\n", GetLastError());
791 ok(got_child_event
, "didn't get any child events (flag: %x).\n", flag
);
793 ok(!got_child_event
, "got child event (flag: %x).\n", flag
);
794 CloseHandle(event_init
);
795 CloseHandle(event_attach
);
797 ret
= CloseHandle(pi
.hThread
);
798 ok(ret
, "CloseHandle failed, last error %d.\n", GetLastError());
799 ret
= CloseHandle(pi
.hProcess
);
800 ok(ret
, "CloseHandle failed, last error %d.\n", GetLastError());
802 load_blackbox(blackbox_file
, &blackbox
, sizeof(blackbox
));
803 ok(!blackbox
.failures
, "Got %d failures from child process.\n", blackbox
.failures
);
805 ret
= DeleteFileA(blackbox_file
);
806 ok(ret
, "DeleteFileA failed, last error %d.\n", GetLastError());
813 hdll
=GetModuleHandleA("kernel32.dll");
814 pCheckRemoteDebuggerPresent
=(void*)GetProcAddress(hdll
, "CheckRemoteDebuggerPresent");
815 pDebugActiveProcessStop
=(void*)GetProcAddress(hdll
, "DebugActiveProcessStop");
816 pDebugSetProcessKillOnExit
=(void*)GetProcAddress(hdll
, "DebugSetProcessKillOnExit");
817 pIsDebuggerPresent
=(void*)GetProcAddress(hdll
, "IsDebuggerPresent");
818 hdll
=GetModuleHandleA("ntdll.dll");
819 if (hdll
) pNtCurrentTeb
= (void*)GetProcAddress(hdll
, "NtCurrentTeb");
821 myARGC
=winetest_get_mainargs(&myARGV
);
822 if (myARGC
>= 3 && strcmp(myARGV
[2], "crash") == 0)
824 doCrash(myARGC
, myARGV
);
826 else if (myARGC
>= 3 && strncmp(myARGV
[2], "dbg,", 4) == 0)
828 doDebugger(myARGC
, myARGV
);
830 else if (myARGC
>= 5 && !strcmp(myARGV
[2], "child"))
832 doChild(myARGC
, myARGV
);
834 else if (myARGC
>= 4 && !strcmp(myARGV
[2], "children"))
836 doChildren(myARGC
, myARGV
);
841 test_RemoteDebugger();
842 test_debug_loop(myARGC
, myARGV
);
843 test_debug_children(myARGV
[0], DEBUG_PROCESS
, TRUE
);
844 test_debug_children(myARGV
[0], DEBUG_ONLY_THIS_PROCESS
, FALSE
);
845 test_debug_children(myARGV
[0], DEBUG_PROCESS
|DEBUG_ONLY_THIS_PROCESS
, FALSE
);
846 test_debug_children(myARGV
[0], 0, FALSE
);