1 // SPDX-License-Identifier: GPL-2.0 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <string.h> 5 #include <assert.h> 6 #include <errno.h> 7 #include <poll.h> 8 #include <unistd.h> 9 #include <linux/perf_event.h> 10 #include <sys/mman.h> 11 #include "trace_helpers.h" 12 13 #define MAX_SYMS 300000 14 static struct ksym syms[MAX_SYMS]; 15 static int sym_cnt; 16 17 static int ksym_cmp(const void *p1, const void *p2) 18 { 19 return ((struct ksym *)p1)->addr - ((struct ksym *)p2)->addr; 20 } 21 22 int load_kallsyms(void) 23 { 24 FILE *f = fopen("/proc/kallsyms", "r"); 25 char func[256], buf[256]; 26 char symbol; 27 void *addr; 28 int i = 0; 29 30 if (!f) 31 return -ENOENT; 32 33 while (!feof(f)) { 34 if (!fgets(buf, sizeof(buf), f)) 35 break; 36 if (sscanf(buf, "%p %c %s", &addr, &symbol, func) != 3) 37 break; 38 if (!addr) 39 continue; 40 syms[i].addr = (long) addr; 41 syms[i].name = strdup(func); 42 i++; 43 } 44 sym_cnt = i; 45 qsort(syms, sym_cnt, sizeof(struct ksym), ksym_cmp); 46 return 0; 47 } 48 49 struct ksym *ksym_search(long key) 50 { 51 int start = 0, end = sym_cnt; 52 int result; 53 54 while (start < end) { 55 size_t mid = start + (end - start) / 2; 56 57 result = key - syms[mid].addr; 58 if (result < 0) 59 end = mid; 60 else if (result > 0) 61 start = mid + 1; 62 else 63 return &syms[mid]; 64 } 65 66 if (start >= 1 && syms[start - 1].addr < key && 67 key < syms[start].addr) 68 /* valid ksym */ 69 return &syms[start - 1]; 70 71 /* out of range. return _stext */ 72 return &syms[0]; 73 } 74 75 long ksym_get_addr(const char *name) 76 { 77 int i; 78 79 for (i = 0; i < sym_cnt; i++) { 80 if (strcmp(syms[i].name, name) == 0) 81 return syms[i].addr; 82 } 83 84 return 0; 85 } 86 87 static int page_size; 88 static int page_cnt = 8; 89 static struct perf_event_mmap_page *header; 90 91 int perf_event_mmap_header(int fd, struct perf_event_mmap_page **header) 92 { 93 void *base; 94 int mmap_size; 95 96 page_size = getpagesize(); 97 mmap_size = page_size * (page_cnt + 1); 98 99 base = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 100 if (base == MAP_FAILED) { 101 printf("mmap err\n"); 102 return -1; 103 } 104 105 *header = base; 106 return 0; 107 } 108 109 int perf_event_mmap(int fd) 110 { 111 return perf_event_mmap_header(fd, &header); 112 } 113 114 static int perf_event_poll(int fd) 115 { 116 struct pollfd pfd = { .fd = fd, .events = POLLIN }; 117 118 return poll(&pfd, 1, 1000); 119 } 120 121 struct perf_event_sample { 122 struct perf_event_header header; 123 __u32 size; 124 char data[]; 125 }; 126 127 static enum bpf_perf_event_ret bpf_perf_event_print(void *event, void *priv) 128 { 129 struct perf_event_sample *e = event; 130 perf_event_print_fn fn = priv; 131 int ret; 132 133 if (e->header.type == PERF_RECORD_SAMPLE) { 134 ret = fn(e->data, e->size); 135 if (ret != LIBBPF_PERF_EVENT_CONT) 136 return ret; 137 } else if (e->header.type == PERF_RECORD_LOST) { 138 struct { 139 struct perf_event_header header; 140 __u64 id; 141 __u64 lost; 142 } *lost = (void *) e; 143 printf("lost %lld events\n", lost->lost); 144 } else { 145 printf("unknown event type=%d size=%d\n", 146 e->header.type, e->header.size); 147 } 148 149 return LIBBPF_PERF_EVENT_CONT; 150 } 151 152 int perf_event_poller(int fd, perf_event_print_fn output_fn) 153 { 154 enum bpf_perf_event_ret ret; 155 void *buf = NULL; 156 size_t len = 0; 157 158 for (;;) { 159 perf_event_poll(fd); 160 ret = bpf_perf_event_read_simple(header, page_cnt * page_size, 161 page_size, &buf, &len, 162 bpf_perf_event_print, 163 output_fn); 164 if (ret != LIBBPF_PERF_EVENT_CONT) 165 break; 166 } 167 free(buf); 168 169 return ret; 170 } 171 172 int perf_event_poller_multi(int *fds, struct perf_event_mmap_page **headers, 173 int num_fds, perf_event_print_fn output_fn) 174 { 175 enum bpf_perf_event_ret ret; 176 struct pollfd *pfds; 177 void *buf = NULL; 178 size_t len = 0; 179 int i; 180 181 pfds = calloc(num_fds, sizeof(*pfds)); 182 if (!pfds) 183 return LIBBPF_PERF_EVENT_ERROR; 184 185 for (i = 0; i < num_fds; i++) { 186 pfds[i].fd = fds[i]; 187 pfds[i].events = POLLIN; 188 } 189 190 for (;;) { 191 poll(pfds, num_fds, 1000); 192 for (i = 0; i < num_fds; i++) { 193 if (!pfds[i].revents) 194 continue; 195 196 ret = bpf_perf_event_read_simple(headers[i], 197 page_cnt * page_size, 198 page_size, &buf, &len, 199 bpf_perf_event_print, 200 output_fn); 201 if (ret != LIBBPF_PERF_EVENT_CONT) 202 break; 203 } 204 } 205 free(buf); 206 free(pfds); 207 208 return ret; 209 } 210