1
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
14static struct ksym syms[MAX_SYMS];
15static int sym_cnt;
16
17static int ksym_cmp(const void *p1, const void *p2)
18{
19 return ((struct ksym *)p1)->addr - ((struct ksym *)p2)->addr;
20}
21
22int 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
49struct 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
69 return &syms[start - 1];
70
71
72 return &syms[0];
73}
74
75long 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
87static int page_size;
88static int page_cnt = 8;
89static struct perf_event_mmap_page *header;
90
91int 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
109int perf_event_mmap(int fd)
110{
111 return perf_event_mmap_header(fd, &header);
112}
113
114static int perf_event_poll(int fd)
115{
116 struct pollfd pfd = { .fd = fd, .events = POLLIN };
117
118 return poll(&pfd, 1, 1000);
119}
120
121struct perf_event_sample {
122 struct perf_event_header header;
123 __u32 size;
124 char data[];
125};
126
127static 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
152int 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
172int 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