1 // SPDX-License-Identifier: GPL-2.0-only 2 /* Copyright (C) 2018 Netronome Systems, Inc. */ 3 /* This program is free software; you can redistribute it and/or 4 * modify it under the terms of version 2 of the GNU General Public 5 * License as published by the Free Software Foundation. 6 */ 7 #include <errno.h> 8 #include <fcntl.h> 9 #include <libbpf.h> 10 #include <poll.h> 11 #include <signal.h> 12 #include <stdbool.h> 13 #include <stdio.h> 14 #include <stdlib.h> 15 #include <string.h> 16 #include <time.h> 17 #include <unistd.h> 18 #include <linux/bpf.h> 19 #include <linux/perf_event.h> 20 #include <sys/ioctl.h> 21 #include <sys/mman.h> 22 #include <sys/syscall.h> 23 24 #include <bpf.h> 25 #include <perf-sys.h> 26 27 #include "main.h" 28 29 #define MMAP_PAGE_CNT 16 30 31 static bool stop; 32 33 struct event_ring_info { 34 int fd; 35 int key; 36 unsigned int cpu; 37 void *mem; 38 }; 39 40 struct perf_event_sample { 41 struct perf_event_header header; 42 u64 time; 43 __u32 size; 44 unsigned char data[]; 45 }; 46 47 static void int_exit(int signo) 48 { 49 fprintf(stderr, "Stopping...\n"); 50 stop = true; 51 } 52 53 static enum bpf_perf_event_ret 54 print_bpf_output(struct perf_event_header *event, void *private_data) 55 { 56 struct perf_event_sample *e = container_of(event, struct perf_event_sample, 57 header); 58 struct event_ring_info *ring = private_data; 59 struct { 60 struct perf_event_header header; 61 __u64 id; 62 __u64 lost; 63 } *lost = (typeof(lost))event; 64 65 if (json_output) { 66 jsonw_start_object(json_wtr); 67 jsonw_name(json_wtr, "type"); 68 jsonw_uint(json_wtr, e->header.type); 69 jsonw_name(json_wtr, "cpu"); 70 jsonw_uint(json_wtr, ring->cpu); 71 jsonw_name(json_wtr, "index"); 72 jsonw_uint(json_wtr, ring->key); 73 if (e->header.type == PERF_RECORD_SAMPLE) { 74 jsonw_name(json_wtr, "timestamp"); 75 jsonw_uint(json_wtr, e->time); 76 jsonw_name(json_wtr, "data"); 77 print_data_json(e->data, e->size); 78 } else if (e->header.type == PERF_RECORD_LOST) { 79 jsonw_name(json_wtr, "lost"); 80 jsonw_start_object(json_wtr); 81 jsonw_name(json_wtr, "id"); 82 jsonw_uint(json_wtr, lost->id); 83 jsonw_name(json_wtr, "count"); 84 jsonw_uint(json_wtr, lost->lost); 85 jsonw_end_object(json_wtr); 86 } 87 jsonw_end_object(json_wtr); 88 } else { 89 if (e->header.type == PERF_RECORD_SAMPLE) { 90 printf("== @%lld.%09lld CPU: %d index: %d =====\n", 91 e->time / 1000000000ULL, e->time % 1000000000ULL, 92 ring->cpu, ring->key); 93 fprint_hex(stdout, e->data, e->size, " "); 94 printf("\n"); 95 } else if (e->header.type == PERF_RECORD_LOST) { 96 printf("lost %lld events\n", lost->lost); 97 } else { 98 printf("unknown event type=%d size=%d\n", 99 e->header.type, e->header.size); 100 } 101 } 102 103 return LIBBPF_PERF_EVENT_CONT; 104 } 105 106 static void 107 perf_event_read(struct event_ring_info *ring, void **buf, size_t *buf_len) 108 { 109 enum bpf_perf_event_ret ret; 110 111 ret = bpf_perf_event_read_simple(ring->mem, 112 MMAP_PAGE_CNT * get_page_size(), 113 get_page_size(), buf, buf_len, 114 print_bpf_output, ring); 115 if (ret != LIBBPF_PERF_EVENT_CONT) { 116 fprintf(stderr, "perf read loop failed with %d\n", ret); 117 stop = true; 118 } 119 } 120 121 static int perf_mmap_size(void) 122 { 123 return get_page_size() * (MMAP_PAGE_CNT + 1); 124 } 125 126 static void *perf_event_mmap(int fd) 127 { 128 int mmap_size = perf_mmap_size(); 129 void *base; 130 131 base = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 132 if (base == MAP_FAILED) { 133 p_err("event mmap failed: %s\n", strerror(errno)); 134 return NULL; 135 } 136 137 return base; 138 } 139 140 static void perf_event_unmap(void *mem) 141 { 142 if (munmap(mem, perf_mmap_size())) 143 fprintf(stderr, "Can't unmap ring memory!\n"); 144 } 145 146 static int bpf_perf_event_open(int map_fd, int key, int cpu) 147 { 148 struct perf_event_attr attr = { 149 .sample_type = PERF_SAMPLE_RAW | PERF_SAMPLE_TIME, 150 .type = PERF_TYPE_SOFTWARE, 151 .config = PERF_COUNT_SW_BPF_OUTPUT, 152 }; 153 int pmu_fd; 154 155 pmu_fd = sys_perf_event_open(&attr, -1, cpu, -1, 0); 156 if (pmu_fd < 0) { 157 p_err("failed to open perf event %d for CPU %d", key, cpu); 158 return -1; 159 } 160 161 if (bpf_map_update_elem(map_fd, &key, &pmu_fd, BPF_ANY)) { 162 p_err("failed to update map for event %d for CPU %d", key, cpu); 163 goto err_close; 164 } 165 if (ioctl(pmu_fd, PERF_EVENT_IOC_ENABLE, 0)) { 166 p_err("failed to enable event %d for CPU %d", key, cpu); 167 goto err_close; 168 } 169 170 return pmu_fd; 171 172 err_close: 173 close(pmu_fd); 174 return -1; 175 } 176 177 int do_event_pipe(int argc, char **argv) 178 { 179 int i, nfds, map_fd, index = -1, cpu = -1; 180 struct bpf_map_info map_info = {}; 181 struct event_ring_info *rings; 182 size_t tmp_buf_sz = 0; 183 void *tmp_buf = NULL; 184 struct pollfd *pfds; 185 __u32 map_info_len; 186 bool do_all = true; 187 188 map_info_len = sizeof(map_info); 189 map_fd = map_parse_fd_and_info(&argc, &argv, &map_info, &map_info_len); 190 if (map_fd < 0) 191 return -1; 192 193 if (map_info.type != BPF_MAP_TYPE_PERF_EVENT_ARRAY) { 194 p_err("map is not a perf event array"); 195 goto err_close_map; 196 } 197 198 while (argc) { 199 if (argc < 2) { 200 BAD_ARG(); 201 goto err_close_map; 202 } 203 204 if (is_prefix(*argv, "cpu")) { 205 char *endptr; 206 207 NEXT_ARG(); 208 cpu = strtoul(*argv, &endptr, 0); 209 if (*endptr) { 210 p_err("can't parse %s as CPU ID", **argv); 211 goto err_close_map; 212 } 213 214 NEXT_ARG(); 215 } else if (is_prefix(*argv, "index")) { 216 char *endptr; 217 218 NEXT_ARG(); 219 index = strtoul(*argv, &endptr, 0); 220 if (*endptr) { 221 p_err("can't parse %s as index", **argv); 222 goto err_close_map; 223 } 224 225 NEXT_ARG(); 226 } else { 227 BAD_ARG(); 228 goto err_close_map; 229 } 230 231 do_all = false; 232 } 233 234 if (!do_all) { 235 if (index == -1 || cpu == -1) { 236 p_err("cpu and index must be specified together"); 237 goto err_close_map; 238 } 239 240 nfds = 1; 241 } else { 242 nfds = min(get_possible_cpus(), map_info.max_entries); 243 cpu = 0; 244 index = 0; 245 } 246 247 rings = calloc(nfds, sizeof(rings[0])); 248 if (!rings) 249 goto err_close_map; 250 251 pfds = calloc(nfds, sizeof(pfds[0])); 252 if (!pfds) 253 goto err_free_rings; 254 255 for (i = 0; i < nfds; i++) { 256 rings[i].cpu = cpu + i; 257 rings[i].key = index + i; 258 259 rings[i].fd = bpf_perf_event_open(map_fd, rings[i].key, 260 rings[i].cpu); 261 if (rings[i].fd < 0) 262 goto err_close_fds_prev; 263 264 rings[i].mem = perf_event_mmap(rings[i].fd); 265 if (!rings[i].mem) 266 goto err_close_fds_current; 267 268 pfds[i].fd = rings[i].fd; 269 pfds[i].events = POLLIN; 270 } 271 272 signal(SIGINT, int_exit); 273 signal(SIGHUP, int_exit); 274 signal(SIGTERM, int_exit); 275 276 if (json_output) 277 jsonw_start_array(json_wtr); 278 279 while (!stop) { 280 poll(pfds, nfds, 200); 281 for (i = 0; i < nfds; i++) 282 perf_event_read(&rings[i], &tmp_buf, &tmp_buf_sz); 283 } 284 free(tmp_buf); 285 286 if (json_output) 287 jsonw_end_array(json_wtr); 288 289 for (i = 0; i < nfds; i++) { 290 perf_event_unmap(rings[i].mem); 291 close(rings[i].fd); 292 } 293 free(pfds); 294 free(rings); 295 close(map_fd); 296 297 return 0; 298 299 err_close_fds_prev: 300 while (i--) { 301 perf_event_unmap(rings[i].mem); 302 err_close_fds_current: 303 close(rings[i].fd); 304 } 305 free(pfds); 306 err_free_rings: 307 free(rings); 308 err_close_map: 309 close(map_fd); 310 return -1; 311 } 312