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