Re: perf: regression with PERF_EVENT_IOC_REFRESH

From: Vince Weaver
Date: Tue May 24 2011 - 02:21:23 EST


On Mon, 23 May 2011, Peter Zijlstra wrote:

> > before the changeset, you could have a counter group where only one of the
> > subevents was sampling. PERF_EVENT_IOC_REFRESH would correctly enable
> > sampling for only that subevent.
>
> But how? it adds to the event_limit of the event you call the ioctl()
> on, not on any of the group siblings.

the old behavior did.

> > With the changeset applied, this fails with EINVALID. Now you can only
> > sample in a group leader.
>
> I'm not quite seeing how group-leaders are relevant here, you can only
> call this ioctl() on sampling events, regardless of if they're they
> group leader or a sibling.

previous behavior was that if you called it on a group leader that wasn't
sampling, a sibling event that was sampling would be activated.

> > Was this an intended change? It breaks various of our PAPI regression
> > tests that worked fine before the change was committed.
>
> I'm not quite seeing what's wrong yet, the change seemed simple enough
> in that calling that ioctl() on an event that wasn't capable of
> generating samples doesn't make sense, since it will increase the event
> limit for the event you call it on.

attached is some code that will return a sampled count on older kernels
but gives EINVAL on current kernels.

The old behavior might have been unintentional, but PAPI unfortunately
depends on it (not code I originaly wrote, but code I have to maintain).

Vince/* sampled_notleader_refresh.c */
/* by Vince Weaver vweaver1 _at_ eecs.utk.edu */

/* Compile with gcc -O2 -Wall -o sampled_notleader_refresh sampled_notleader_refresh.c */

#define _GNU_SOURCE 1

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

#include <unistd.h>
#include <fcntl.h>

#include <errno.h>

#include <signal.h>

#include <sys/mman.h>

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



static int count=0;

static void our_handler(int signum,siginfo_t *oh, void *blah) {
count++;
}

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

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 ret,fd1,fd2;
double result;

struct perf_event_attr pe;

struct sigaction sa;

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=000000;
pe.sample_type=0;
pe.read_format=PERF_FORMAT_GROUP|PERF_FORMAT_ID;
pe.disabled=1;
pe.pinned=0;
pe.exclude_kernel=1;
pe.exclude_hv=1;
pe.wakeup_events=1;

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

pe.type=PERF_TYPE_HARDWARE;
pe.config=PERF_COUNT_HW_INSTRUCTIONS;
pe.sample_period=1000000;
pe.sample_type=PERF_SAMPLE_IP;
pe.read_format=PERF_FORMAT_GROUP|PERF_FORMAT_ID;
pe.disabled=0;
pe.pinned=0;
pe.exclude_kernel=1;
pe.exclude_hv=1;
pe.wakeup_events=1;

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


void *blargh;

/* important! */
blargh=mmap(NULL, (1+1)*4096,
PROT_READ|PROT_WRITE, MAP_SHARED, fd2, 0);

fcntl(fd2, F_SETFL, O_RDWR|O_NONBLOCK|O_ASYNC);
fcntl(fd2, F_SETSIG, SIGIO);
fcntl(fd2,F_SETOWN,getpid());

ioctl(fd1, PERF_EVENT_IOC_RESET, 0);
ioctl(fd2, PERF_EVENT_IOC_RESET, 0);

ret=ioctl(fd1, PERF_EVENT_IOC_REFRESH,0);
if (ret<0) {
fprintf(stderr,"Error with PERF_EVENT_IOC_REFRESH of group leader: "
"%d %s\n",errno,strerror(errno));
exit(1);
}

result=busywork(10000000);

ret=ioctl(fd2, PERF_EVENT_IOC_DISABLE,0);


printf("Count: %d %lf\n",count,result);


return 0;
}