Add '--' option for passing extra arguments to the test.
[wine] / server / debugger.c
index bde3444..ba08bd2 100644 (file)
@@ -2,9 +2,27 @@
  * Server-side debugger functions
  *
  * Copyright (C) 1999 Alexandre Julliard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
+#include "config.h"
+#include "wine/port.h"
+
 #include <assert.h>
+#include <signal.h>
 #include <string.h>
 #include <stdio.h>
 
@@ -14,6 +32,7 @@
 #include "process.h"
 #include "thread.h"
 #include "request.h"
+#include "console.h"
 
 enum debug_event_state { EVENT_QUEUED, EVENT_SENT, EVENT_CONTINUED };
 
@@ -37,6 +56,7 @@ struct debug_ctx
     struct object        obj;         /* object header */
     struct debug_event  *event_head;  /* head of pending events queue */
     struct debug_event  *event_tail;  /* tail of pending events queue */
+    int                  kill_on_exit;/* kill debuggees on debugger exit ? */
 };
 
 
@@ -57,6 +77,7 @@ static const struct object_ops debug_event_ops =
     no_get_fd,                     /* get_fd */
     no_flush,                      /* flush */
     no_get_file_info,              /* get_file_info */
+    NULL,                          /* queue_async */
     debug_event_destroy            /* destroy */
 };
 
@@ -77,6 +98,7 @@ static const struct object_ops debug_ctx_ops =
     no_get_fd,                     /* get_fd */
     no_flush,                      /* flush */
     no_get_file_info,              /* get_file_info */
+    NULL,                          /* queue_async */
     debug_ctx_destroy              /* destroy */
 };
 
@@ -93,7 +115,7 @@ static int fill_create_thread_event( struct debug_event *event, void *arg )
 {
     struct process *debugger = event->debugger->process;
     struct thread *thread = event->sender;
-    handle_t handle;
+    obj_handle_t handle;
 
     /* documented: THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | THREAD_SUSPEND_RESUME */
     if (!(handle = alloc_handle( debugger, thread, THREAD_ALL_ACCESS, FALSE ))) return 0;
@@ -108,7 +130,7 @@ static int fill_create_process_event( struct debug_event *event, void *arg )
     struct process *debugger = event->debugger->process;
     struct thread *thread = event->sender;
     struct process *process = thread->process;
-    handle_t handle;
+    obj_handle_t handle;
 
     /* documented: PROCESS_VM_READ | PROCESS_VM_WRITE */
     if (!(handle = alloc_handle( debugger, process, PROCESS_ALL_ACCESS, FALSE ))) return 0;
@@ -160,7 +182,7 @@ static int fill_load_dll_event( struct debug_event *event, void *arg )
 {
     struct process *debugger = event->debugger->process;
     struct process_dll *dll = arg;
-    handle_t handle = 0;
+    obj_handle_t handle = 0;
 
     if (dll->file && !(handle = alloc_handle( debugger, dll->file, GENERIC_READ, FALSE )))
         return 0;
@@ -352,7 +374,7 @@ static int continue_debug_event( struct process *process, struct thread *thread,
 
 /* alloc a debug event for a debugger */
 static struct debug_event *alloc_debug_event( struct thread *thread, int code,
-                                              void *arg, CONTEXT *context )
+                                              void *arg, const CONTEXT *context )
 {
     struct thread *debugger = thread->process->debugger;
     struct debug_event *event;
@@ -405,23 +427,88 @@ static int debugger_attach( struct process *process, struct thread *debugger )
     struct thread *thread;
 
     if (process->debugger) goto error;  /* already being debugged */
-    if (process->init_event) goto error;  /* still starting up */
+    if (!is_process_init_done( process )) goto error;  /* still starting up */
+    if (!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 && debugger->process->console->renderer->process == process)
+        goto error;
+
     suspend_process( process );
 
     /* we must have been able to attach all threads */
+    if (!process->thread_list) goto error2;
     for (thread = process->thread_list; thread; thread = thread->proc_next)
-        if (!thread->attached)
+    {
+        if (!thread->attached) goto error2;
+    }
+
+    if (set_process_debugger( process, debugger )) return 1;
+    resume_process( process );
+    return 0;
+
+ error2:
+    resume_process( process );
+ error:
+    set_error( STATUS_ACCESS_DENIED );
+    return 0;
+}
+
+
+/* detach a process from a debugger thread (and resume it ?) */
+int debugger_detach( struct process *process, struct thread *debugger )
+{
+    struct thread *thread;
+    struct debug_event *event;
+    struct debug_ctx *debug_ctx;
+
+    if (!process->debugger || process->debugger != debugger)
+        goto error;  /* not currently debugged, or debugged by another debugger */
+    if (!debugger->debug_ctx ) goto error; /* should be a debugger */
+    /* init should be done, otherwise wouldn't be attached */
+    assert(is_process_init_done(process));
+
+    suspend_process( process );
+    /* send continue indication for all events */
+    debug_ctx = debugger->debug_ctx;
+
+    /* find the event in the queue
+     * FIXME: could loop on process' threads and look the debug_event field */
+    for (event = debug_ctx->event_head; event; event = event->next)
+    {
+        if (event->state != EVENT_QUEUED) continue;
+
+        if (event->sender->process == process)
         {
+            assert( event->sender->debug_event == event );
+            event->status = DBG_CONTINUE;
+            event->state  = EVENT_CONTINUED;
+            wake_up( &event->obj, 0 );
+            unlink_event( debug_ctx, event );
+            /* from queued debug event */
             resume_process( process );
-            goto error;
         }
+    }
 
-    if (set_process_debugger( process, debugger )) return 1;
+    /* remove relationships between process and its debugger */
+    process->debugger = NULL;
+    release_object( debugger->debug_ctx );
+    debugger->debug_ctx = NULL;
+
+    /* now detach all the threads */
+    for (thread = process->thread_list; thread; thread = thread->proc_next)
+    {
+        if (thread->attached)
+        {
+            detach_thread( thread, 0 );
+        }
+    }
+
+    /* from this function */
     resume_process( process );
     return 0;
 
@@ -463,6 +550,7 @@ int set_process_debugger( struct process *process, struct thread *debugger )
         if (!(debug_ctx = alloc_object( &debug_ctx_ops, -1 ))) return 0;
         debug_ctx->event_head = NULL;
         debug_ctx->event_tail = NULL;
+        debug_ctx->kill_on_exit = 1;
         debugger->debug_ctx = debug_ctx;
     }
     process->debugger = debugger;
@@ -474,8 +562,15 @@ void debug_exit_thread( struct thread *thread )
 {
     if (thread->debug_ctx)  /* this thread is a debugger */
     {
-        /* kill all debugged processes */
-        kill_debugged_processes( thread, thread->exit_code );
+        if (thread->debug_ctx->kill_on_exit)
+        {
+            /* kill all debugged processes */
+            kill_debugged_processes( thread, thread->exit_code );
+        }
+        else
+        {
+            detach_debugged_processes( thread );
+        }
         release_object( thread->debug_ctx );
         thread->debug_ctx = NULL;
     }
@@ -492,25 +587,23 @@ DECL_HANDLER(wait_debug_event)
         set_error( STATUS_INVALID_HANDLE );
         return;
     }
-    req->wait = 0;
+    reply->wait = 0;
     if ((event = find_event_to_send( debug_ctx )))
     {
-        size_t size = get_req_data_size(req);
+        size_t size = get_reply_max_size();
         event->state = EVENT_SENT;
         event->sender->debug_event = event;
-        req->pid = event->sender->process;
-        req->tid = event->sender;
+        reply->pid = get_process_id( event->sender->process );
+        reply->tid = get_thread_id( event->sender );
         if (size > sizeof(debug_event_t)) size = sizeof(debug_event_t);
-        memcpy( get_req_data(req), &event->data, size );
-        set_req_data_size( req, size );
+        set_reply_data( &event->data, size );
     }
     else  /* no event ready */
     {
-        req->pid  = 0;
-        req->tid  = 0;
-        set_req_data_size( req, 0 );
+        reply->pid  = 0;
+        reply->tid  = 0;
         if (req->get_handle)
-            req->wait = alloc_handle( current->process, debug_ctx, SYNCHRONIZE, FALSE );
+            reply->wait = alloc_handle( current->process, debug_ctx, SYNCHRONIZE, FALSE );
     }
 }
 
@@ -533,12 +626,17 @@ DECL_HANDLER(continue_debug_event)
 /* Start debugging an existing process */
 DECL_HANDLER(debug_process)
 {
-    struct debug_event_exception data;
     struct process *process = get_process_from_id( req->pid );
     if (!process) return;
 
-    if (debugger_attach( process, current ))
+    if (!req->attach)
+    {
+        debugger_detach( process, current );
+    }
+    else if (debugger_attach( process, current ))
     {
+        struct debug_event_exception data;
+
         generate_startup_debug_events( process, NULL );
         resume_process( process );
 
@@ -556,15 +654,15 @@ DECL_HANDLER(debug_process)
 /* queue an exception event */
 DECL_HANDLER(queue_exception_event)
 {
-    req->handle = 0;
+    reply->handle = 0;
     if (current->process->debugger)
     {
         struct debug_event_exception data;
         struct debug_event *event;
-        CONTEXT *context = get_req_data( req );
+        const CONTEXT *context = get_req_data();
         EXCEPTION_RECORD *rec = (EXCEPTION_RECORD *)(context + 1);
 
-        if (get_req_data_size( req ) < sizeof(*rec) + sizeof(*context))
+        if (get_req_data_size() < sizeof(*rec) + sizeof(*context))
         {
             set_error( STATUS_INVALID_PARAMETER );
             return;
@@ -573,7 +671,7 @@ DECL_HANDLER(queue_exception_event)
         data.first  = req->first;
         if ((event = alloc_debug_event( current, EXCEPTION_DEBUG_EVENT, &data, context )))
         {
-            if ((req->handle = alloc_handle( current->process, event, SYNCHRONIZE, FALSE )))
+            if ((reply->handle = alloc_handle( current->process, event, SYNCHRONIZE, FALSE )))
             {
                 link_event( event );
                 suspend_process( current->process );
@@ -587,26 +685,24 @@ DECL_HANDLER(queue_exception_event)
 DECL_HANDLER(get_exception_status)
 {
     struct debug_event *event;
-    size_t size = 0;
 
-    req->status = 0;
+    reply->status = 0;
     if ((event = (struct debug_event *)get_handle_obj( current->process, req->handle,
                                                        0, &debug_event_ops )))
     {
         if (event->state == EVENT_CONTINUED)
         {
-            req->status = event->status;
+            reply->status = event->status;
             if (current->context == &event->context)
             {
-                size = min( sizeof(CONTEXT), get_req_data_size(req) );
-                memcpy( get_req_data(req), &event->context, size );
+                size_t size = min( sizeof(CONTEXT), get_reply_max_size() );
+                set_reply_data( &event->context, size );
                 current->context = NULL;
             }
         }
         else set_error( STATUS_PENDING );
         release_object( event );
     }
-    set_req_data_size( req, size );
 }
 
 /* send an output string to the debugger */
@@ -619,3 +715,40 @@ DECL_HANDLER(output_debug_string)
     data.length  = req->length;
     generate_debug_event( current, OUTPUT_DEBUG_STRING_EVENT, &data );
 }
+
+/* simulate a breakpoint in a process */
+DECL_HANDLER(debug_break)
+{
+    struct process *process;
+
+    reply->self = 0;
+    if (!(process = get_process_from_handle( req->handle, PROCESS_SET_INFORMATION /*FIXME*/ )))
+        return;
+    if (process != current->process)
+    {
+        /* find a suitable thread to signal */
+        struct thread *thread;
+        for (thread = process->thread_list; thread; thread = thread->proc_next)
+        {
+            if (thread->unix_pid)
+            {
+                kill( thread->unix_pid, SIGTRAP );
+                break;
+            }
+        }
+        if (!thread) set_error( STATUS_ACCESS_DENIED );
+    }
+    else reply->self = 1;
+    release_object( process );
+}
+
+/* set debugger kill on exit flag */
+DECL_HANDLER(set_debugger_kill_on_exit)
+{
+    if (!current->debug_ctx)
+    {
+        set_error( STATUS_ACCESS_DENIED );
+        return;
+    }
+    current->debug_ctx->kill_on_exit = req->kill_on_exit;
+}