linux/samples/bpf/trace_output_user.c
<<
>>
Prefs
   1/* This program is free software; you can redistribute it and/or
   2 * modify it under the terms of version 2 of the GNU General Public
   3 * License as published by the Free Software Foundation.
   4 */
   5#include <stdio.h>
   6#include <unistd.h>
   7#include <stdlib.h>
   8#include <stdbool.h>
   9#include <string.h>
  10#include <fcntl.h>
  11#include <poll.h>
  12#include <linux/perf_event.h>
  13#include <linux/bpf.h>
  14#include <errno.h>
  15#include <assert.h>
  16#include <sys/syscall.h>
  17#include <sys/ioctl.h>
  18#include <sys/mman.h>
  19#include <time.h>
  20#include <signal.h>
  21#include "libbpf.h"
  22#include "bpf_load.h"
  23#include "perf-sys.h"
  24
  25static int pmu_fd;
  26
  27int page_size;
  28int page_cnt = 8;
  29volatile struct perf_event_mmap_page *header;
  30
  31typedef void (*print_fn)(void *data, int size);
  32
  33static int perf_event_mmap(int fd)
  34{
  35        void *base;
  36        int mmap_size;
  37
  38        page_size = getpagesize();
  39        mmap_size = page_size * (page_cnt + 1);
  40
  41        base = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
  42        if (base == MAP_FAILED) {
  43                printf("mmap err\n");
  44                return -1;
  45        }
  46
  47        header = base;
  48        return 0;
  49}
  50
  51static int perf_event_poll(int fd)
  52{
  53        struct pollfd pfd = { .fd = fd, .events = POLLIN };
  54
  55        return poll(&pfd, 1, 1000);
  56}
  57
  58struct perf_event_sample {
  59        struct perf_event_header header;
  60        __u32 size;
  61        char data[];
  62};
  63
  64static void perf_event_read(print_fn fn)
  65{
  66        __u64 data_tail = header->data_tail;
  67        __u64 data_head = header->data_head;
  68        __u64 buffer_size = page_cnt * page_size;
  69        void *base, *begin, *end;
  70        char buf[256];
  71
  72        asm volatile("" ::: "memory"); /* in real code it should be smp_rmb() */
  73        if (data_head == data_tail)
  74                return;
  75
  76        base = ((char *)header) + page_size;
  77
  78        begin = base + data_tail % buffer_size;
  79        end = base + data_head % buffer_size;
  80
  81        while (begin != end) {
  82                struct perf_event_sample *e;
  83
  84                e = begin;
  85                if (begin + e->header.size > base + buffer_size) {
  86                        long len = base + buffer_size - begin;
  87
  88                        assert(len < e->header.size);
  89                        memcpy(buf, begin, len);
  90                        memcpy(buf + len, base, e->header.size - len);
  91                        e = (void *) buf;
  92                        begin = base + e->header.size - len;
  93                } else if (begin + e->header.size == base + buffer_size) {
  94                        begin = base;
  95                } else {
  96                        begin += e->header.size;
  97                }
  98
  99                if (e->header.type == PERF_RECORD_SAMPLE) {
 100                        fn(e->data, e->size);
 101                } else if (e->header.type == PERF_RECORD_LOST) {
 102                        struct {
 103                                struct perf_event_header header;
 104                                __u64 id;
 105                                __u64 lost;
 106                        } *lost = (void *) e;
 107                        printf("lost %lld events\n", lost->lost);
 108                } else {
 109                        printf("unknown event type=%d size=%d\n",
 110                               e->header.type, e->header.size);
 111                }
 112        }
 113
 114        __sync_synchronize(); /* smp_mb() */
 115        header->data_tail = data_head;
 116}
 117
 118static __u64 time_get_ns(void)
 119{
 120        struct timespec ts;
 121
 122        clock_gettime(CLOCK_MONOTONIC, &ts);
 123        return ts.tv_sec * 1000000000ull + ts.tv_nsec;
 124}
 125
 126static __u64 start_time;
 127
 128#define MAX_CNT 100000ll
 129
 130static void print_bpf_output(void *data, int size)
 131{
 132        static __u64 cnt;
 133        struct {
 134                __u64 pid;
 135                __u64 cookie;
 136        } *e = data;
 137
 138        if (e->cookie != 0x12345678) {
 139                printf("BUG pid %llx cookie %llx sized %d\n",
 140                       e->pid, e->cookie, size);
 141                kill(0, SIGINT);
 142        }
 143
 144        cnt++;
 145
 146        if (cnt == MAX_CNT) {
 147                printf("recv %lld events per sec\n",
 148                       MAX_CNT * 1000000000ll / (time_get_ns() - start_time));
 149                kill(0, SIGINT);
 150        }
 151}
 152
 153static void test_bpf_perf_event(void)
 154{
 155        struct perf_event_attr attr = {
 156                .sample_type = PERF_SAMPLE_RAW,
 157                .type = PERF_TYPE_SOFTWARE,
 158                .config = PERF_COUNT_SW_BPF_OUTPUT,
 159        };
 160        int key = 0;
 161
 162        pmu_fd = sys_perf_event_open(&attr, -1/*pid*/, 0/*cpu*/, -1/*group_fd*/, 0);
 163
 164        assert(pmu_fd >= 0);
 165        assert(bpf_map_update_elem(map_fd[0], &key, &pmu_fd, BPF_ANY) == 0);
 166        ioctl(pmu_fd, PERF_EVENT_IOC_ENABLE, 0);
 167}
 168
 169int main(int argc, char **argv)
 170{
 171        char filename[256];
 172        FILE *f;
 173
 174        snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
 175
 176        if (load_bpf_file(filename)) {
 177                printf("%s", bpf_log_buf);
 178                return 1;
 179        }
 180
 181        test_bpf_perf_event();
 182
 183        if (perf_event_mmap(pmu_fd) < 0)
 184                return 1;
 185
 186        f = popen("taskset 1 dd if=/dev/zero of=/dev/null", "r");
 187        (void) f;
 188
 189        start_time = time_get_ns();
 190        for (;;) {
 191                perf_event_poll(pmu_fd);
 192                perf_event_read(print_bpf_output);
 193        }
 194
 195        return 0;
 196}
 197