Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Broadcast SIGPROF, rather than SIGALRM, when doing real time profiling #207

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 2 additions & 6 deletions src/vmprof_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
86 changes: 62 additions & 24 deletions src/vmprof_unix.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
}

Expand All @@ -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;
Expand All @@ -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));
Expand All @@ -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);
}
}
Expand All @@ -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);
}
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand Down
8 changes: 6 additions & 2 deletions src/vmprof_unix.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,17 +53,21 @@ 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);


/* *************************************************************
* the setup and teardown functions
* *************************************************************
*/


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);
Expand Down
1 change: 0 additions & 1 deletion vmprof/test/test_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -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", [
Expand Down