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 
test_child_ok(int condition,const char * msg,...)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 */
get_file_name(char * buf)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 
save_value(HKEY hkey,const char * value,reg_save_value * saved)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 
restore_value(HKEY hkey,reg_save_value * saved)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 
get_events(const char * name,HANDLE * start_event,HANDLE * done_event)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 
save_blackbox(const char * logfile,void * blackbox,int size)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 
load_blackbox(const char * logfile,void * blackbox,int size)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 
doCrash(int argc,char ** argv)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 
doDebugger(int argc,char ** argv)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 
crash_and_debug(HKEY hkey,const char * argv0,const char * dbgtasks)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 
crash_and_winedbg(HKEY hkey,const char * argv0)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 
test_ExitCode(void)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 
test_RemoteDebugger(void)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 
doChild(int argc,char ** argv)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 
test_debug_loop(int argc,char ** argv)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 
doChildren(int argc,char ** argv)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 
test_debug_children(char * name,DWORD flag,BOOL debug_child)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 
START_TEST(debugger)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