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