server: Allow the debugger to be debugged.
authorHenri Verbeet <hverbeet@codeweavers.com>
Fri, 5 Mar 2010 11:28:12 +0000 (12:28 +0100)
committerAlexandre Julliard <julliard@winehq.org>
Fri, 5 Mar 2010 12:21:22 +0000 (13:21 +0100)
dlls/kernel32/tests/debugger.c
server/debugger.c

index 9acea22821a0dabf1d44db09b4feab5916f57781..d37217a8bd30df6e21b391251eb931ac0879be81 100644 (file)
@@ -22,6 +22,7 @@
 #include <assert.h>
 
 #include <windows.h>
+#include <winternl.h>
 #include <winreg.h>
 #include "wine/test.h"
 
 #define STATUS_DEBUGGER_INACTIVE         ((NTSTATUS) 0xC0000354)
 #endif
 
+#ifdef __GNUC__
+#define PRINTF_ATTR(fmt,args) __attribute__((format (printf,fmt,args)))
+#else
+#define PRINTF_ATTR(fmt,args)
+#endif
+
+#define child_ok (winetest_set_location(__FILE__, __LINE__), 0) ? (void)0 : test_child_ok
+
 static int    myARGC;
 static char** myARGV;
 
@@ -36,6 +45,18 @@ static BOOL (WINAPI *pCheckRemoteDebuggerPresent)(HANDLE,PBOOL);
 static BOOL (WINAPI *pDebugActiveProcessStop)(DWORD);
 static BOOL (WINAPI *pDebugSetProcessKillOnExit)(BOOL);
 
+static LONG child_failures;
+
+static void PRINTF_ATTR(2, 3) test_child_ok(int condition, const char *msg, ...)
+{
+    va_list valist;
+
+    va_start(valist, msg);
+    winetest_vok(condition, msg, valist);
+    va_end(valist);
+    if (!condition) ++child_failures;
+}
+
 /* Copied from the process test */
 static void get_file_name(char* buf)
 {
@@ -466,6 +487,94 @@ static void test_RemoteDebugger(void)
        "expected error ERROR_INVALID_PARAMETER, got %d/%x\n",GetLastError(), GetLastError());
 }
 
+struct child_blackbox
+{
+    LONG failures;
+};
+
+static void doChild(int argc, char **argv)
+{
+    struct child_blackbox blackbox;
+    const char *blackbox_file;
+    HANDLE parent;
+    DWORD ppid;
+    BOOL ret;
+
+    blackbox_file = argv[4];
+    sscanf(argv[3], "%08x", &ppid);
+
+    parent = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, ppid);
+    child_ok(!!parent, "OpenProcess failed, last error %#x.\n", GetLastError());
+
+    ret = DebugActiveProcess(ppid);
+    child_ok(ret, "DebugActiveProcess failed, last error %#x.\n", GetLastError());
+
+    ret = pDebugActiveProcessStop(ppid);
+    child_ok(ret, "DebugActiveProcessStop failed, last error %#x.\n", GetLastError());
+
+    ret = CloseHandle(parent);
+    child_ok(ret, "CloseHandle failed, last error %#x.\n", GetLastError());
+
+    blackbox.failures = child_failures;
+    save_blackbox(blackbox_file, &blackbox, sizeof(blackbox));
+}
+
+static void test_debug_loop(int argc, char **argv)
+{
+    const char *arguments = " debugger child ";
+    struct child_blackbox blackbox;
+    char blackbox_file[MAX_PATH];
+    PROCESS_INFORMATION pi;
+    STARTUPINFOA si;
+    DWORD pid;
+    char *cmd;
+    BOOL ret;
+
+    if (!pDebugActiveProcessStop)
+    {
+        win_skip("DebugActiveProcessStop not available, skipping test.\n");
+        return;
+    }
+
+    pid = GetCurrentProcessId();
+    get_file_name(blackbox_file);
+    cmd = HeapAlloc(GetProcessHeap(), 0, strlen(argv[0]) + strlen(arguments) + strlen(blackbox_file) + 10);
+    sprintf(cmd, "%s%s%08x %s", argv[0], arguments, pid, blackbox_file);
+
+    memset(&si, 0, sizeof(si));
+    si.cb = sizeof(si);
+    ret = CreateProcessA(NULL, cmd, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &si, &pi);
+    ok(ret, "CreateProcess failed, last error %#x.\n", GetLastError());
+
+    HeapFree(GetProcessHeap(), 0, cmd);
+
+    for (;;)
+    {
+        DEBUG_EVENT ev;
+
+        ret = WaitForDebugEvent(&ev, INFINITE);
+        ok(ret, "WaitForDebugEvent failed, last error %#x.\n", GetLastError());
+        if (!ret) break;
+
+        if (ev.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT) break;
+
+        ret = ContinueDebugEvent(ev.dwProcessId, ev.dwThreadId, DBG_CONTINUE);
+        ok(ret, "ContinueDebugEvent failed, last error %#x.\n", GetLastError());
+        if (!ret) break;
+    }
+
+    ret = CloseHandle(pi.hThread);
+    ok(ret, "CloseHandle failed, last error %#x.\n", GetLastError());
+    ret = CloseHandle(pi.hProcess);
+    ok(ret, "CloseHandle failed, last error %#x.\n", GetLastError());
+
+    load_blackbox(blackbox_file, &blackbox, sizeof(blackbox));
+    ok(!blackbox.failures, "Got %d failures from child process.\n", blackbox.failures);
+
+    ret = DeleteFileA(blackbox_file);
+    ok(ret, "DeleteFileA failed, last error %#x.\n", GetLastError());
+}
+
 START_TEST(debugger)
 {
     HMODULE hdll;
@@ -484,9 +593,14 @@ START_TEST(debugger)
     {
         doDebugger(myARGC, myARGV);
     }
+    else if (myARGC >= 5 && !strcmp(myARGV[2], "child"))
+    {
+        doChild(myARGC, myARGV);
+    }
     else
     {
         test_ExitCode();
         test_RemoteDebugger();
+        test_debug_loop(myARGC, myARGV);
     }
 }
index 795a24a2f3743d4db00f45bd6708d5c118f9db7b..727f3be6182b1ee61ff16849047f4e1a7b4ede98 100644 (file)
@@ -420,16 +420,10 @@ void generate_debug_event( struct thread *thread, int code, const void *arg )
 /* attach a process to a debugger thread and suspend it */
 static int debugger_attach( struct process *process, struct thread *debugger )
 {
-    struct thread *thread;
-
     if (process->debugger) goto error;  /* already being debugged */
     if (!is_process_init_done( process )) goto error;  /* still starting up */
     if (list_empty( &process->thread_list )) goto error;  /* no thread running in the process */
 
-    /* make sure we don't create a debugging loop */
-    for (thread = debugger; thread; thread = thread->process->debugger)
-        if (thread->process == process) goto error;
-
     /* don't let a debugger debug its console... won't work */
     if (debugger->process->console && console_get_renderer(debugger->process->console)->process == process)
         goto error;