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