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