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