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 sym_cnt = i;
45 qsort(syms, sym_cnt, sizeof(struct ksym), ksym_cmp);
46 return 0;
47 }
48
ksym_search(long key)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
ksym_get_addr(const char * name)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
perf_event_mmap_header(int fd,struct perf_event_mmap_page ** header)91 int perf_event_mmap_header(int fd, struct perf_event_mmap_page **header)
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
perf_event_mmap(int fd)109 int perf_event_mmap(int fd)
110 {
111 return perf_event_mmap_header(fd, &header);
112 }
113
perf_event_poll(int fd)114 static int perf_event_poll(int fd)
115 {
116 struct pollfd pfd = { .fd = fd, .events = POLLIN };
117
118 return poll(&pfd, 1, 1000);
119 }
120
121 struct perf_event_sample {
122 struct perf_event_header header;
123 __u32 size;
124 char data[];
125 };
126
bpf_perf_event_print(void * event,void * priv)127 static enum bpf_perf_event_ret bpf_perf_event_print(void *event, void *priv)
128 {
129 struct perf_event_sample *e = event;
130 perf_event_print_fn fn = priv;
131 int ret;
132
133 if (e->header.type == PERF_RECORD_SAMPLE) {
134 ret = fn(e->data, e->size);
135 if (ret != LIBBPF_PERF_EVENT_CONT)
136 return ret;
137 } else if (e->header.type == PERF_RECORD_LOST) {
138 struct {
139 struct perf_event_header header;
140 __u64 id;
141 __u64 lost;
142 } *lost = (void *) e;
143 printf("lost %lld events\n", lost->lost);
144 } else {
145 printf("unknown event type=%d size=%d\n",
146 e->header.type, e->header.size);
147 }
148
149 return LIBBPF_PERF_EVENT_CONT;
150 }
151
perf_event_poller(int fd,perf_event_print_fn output_fn)152 int perf_event_poller(int fd, perf_event_print_fn output_fn)
153 {
154 enum bpf_perf_event_ret ret;
155 void *buf = NULL;
156 size_t len = 0;
157
158 for (;;) {
159 perf_event_poll(fd);
160 ret = bpf_perf_event_read_simple(header, page_cnt * page_size,
161 page_size, &buf, &len,
162 bpf_perf_event_print,
163 output_fn);
164 if (ret != LIBBPF_PERF_EVENT_CONT)
165 break;
166 }
167 free(buf);
168
169 return ret;
170 }
171
perf_event_poller_multi(int * fds,struct perf_event_mmap_page ** headers,int num_fds,perf_event_print_fn output_fn)172 int perf_event_poller_multi(int *fds, struct perf_event_mmap_page **headers,
173 int num_fds, perf_event_print_fn output_fn)
174 {
175 enum bpf_perf_event_ret ret;
176 struct pollfd *pfds;
177 void *buf = NULL;
178 size_t len = 0;
179 int i;
180
181 pfds = calloc(num_fds, sizeof(*pfds));
182 if (!pfds)
183 return LIBBPF_PERF_EVENT_ERROR;
184
185 for (i = 0; i < num_fds; i++) {
186 pfds[i].fd = fds[i];
187 pfds[i].events = POLLIN;
188 }
189
190 for (;;) {
191 poll(pfds, num_fds, 1000);
192 for (i = 0; i < num_fds; i++) {
193 if (!pfds[i].revents)
194 continue;
195
196 ret = bpf_perf_event_read_simple(headers[i],
197 page_cnt * page_size,
198 page_size, &buf, &len,
199 bpf_perf_event_print,
200 output_fn);
201 if (ret != LIBBPF_PERF_EVENT_CONT)
202 break;
203 }
204 }
205 free(buf);
206 free(pfds);
207
208 return ret;
209 }
210