1 // SPDX-License-Identifier: GPL-2.0-only !! 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 */ 2 #include <stdio.h> 5 #include <stdio.h> >> 6 #include <unistd.h> >> 7 #include <stdlib.h> >> 8 #include <stdbool.h> >> 9 #include <string.h> 3 #include <fcntl.h> 10 #include <fcntl.h> 4 #include <poll.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> 5 #include <time.h> 19 #include <time.h> 6 #include <signal.h> 20 #include <signal.h> 7 #include <bpf/libbpf.h> !! 21 #include "libbpf.h" >> 22 #include "bpf_load.h" >> 23 #include "perf-sys.h" >> 24 >> 25 static int pmu_fd; >> 26 >> 27 int page_size; >> 28 int page_cnt = 8; >> 29 volatile struct perf_event_mmap_page *header; >> 30 >> 31 typedef void (*print_fn)(void *data, int size); >> 32 >> 33 static 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 >> 51 static int perf_event_poll(int fd) >> 52 { >> 53 struct pollfd pfd = { .fd = fd, .events = POLLIN }; >> 54 >> 55 return poll(&pfd, 1, 1000); >> 56 } >> 57 >> 58 struct perf_event_sample { >> 59 struct perf_event_header header; >> 60 __u32 size; >> 61 char data[]; >> 62 }; >> 63 >> 64 static 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 } 8 117 9 static __u64 time_get_ns(void) 118 static __u64 time_get_ns(void) 10 { 119 { 11 struct timespec ts; 120 struct timespec ts; 12 121 13 clock_gettime(CLOCK_MONOTONIC, &ts); 122 clock_gettime(CLOCK_MONOTONIC, &ts); 14 return ts.tv_sec * 1000000000ull + ts. 123 return ts.tv_sec * 1000000000ull + ts.tv_nsec; 15 } 124 } 16 125 17 static __u64 start_time; 126 static __u64 start_time; 18 static __u64 cnt; << 19 127 20 #define MAX_CNT 100000ll 128 #define MAX_CNT 100000ll 21 129 22 static void print_bpf_output(void *ctx, int cp !! 130 static void print_bpf_output(void *data, int size) 23 { 131 { >> 132 static __u64 cnt; 24 struct { 133 struct { 25 __u64 pid; 134 __u64 pid; 26 __u64 cookie; 135 __u64 cookie; 27 } *e = data; 136 } *e = data; 28 137 29 if (e->cookie != 0x12345678) { 138 if (e->cookie != 0x12345678) { 30 printf("BUG pid %llx cookie %l 139 printf("BUG pid %llx cookie %llx sized %d\n", 31 e->pid, e->cookie, size 140 e->pid, e->cookie, size); 32 return; !! 141 kill(0, SIGINT); 33 } 142 } 34 143 35 cnt++; 144 cnt++; 36 145 37 if (cnt == MAX_CNT) { 146 if (cnt == MAX_CNT) { 38 printf("recv %lld events per s 147 printf("recv %lld events per sec\n", 39 MAX_CNT * 1000000000ll 148 MAX_CNT * 1000000000ll / (time_get_ns() - start_time)); 40 return; !! 149 kill(0, SIGINT); 41 } 150 } 42 } 151 } 43 152 >> 153 static 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 44 int main(int argc, char **argv) 169 int main(int argc, char **argv) 45 { 170 { 46 struct bpf_link *link = NULL; << 47 struct bpf_program *prog; << 48 struct perf_buffer *pb; << 49 struct bpf_object *obj; << 50 int map_fd, ret = 0; << 51 char filename[256]; 171 char filename[256]; 52 FILE *f; 172 FILE *f; 53 173 54 snprintf(filename, sizeof(filename), " !! 174 snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); 55 obj = bpf_object__open_file(filename, << 56 if (libbpf_get_error(obj)) { << 57 fprintf(stderr, "ERROR: openin << 58 return 0; << 59 } << 60 175 61 /* load BPF program */ !! 176 if (load_bpf_file(filename)) { 62 if (bpf_object__load(obj)) { !! 177 printf("%s", bpf_log_buf); 63 fprintf(stderr, "ERROR: loadin !! 178 return 1; 64 goto cleanup; << 65 } << 66 << 67 map_fd = bpf_object__find_map_fd_by_na << 68 if (map_fd < 0) { << 69 fprintf(stderr, "ERROR: findin << 70 goto cleanup; << 71 } << 72 << 73 prog = bpf_object__find_program_by_nam << 74 if (libbpf_get_error(prog)) { << 75 fprintf(stderr, "ERROR: findin << 76 goto cleanup; << 77 } 179 } 78 180 79 link = bpf_program__attach(prog); !! 181 test_bpf_perf_event(); 80 if (libbpf_get_error(link)) { << 81 fprintf(stderr, "ERROR: bpf_pr << 82 link = NULL; << 83 goto cleanup; << 84 } << 85 182 86 pb = perf_buffer__new(map_fd, 8, print !! 183 if (perf_event_mmap(pmu_fd) < 0) 87 ret = libbpf_get_error(pb); << 88 if (ret) { << 89 printf("failed to setup perf_b << 90 return 1; 184 return 1; 91 } << 92 185 93 f = popen("taskset 1 dd if=/dev/zero o 186 f = popen("taskset 1 dd if=/dev/zero of=/dev/null", "r"); 94 (void) f; 187 (void) f; 95 188 96 start_time = time_get_ns(); 189 start_time = time_get_ns(); 97 while ((ret = perf_buffer__poll(pb, 10 !! 190 for (;;) { >> 191 perf_event_poll(pmu_fd); >> 192 perf_event_read(print_bpf_output); 98 } 193 } 99 kill(0, SIGINT); << 100 194 101 cleanup: !! 195 return 0; 102 bpf_link__destroy(link); << 103 bpf_object__close(obj); << 104 return ret; << 105 } 196 } 106 197
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.