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(int fd) 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 static int perf_event_poll(int fd) 110 { 111 struct pollfd pfd = { .fd = fd, .events = POLLIN }; 112 113 return poll(&pfd, 1, 1000); 114 } 115 116 struct perf_event_sample { 117 struct perf_event_header header; 118 __u32 size; 119 char data[]; 120 }; 121 122 static enum bpf_perf_event_ret bpf_perf_event_print(void *event, void *priv) 123 { 124 struct perf_event_sample *e = event; 125 perf_event_print_fn fn = priv; 126 int ret; 127 128 if (e->header.type == PERF_RECORD_SAMPLE) { 129 ret = fn(e->data, e->size); 130 if (ret != LIBBPF_PERF_EVENT_CONT) 131 return ret; 132 } else if (e->header.type == PERF_RECORD_LOST) { 133 struct { 134 struct perf_event_header header; 135 __u64 id; 136 __u64 lost; 137 } *lost = (void *) e; 138 printf("lost %lld events\n", lost->lost); 139 } else { 140 printf("unknown event type=%d size=%d\n", 141 e->header.type, e->header.size); 142 } 143 144 return LIBBPF_PERF_EVENT_CONT; 145 } 146 147 int perf_event_poller(int fd, perf_event_print_fn output_fn) 148 { 149 enum bpf_perf_event_ret ret; 150 void *buf = NULL; 151 size_t len = 0; 152 153 for (;;) { 154 perf_event_poll(fd); 155 ret = bpf_perf_event_read_simple(header, page_cnt * page_size, 156 page_size, &buf, &len, 157 bpf_perf_event_print, 158 output_fn); 159 if (ret != LIBBPF_PERF_EVENT_CONT) 160 break; 161 } 162 free(buf); 163 164 return ret; 165 } 166