Messages in this thread | | | Date | Tue, 23 Aug 2011 11:36:10 -0400 | From | Vince Weaver <> | Subject | perf: overflow signal survives an exec call starting in 3.0 |
| |
Hello
Mark Krentel noticed that starting with Linux 3.0 perf_event signals survive a call to exec().
This means that if you exec() from within a perf-monitored process and don't immediately start a signal handler, your process will quickly be killed with a SIGIO signal.
I'm guessing this was an unintended change, although what to do in this situation is a bit vague.
I tediously bisected this to the following commit:
commit f506b3dc0ec454a16d40cab9ee5d75435b39dc50 Author: Peter Zijlstra <a.p.zijlstra@chello.nl> Date: Thu May 26 17:02:53 2011 +0200
perf: Fix SIGIO handling
Attached is an example program that exhibits the problem.
Vince /* signal_after_exec.c */ /* by Vince Weaver vweaver1 _at_ eecs.utk.edu */
/* Compile with gcc -O2 -Wall -o signal_after_exec signal_after_exec.c */
/* On 2.6.39 and earlier the execd process gets no signals */ /* On 3.0 and normal it does, which quickly kills the program with SIGIO */
#define _GNU_SOURCE 1
#include <stdio.h> #include <stdlib.h> #include <string.h>
#include <unistd.h> #include <fcntl.h>
#include <signal.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <asm/unistd.h> #include <sys/prctl.h> #include "perf_event.h"
int perf_event_open(struct perf_event_attr *hw_event_uptr, pid_t pid, int cpu, int group_fd, unsigned long flags) { return syscall(__NR_perf_event_open,hw_event_uptr, pid, cpu, group_fd, flags); }
static int count=0;
static void our_handler(int signum,siginfo_t *oh, void *blah) { int ret,fd1; fd1=oh->si_fd; ret=ioctl(fd1, PERF_EVENT_IOC_DISABLE,0); count++; ret=ioctl(fd1, PERF_EVENT_IOC_REFRESH,1); }
double busywork(int count) { int i; double sum=0.0012; for(i=0;i<count;i++) { sum+=0.01; } return sum; }
int main(int argc, char** argv) { int fd,ret; double result;
struct perf_event_attr pe; struct sigaction sa;
if (argc>1) { result=busywork(10000000); printf("Count after exec=%d (%lf)\n",count,result); exit(0); }
printf("\nOn 2.6.39 and earlier the exec'd process gets no signals.\n"); printf("On 3.0 and later the exec'd process gets a signal, which\n"); printf(" is not handled and it dies with SIGIO.\n\n");
/* set up signal handler */ memset(&sa, 0, sizeof(struct sigaction)); sa.sa_sigaction = our_handler; sa.sa_flags = SA_SIGINFO;
if (sigaction( SIGIO, &sa, NULL) < 0) { fprintf(stderr,"Error setting up signal handler\n"); exit(1); }
memset(&pe,0,sizeof(struct perf_event_attr));
pe.type=PERF_TYPE_HARDWARE; pe.size=sizeof(struct perf_event_attr); pe.config=PERF_COUNT_HW_INSTRUCTIONS; pe.sample_period=100000; pe.sample_type=PERF_SAMPLE_IP; pe.read_format=0; pe.disabled=1; pe.pinned=1; pe.wakeup_events=1; fd=perf_event_open(&pe,0,-1,-1,0); if (fd<0) { fprintf(stderr,"Error opening\n"); exit(1); }
void *blargh; blargh=mmap(NULL, (1+1)*4096, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
/* setup event 2 to have overflow signals */ fcntl(fd, F_SETFL, O_RDWR|O_NONBLOCK|O_ASYNC); fcntl(fd, F_SETSIG, SIGIO); fcntl(fd, F_SETOWN,getpid()); ioctl(fd, PERF_EVENT_IOC_RESET, 0);
/* enable counting */ ret=ioctl(fd, PERF_EVENT_IOC_ENABLE,0);
result=busywork(10000000);
printf("Count before exec=%d (%lf)\n",count,result);
/* exec ourselves, but call a busy function */ execl(argv[0],argv[0],"busy",NULL);
return 0; }
| |