From d04ccb8ebe12c52d9c4fb560aadd48900b9e9dfb Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Tue, 4 Mar 2003 22:18:43 +0000 Subject: [PATCH] Use SIGUSR1 instead of SIGSTOP to suspend threads. --- dlls/ntdll/signal_i386.c | 46 +++++++++++++++++++ dlls/ntdll/signal_powerpc.c | 15 +++++++ dlls/ntdll/signal_sparc.c | 15 +++++++ include/wine/server_protocol.h | 2 +- scheduler/client.c | 2 + scheduler/process.c | 13 +++--- server/context_i386.c | 6 +-- server/context_powerpc.c | 6 +-- server/context_sparc.c | 6 +-- server/process.c | 10 ++--- server/ptrace.c | 81 ++++++++++++++-------------------- server/thread.c | 24 +++++----- server/thread.h | 7 +-- server/trace.c | 11 +++++ 14 files changed, 158 insertions(+), 86 deletions(-) diff --git a/dlls/ntdll/signal_i386.c b/dlls/ntdll/signal_i386.c index 668e5e242a2..fd80bd2f1fb 100644 --- a/dlls/ntdll/signal_i386.c +++ b/dlls/ntdll/signal_i386.c @@ -654,6 +654,36 @@ static void restore_context( const CONTEXT *context, SIGCONTEXT *sigcontext ) } +/*********************************************************************** + * init_handler + * + * Handler initialization when the full context is not needed. + */ +static void init_handler( const SIGCONTEXT *sigcontext ) +{ + /* restore a proper %fs for the fault handler */ + if (!IS_SELECTOR_SYSTEM(CS_sig(sigcontext)) || + !IS_SELECTOR_SYSTEM(SS_sig(sigcontext))) /* 16-bit mode */ + { + wine_set_fs( SYSLEVEL_Win16CurrentTeb ); + } +#ifdef __HAVE_VM86 + else if ((void *)EIP_sig(sigcontext) == vm86_return) /* vm86 mode */ + { + /* fetch the saved %fs on the stack */ + wine_set_fs( *(unsigned int *)ESP_sig(sigcontext) ); + } +#endif /* __HAVE_VM86 */ +#ifdef FS_sig + else /* 32-bit mode, get %fs at time of the fault */ + { + wine_set_fs( FS_sig(sigcontext) ); + } +#endif /* FS_sig */ + wine_set_gs( NtCurrentTeb()->gs_sel ); +} + + /*********************************************************************** * save_fpu * @@ -1037,6 +1067,7 @@ static HANDLER_DEF(fpe_handler) */ static HANDLER_DEF(int_handler) { + init_handler( HANDLER_CONTEXT ); if (!dispatch_signal(SIGINT)) { EXCEPTION_RECORD rec; @@ -1074,6 +1105,19 @@ static HANDLER_DEF(abrt_handler) } +/********************************************************************** + * usr1_handler + * + * Handler for SIGUSR1, used to signal a thread that it got suspended. + */ +static HANDLER_DEF(usr1_handler) +{ + init_handler( HANDLER_CONTEXT ); + /* wait with 0 timeout, will only return once the thread is no longer suspended */ + WaitForMultipleObjectsEx( 0, NULL, FALSE, 0, FALSE ); +} + + /*********************************************************************** * set_handler * @@ -1160,6 +1204,7 @@ BOOL SIGNAL_Init(void) if (set_handler( SIGSEGV, have_sigaltstack, (void (*)())segv_handler ) == -1) goto error; if (set_handler( SIGILL, have_sigaltstack, (void (*)())segv_handler ) == -1) goto error; if (set_handler( SIGABRT, have_sigaltstack, (void (*)())abrt_handler ) == -1) goto error; + if (set_handler( SIGUSR1, have_sigaltstack, (void (*)())usr1_handler ) == -1) goto error; #ifdef SIGBUS if (set_handler( SIGBUS, have_sigaltstack, (void (*)())segv_handler ) == -1) goto error; #endif @@ -1192,6 +1237,7 @@ void SIGNAL_Reset(void) sigaddset( &block_set, SIGALRM ); sigaddset( &block_set, SIGIO ); sigaddset( &block_set, SIGHUP ); + sigaddset( &block_set, SIGUSR1 ); sigaddset( &block_set, SIGUSR2 ); sigprocmask( SIG_BLOCK, &block_set, NULL ); diff --git a/dlls/ntdll/signal_powerpc.c b/dlls/ntdll/signal_powerpc.c index 6259d12c9ec..b3d279ae6a7 100644 --- a/dlls/ntdll/signal_powerpc.c +++ b/dlls/ntdll/signal_powerpc.c @@ -381,6 +381,7 @@ static HANDLER_DEF(int_handler) } } + /********************************************************************** * abrt_handler * @@ -402,6 +403,18 @@ static HANDLER_DEF(abrt_handler) } +/********************************************************************** + * usr1_handler + * + * Handler for SIGUSR1, used to signal a thread that it got suspended. + */ +static HANDLER_DEF(usr1_handler) +{ + /* wait with 0 timeout, will only return once the thread is no longer suspended */ + WaitForMultipleObjectsEx( 0, NULL, FALSE, 0, FALSE ); +} + + /*********************************************************************** * set_handler * @@ -461,6 +474,7 @@ BOOL SIGNAL_Init(void) if (set_handler( SIGSEGV, have_sigaltstack, (void (*)())segv_handler ) == -1) goto error; if (set_handler( SIGILL, have_sigaltstack, (void (*)())segv_handler ) == -1) goto error; if (set_handler( SIGABRT, have_sigaltstack, (void (*)())abrt_handler ) == -1) goto error; + if (set_handler( SIGUSR1, have_sigaltstack, (void (*)())usr1_handler ) == -1) goto error; #ifdef SIGBUS if (set_handler( SIGBUS, have_sigaltstack, (void (*)())segv_handler ) == -1) goto error; #endif @@ -488,6 +502,7 @@ void SIGNAL_Reset(void) sigaddset( &block_set, SIGALRM ); sigaddset( &block_set, SIGIO ); sigaddset( &block_set, SIGHUP ); + sigaddset( &block_set, SIGUSR1 ); sigaddset( &block_set, SIGUSR2 ); sigprocmask( SIG_BLOCK, &block_set, NULL ); diff --git a/dlls/ntdll/signal_sparc.c b/dlls/ntdll/signal_sparc.c index 7ac34c8ba36..1ba4038681c 100644 --- a/dlls/ntdll/signal_sparc.c +++ b/dlls/ntdll/signal_sparc.c @@ -367,6 +367,19 @@ static HANDLER_DEF(abrt_handler) restore_context( &context, HANDLER_CONTEXT ); } + +/********************************************************************** + * usr1_handler + * + * Handler for SIGUSR1, used to signal a thread that it got suspended. + */ +static HANDLER_DEF(usr1_handler) +{ + /* wait with 0 timeout, will only return once the thread is no longer suspended */ + WaitForMultipleObjectsEx( 0, NULL, FALSE, 0, FALSE ); +} + + /*********************************************************************** * set_handler * @@ -422,6 +435,7 @@ BOOL SIGNAL_Init(void) if (set_handler( SIGBUS, (void (*)())bus_handler ) == -1) goto error; if (set_handler( SIGTRAP, (void (*)())trap_handler ) == -1) goto error; if (set_handler( SIGABRT, (void (*)())abrt_handler ) == -1) goto error; + if (set_handler( SIGUSR1, (void (*)())usr1_handler ) == -1) goto error; return TRUE; error: @@ -442,6 +456,7 @@ void SIGNAL_Reset(void) sigaddset( &block_set, SIGALRM ); sigaddset( &block_set, SIGIO ); sigaddset( &block_set, SIGHUP ); + sigaddset( &block_set, SIGUSR1 ); sigaddset( &block_set, SIGUSR2 ); sigprocmask( SIG_BLOCK, &block_set, NULL ); diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index c5b5e4c7be9..5555f062f0b 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -3551,6 +3551,6 @@ union generic_reply struct get_next_hook_reply get_next_hook_reply; }; -#define SERVER_PROTOCOL_VERSION 98 +#define SERVER_PROTOCOL_VERSION 99 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */ diff --git a/scheduler/client.c b/scheduler/client.c index ef6db594a77..466fef74100 100644 --- a/scheduler/client.c +++ b/scheduler/client.c @@ -653,6 +653,8 @@ void CLIENT_InitServer(void) sigaddset( &block_set, SIGIO ); sigaddset( &block_set, SIGINT ); sigaddset( &block_set, SIGHUP ); + sigaddset( &block_set, SIGUSR1 ); + sigaddset( &block_set, SIGUSR2 ); /* receive the first thread request fd on the main socket */ NtCurrentTeb()->request_fd = receive_fd( &dummy_handle ); diff --git a/scheduler/process.c b/scheduler/process.c index 2d1eb95e523..ea9a3056bf4 100644 --- a/scheduler/process.c +++ b/scheduler/process.c @@ -508,6 +508,14 @@ static void start_process(void) console_app = (nt->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI); if (console_app) current_process.flags |= PDB32_CONSOLE_PROC; + /* Install signal handlers; this cannot be done before, since we cannot + * send exceptions to the debugger before the create process event that + * is sent by REQ_INIT_PROCESS_DONE. + * We do need the handlers in place by the time the request is over, so + * we set them up here. If we segfault between here and the server call + * something is very wrong... */ + if (!SIGNAL_Init()) goto error; + /* Signal the parent process to continue */ SERVER_START_REQ( init_process_done ) { @@ -524,11 +532,6 @@ static void start_process(void) } SERVER_END_REQ; - /* Install signal handlers; this cannot be done before, since we cannot - * send exceptions to the debugger before the create process event that - * is sent by REQ_INIT_PROCESS_DONE */ - if (!SIGNAL_Init()) goto error; - /* create the main modref and load dependencies */ if (!(wm = PE_CreateModule( current_process.module, main_exe_name, 0, 0, FALSE ))) goto error; diff --git a/server/context_i386.c b/server/context_i386.c index b7a7bb03751..75028cf1376 100644 --- a/server/context_i386.c +++ b/server/context_i386.c @@ -534,7 +534,7 @@ void *get_thread_ip( struct thread *thread ) if (suspend_for_ptrace( thread )) { get_thread_context( thread, CONTEXT_CONTROL, &context ); - resume_thread( thread ); + resume_after_ptrace( thread ); } return (void *)context.Eip; } @@ -576,7 +576,7 @@ DECL_HANDLER(get_thread_context) if (flags && suspend_for_ptrace( thread )) { get_thread_context( thread, flags, data ); - resume_thread( thread ); + resume_after_ptrace( thread ); } } release_object( thread ); @@ -604,7 +604,7 @@ DECL_HANDLER(set_thread_context) if (flags && suspend_for_ptrace( thread )) { set_thread_context( thread, flags, get_req_data() ); - resume_thread( thread ); + resume_after_ptrace( thread ); } release_object( thread ); } diff --git a/server/context_powerpc.c b/server/context_powerpc.c index b1dde4ff792..b6a2598881d 100644 --- a/server/context_powerpc.c +++ b/server/context_powerpc.c @@ -252,7 +252,7 @@ void *get_thread_ip( struct thread *thread ) if (suspend_for_ptrace( thread )) { get_thread_context( thread, CONTEXT_CONTROL, &context ); - resume_thread( thread ); + resume_after_ptrace( thread ); } return (void *)context.Iar; } @@ -293,7 +293,7 @@ DECL_HANDLER(get_thread_context) if (flags && suspend_for_ptrace( thread )) { get_thread_context( thread, flags, data ); - resume_thread( thread ); + resume_after_ptrace( thread ); } } release_object( thread ); @@ -321,7 +321,7 @@ DECL_HANDLER(set_thread_context) if (flags && suspend_for_ptrace( thread )) { set_thread_context( thread, flags, get_req_data() ); - resume_thread( thread ); + resume_after_ptrace( thread ); } release_object( thread ); } diff --git a/server/context_sparc.c b/server/context_sparc.c index bc1b3afbf23..20b4a26f45f 100644 --- a/server/context_sparc.c +++ b/server/context_sparc.c @@ -164,7 +164,7 @@ void *get_thread_ip( struct thread *thread ) if (suspend_for_ptrace( thread )) { get_thread_context( thread, CONTEXT_CONTROL, &context ); - resume_thread( thread ); + resume_after_ptrace( thread ); } return (void *)context.pc; } @@ -199,7 +199,7 @@ DECL_HANDLER(get_thread_context) if (flags && suspend_for_ptrace( thread )) { get_thread_context( thread, flags, data ); - resume_thread( thread ); + resume_after_ptrace( thread ); } } release_object( thread ); @@ -227,7 +227,7 @@ DECL_HANDLER(set_thread_context) if (flags && suspend_for_ptrace( thread )) { set_thread_context( thread, flags, get_req_data() ); - resume_thread( thread ); + resume_after_ptrace( thread ); } release_object( thread ); } diff --git a/server/process.c b/server/process.c index c3cf4366d7f..96482ebc882 100644 --- a/server/process.c +++ b/server/process.c @@ -673,11 +673,7 @@ void resume_process( struct process *process ) while (thread) { struct thread *next = thread->proc_next; - if (!thread->suspend) - { - continue_thread( thread ); - wake_thread( thread ); - } + if (!thread->suspend) wake_thread( thread ); thread = next; } } @@ -781,7 +777,7 @@ static int read_process_memory( struct process *process, const int *addr, size_t if (read_thread_int( thread, addr++, dest++ ) == -1) break; len--; } - resume_thread( thread ); + resume_after_ptrace( thread ); } return !len; } @@ -842,7 +838,7 @@ static void write_process_memory( struct process *process, int *addr, size_t len if (write_thread_int( thread, addr, *src, last_mask ) == -1) goto done; done: - resume_thread( thread ); + resume_after_ptrace( thread ); } } diff --git a/server/ptrace.c b/server/ptrace.c index b83ab7d4e27..33b71581d17 100644 --- a/server/ptrace.c +++ b/server/ptrace.c @@ -69,24 +69,20 @@ inline static int ptrace(int req, ...) { errno = EPERM; return -1; /*FAIL*/ } static const int use_ptrace = 1; /* set to 0 to disable ptrace */ /* handle a status returned by wait4 */ -static int handle_child_status( struct thread *thread, int pid, int status ) +static int handle_child_status( struct thread *thread, int pid, int status, int want_sig ) { if (WIFSTOPPED(status)) { int sig = WSTOPSIG(status); if (debug_level && thread) fprintf( stderr, "%04x: *signal* signal=%d\n", thread->id, sig ); - switch(sig) + if (sig != want_sig) { - case SIGSTOP: /* continue at once if not suspended */ - if (thread && (thread->process->suspend + thread->suspend)) break; - /* fall through */ - default: /* ignore other signals for now */ + /* ignore other signals for now */ if (thread && get_thread_single_step( thread )) ptrace( PTRACE_SINGLESTEP, pid, (caddr_t)1, sig ); else ptrace( PTRACE_CONT, pid, (caddr_t)1, sig ); - break; } return sig; } @@ -115,13 +111,13 @@ void sigchld_handler() for (;;) { if (!(pid = wait4( -1, &status, WUNTRACED | WNOHANG, NULL ))) break; - if (pid != -1) handle_child_status( get_thread_from_pid(pid), pid, status ); + if (pid != -1) handle_child_status( get_thread_from_pid(pid), pid, status, -1 ); else break; } } /* wait for a ptraced child to get a certain signal */ -void wait4_thread( struct thread *thread, int signal ) +static void wait4_thread( struct thread *thread, int signal ) { int res, status; @@ -129,10 +125,15 @@ void wait4_thread( struct thread *thread, int signal ) { if ((res = wait4( thread->unix_pid, &status, WUNTRACED, NULL )) == -1) { - perror( "wait4" ); + if (errno == ECHILD) /* must have died */ + { + thread->unix_pid = -1; + thread->attached = 0; + } + else perror( "wait4" ); return; } - res = handle_child_status( thread, res, status ); + res = handle_child_status( thread, res, status, signal ); } while (res && res != signal); } @@ -176,66 +177,48 @@ void detach_thread( struct thread *thread, int sig ) if (thread->attached) { /* make sure it is stopped */ - suspend_thread( thread, 0 ); + suspend_for_ptrace( thread ); if (sig) send_thread_signal( thread, sig ); if (thread->unix_pid == -1) return; if (debug_level) fprintf( stderr, "%04x: *detached*\n", thread->id ); ptrace( PTRACE_DETACH, thread->unix_pid, (caddr_t)1, sig ); - thread->suspend = 0; /* detach makes it continue */ thread->attached = 0; } - else - { - if (sig) send_thread_signal( thread, sig ); - if (thread->suspend + thread->process->suspend) continue_thread( thread ); - } -} - -/* stop a thread (at the Unix level) */ -void stop_thread( struct thread *thread ) -{ - /* can't stop a thread while initialisation is in progress */ - if (thread->unix_pid == -1 || !is_process_init_done(thread->process)) return; - /* first try to attach to it */ - if (!thread->attached) - if (attach_thread( thread )) return; /* this will have stopped it */ - /* attached already, or attach failed -> send a signal */ - if (thread->unix_pid == -1) return; - send_thread_signal( thread, SIGSTOP ); - if (thread->attached) wait4_thread( thread, SIGSTOP ); -} - -/* make a thread continue (at the Unix level) */ -void continue_thread( struct thread *thread ) -{ - if (thread->unix_pid == -1) return; - if (!thread->attached) send_thread_signal( thread, SIGCONT ); - else ptrace( get_thread_single_step(thread) ? PTRACE_SINGLESTEP : PTRACE_CONT, - thread->unix_pid, (caddr_t)1, SIGSTOP ); + else if (sig) send_thread_signal( thread, sig ); } /* suspend a thread to allow using ptrace on it */ -/* you must do a resume_thread when finished with the thread */ +/* you must do a resume_after_ptrace when finished with the thread */ int suspend_for_ptrace( struct thread *thread ) { - if (thread->attached) - { - suspend_thread( thread, 0 ); - return 1; - } /* can't stop a thread while initialisation is in progress */ if (thread->unix_pid == -1 || !is_process_init_done(thread->process)) goto error; - thread->suspend++; + + if (thread->attached) + { + send_thread_signal( thread, SIGSTOP ); + wait4_thread( thread, SIGSTOP ); + return 1; + } if (attach_thread( thread )) return 1; - thread->suspend--; error: set_error( STATUS_ACCESS_DENIED ); return 0; } +/* resume a thread after we have used pthread on it */ +void resume_after_ptrace( struct thread *thread ) +{ + if (thread->unix_pid == -1) return; + assert( thread->attached ); + ptrace( get_thread_single_step(thread) ? PTRACE_SINGLESTEP : PTRACE_CONT, + thread->unix_pid, (caddr_t)1, 0 /* cancel the SIGSTOP */ ); +} + /* read an int from a thread address space */ int read_thread_int( struct thread *thread, const int *addr, int *data ) { + errno = 0; *data = ptrace( PTRACE_PEEKDATA, thread->unix_pid, (caddr_t)addr, 0 ); if ( *data == -1 && errno) { diff --git a/server/thread.c b/server/thread.c index 7e4ae690699..5d41a4a8ab5 100644 --- a/server/thread.c +++ b/server/thread.c @@ -301,11 +301,18 @@ static void set_thread_info( struct thread *thread, } } +/* stop a thread (at the Unix level) */ +void stop_thread( struct thread *thread ) +{ + /* can't stop a thread while initialisation is in progress */ + if (is_process_init_done(thread->process)) send_thread_signal( thread, SIGUSR1 ); +} + /* suspend a thread */ -int suspend_thread( struct thread *thread, int check_limit ) +static int suspend_thread( struct thread *thread ) { int old_count = thread->suspend; - if (thread->suspend < MAXIMUM_SUSPEND_COUNT || !check_limit) + if (thread->suspend < MAXIMUM_SUSPEND_COUNT) { if (!(thread->process->suspend + thread->suspend++)) stop_thread( thread ); } @@ -314,16 +321,12 @@ int suspend_thread( struct thread *thread, int check_limit ) } /* resume a thread */ -int resume_thread( struct thread *thread ) +static int resume_thread( struct thread *thread ) { int old_count = thread->suspend; if (thread->suspend > 0) { - if (!(--thread->suspend + thread->process->suspend)) - { - continue_thread( thread ); - wake_thread( thread ); - } + if (!(--thread->suspend + thread->process->suspend)) wake_thread( thread ); } return old_count; } @@ -500,6 +503,7 @@ static void thread_timeout( void *ptr ) wait->user = NULL; if (thread->wait != wait) return; /* not the top-level wait, ignore it */ + if (thread->suspend + thread->process->suspend > 0) return; /* suspended, ignore it */ if (debug_level) fprintf( stderr, "%04x: *wakeup* signaled=%d cookie=%p\n", thread->id, STATUS_TIMEOUT, cookie ); @@ -722,7 +726,7 @@ static void get_selector_entry( struct thread *thread, int entry, if (read_thread_int( thread, addr, (int *)flags_buf ) == -1) goto done; *flags = flags_buf[entry & 3]; done: - resume_thread( thread ); + resume_after_ptrace( thread ); } } @@ -933,7 +937,7 @@ DECL_HANDLER(suspend_thread) if ((thread = get_thread_from_handle( req->handle, THREAD_SUSPEND_RESUME ))) { if (thread->state == TERMINATED) set_error( STATUS_ACCESS_DENIED ); - else reply->count = suspend_thread( thread, 1 ); + else reply->count = suspend_thread( thread ); release_object( thread ); } } diff --git a/server/thread.h b/server/thread.h index 6e244c6c1a5..bf83f0c0998 100644 --- a/server/thread.h +++ b/server/thread.h @@ -110,8 +110,7 @@ extern struct thread *create_thread( int fd, struct process *process ); extern struct thread *get_thread_from_id( thread_id_t id ); extern struct thread *get_thread_from_handle( obj_handle_t handle, unsigned int access ); extern struct thread *get_thread_from_pid( int pid ); -extern int suspend_thread( struct thread *thread, int check_limit ); -extern int resume_thread( struct thread *thread ); +extern void stop_thread( struct thread *thread ); extern int wake_thread( struct thread *thread ); extern int add_queue( struct object *obj, struct wait_queue_entry *entry ); extern void remove_queue( struct object *obj, struct wait_queue_entry *entry ); @@ -127,11 +126,9 @@ extern struct thread_snapshot *thread_snap( int *count ); /* ptrace functions */ extern void sigchld_handler(); -extern void wait4_thread( struct thread *thread, int signal ); -extern void stop_thread( struct thread *thread ); -extern void continue_thread( struct thread *thread ); extern void detach_thread( struct thread *thread, int sig ); extern int suspend_for_ptrace( struct thread *thread ); +extern void resume_after_ptrace( struct thread *thread ); extern int read_thread_int( struct thread *thread, const int *addr, int *data ); extern int write_thread_int( struct thread *thread, int *addr, int data, unsigned int mask ); extern void *get_thread_ip( struct thread *thread ); diff --git a/server/trace.c b/server/trace.c index 65d05cf7499..1e09f62f102 100644 --- a/server/trace.c +++ b/server/trace.c @@ -195,6 +195,11 @@ static void dump_varargs_unicode_str( size_t size ) static void dump_varargs_context( size_t size ) { + if (!size) + { + fprintf( stderr, "{}" ); + return; + } dump_context( cur_data ); remove_data( size ); } @@ -202,6 +207,12 @@ static void dump_varargs_context( size_t size ) static void dump_varargs_exc_event( size_t size ) { const CONTEXT *ptr = cur_data; + + if (!size) + { + fprintf( stderr, "{}" ); + return; + } fprintf( stderr, "{context=" ); dump_context( ptr ); fprintf( stderr, ",rec=" );