1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 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 <bpf/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/bpf.h> 25 26 #include "main.h" 27 28 #define MMAP_PAGE_CNT 16 29 30 static volatile bool stop; 31 32 struct perf_event_sample { 33 struct perf_event_header header; 34 __u64 time; 35 __u32 size; 36 unsigned char data[]; 37 }; 38 39 struct perf_event_lost { 40 struct perf_event_header header; 41 __u64 id; 42 __u64 lost; 43 }; 44 45 static void int_exit(int signo) 46 { 47 fprintf(stderr, "Stopping...\n"); 48 stop = true; 49 } 50 51 struct event_pipe_ctx { 52 bool all_cpus; 53 int cpu; 54 int idx; 55 }; 56 57 static enum bpf_perf_event_ret 58 print_bpf_output(void *private_data, int cpu, struct perf_event_header *event) 59 { 60 struct perf_event_sample *e = container_of(event, 61 struct perf_event_sample, 62 header); 63 struct perf_event_lost *lost = container_of(event, 64 struct perf_event_lost, 65 header); 66 struct event_pipe_ctx *ctx = private_data; 67 int idx = ctx->all_cpus ? cpu : ctx->idx; 68 69 if (json_output) { 70 jsonw_start_object(json_wtr); 71 jsonw_name(json_wtr, "type"); 72 jsonw_uint(json_wtr, e->header.type); 73 jsonw_name(json_wtr, "cpu"); 74 jsonw_uint(json_wtr, cpu); 75 jsonw_name(json_wtr, "index"); 76 jsonw_uint(json_wtr, idx); 77 if (e->header.type == PERF_RECORD_SAMPLE) { 78 jsonw_name(json_wtr, "timestamp"); 79 jsonw_uint(json_wtr, e->time); 80 jsonw_name(json_wtr, "data"); 81 print_data_json(e->data, e->size); 82 } else if (e->header.type == PERF_RECORD_LOST) { 83 jsonw_name(json_wtr, "lost"); 84 jsonw_start_object(json_wtr); 85 jsonw_name(json_wtr, "id"); 86 jsonw_uint(json_wtr, lost->id); 87 jsonw_name(json_wtr, "count"); 88 jsonw_uint(json_wtr, lost->lost); 89 jsonw_end_object(json_wtr); 90 } 91 jsonw_end_object(json_wtr); 92 } else { 93 if (e->header.type == PERF_RECORD_SAMPLE) { 94 printf("== @%lld.%09lld CPU: %d index: %d =====\n", 95 e->time / 1000000000ULL, e->time % 1000000000ULL, 96 cpu, idx); 97 fprint_hex(stdout, e->data, e->size, " "); 98 printf("\n"); 99 } else if (e->header.type == PERF_RECORD_LOST) { 100 printf("lost %lld events\n", lost->lost); 101 } else { 102 printf("unknown event type=%d size=%d\n", 103 e->header.type, e->header.size); 104 } 105 } 106 107 return LIBBPF_PERF_EVENT_CONT; 108 } 109 110 int do_event_pipe(int argc, char **argv) 111 { 112 struct perf_event_attr perf_attr = { 113 .sample_type = PERF_SAMPLE_RAW | PERF_SAMPLE_TIME, 114 .type = PERF_TYPE_SOFTWARE, 115 .config = PERF_COUNT_SW_BPF_OUTPUT, 116 .sample_period = 1, 117 .wakeup_events = 1, 118 }; 119 struct bpf_map_info map_info = {}; 120 LIBBPF_OPTS(perf_buffer_raw_opts, opts); 121 struct event_pipe_ctx ctx = { 122 .all_cpus = true, 123 .cpu = -1, 124 .idx = -1, 125 }; 126 struct perf_buffer *pb; 127 __u32 map_info_len; 128 int err, map_fd; 129 130 map_info_len = sizeof(map_info); 131 map_fd = map_parse_fd_and_info(&argc, &argv, &map_info, &map_info_len); 132 if (map_fd < 0) 133 return -1; 134 135 if (map_info.type != BPF_MAP_TYPE_PERF_EVENT_ARRAY) { 136 p_err("map is not a perf event array"); 137 goto err_close_map; 138 } 139 140 while (argc) { 141 if (argc < 2) { 142 BAD_ARG(); 143 goto err_close_map; 144 } 145 146 if (is_prefix(*argv, "cpu")) { 147 char *endptr; 148 149 NEXT_ARG(); 150 ctx.cpu = strtoul(*argv, &endptr, 0); 151 if (*endptr) { 152 p_err("can't parse %s as CPU ID", *argv); 153 goto err_close_map; 154 } 155 156 NEXT_ARG(); 157 } else if (is_prefix(*argv, "index")) { 158 char *endptr; 159 160 NEXT_ARG(); 161 ctx.idx = strtoul(*argv, &endptr, 0); 162 if (*endptr) { 163 p_err("can't parse %s as index", *argv); 164 goto err_close_map; 165 } 166 167 NEXT_ARG(); 168 } else { 169 BAD_ARG(); 170 goto err_close_map; 171 } 172 173 ctx.all_cpus = false; 174 } 175 176 if (!ctx.all_cpus) { 177 if (ctx.idx == -1 || ctx.cpu == -1) { 178 p_err("cpu and index must be specified together"); 179 goto err_close_map; 180 } 181 } else { 182 ctx.cpu = 0; 183 ctx.idx = 0; 184 } 185 186 opts.cpu_cnt = ctx.all_cpus ? 0 : 1; 187 opts.cpus = &ctx.cpu; 188 opts.map_keys = &ctx.idx; 189 pb = perf_buffer__new_raw(map_fd, MMAP_PAGE_CNT, &perf_attr, 190 print_bpf_output, &ctx, &opts); 191 if (!pb) { 192 p_err("failed to create perf buffer: %s (%d)", 193 strerror(errno), errno); 194 goto err_close_map; 195 } 196 197 signal(SIGINT, int_exit); 198 signal(SIGHUP, int_exit); 199 signal(SIGTERM, int_exit); 200 201 if (json_output) 202 jsonw_start_array(json_wtr); 203 204 while (!stop) { 205 err = perf_buffer__poll(pb, 200); 206 if (err < 0 && err != -EINTR) { 207 p_err("perf buffer polling failed: %s (%d)", 208 strerror(errno), errno); 209 goto err_close_pb; 210 } 211 } 212 213 if (json_output) 214 jsonw_end_array(json_wtr); 215 216 perf_buffer__free(pb); 217 close(map_fd); 218 219 return 0; 220 221 err_close_pb: 222 perf_buffer__free(pb); 223 err_close_map: 224 close(map_fd); 225 return -1; 226 } 227