0001
0002
0003
0004
0005
0006
0007 #include <errno.h>
0008 #include <fcntl.h>
0009 #include <bpf/libbpf.h>
0010 #include <poll.h>
0011 #include <signal.h>
0012 #include <stdbool.h>
0013 #include <stdio.h>
0014 #include <stdlib.h>
0015 #include <string.h>
0016 #include <time.h>
0017 #include <unistd.h>
0018 #include <linux/bpf.h>
0019 #include <linux/perf_event.h>
0020 #include <sys/ioctl.h>
0021 #include <sys/mman.h>
0022 #include <sys/syscall.h>
0023
0024 #include <bpf/bpf.h>
0025
0026 #include "main.h"
0027
0028 #define MMAP_PAGE_CNT 16
0029
0030 static volatile bool stop;
0031
0032 struct event_ring_info {
0033 int fd;
0034 int key;
0035 unsigned int cpu;
0036 void *mem;
0037 };
0038
0039 struct perf_event_sample {
0040 struct perf_event_header header;
0041 __u64 time;
0042 __u32 size;
0043 unsigned char data[];
0044 };
0045
0046 struct perf_event_lost {
0047 struct perf_event_header header;
0048 __u64 id;
0049 __u64 lost;
0050 };
0051
0052 static void int_exit(int signo)
0053 {
0054 fprintf(stderr, "Stopping...\n");
0055 stop = true;
0056 }
0057
0058 struct event_pipe_ctx {
0059 bool all_cpus;
0060 int cpu;
0061 int idx;
0062 };
0063
0064 static enum bpf_perf_event_ret
0065 print_bpf_output(void *private_data, int cpu, struct perf_event_header *event)
0066 {
0067 struct perf_event_sample *e = container_of(event,
0068 struct perf_event_sample,
0069 header);
0070 struct perf_event_lost *lost = container_of(event,
0071 struct perf_event_lost,
0072 header);
0073 struct event_pipe_ctx *ctx = private_data;
0074 int idx = ctx->all_cpus ? cpu : ctx->idx;
0075
0076 if (json_output) {
0077 jsonw_start_object(json_wtr);
0078 jsonw_name(json_wtr, "type");
0079 jsonw_uint(json_wtr, e->header.type);
0080 jsonw_name(json_wtr, "cpu");
0081 jsonw_uint(json_wtr, cpu);
0082 jsonw_name(json_wtr, "index");
0083 jsonw_uint(json_wtr, idx);
0084 if (e->header.type == PERF_RECORD_SAMPLE) {
0085 jsonw_name(json_wtr, "timestamp");
0086 jsonw_uint(json_wtr, e->time);
0087 jsonw_name(json_wtr, "data");
0088 print_data_json(e->data, e->size);
0089 } else if (e->header.type == PERF_RECORD_LOST) {
0090 jsonw_name(json_wtr, "lost");
0091 jsonw_start_object(json_wtr);
0092 jsonw_name(json_wtr, "id");
0093 jsonw_uint(json_wtr, lost->id);
0094 jsonw_name(json_wtr, "count");
0095 jsonw_uint(json_wtr, lost->lost);
0096 jsonw_end_object(json_wtr);
0097 }
0098 jsonw_end_object(json_wtr);
0099 } else {
0100 if (e->header.type == PERF_RECORD_SAMPLE) {
0101 printf("== @%lld.%09lld CPU: %d index: %d =====\n",
0102 e->time / 1000000000ULL, e->time % 1000000000ULL,
0103 cpu, idx);
0104 fprint_hex(stdout, e->data, e->size, " ");
0105 printf("\n");
0106 } else if (e->header.type == PERF_RECORD_LOST) {
0107 printf("lost %lld events\n", lost->lost);
0108 } else {
0109 printf("unknown event type=%d size=%d\n",
0110 e->header.type, e->header.size);
0111 }
0112 }
0113
0114 return LIBBPF_PERF_EVENT_CONT;
0115 }
0116
0117 int do_event_pipe(int argc, char **argv)
0118 {
0119 struct perf_event_attr perf_attr = {
0120 .sample_type = PERF_SAMPLE_RAW | PERF_SAMPLE_TIME,
0121 .type = PERF_TYPE_SOFTWARE,
0122 .config = PERF_COUNT_SW_BPF_OUTPUT,
0123 .sample_period = 1,
0124 .wakeup_events = 1,
0125 };
0126 struct bpf_map_info map_info = {};
0127 LIBBPF_OPTS(perf_buffer_raw_opts, opts);
0128 struct event_pipe_ctx ctx = {
0129 .all_cpus = true,
0130 .cpu = -1,
0131 .idx = -1,
0132 };
0133 struct perf_buffer *pb;
0134 __u32 map_info_len;
0135 int err, map_fd;
0136
0137 map_info_len = sizeof(map_info);
0138 map_fd = map_parse_fd_and_info(&argc, &argv, &map_info, &map_info_len);
0139 if (map_fd < 0)
0140 return -1;
0141
0142 if (map_info.type != BPF_MAP_TYPE_PERF_EVENT_ARRAY) {
0143 p_err("map is not a perf event array");
0144 goto err_close_map;
0145 }
0146
0147 while (argc) {
0148 if (argc < 2) {
0149 BAD_ARG();
0150 goto err_close_map;
0151 }
0152
0153 if (is_prefix(*argv, "cpu")) {
0154 char *endptr;
0155
0156 NEXT_ARG();
0157 ctx.cpu = strtoul(*argv, &endptr, 0);
0158 if (*endptr) {
0159 p_err("can't parse %s as CPU ID", *argv);
0160 goto err_close_map;
0161 }
0162
0163 NEXT_ARG();
0164 } else if (is_prefix(*argv, "index")) {
0165 char *endptr;
0166
0167 NEXT_ARG();
0168 ctx.idx = strtoul(*argv, &endptr, 0);
0169 if (*endptr) {
0170 p_err("can't parse %s as index", *argv);
0171 goto err_close_map;
0172 }
0173
0174 NEXT_ARG();
0175 } else {
0176 BAD_ARG();
0177 goto err_close_map;
0178 }
0179
0180 ctx.all_cpus = false;
0181 }
0182
0183 if (!ctx.all_cpus) {
0184 if (ctx.idx == -1 || ctx.cpu == -1) {
0185 p_err("cpu and index must be specified together");
0186 goto err_close_map;
0187 }
0188 } else {
0189 ctx.cpu = 0;
0190 ctx.idx = 0;
0191 }
0192
0193 opts.cpu_cnt = ctx.all_cpus ? 0 : 1;
0194 opts.cpus = &ctx.cpu;
0195 opts.map_keys = &ctx.idx;
0196 pb = perf_buffer__new_raw(map_fd, MMAP_PAGE_CNT, &perf_attr,
0197 print_bpf_output, &ctx, &opts);
0198 err = libbpf_get_error(pb);
0199 if (err) {
0200 p_err("failed to create perf buffer: %s (%d)",
0201 strerror(err), err);
0202 goto err_close_map;
0203 }
0204
0205 signal(SIGINT, int_exit);
0206 signal(SIGHUP, int_exit);
0207 signal(SIGTERM, int_exit);
0208
0209 if (json_output)
0210 jsonw_start_array(json_wtr);
0211
0212 while (!stop) {
0213 err = perf_buffer__poll(pb, 200);
0214 if (err < 0 && err != -EINTR) {
0215 p_err("perf buffer polling failed: %s (%d)",
0216 strerror(err), err);
0217 goto err_close_pb;
0218 }
0219 }
0220
0221 if (json_output)
0222 jsonw_end_array(json_wtr);
0223
0224 perf_buffer__free(pb);
0225 close(map_fd);
0226
0227 return 0;
0228
0229 err_close_pb:
0230 perf_buffer__free(pb);
0231 err_close_map:
0232 close(map_fd);
0233 return -1;
0234 }