Currently, there is no way to ask for signals to be delivered when a
certain number of sideband events have been logged into the ring
buffer. This is problematic if we are only interested in, say, context
switch events. This patch provides for a way to achieve this.

As noted, this is a RFC and I am not too specific about the interface or
the ioctl name. Kindly suggest if you think there is a better way to
achieve this.

- Naveen

---
Here is a sample program demonstrating the same:

    #define _GNU_SOURCE

    #include <stdlib.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <string.h>
    #include <signal.h>
    #include <sys/ioctl.h>
    #include <sys/mman.h>
    #include <linux/perf_event.h>
    #include <asm/unistd.h>

    static long
    perf_event_open(struct perf_event_attr *hw_event, pid_t pid,
                   int cpu, int group_fd, unsigned long flags)
    {
        return syscall(__NR_perf_event_open, hw_event, pid, cpu,
                      group_fd, flags);
    }

    static void sigio_handler(int n, siginfo_t *info, void *uc)
    {
        fprintf (stderr, "Caught %s\n", info->si_code == POLL_HUP ? "POLL_HUP" :
                       (info->si_code == POLL_IN ? "POLL_IN" : "other signal"));

        if (ioctl(info->si_fd, PERF_EVENT_IOC_REFRESH, 2) == -1)
            perror("SIGIO: IOC_REFRESH");
    }

    int main(int argc, char **argv)
    {
        struct perf_event_attr pe;
        struct sigaction act;
        int fd;
        void *buf;

        memset(&act, 0, sizeof(act));
        act.sa_sigaction = sigio_handler;
        act.sa_flags = SA_SIGINFO;
        sigaction(SIGIO, &act, 0);

        memset(&pe, 0, sizeof(struct perf_event_attr));
        pe.size = sizeof(struct perf_event_attr);
        pe.type = PERF_TYPE_SOFTWARE;
        pe.config = PERF_COUNT_SW_DUMMY;
        pe.disabled = 1;
        pe.sample_period = 1;
        pe.context_switch = 1;

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

        buf = mmap(NULL, sysconf(_SC_PAGESIZE) * 2, PROT_READ|PROT_WRITE, 
MAP_SHARED, fd, 0);
        if (buf == MAP_FAILED) {
            fprintf(stderr, "Can't mmap buffer\n");
            return -1;
        }

        if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_ASYNC) == -1)
            return -2;

        if (fcntl(fd, F_SETSIG, SIGIO) == -1)
            return -3;

        if (fcntl(fd, F_SETOWN, getpid()) == -1)
            return -4;

        if (ioctl(fd, PERF_EVENT_IOC_COUNT_RECORDS, 0) == -1)
            return -5;

        if (ioctl(fd, PERF_EVENT_IOC_REFRESH, 2) == -1)
            return -6;

        fprintf (stderr, "Sleep 1\n");
        sleep(1);
        fprintf (stderr, "Sleep 2\n");
        sleep(1);
        fprintf (stderr, "Sleep 3\n");
        sleep(1);

        /* Disable the event counter */
        ioctl(fd, PERF_EVENT_IOC_DISABLE, 1);

        close(fd);

        return 0;
    }

A sample output:
    $ time ./cs
    Sleep 1
    Caught POLL_HUP
    Sleep 2
    Caught POLL_HUP
    Sleep 3
    Caught POLL_HUP

    real        0m3.060s
    user        0m0.001s
    sys 0m0.005s


Naveen N. Rao (1):
  kernel/events: Introduce IOC_COUNT_RECORDS

 include/linux/perf_event.h      |  1 +
 include/uapi/linux/perf_event.h |  1 +
 kernel/events/core.c            | 16 +++++++++++++++-
 kernel/events/ring_buffer.c     |  9 +++++++++
 4 files changed, 26 insertions(+), 1 deletion(-)

-- 
2.12.2

Reply via email to