perf: overflow signal survives an exec call starting in 3.0
From: Vince Weaver
Date: Tue Aug 23 2011 - 11:36:21 EST
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@xxxxxxxxx>
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;
}