perf: kernel memory leak when inherit enabled

From: Vince Weaver
Date: Fri Mar 11 2011 - 18:05:17 EST


Hello

While trying to use perf events with inherit enabled to profile some
multi-threaded BLAS routines (using PAPI) I ended up out-of-memorying my
machine. It turns out you can quickly leak gigabytes of kernel memory
that isn't freed when the process exits.

I've attached a test case that reproduces this.

It leaks on 2.6.37 and 2.6.35.9. It does not on 2.6.32.

I would like to try on current git and maybe even bisect, but my devel
machine currently is broken with the ".size expression does not evaluate
to a constant" binutils nonsense.

Unfortunately I won't have much chance to test any more until Monday, but
I thought I'd send this in case it's something obvious that can be easily
reproduced and fixed.

Thanks,

Vince
vweaver1@xxxxxxxxxxxx/* pe_inherit_memleak.c */
/* by Vince Weaver vweaver1 _at_ eecs.utk.edu */

/* Each time run will leak hundreds of megabytes of kernel memory */

/* Compile with gcc -O2 -Wall -o pe_inherit_memleak pe_inherit_memleak.c -lpthread */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>

#include <unistd.h>
#include <asm/unistd.h>
#include <sys/prctl.h>
#include <linux/perf_event.h>

void *thread_work(void *blah) {

return NULL;
}

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);
}

int main(int argc, char** argv) {

int i,fd1,fd2;

struct perf_event_attr pe;

memset(&pe,0,sizeof(struct perf_event_attr));

pe.type=PERF_TYPE_HARDWARE;
pe.config=PERF_COUNT_HW_CPU_CYCLES;
pe.disabled=1;
pe.inherit=1;
pe.exclude_kernel=1;
pe.exclude_hv=1;

fd1=perf_event_open(&pe,0,-1,-1,0);
if (fd1<0) {
fprintf(stderr,"Error opening\n");
exit(1);
}

pe.type=PERF_TYPE_HARDWARE;
pe.config=PERF_COUNT_HW_INSTRUCTIONS;
pe.disabled=0;
pe.inherit=1;
pe.exclude_kernel=1;
pe.exclude_hv=1;

fd2=perf_event_open(&pe,0,-1,fd1,0);
if (fd1<0) {
fprintf(stderr,"Error opening\n");
exit(1);
}

for(i=0;i<10000;i++) {

int j;
pthread_t our_thread[8];

for(j=0;j<8;j++) {
pthread_create(&our_thread[j],NULL,thread_work,0);
}

for(j=0;j<8;j++) {
pthread_join(our_thread[j],NULL);
}

}

return 0;
}