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 static int page_size; 76 static int page_cnt = 8; 77 static volatile struct perf_event_mmap_page *header; 78 79 int perf_event_mmap(int fd) 80 { 81 void *base; 82 int mmap_size; 83 84 page_size = getpagesize(); 85 mmap_size = page_size * (page_cnt + 1); 86 87 base = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 88 if (base == MAP_FAILED) { 89 printf("mmap err\n"); 90 return -1; 91 } 92 93 header = base; 94 return 0; 95 } 96 97 static int perf_event_poll(int fd) 98 { 99 struct pollfd pfd = { .fd = fd, .events = POLLIN }; 100 101 return poll(&pfd, 1, 1000); 102 } 103 104 struct perf_event_sample { 105 struct perf_event_header header; 106 __u32 size; 107 char data[]; 108 }; 109 110 static int perf_event_read(perf_event_print_fn fn) 111 { 112 __u64 data_tail = header->data_tail; 113 __u64 data_head = header->data_head; 114 __u64 buffer_size = page_cnt * page_size; 115 void *base, *begin, *end; 116 char buf[256]; 117 int ret; 118 119 asm volatile("" ::: "memory"); /* in real code it should be smp_rmb() */ 120 if (data_head == data_tail) 121 return PERF_EVENT_CONT; 122 123 base = ((char *)header) + page_size; 124 125 begin = base + data_tail % buffer_size; 126 end = base + data_head % buffer_size; 127 128 while (begin != end) { 129 struct perf_event_sample *e; 130 131 e = begin; 132 if (begin + e->header.size > base + buffer_size) { 133 long len = base + buffer_size - begin; 134 135 assert(len < e->header.size); 136 memcpy(buf, begin, len); 137 memcpy(buf + len, base, e->header.size - len); 138 e = (void *) buf; 139 begin = base + e->header.size - len; 140 } else if (begin + e->header.size == base + buffer_size) { 141 begin = base; 142 } else { 143 begin += e->header.size; 144 } 145 146 if (e->header.type == PERF_RECORD_SAMPLE) { 147 ret = fn(e->data, e->size); 148 if (ret != PERF_EVENT_CONT) 149 return ret; 150 } else if (e->header.type == PERF_RECORD_LOST) { 151 struct { 152 struct perf_event_header header; 153 __u64 id; 154 __u64 lost; 155 } *lost = (void *) e; 156 printf("lost %lld events\n", lost->lost); 157 } else { 158 printf("unknown event type=%d size=%d\n", 159 e->header.type, e->header.size); 160 } 161 } 162 163 __sync_synchronize(); /* smp_mb() */ 164 header->data_tail = data_head; 165 return PERF_EVENT_CONT; 166 } 167 168 int perf_event_poller(int fd, perf_event_print_fn output_fn) 169 { 170 int ret; 171 172 for (;;) { 173 perf_event_poll(fd); 174 ret = perf_event_read(output_fn); 175 if (ret != PERF_EVENT_CONT) 176 return ret; 177 } 178 179 return PERF_EVENT_DONE; 180 } 181