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
ksym_cmp(const void * p1,const void * p2)17 static int ksym_cmp(const void *p1, const void *p2)
18 {
19 return ((struct ksym *)p1)->addr - ((struct ksym *)p2)->addr;
20 }
21
load_kallsyms(void)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
ksym_search(long key)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
ksym_get_addr(const char * name)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
perf_event_mmap_header(int fd,struct perf_event_mmap_page ** header)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
perf_event_mmap(int fd)110 int perf_event_mmap(int fd)
111 {
112 return perf_event_mmap_header(fd, &header);
113 }
114
perf_event_poll(int fd)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
bpf_perf_event_print(struct perf_event_header * hdr,void * private_data)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
perf_event_poller(int fd,perf_event_print_fn output_fn)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
perf_event_poller_multi(int * fds,struct perf_event_mmap_page ** headers,int num_fds,perf_event_print_fn output_fn)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