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 26 static int pmu_fd; 27 28 int page_size; 29 int page_cnt = 8; 30 volatile struct perf_event_mmap_page *header; 31 32 typedef void (*print_fn)(void *data, int size); 33 34 static 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 52 static int perf_event_poll(int fd) 53 { 54 struct pollfd pfd = { .fd = fd, .events = POLLIN }; 55 56 return poll(&pfd, 1, 1000); 57 } 58 59 struct perf_event_sample { 60 struct perf_event_header header; 61 __u32 size; 62 char data[]; 63 }; 64 65 static 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 119 static __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 127 static __u64 start_time; 128 129 #define MAX_CNT 100000ll 130 131 static 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 154 static 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 170 int 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 } 198