From fbccb38e187abf139cacb304f6363fb7cba2e85b Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Wed, 27 Feb 2002 01:28:30 +0000 Subject: [PATCH] New XP debugging APIs: implemented DebugActiveProcessStop, DebugSetProcessKillOnExit, DebugBreakProcess. --- dlls/kernel/debugger.c | 72 +++++++++++++++++++++++++- dlls/kernel/kernel32.spec | 5 ++ include/winbase.h | 3 ++ include/wine/server_protocol.h | 18 ++++++- server/debugger.c | 94 ++++++++++++++++++++++++++++++++-- server/process.c | 16 +++++- server/process.h | 3 ++ server/protocol.def | 9 +++- server/request.h | 2 + server/trace.c | 11 +++- 10 files changed, 224 insertions(+), 9 deletions(-) diff --git a/dlls/kernel/debugger.c b/dlls/kernel/debugger.c index fbe8ff6aba6..1b6a90c3790 100644 --- a/dlls/kernel/debugger.c +++ b/dlls/kernel/debugger.c @@ -150,7 +150,7 @@ BOOL WINAPI ContinueDebugEvent( /********************************************************************** * DebugActiveProcess (KERNEL32.@) * - * Attempts to attach the dugger to a process. + * Attempts to attach the debugger to a process. * * RETURNS * @@ -163,6 +163,30 @@ BOOL WINAPI DebugActiveProcess( 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 +260,32 @@ void WINAPI DebugBreak(void) 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) +{ +#if 0 /* FIXME: not correct */ + 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; +#endif + return FALSE; +} + /*********************************************************************** * DebugBreak (KERNEL.203) @@ -309,3 +359,23 @@ void WINAPIV _DebugOutput( void ) /* 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; +} + diff --git a/dlls/kernel/kernel32.spec b/dlls/kernel/kernel32.spec index 301737ee973..922f8fa0db0 100644 --- a/dlls/kernel/kernel32.spec +++ b/dlls/kernel/kernel32.spec @@ -970,6 +970,11 @@ debug_channels (comm console debugstr dll int resource stress thunk toolhelp @ 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 # diff --git a/include/winbase.h b/include/winbase.h index dd083002dfd..12506d3a7a8 100644 --- a/include/winbase.h +++ b/include/winbase.h @@ -1135,7 +1135,10 @@ HANDLE WINAPI CreateWaitableTimerA(LPSECURITY_ATTRIBUTES,BOOL,LPCSTR); 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); diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index af9fa98bfc4..d2b9ccb84de 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -1554,6 +1554,7 @@ struct debug_process_request { struct request_header __header; void* pid; + int attach; }; struct debug_process_reply { @@ -1562,6 +1563,18 @@ struct debug_process_reply +struct set_debugger_kill_on_exit_request +{ + struct request_header __header; + int kill_on_exit; +}; +struct set_debugger_kill_on_exit_reply +{ + struct reply_header __header; +}; + + + struct read_process_memory_request { struct request_header __header; @@ -2668,6 +2681,7 @@ enum request REQ_output_debug_string, REQ_continue_debug_event, REQ_debug_process, + REQ_set_debugger_kill_on_exit, REQ_read_process_memory, REQ_write_process_memory, REQ_create_key, @@ -2823,6 +2837,7 @@ union generic_request struct output_debug_string_request output_debug_string_request; struct continue_debug_event_request continue_debug_event_request; struct debug_process_request debug_process_request; + struct set_debugger_kill_on_exit_request set_debugger_kill_on_exit_request; struct read_process_memory_request read_process_memory_request; struct write_process_memory_request write_process_memory_request; struct create_key_request create_key_request; @@ -2976,6 +2991,7 @@ union generic_reply struct output_debug_string_reply output_debug_string_reply; struct continue_debug_event_reply continue_debug_event_reply; struct debug_process_reply debug_process_reply; + struct set_debugger_kill_on_exit_reply set_debugger_kill_on_exit_reply; struct read_process_memory_reply read_process_memory_reply; struct write_process_memory_reply write_process_memory_reply; struct create_key_reply create_key_reply; @@ -3043,6 +3059,6 @@ union generic_reply struct get_window_properties_reply get_window_properties_reply; }; -#define SERVER_PROTOCOL_VERSION 70 +#define SERVER_PROTOCOL_VERSION 71 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */ diff --git a/server/debugger.c b/server/debugger.c index e487eb0480c..42d73622a11 100644 --- a/server/debugger.c +++ b/server/debugger.c @@ -38,6 +38,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 ? */ }; @@ -422,11 +423,13 @@ static int debugger_attach( struct process *process, struct thread *debugger ) /* 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,65 @@ static int debugger_attach( struct process *process, struct thread *debugger ) 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 +532,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; @@ -481,8 +544,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; } @@ -538,12 +608,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 ); @@ -622,3 +697,14 @@ DECL_HANDLER(output_debug_string) data.length = req->length; generate_debug_event( current, OUTPUT_DEBUG_STRING_EVENT, &data ); } + +/* 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; +} diff --git a/server/process.c b/server/process.c index 9b711080b49..43b5555e7ce 100644 --- a/server/process.c +++ b/server/process.c @@ -573,10 +573,24 @@ void kill_debugged_processes( struct thread *debugger, int exit_code ) } +/* detach 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 ) { - reply->pid = process; + reply->pid = get_process_id( process ); reply->debugged = (process->debugger != 0); reply->exit_code = process->exit_code; reply->priority = process->priority; diff --git a/server/process.h b/server/process.h index cc2dc981591..93b4354fc0a 100644 --- a/server/process.h +++ b/server/process.h @@ -71,6 +71,8 @@ extern struct thread *create_process( int fd ); 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 +82,7 @@ extern void resume_process( struct process *process ); 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 ); diff --git a/server/protocol.def b/server/protocol.def index 7036de158ec..f5dd291b9af 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1117,9 +1117,16 @@ enum char_info_mode @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 debugger kill on exit flag */ +@REQ(set_debugger_kill_on_exit) + int kill_on_exit; /* 0=detach/1=kill debuggee when debugger dies */ @END diff --git a/server/request.h b/server/request.h index ee18ecbb7f8..44f2cafb20a 100644 --- a/server/request.h +++ b/server/request.h @@ -170,6 +170,7 @@ DECL_HANDLER(get_exception_status); DECL_HANDLER(output_debug_string); DECL_HANDLER(continue_debug_event); DECL_HANDLER(debug_process); +DECL_HANDLER(set_debugger_kill_on_exit); DECL_HANDLER(read_process_memory); DECL_HANDLER(write_process_memory); DECL_HANDLER(create_key); @@ -324,6 +325,7 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] = (req_handler)req_output_debug_string, (req_handler)req_continue_debug_event, (req_handler)req_debug_process, + (req_handler)req_set_debugger_kill_on_exit, (req_handler)req_read_process_memory, (req_handler)req_write_process_memory, (req_handler)req_create_key, diff --git a/server/trace.c b/server/trace.c index c8bb3c50e31..d7859db553a 100644 --- a/server/trace.c +++ b/server/trace.c @@ -1284,7 +1284,13 @@ static void dump_continue_debug_event_request( const struct continue_debug_event static void dump_debug_process_request( const struct debug_process_request *req ) { - fprintf( stderr, " pid=%p", req->pid ); + fprintf( stderr, " pid=%p,", req->pid ); + fprintf( stderr, " attach=%d", req->attach ); +} + +static void dump_set_debugger_kill_on_exit_request( const struct set_debugger_kill_on_exit_request *req ) +{ + fprintf( stderr, " kill_on_exit=%d", req->kill_on_exit ); } static void dump_read_process_memory_request( const struct read_process_memory_request *req ) @@ -2115,6 +2121,7 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_output_debug_string_request, (dump_func)dump_continue_debug_event_request, (dump_func)dump_debug_process_request, + (dump_func)dump_set_debugger_kill_on_exit_request, (dump_func)dump_read_process_memory_request, (dump_func)dump_write_process_memory_request, (dump_func)dump_create_key_request, @@ -2266,6 +2273,7 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { (dump_func)0, (dump_func)0, (dump_func)0, + (dump_func)0, (dump_func)dump_read_process_memory_reply, (dump_func)0, (dump_func)dump_create_key_reply, @@ -2417,6 +2425,7 @@ static const char * const req_names[REQ_NB_REQUESTS] = { "output_debug_string", "continue_debug_event", "debug_process", + "set_debugger_kill_on_exit", "read_process_memory", "write_process_memory", "create_key",