kernel32: Add tests for FormatMessageA/W.
[wine] / dlls / kernel32 / tests / debugger.c
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 <winreg.h>
26 #include "wine/test.h"
27
28 #ifndef STATUS_DEBUGGER_INACTIVE
29 #define STATUS_DEBUGGER_INACTIVE         ((NTSTATUS) 0xC0000354)
30 #endif
31
32 static int    myARGC;
33 static char** myARGV;
34
35 static BOOL (WINAPI *pDebugActiveProcessStop)(DWORD);
36 static BOOL (WINAPI *pDebugSetProcessKillOnExit)(BOOL);
37
38 /* Copied from the process test */
39 static void get_file_name(char* buf)
40 {
41     char path[MAX_PATH];
42
43     buf[0] = '\0';
44     GetTempPathA(sizeof(path), path);
45     GetTempFileNameA(path, "wt", 0, buf);
46 }
47
48 typedef struct tag_reg_save_value
49 {
50     const char *name;
51     DWORD type;
52     BYTE *data;
53     DWORD size;
54 } reg_save_value;
55
56 static DWORD save_value(HKEY hkey, const char *value, reg_save_value *saved)
57 {
58     DWORD ret;
59     saved->name=value;
60     saved->data=0;
61     saved->size=0;
62     ret=RegQueryValueExA(hkey, value, NULL, &saved->type, NULL, &saved->size);
63     if (ret == ERROR_SUCCESS)
64     {
65         saved->data=HeapAlloc(GetProcessHeap(), 0, saved->size);
66         RegQueryValueExA(hkey, value, NULL, &saved->type, saved->data, &saved->size);
67     }
68     return ret;
69 }
70
71 static void restore_value(HKEY hkey, reg_save_value *saved)
72 {
73     if (saved->data)
74     {
75         RegSetValueExA(hkey, saved->name, 0, saved->type, saved->data, saved->size);
76         HeapFree(GetProcessHeap(), 0, saved->data);
77     }
78     else
79         RegDeleteValueA(hkey, saved->name);
80 }
81
82 static void get_events(const char* name, HANDLE *start_event, HANDLE *done_event)
83 {
84     const char* basename;
85     char* event_name;
86
87     basename=strrchr(name, '\\');
88     basename=(basename ? basename+1 : name);
89     event_name=HeapAlloc(GetProcessHeap(), 0, 6+strlen(basename)+1);
90
91     sprintf(event_name, "start_%s", basename);
92     *start_event=CreateEvent(NULL, 0,0, event_name);
93     sprintf(event_name, "done_%s", basename);
94     *done_event=CreateEvent(NULL, 0,0, event_name);
95     HeapFree(GetProcessHeap(), 0, event_name);
96 }
97
98 static void save_blackbox(const char* logfile, void* blackbox, int size)
99 {
100     HANDLE hFile;
101     DWORD written;
102
103     hFile=CreateFileA(logfile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0);
104     if (hFile == INVALID_HANDLE_VALUE)
105         return;
106     WriteFile(hFile, blackbox, size, &written, NULL);
107     CloseHandle(hFile);
108 }
109
110 static int load_blackbox(const char* logfile, void* blackbox, int size)
111 {
112     HANDLE hFile;
113     DWORD read;
114     BOOL ret;
115
116     hFile=CreateFileA(logfile, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, 0);
117     if (hFile == INVALID_HANDLE_VALUE)
118     {
119         ok(0, "unable to open '%s'\n", logfile);
120         return 0;
121     }
122     ret=ReadFile(hFile, blackbox, size, &read, NULL);
123     ok(read == size, "wrong size for '%s': read=%d\n", logfile, read);
124     CloseHandle(hFile);
125     return 1;
126 }
127
128 typedef struct
129 {
130     DWORD pid;
131 } crash_blackbox_t;
132
133 static void doCrash(int argc,  char** argv)
134 {
135     char* p;
136
137     if (argc >= 4)
138     {
139         crash_blackbox_t blackbox;
140         blackbox.pid=GetCurrentProcessId();
141         save_blackbox(argv[3], &blackbox, sizeof(blackbox));
142     }
143
144     /* Just crash */
145     trace("child: crashing...\n");
146     p=NULL;
147     *p=0;
148 }
149
150 typedef struct
151 {
152     int argc;
153     DWORD pid;
154     BOOL debug_rc;
155     DWORD debug_err;
156     BOOL attach_rc;
157     DWORD attach_err;
158     BOOL nokill_rc;
159     DWORD nokill_err;
160     BOOL detach_rc;
161     DWORD detach_err;
162 } debugger_blackbox_t;
163
164 static void doDebugger(int argc, char** argv)
165 {
166     const char* logfile;
167     debugger_blackbox_t blackbox;
168     HANDLE start_event = 0, done_event = 0, debug_event;
169
170     blackbox.argc=argc;
171     logfile=(argc >= 4 ? argv[3] : NULL);
172     blackbox.pid=(argc >= 5 ? atol(argv[4]) : 0);
173
174     blackbox.attach_err=0;
175     if (strstr(myARGV[2], "attach"))
176     {
177         blackbox.attach_rc=DebugActiveProcess(blackbox.pid);
178         if (!blackbox.attach_rc)
179             blackbox.attach_err=GetLastError();
180     }
181     else
182         blackbox.attach_rc=TRUE;
183
184     debug_event=(argc >= 6 ? (HANDLE)(INT_PTR)atol(argv[5]) : NULL);
185     blackbox.debug_err=0;
186     if (debug_event && strstr(myARGV[2], "event"))
187     {
188         blackbox.debug_rc=SetEvent(debug_event);
189         if (!blackbox.debug_rc)
190             blackbox.debug_err=GetLastError();
191     }
192     else
193         blackbox.debug_rc=TRUE;
194
195     if (logfile)
196     {
197         get_events(logfile, &start_event, &done_event);
198     }
199
200     if (strstr(myARGV[2], "order"))
201     {
202         trace("debugger: waiting for the start signal...\n");
203         WaitForSingleObject(start_event, INFINITE);
204     }
205
206     blackbox.nokill_err=0;
207     if (strstr(myARGV[2], "nokill"))
208     {
209         blackbox.nokill_rc=pDebugSetProcessKillOnExit(FALSE);
210         if (!blackbox.nokill_rc)
211             blackbox.nokill_err=GetLastError();
212     }
213     else
214         blackbox.nokill_rc=TRUE;
215
216     blackbox.detach_err=0;
217     if (strstr(myARGV[2], "detach"))
218     {
219         blackbox.detach_rc=pDebugActiveProcessStop(blackbox.pid);
220         if (!blackbox.detach_rc)
221             blackbox.detach_err=GetLastError();
222     }
223     else
224         blackbox.detach_rc=TRUE;
225
226     if (logfile)
227     {
228         save_blackbox(logfile, &blackbox, sizeof(blackbox));
229     }
230     trace("debugger: done debugging...\n");
231     SetEvent(done_event);
232
233     /* Just exit with a known value */
234     ExitProcess(0xdeadbeef);
235 }
236
237 static void crash_and_debug(HKEY hkey, const char* argv0, const char* dbgtasks)
238 {
239     DWORD ret;
240     HANDLE start_event, done_event;
241     char* cmd;
242     char dbglog[MAX_PATH];
243     char childlog[MAX_PATH];
244     PROCESS_INFORMATION info;
245     STARTUPINFOA startup;
246     DWORD exit_code;
247     crash_blackbox_t crash_blackbox;
248     debugger_blackbox_t dbg_blackbox;
249
250     ret=RegSetValueExA(hkey, "auto", 0, REG_SZ, (BYTE*)"1", 2);
251     ok(ret == ERROR_SUCCESS, "unable to set AeDebug/auto: ret=%d\n", ret);
252
253     get_file_name(dbglog);
254     get_events(dbglog, &start_event, &done_event);
255     cmd=HeapAlloc(GetProcessHeap(), 0, strlen(argv0)+10+strlen(dbgtasks)+1+strlen(dbglog)+34+1);
256     sprintf(cmd, "%s debugger %s %s %%ld %%ld", argv0, dbgtasks, dbglog);
257     ret=RegSetValueExA(hkey, "debugger", 0, REG_SZ, (BYTE*)cmd, strlen(cmd)+1);
258     ok(ret == ERROR_SUCCESS, "unable to set AeDebug/debugger: ret=%d\n", ret);
259     HeapFree(GetProcessHeap(), 0, cmd);
260
261     get_file_name(childlog);
262     cmd=HeapAlloc(GetProcessHeap(), 0, strlen(argv0)+16+strlen(dbglog)+1);
263     sprintf(cmd, "%s debugger crash %s", argv0, childlog);
264
265     memset(&startup, 0, sizeof(startup));
266     startup.cb = sizeof(startup);
267     startup.dwFlags = STARTF_USESHOWWINDOW;
268     startup.wShowWindow = SW_SHOWNORMAL;
269     ret=CreateProcessA(NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info);
270     ok(ret, "CreateProcess: err=%d\n", GetLastError());
271     HeapFree(GetProcessHeap(), 0, cmd);
272     CloseHandle(info.hThread);
273
274     /* The process exits... */
275     trace("waiting for child exit...\n");
276     ok(WaitForSingleObject(info.hProcess, 60000) == WAIT_OBJECT_0, "Timed out waiting for the child to crash\n");
277     ok(GetExitCodeProcess(info.hProcess, &exit_code), "GetExitCodeProcess failed: err=%d\n", GetLastError());
278     if (strstr(dbgtasks, "code2"))
279     {
280         /* If, after attaching to the debuggee, the debugger exits without
281          * detaching, then the debuggee gets a special exit code.
282          */
283         ok(exit_code == 0xffffffff || /* Win 9x */
284            exit_code == 0x80 || /* NT4 */
285            exit_code == STATUS_DEBUGGER_INACTIVE, /* Win >= XP */
286            "wrong exit code : %08x\n", exit_code);
287     }
288     else
289          ok(exit_code == STATUS_ACCESS_VIOLATION ||
290             exit_code == WAIT_ABANDONED, /* win2k3 */
291             "exit code = %08x instead of STATUS_ACCESS_VIOLATION or WAIT_ABANDONED\n", exit_code);
292     CloseHandle(info.hProcess);
293
294     /* ...before the debugger */
295     if (strstr(dbgtasks, "order"))
296         ok(SetEvent(start_event), "SetEvent(start_event) failed\n");
297
298     trace("waiting for the debugger...\n");
299     ok(WaitForSingleObject(done_event, 60000) == WAIT_OBJECT_0, "Timed out waiting for the debugger\n");
300
301     assert(load_blackbox(childlog, &crash_blackbox, sizeof(crash_blackbox)));
302     assert(load_blackbox(dbglog, &dbg_blackbox, sizeof(dbg_blackbox)));
303
304     ok(dbg_blackbox.argc == 6, "wrong debugger argument count: %d\n", dbg_blackbox.argc);
305     ok(dbg_blackbox.pid == crash_blackbox.pid, "the child and debugged pids don't match: %d != %d\n", crash_blackbox.pid, dbg_blackbox.pid);
306     ok(dbg_blackbox.debug_rc, "debugger: SetEvent(debug_event) failed err=%d\n", dbg_blackbox.debug_err);
307     ok(dbg_blackbox.attach_rc, "DebugActiveProcess(%d) failed err=%d\n", dbg_blackbox.pid, dbg_blackbox.attach_err);
308     ok(dbg_blackbox.nokill_rc, "DebugSetProcessKillOnExit(FALSE) failed err=%d\n", dbg_blackbox.nokill_err);
309     ok(dbg_blackbox.detach_rc, "DebugActiveProcessStop(%d) failed err=%d\n", dbg_blackbox.pid, dbg_blackbox.detach_err);
310
311     assert(DeleteFileA(dbglog) != 0);
312     assert(DeleteFileA(childlog) != 0);
313 }
314
315 static void crash_and_winedbg(HKEY hkey, const char* argv0)
316 {
317     DWORD ret;
318     char* cmd;
319     PROCESS_INFORMATION info;
320     STARTUPINFOA startup;
321     DWORD exit_code;
322
323     ret=RegSetValueExA(hkey, "auto", 0, REG_SZ, (BYTE*)"1", 2);
324     ok(ret == ERROR_SUCCESS, "unable to set AeDebug/auto: ret=%d\n", ret);
325
326     cmd=HeapAlloc(GetProcessHeap(), 0, strlen(argv0)+15+1);
327     sprintf(cmd, "%s debugger crash", argv0);
328
329     memset(&startup, 0, sizeof(startup));
330     startup.cb = sizeof(startup);
331     startup.dwFlags = STARTF_USESHOWWINDOW;
332     startup.wShowWindow = SW_SHOWNORMAL;
333     ret=CreateProcessA(NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info);
334     ok(ret, "CreateProcess: err=%d\n", GetLastError());
335     HeapFree(GetProcessHeap(), 0, cmd);
336     CloseHandle(info.hThread);
337
338     trace("waiting for child exit...\n");
339     ok(WaitForSingleObject(info.hProcess, 60000) == WAIT_OBJECT_0, "Timed out waiting for the child to crash\n");
340     ok(GetExitCodeProcess(info.hProcess, &exit_code), "GetExitCodeProcess failed: err=%d\n", GetLastError());
341     ok(exit_code == STATUS_ACCESS_VIOLATION, "exit code = %08x\n", exit_code);
342     CloseHandle(info.hProcess);
343 }
344
345 static void test_ExitCode(void)
346 {
347     static const char* AeDebug="Software\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug";
348     static const char* WineDbg="Software\\Wine\\WineDbg";
349     char test_exe[MAX_PATH];
350     DWORD ret;
351     HKEY hkey;
352     DWORD disposition;
353     reg_save_value auto_value;
354     reg_save_value debugger_value;
355
356     GetModuleFileNameA(GetModuleHandle(NULL), test_exe, sizeof(test_exe));
357     if (GetFileAttributes(test_exe) == INVALID_FILE_ATTRIBUTES)
358         strcat(test_exe, ".so");
359     if (GetFileAttributesA(test_exe) == INVALID_FILE_ATTRIBUTES)
360     {
361         ok(0, "could not find the test executable '%s'\n", test_exe);
362         return;
363     }
364
365     ret=RegCreateKeyExA(HKEY_LOCAL_MACHINE, AeDebug, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, &disposition);
366     if (ret == ERROR_SUCCESS)
367     {
368         save_value(hkey, "auto", &auto_value);
369         save_value(hkey, "debugger", &debugger_value);
370         trace("HKLM\\%s\\debugger is set to '%s'\n", AeDebug, debugger_value.data);
371     }
372     else if (ret == ERROR_ACCESS_DENIED)
373     {
374         skip("not enough privileges to change the debugger\n");
375         return;
376     }
377     else if (ret != ERROR_FILE_NOT_FOUND)
378     {
379         ok(0, "could not open the AeDebug key: %d\n", ret);
380         return;
381     }
382
383     if (debugger_value.data && debugger_value.type == REG_SZ &&
384         strstr((char*)debugger_value.data, "winedbg --auto"))
385     {
386         HKEY hkeyWinedbg;
387         ret=RegCreateKeyA(HKEY_CURRENT_USER, WineDbg, &hkeyWinedbg);
388         if (ret == ERROR_SUCCESS)
389         {
390             static DWORD zero;
391             reg_save_value crash_dlg_value;
392             save_value(hkeyWinedbg, "ShowCrashDialog", &crash_dlg_value);
393             RegSetValueExA(hkeyWinedbg, "ShowCrashDialog", 0, REG_DWORD, (BYTE *)&zero, sizeof(DWORD));
394             crash_and_winedbg(hkey, test_exe);
395             restore_value(hkeyWinedbg, &crash_dlg_value);
396             RegCloseKey(hkeyWinedbg);
397         }
398         else
399             ok(0, "Couldn't access WineDbg Key - error %u\n", ret);
400     }
401
402     if (winetest_interactive)
403         /* Since the debugging process never sets the debug event, it isn't recognized
404            as a valid debugger and, after the debugger exits, Windows will show a dialog box
405            asking the user what to do */
406         crash_and_debug(hkey, test_exe, "dbg,none");
407     else
408         skip("\"none\" debugger test needs user interaction\n");
409     crash_and_debug(hkey, test_exe, "dbg,event,order");
410     crash_and_debug(hkey, test_exe, "dbg,attach,event,code2");
411     if (pDebugSetProcessKillOnExit)
412         crash_and_debug(hkey, test_exe, "dbg,attach,event,nokill");
413     if (pDebugActiveProcessStop)
414         crash_and_debug(hkey, test_exe, "dbg,attach,event,detach");
415
416     if (disposition == REG_CREATED_NEW_KEY)
417     {
418         RegCloseKey(hkey);
419         RegDeleteKeyA(HKEY_LOCAL_MACHINE, AeDebug);
420     }
421     else
422     {
423         restore_value(hkey, &auto_value);
424         restore_value(hkey, &debugger_value);
425         RegCloseKey(hkey);
426     }
427 }
428
429 START_TEST(debugger)
430 {
431     HMODULE hdll;
432
433     hdll=GetModuleHandle("kernel32.dll");
434     pDebugActiveProcessStop=(void*)GetProcAddress(hdll, "DebugActiveProcessStop");
435     pDebugSetProcessKillOnExit=(void*)GetProcAddress(hdll, "DebugSetProcessKillOnExit");
436
437     myARGC=winetest_get_mainargs(&myARGV);
438     if (myARGC >= 3 && strcmp(myARGV[2], "crash") == 0)
439     {
440         doCrash(myARGC, myARGV);
441     }
442     else if (myARGC >= 3 && strncmp(myARGV[2], "dbg,", 4) == 0)
443     {
444         doDebugger(myARGC, myARGV);
445     }
446     else
447     {
448         test_ExitCode();
449     }
450 }