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 fclose(f); 45 sym_cnt = i; 46 qsort(syms, sym_cnt, sizeof(struct ksym), ksym_cmp); 47 return 0; 48 } 49 50 struct ksym *ksym_search(long key) 51 { 52 int start = 0, end = sym_cnt; 53 int result; 54 55 /* kallsyms not loaded. return NULL */ 56 if (sym_cnt <= 0) 57 return NULL; 58 59 while (start < end) { 60 size_t mid = start + (end - start) / 2; 61 62 result = key - syms[mid].addr; 63 if (result < 0) 64 end = mid; 65 else if (result > 0) 66 start = mid + 1; 67 else 68 return &syms[mid]; 69 } 70 71 if (start >= 1 && syms[start - 1].addr < key && 72 key < syms[start].addr) 73 /* valid ksym */ 74 return &syms[start - 1]; 75 76 /* out of range. return _stext */ 77 return &syms[0]; 78 } 79 80 long ksym_get_addr(const char *name) 81 { 82 int i; 83 84 for (i = 0; i < sym_cnt; i++) { 85 if (strcmp(syms[i].name, name) == 0) 86 return syms[i].addr; 87 } 88 89 return 0; 90 } 91 92 static int page_size; 93 static int page_cnt = 8; 94 static struct perf_event_mmap_page *header; 95 96 int perf_event_mmap_header(int fd, struct perf_event_mmap_page **header) 97 { 98 void *base; 99 int mmap_size; 100 101 page_size = getpagesize(); 102 mmap_size = page_size * (page_cnt + 1); 103 104 base = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 105 if (base == MAP_FAILED) { 106 printf("mmap err\n"); 107 return -1; 108 } 109 110 *header = base; 111 return 0; 112 } 113 114 int perf_event_mmap(int fd) 115 { 116 return perf_event_mmap_header(fd, &header); 117 } 118 119 static int perf_event_poll(int fd) 120 { 121 struct pollfd pfd = { .fd = fd, .events = POLLIN }; 122 123 return poll(&pfd, 1, 1000); 124 } 125 126 struct perf_event_sample { 127 struct perf_event_header header; 128 __u32 size; 129 char data[]; 130 }; 131 132 static enum bpf_perf_event_ret 133 bpf_perf_event_print(struct perf_event_header *hdr, void *private_data) 134 { 135 struct perf_event_sample *e = (struct perf_event_sample *)hdr; 136 perf_event_print_fn fn = private_data; 137 int ret; 138 139 if (e->header.type == PERF_RECORD_SAMPLE) { 140 ret = fn(e->data, e->size); 141 if (ret != LIBBPF_PERF_EVENT_CONT) 142 return ret; 143 } else if (e->header.type == PERF_RECORD_LOST) { 144 struct { 145 struct perf_event_header header; 146 __u64 id; 147 __u64 lost; 148 } *lost = (void *) e; 149 printf("lost %lld events\n", lost->lost); 150 } else { 151 printf("unknown event type=%d size=%d\n", 152 e->header.type, e->header.size); 153 } 154 155 return LIBBPF_PERF_EVENT_CONT; 156 } 157 158 int perf_event_poller(int fd, perf_event_print_fn output_fn) 159 { 160 enum bpf_perf_event_ret ret; 161 void *buf = NULL; 162 size_t len = 0; 163 164 for (;;) { 165 perf_event_poll(fd); 166 ret = bpf_perf_event_read_simple(header, page_cnt * page_size, 167 page_size, &buf, &len, 168 bpf_perf_event_print, 169 output_fn); 170 if (ret != LIBBPF_PERF_EVENT_CONT) 171 break; 172 } 173 free(buf); 174 175 return ret; 176 } 177 178 int perf_event_poller_multi(int *fds, struct perf_event_mmap_page **headers, 179 int num_fds, perf_event_print_fn output_fn) 180 { 181 enum bpf_perf_event_ret ret; 182 struct pollfd *pfds; 183 void *buf = NULL; 184 size_t len = 0; 185 int i; 186 187 pfds = calloc(num_fds, sizeof(*pfds)); 188 if (!pfds) 189 return LIBBPF_PERF_EVENT_ERROR; 190 191 for (i = 0; i < num_fds; i++) { 192 pfds[i].fd = fds[i]; 193 pfds[i].events = POLLIN; 194 } 195 196 for (;;) { 197 poll(pfds, num_fds, 1000); 198 for (i = 0; i < num_fds; i++) { 199 if (!pfds[i].revents) 200 continue; 201 202 ret = bpf_perf_event_read_simple(headers[i], 203 page_cnt * page_size, 204 page_size, &buf, &len, 205 bpf_perf_event_print, 206 output_fn); 207 if (ret != LIBBPF_PERF_EVENT_CONT) 208 break; 209 } 210 } 211 free(buf); 212 free(pfds); 213 214 return ret; 215 } 216