// SPDX-License-Identifier: GPL-2.0 #include #include #include #include #include #include #include #include #include #include "trace_helpers.h" #define MAX_SYMS 300000 static struct ksym syms[MAX_SYMS]; static int sym_cnt; static int ksym_cmp(const void *p1, const void *p2) { return ((struct ksym *)p1)->addr - ((struct ksym *)p2)->addr; } int load_kallsyms(void) { FILE *f = fopen("/proc/kallsyms", "r"); char func[256], buf[256]; char symbol; void *addr; int i = 0; if (!f) return -ENOENT; while (!feof(f)) { if (!fgets(buf, sizeof(buf), f)) break; if (sscanf(buf, "%p %c %s", &addr, &symbol, func) != 3) break; if (!addr) continue; syms[i].addr = (long) addr; syms[i].name = strdup(func); i++; } sym_cnt = i; qsort(syms, sym_cnt, sizeof(struct ksym), ksym_cmp); return 0; } struct ksym *ksym_search(long key) { int start = 0, end = sym_cnt; int result; while (start < end) { size_t mid = start + (end - start) / 2; result = key - syms[mid].addr; if (result < 0) end = mid; else if (result > 0) start = mid + 1; else return &syms[mid]; } if (start >= 1 && syms[start - 1].addr < key && key < syms[start].addr) /* valid ksym */ return &syms[start - 1]; /* out of range. return _stext */ return &syms[0]; } static int page_size; static int page_cnt = 8; static volatile struct perf_event_mmap_page *header; int perf_event_mmap(int fd) { void *base; int mmap_size; page_size = getpagesize(); mmap_size = page_size * (page_cnt + 1); base = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (base == MAP_FAILED) { printf("mmap err\n"); return -1; } header = base; return 0; } static int perf_event_poll(int fd) { struct pollfd pfd = { .fd = fd, .events = POLLIN }; return poll(&pfd, 1, 1000); } struct perf_event_sample { struct perf_event_header header; __u32 size; char data[]; }; static int perf_event_read(perf_event_print_fn fn) { __u64 data_tail = header->data_tail; __u64 data_head = header->data_head; __u64 buffer_size = page_cnt * page_size; void *base, *begin, *end; char buf[256]; int ret; asm volatile("" ::: "memory"); /* in real code it should be smp_rmb() */ if (data_head == data_tail) return PERF_EVENT_CONT; base = ((char *)header) + page_size; begin = base + data_tail % buffer_size; end = base + data_head % buffer_size; while (begin != end) { struct perf_event_sample *e; e = begin; if (begin + e->header.size > base + buffer_size) { long len = base + buffer_size - begin; assert(len < e->header.size); memcpy(buf, begin, len); memcpy(buf + len, base, e->header.size - len); e = (void *) buf; begin = base + e->header.size - len; } else if (begin + e->header.size == base + buffer_size) { begin = base; } else { begin += e->header.size; } if (e->header.type == PERF_RECORD_SAMPLE) { ret = fn(e->data, e->size); if (ret != PERF_EVENT_CONT) return ret; } else if (e->header.type == PERF_RECORD_LOST) { struct { struct perf_event_header header; __u64 id; __u64 lost; } *lost = (void *) e; printf("lost %lld events\n", lost->lost); } else { printf("unknown event type=%d size=%d\n", e->header.type, e->header.size); } } __sync_synchronize(); /* smp_mb() */ header->data_tail = data_head; return PERF_EVENT_CONT; } int perf_event_poller(int fd, perf_event_print_fn output_fn) { int ret; for (;;) { perf_event_poll(fd); ret = perf_event_read(output_fn); if (ret != PERF_EVENT_CONT) return ret; } return PERF_EVENT_DONE; }