XP adds a few new debugging APIs. The most interesting is used to detach the debugger from its debuggee(s) without actually killing the debuggee. this patch implements this (as well as two other untested features) A+
Name: xp_dbg ChangeLog: new XP debugging APIs: \timplemented DebugActiveProcessStop, DebugSetProcessKillOnExit, DebugBreakProcess) GenDate: 2002/02/23 21:32:29 UTC ModifiedFiles: include/winbase.h dlls/kernel/kernel32.spec dlls/kernel/debugger.c server/protocol.def server/debugger.c server/process.c server/process.h AddedFiles: =================================================================== RCS file: /home/cvs/cvsroot/wine/wine/include/winbase.h,v retrieving revision 1.134 diff -u -u -r1.134 winbase.h --- include/winbase.h 31 Jan 2002 23:22:08 -0000 1.134 +++ include/winbase.h 1 Feb 2002 06:11:24 -0000 @@ -1135,7 +1135,10 @@ HANDLE WINAPI CreateWaitableTimerW(LPSECURITY_ATTRIBUTES,BOOL,LPCWSTR); #define CreateWaitableTimer WINELIB_NAME_AW(CreateWaitableTimer) BOOL WINAPI DebugActiveProcess(DWORD); +BOOL WINAPI DebugActiveProcessStop(DWORD); void WINAPI DebugBreak(void); +BOOL WINAPI DebugBreakProcess(HANDLE); +BOOL WINAPI DebugSetProcessKillOnExit(BOOL); BOOL WINAPI DeregisterEventSource(HANDLE); BOOL WINAPI DeviceIoControl(HANDLE,DWORD,LPVOID,DWORD,LPVOID,DWORD,LPDWORD,LPOVERLAPPED); BOOL WINAPI DisableThreadLibraryCalls(HMODULE); Index: dlls/kernel/kernel32.spec =================================================================== RCS file: /home/cvs/cvsroot/wine/wine/dlls/kernel/kernel32.spec,v retrieving revision 1.46 diff -u -u -r1.46 kernel32.spec --- dlls/kernel/kernel32.spec 29 Jan 2002 18:15:11 -0000 1.46 +++ dlls/kernel/kernel32.spec 29 Jan 2002 20:50:16 -0000 @@ -970,6 +970,11 @@ @ stdcall SetCalendarInfoA(long long long str) SetCalendarInfoA @ stdcall SetCalendarInfoW(long long long wstr) SetCalendarInfoW +# XP extensions +@ stdcall DebugActiveProcessStop(long) DebugActiveProcessStop +@ stdcall DebugBreakProcess(long) DebugBreakProcess +@ stdcall DebugSetProcessKillOnExit(long) DebugSetProcessKillOnExit + ################################################################ # Wine extensions: Win16 functions that are needed by other dlls # Index: dlls/kernel/debugger.c =================================================================== RCS file: /home/cvs/cvsroot/wine/wine/dlls/kernel/debugger.c,v retrieving revision 1.17 diff -u -u -r1.17 debugger.c --- dlls/kernel/debugger.c 30 Nov 2001 18:46:43 -0000 1.17 +++ dlls/kernel/debugger.c 21 Feb 2002 21:40:15 -0000 @@ -6,6 +6,7 @@ #include <stdio.h> #include <string.h> +#include <signal.h> #include "winerror.h" #include "wine/winbase16.h" @@ -150,7 +151,7 @@ /********************************************************************** * DebugActiveProcess (KERNEL32.@) * - * Attempts to attach the dugger to a process. + * Attempts to attach the debugger to a process. * * RETURNS * @@ -163,6 +164,30 @@ SERVER_START_REQ( debug_process ) { req->pid = (void *)pid; + req->attach = 1; + ret = !wine_server_call_err( req ); + } + SERVER_END_REQ; + return ret; +} + +/********************************************************************** + * DebugActiveProcessStop (KERNEL32.@) + * + * Attempts to detach the debugger from a process. + * + * RETURNS + * + * True if the debugger was detached from the process. + */ +BOOL WINAPI DebugActiveProcessStop( + DWORD pid) /* [in] The process to be detached. */ +{ + BOOL ret; + SERVER_START_REQ( debug_process ) + { + req->pid = (void *)pid; + req->attach = 0; ret = !wine_server_call_err( req ); } SERVER_END_REQ; @@ -236,6 +261,29 @@ DbgBreakPoint(); } +/*********************************************************************** + * DebugBreakProcess (KERNEL32.@) + * + * Raises an exception so that a debugger (if attached) + * can take some action. Same as DebugBreak, but applies to any process. + */ +BOOL WINAPI DebugBreakProcess(HANDLE hProc) +{ + int res; + int pid; + + TRACE("(%08lx)\n", (DWORD)hProc); + + SERVER_START_REQ( get_process_info ) + { + req->handle = hProc; + res = wine_server_call_err( req ); + pid = (int)reply->pid; + } + SERVER_END_REQ; + return !res && kill(pid, SIGINT) == 0; +} + /*********************************************************************** * DebugBreak (KERNEL.203) @@ -309,3 +357,23 @@ /* Output */ FIXME("%s %04x %s\n", caller, flags, debugstr_a(MapSL(spec)) ); } + +/*********************************************************************** + * DebugSetProcessKillOnExit (KERNEL.328) + * + * Let a debugger decide wether a debuggee will be killed upon debugger + * termination + */ +BOOL WINAPI DebugSetProcessKillOnExit(BOOL kill) +{ + BOOL ret = FALSE; + + SERVER_START_REQ( set_debugger_kill_on_exit ) + { + req->kill_on_exit = kill; + ret = !wine_server_call_err( req ); + } + SERVER_END_REQ; + return ret; +} + Index: server/protocol.def =================================================================== RCS file: /home/cvs/cvsroot/wine/wine/server/protocol.def,v retrieving revision 1.24 diff -u -u -r1.24 protocol.def --- server/protocol.def 9 Jan 2002 21:16:24 -0000 1.24 +++ server/protocol.def 21 Feb 2002 20:54:46 -0000 @@ -1117,11 +1119,17 @@ @END -/* Start debugging an existing process */ +/* Start/Stop debugging an existing process */ @REQ(debug_process) void* pid; /* id of the process to debug */ + int attach; /* 1=attaching / 0=detaching from the process */ @END + +/* Set debugging features */ +@REQ(set_debugger_kill_on_exit) + unsigned int kill_on_exit; /* 0=detach/1=kill debuggee when debugger dies */ +@END /* Read data from a process address space */ @REQ(read_process_memory) Index: server/debugger.c =================================================================== RCS file: /home/cvs/cvsroot/wine/wine/server/debugger.c,v retrieving revision 1.35 diff -u -u -r1.35 debugger.c --- server/debugger.c 20 Dec 2001 00:07:08 -0000 1.35 +++ server/debugger.c 21 Feb 2002 21:44:00 -0000 @@ -38,6 +38,7 @@ 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 ? */ }; @@ -422,11 +423,13 @@ /* we must have been able to attach all threads */ for (thread = process->thread_list; thread; thread = thread->proc_next) + { if (!thread->attached) { resume_process( process ); goto error; } + } if (set_process_debugger( process, debugger )) return 1; resume_process( process ); @@ -437,6 +440,66 @@ 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(!process->init_event); + + 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 ); + } + } + + /* 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; + + error: + set_error( STATUS_ACCESS_DENIED ); + return 0; +} + /* generate all startup events of a given process */ void generate_startup_debug_events( struct process *process, void *entry ) { @@ -470,6 +533,7 @@ 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; @@ -481,8 +545,15 @@ { 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; } @@ -538,12 +609,17 @@ /* 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 ); @@ -621,4 +697,14 @@ data.unicode = req->unicode; data.length = req->length; generate_debug_event( current, OUTPUT_DEBUG_STRING_EVENT, &data ); +} + +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; } Index: server/process.c =================================================================== RCS file: /home/cvs/cvsroot/wine/wine/server/process.c,v retrieving revision 1.76 diff -u -u -r1.76 process.c --- server/process.c 20 Dec 2001 00:07:09 -0000 1.76 +++ server/process.c 23 Feb 2002 21:24:53 -0000 18 a debugger from all its debuggees */ +void detach_debugged_processes( struct thread *debugger ) +{ + struct process *process; + for (process = first_process; process; process = process->next) + { + if (process->debugger == debugger && process->running_threads) + { + debugger_detach( process, debugger ); + } + } +} + + /* get all information about a process */ static void get_process_info( struct process *process, struct get_process_info_reply *reply ) { Index: server/process.h =================================================================== RCS file: /home/cvs/cvsroot/wine/wine/server/process.h,v retrieving revision 1.24 diff -u -u -r1.24 process.h --- server/process.h 4 Dec 2001 20:17:44 -0000 1.24 +++ server/process.h 21 Feb 2002 20:46:16 -0000 @@ -71,6 +73,8 @@ extern struct process *get_process_from_id( void *id ); extern struct process *get_process_from_handle( handle_t handle, unsigned int access ); extern int process_set_debugger( struct process *process, struct thread *thread ); +extern int debugger_detach( struct process* process, struct thread* debugger ); + extern void add_process_thread( struct process *process, struct thread *thread ); extern void remove_process_thread( struct process *process, @@ -80,6 +84,7 @@ extern void kill_process( struct process *process, struct thread *skip, int exit_code ); extern void kill_console_processes( struct thread *renderer, int exit_code ); extern void kill_debugged_processes( struct thread *debugger, int exit_code ); +extern void detach_debugged_processes( struct thread *debugger ); extern struct process_snapshot *process_snap( int *count ); extern struct module_snapshot *module_snap( struct process *process, int *count );