diff --git a/src/vmprof_common.c b/src/vmprof_common.c index 729e69e9..00fe1fbe 100644 --- a/src/vmprof_common.c +++ b/src/vmprof_common.c @@ -281,20 +281,16 @@ ssize_t remove_threads(void) int broadcast_signal_for_threads(void) { - int done = 1; size_t i = 0; - pthread_t self = pthread_self(); pthread_t tid; while (i < thread_count) { tid = threads[i]; - if (pthread_equal(tid, self)) { - done = 0; - } else if (pthread_kill(tid, SIGALRM)) { + if (pthread_kill(tid, SIGPROF)) { remove_thread(tid, i); } i++; } - return done; + return 0; } int is_main_thread(void) diff --git a/src/vmprof_unix.c b/src/vmprof_unix.c index 1a85a10c..5fe4a530 100644 --- a/src/vmprof_unix.c +++ b/src/vmprof_unix.c @@ -170,6 +170,19 @@ void vmprof_release_lock(void) { __sync_lock_release(&spinlock); } +void sigalrm_handler(int sig_nr, siginfo_t* info, void *ucontext) +{ + // SIGNAL ABUSE AHEAD + // On linux, the prof timer will deliver the signal to the thread which triggered the timer, + // because these timers are based on process and system time, and as such, are thread-aware. + // For the real timer, the signal gets delivered to the main thread, seemingly always. + // Consequently if we want to sample multiple threads, we need to forward this signal. + // This sigalrm handler broadcasts SIGPROF to all registered threads to trigger stack sampling. + if (vmprof_get_signal_type() == SIGALRM && is_main_thread()) { + broadcast_signal_for_threads(); + } +} + void sigprof_handler(int sig_nr, siginfo_t* info, void *ucontext) { int commit; @@ -197,20 +210,6 @@ void sigprof_handler(int sig_nr, siginfo_t* info, void *ucontext) while (__sync_lock_test_and_set(&spinlock, 1)) { } -#ifdef VMPROF_UNIX - // SIGNAL ABUSE AHEAD - // On linux, the prof timer will deliver the signal to the thread which triggered the timer, - // because these timers are based on process and system time, and as such, are thread-aware. - // For the real timer, the signal gets delivered to the main thread, seemingly always. - // Consequently if we want to sample multiple threads, we need to forward this signal. - if (vmprof_get_signal_type() == SIGALRM) { - if (is_main_thread() && broadcast_signal_for_threads()) { - __sync_lock_release(&spinlock); - return; - } - } -#endif - prevhandler = signal(SIGSEGV, &segfault_handler); int fault_code = setjmp(restore_point); if (fault_code == 0) { @@ -266,8 +265,9 @@ int install_sigprof_handler(void) sa.sa_sigaction = sigprof_handler; sa.sa_flags = SA_RESTART | SA_SIGINFO; if (sigemptyset(&sa.sa_mask) == -1 || - sigaction(vmprof_get_signal_type(), &sa, NULL) == -1) + sigaction(SIGPROF, &sa, NULL) == -1) return -1; + return 0; } @@ -278,14 +278,43 @@ int remove_sigprof_handler(void) ign_sigint.sa_flags = 0; sigemptyset(&ign_sigint.sa_mask); - if (sigaction(vmprof_get_signal_type(), &ign_sigint, NULL) < 0) { + if (sigaction(SIGPROF, &ign_sigint, NULL) < 0) { + fprintf(stderr, "Could not remove the signal handler (for profiling)\n"); + return -1; + } + + return 0; +} + +int install_sigalrm_handler(void) +{ + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_sigaction = sigalrm_handler; + sa.sa_flags = SA_RESTART | SA_SIGINFO; + if (sigemptyset(&sa.sa_mask) == -1 || + sigaction(SIGALRM, &sa, NULL) == -1) { + return -1; + } + return 0; +} + +int remove_sigalrm_handler(void) +{ + struct sigaction ign_sigint, prev; + ign_sigint.sa_handler = SIG_IGN; + ign_sigint.sa_flags = 0; + sigemptyset(&ign_sigint.sa_mask); + + if (sigaction(SIGALRM, &ign_sigint, NULL) < 0) { fprintf(stderr, "Could not remove the signal handler (for profiling)\n"); return -1; } + return 0; } -int install_sigprof_timer(void) +int install_timer(void) { static struct itimerval timer; timer.it_interval.tv_sec = 0; @@ -296,7 +325,7 @@ int install_sigprof_timer(void) return 0; } -int remove_sigprof_timer(void) +int remove_timer(void) { static struct itimerval timer; timerclear(&(timer.it_interval)); @@ -311,7 +340,7 @@ int remove_sigprof_timer(void) void atfork_disable_timer(void) { if (vmprof_get_profile_interval_usec() > 0) { - remove_sigprof_timer(); + remove_timer(); vmprof_set_enabled(0); } } @@ -326,7 +355,7 @@ void atfork_close_profile_file(void) void atfork_enable_timer(void) { if (vmprof_get_profile_interval_usec() > 0) { - install_sigprof_timer(); + install_timer(); vmprof_set_enabled(1); } } @@ -366,7 +395,14 @@ int vmprof_enable(int memory, int native, int real_time) goto error; if (install_sigprof_handler() == -1) goto error; - if (install_sigprof_timer() == -1) + /* When using real-time profiling, we also register a handler for SIGALRM, + * which will broadcast SIGPROF to all registered threads. + */ + if (real_time) { + if (install_sigalrm_handler() == -1) + goto error; + } + if (install_timer() == -1) goto error; signal_handler_ignore = 0; return 0; @@ -398,15 +434,17 @@ int vmprof_disable(void) disable_cpyprof(); #endif - if (remove_sigprof_timer() == -1) { + if (remove_timer() == -1) { return -1; } if (remove_sigprof_handler() == -1) { return -1; } #ifdef VMPROF_UNIX - if ((vmprof_get_signal_type() == SIGALRM) && remove_threads() == -1) { - return -1; + if (vmprof_get_signal_type() == SIGALRM) { + if (remove_threads() == -1 || remove_sigalrm_handler() == -1) { + return -1; + } } #endif flush_codes(); diff --git a/src/vmprof_unix.h b/src/vmprof_unix.h index d8159332..bffaaf69 100644 --- a/src/vmprof_unix.h +++ b/src/vmprof_unix.h @@ -53,6 +53,7 @@ int get_stack_trace(PY_THREAD_STATE_T * current, void** result, int max_depth, i void segfault_handler(int arg); int _vmprof_sample_stack(struct profbuf_s *p, PY_THREAD_STATE_T * tstate, ucontext_t * uc); void sigprof_handler(int sig_nr, siginfo_t* info, void *ucontext); +void sigalrm_handler(int sig_nr, siginfo_t* info, void *ucontext); /* ************************************************************* @@ -60,10 +61,13 @@ void sigprof_handler(int sig_nr, siginfo_t* info, void *ucontext); * ************************************************************* */ + int install_sigprof_handler(void); int remove_sigprof_handler(void); -int install_sigprof_timer(void); -int remove_sigprof_timer(void); +int install_sigalrm_handler(void); +int remove_sigalrm_handler(void); +int install_timer(void); +int remove_timer(void); void atfork_disable_timer(void); void atfork_enable_timer(void); void atfork_close_profile_file(void); diff --git a/vmprof/test/test_run.py b/vmprof/test/test_run.py index f1790cea..5462033e 100644 --- a/vmprof/test/test_run.py +++ b/vmprof/test/test_run.py @@ -255,7 +255,6 @@ def test_vmprof_real_time(): assert d[foo_time_name] > 0 -@py.test.mark.xfail() @py.test.mark.skipif("'__pypy__' in sys.builtin_module_names") @py.test.mark.skipif("sys.platform == 'win32'") @py.test.mark.parametrize("insert_foo,remove_bar", [