xref: /aosp_15_r20/external/bcc/libbpf-tools/trace_helpers.c (revision 387f9dfdfa2baef462e92476d413c7bc2470293e)
1 /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
2 // Copyright (c) 2020 Wenbo Zhang
3 //
4 // Based on ksyms improvements from Andrii Nakryiko, add more helpers.
5 // 28-Feb-2020   Wenbo Zhang   Created this.
6 #ifndef _GNU_SOURCE
7 #define _GNU_SOURCE
8 #endif
9 #include <ctype.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <unistd.h>
14 #include <errno.h>
15 #include <fcntl.h>
16 #include <sys/resource.h>
17 #include <time.h>
18 #include <bpf/bpf.h>
19 #include <bpf/btf.h>
20 #include <bpf/libbpf.h>
21 #include <limits.h>
22 #include "trace_helpers.h"
23 #include "uprobe_helpers.h"
24 
25 #define min(x, y) ({				\
26 	typeof(x) _min1 = (x);			\
27 	typeof(y) _min2 = (y);			\
28 	(void) (&_min1 == &_min2);		\
29 	_min1 < _min2 ? _min1 : _min2; })
30 
31 #define DISK_NAME_LEN	32
32 
33 #define MINORBITS	20
34 #define MINORMASK	((1U << MINORBITS) - 1)
35 
36 #define MKDEV(ma, mi)	(((ma) << MINORBITS) | (mi))
37 
38 struct ksyms {
39 	struct ksym *syms;
40 	int syms_sz;
41 	int syms_cap;
42 	char *strs;
43 	int strs_sz;
44 	int strs_cap;
45 };
46 
ksyms__add_symbol(struct ksyms * ksyms,const char * name,unsigned long addr)47 static int ksyms__add_symbol(struct ksyms *ksyms, const char *name, unsigned long addr)
48 {
49 	size_t new_cap, name_len = strlen(name) + 1;
50 	struct ksym *ksym;
51 	void *tmp;
52 
53 	if (ksyms->strs_sz + name_len > ksyms->strs_cap) {
54 		new_cap = ksyms->strs_cap * 4 / 3;
55 		if (new_cap < ksyms->strs_sz + name_len)
56 			new_cap = ksyms->strs_sz + name_len;
57 		if (new_cap < 1024)
58 			new_cap = 1024;
59 		tmp = realloc(ksyms->strs, new_cap);
60 		if (!tmp)
61 			return -1;
62 		ksyms->strs = tmp;
63 		ksyms->strs_cap = new_cap;
64 	}
65 	if (ksyms->syms_sz + 1 > ksyms->syms_cap) {
66 		new_cap = ksyms->syms_cap * 4 / 3;
67 		if (new_cap < 1024)
68 			new_cap = 1024;
69 		tmp = realloc(ksyms->syms, sizeof(*ksyms->syms) * new_cap);
70 		if (!tmp)
71 			return -1;
72 		ksyms->syms = tmp;
73 		ksyms->syms_cap = new_cap;
74 	}
75 
76 	ksym = &ksyms->syms[ksyms->syms_sz];
77 	/* while constructing, re-use pointer as just a plain offset */
78 	ksym->name = (void *)(unsigned long)ksyms->strs_sz;
79 	ksym->addr = addr;
80 
81 	memcpy(ksyms->strs + ksyms->strs_sz, name, name_len);
82 	ksyms->strs_sz += name_len;
83 	ksyms->syms_sz++;
84 
85 	return 0;
86 }
87 
ksym_cmp(const void * p1,const void * p2)88 static int ksym_cmp(const void *p1, const void *p2)
89 {
90 	const struct ksym *s1 = p1, *s2 = p2;
91 
92 	if (s1->addr == s2->addr)
93 		return strcmp(s1->name, s2->name);
94 	return s1->addr < s2->addr ? -1 : 1;
95 }
96 
ksyms__load(void)97 struct ksyms *ksyms__load(void)
98 {
99 	char sym_type, sym_name[256];
100 	struct ksyms *ksyms;
101 	unsigned long sym_addr;
102 	int i, ret;
103 	FILE *f;
104 
105 	f = fopen("/proc/kallsyms", "r");
106 	if (!f)
107 		return NULL;
108 
109 	ksyms = calloc(1, sizeof(*ksyms));
110 	if (!ksyms)
111 		goto err_out;
112 
113 	while (true) {
114 		ret = fscanf(f, "%lx %c %s%*[^\n]\n",
115 			     &sym_addr, &sym_type, sym_name);
116 		if (ret == EOF && feof(f))
117 			break;
118 		if (ret != 3)
119 			goto err_out;
120 		if (ksyms__add_symbol(ksyms, sym_name, sym_addr))
121 			goto err_out;
122 	}
123 
124 	/* now when strings are finalized, adjust pointers properly */
125 	for (i = 0; i < ksyms->syms_sz; i++)
126 		ksyms->syms[i].name += (unsigned long)ksyms->strs;
127 
128 	qsort(ksyms->syms, ksyms->syms_sz, sizeof(*ksyms->syms), ksym_cmp);
129 
130 	fclose(f);
131 	return ksyms;
132 
133 err_out:
134 	ksyms__free(ksyms);
135 	fclose(f);
136 	return NULL;
137 }
138 
ksyms__free(struct ksyms * ksyms)139 void ksyms__free(struct ksyms *ksyms)
140 {
141 	if (!ksyms)
142 		return;
143 
144 	free(ksyms->syms);
145 	free(ksyms->strs);
146 	free(ksyms);
147 }
148 
ksyms__map_addr(const struct ksyms * ksyms,unsigned long addr)149 const struct ksym *ksyms__map_addr(const struct ksyms *ksyms,
150 				   unsigned long addr)
151 {
152 	int start = 0, end = ksyms->syms_sz - 1, mid;
153 	unsigned long sym_addr;
154 
155 	/* find largest sym_addr <= addr using binary search */
156 	while (start < end) {
157 		mid = start + (end - start + 1) / 2;
158 		sym_addr = ksyms->syms[mid].addr;
159 
160 		if (sym_addr <= addr)
161 			start = mid;
162 		else
163 			end = mid - 1;
164 	}
165 
166 	if (start == end && ksyms->syms[start].addr <= addr)
167 		return &ksyms->syms[start];
168 	return NULL;
169 }
170 
ksyms__get_symbol(const struct ksyms * ksyms,const char * name)171 const struct ksym *ksyms__get_symbol(const struct ksyms *ksyms,
172 				     const char *name)
173 {
174 	int i;
175 
176 	for (i = 0; i < ksyms->syms_sz; i++) {
177 		if (strcmp(ksyms->syms[i].name, name) == 0)
178 			return &ksyms->syms[i];
179 	}
180 
181 	return NULL;
182 }
183 
184 struct load_range {
185 	uint64_t start;
186 	uint64_t end;
187 	uint64_t file_off;
188 };
189 
190 enum elf_type {
191 	EXEC,
192 	DYN,
193 	PERF_MAP,
194 	VDSO,
195 	UNKNOWN,
196 };
197 
198 struct dso {
199 	char *name;
200 	struct load_range *ranges;
201 	int range_sz;
202 	/* Dyn's first text section virtual addr at execution */
203 	uint64_t sh_addr;
204 	/* Dyn's first text section file offset */
205 	uint64_t sh_offset;
206 	enum elf_type type;
207 
208 	struct sym *syms;
209 	int syms_sz;
210 	int syms_cap;
211 
212 	/*
213 	 * libbpf's struct btf is actually a pretty efficient
214 	 * "set of strings" data structure, so we create an
215 	 * empty one and use it to store symbol names.
216 	 */
217 	struct btf *btf;
218 };
219 
220 struct map {
221 	uint64_t start_addr;
222 	uint64_t end_addr;
223 	uint64_t file_off;
224 	uint64_t dev_major;
225 	uint64_t dev_minor;
226 	uint64_t inode;
227 };
228 
229 struct syms {
230 	struct dso *dsos;
231 	int dso_sz;
232 };
233 
is_file_backed(const char * mapname)234 static bool is_file_backed(const char *mapname)
235 {
236 #define STARTS_WITH(mapname, prefix) \
237 	(!strncmp(mapname, prefix, sizeof(prefix) - 1))
238 
239 	return mapname[0] && !(
240 		STARTS_WITH(mapname, "//anon") ||
241 		STARTS_WITH(mapname, "/dev/zero") ||
242 		STARTS_WITH(mapname, "/anon_hugepage") ||
243 		STARTS_WITH(mapname, "[stack") ||
244 		STARTS_WITH(mapname, "/SYSV") ||
245 		STARTS_WITH(mapname, "[heap]") ||
246 		STARTS_WITH(mapname, "[uprobes]") ||
247 		STARTS_WITH(mapname, "[vsyscall]"));
248 }
249 
is_perf_map(const char * path)250 static bool is_perf_map(const char *path)
251 {
252 	return false;
253 }
254 
is_vdso(const char * path)255 static bool is_vdso(const char *path)
256 {
257 	return !strcmp(path, "[vdso]");
258 }
259 
is_uprobes(const char * path)260 static bool is_uprobes(const char *path)
261 {
262 	return !strcmp(path, "[uprobes]");
263 }
264 
get_elf_type(const char * path)265 static int get_elf_type(const char *path)
266 {
267 	GElf_Ehdr hdr;
268 	void *res;
269 	Elf *e;
270 	int fd;
271 
272 	if (is_vdso(path))
273 		return -1;
274 	if (is_uprobes(path))
275 		return -1;
276 	e = open_elf(path, &fd);
277 	if (!e)
278 		return -1;
279 	res = gelf_getehdr(e, &hdr);
280 	close_elf(e, fd);
281 	if (!res)
282 		return -1;
283 	return hdr.e_type;
284 }
285 
get_elf_text_scn_info(const char * path,uint64_t * addr,uint64_t * offset)286 static int get_elf_text_scn_info(const char *path, uint64_t *addr,
287 				 uint64_t *offset)
288 {
289 	Elf_Scn *section = NULL;
290 	int fd = -1, err = -1;
291 	GElf_Shdr header;
292 	size_t stridx;
293 	Elf *e = NULL;
294 	char *name;
295 
296 	e = open_elf(path, &fd);
297 	if (!e)
298 		goto err_out;
299 	err = elf_getshdrstrndx(e, &stridx);
300 	if (err < 0)
301 		goto err_out;
302 
303 	err = -1;
304 	while ((section = elf_nextscn(e, section)) != 0) {
305 		if (!gelf_getshdr(section, &header))
306 			continue;
307 
308 		name = elf_strptr(e, stridx, header.sh_name);
309 		if (name && !strcmp(name, ".text")) {
310 			*addr = (uint64_t)header.sh_addr;
311 			*offset = (uint64_t)header.sh_offset;
312 			err = 0;
313 			break;
314 		}
315 	}
316 
317 err_out:
318 	close_elf(e, fd);
319 	return err;
320 }
321 
syms__add_dso(struct syms * syms,struct map * map,const char * name)322 static int syms__add_dso(struct syms *syms, struct map *map, const char *name)
323 {
324 	struct dso *dso = NULL;
325 	int i, type;
326 	void *tmp;
327 
328 	for (i = 0; i < syms->dso_sz; i++) {
329 		if (!strcmp(syms->dsos[i].name, name)) {
330 			dso = &syms->dsos[i];
331 			break;
332 		}
333 	}
334 
335 	if (!dso) {
336 		tmp = realloc(syms->dsos, (syms->dso_sz + 1) *
337 			      sizeof(*syms->dsos));
338 		if (!tmp)
339 			return -1;
340 		syms->dsos = tmp;
341 		dso = &syms->dsos[syms->dso_sz++];
342 		memset(dso, 0, sizeof(*dso));
343 		dso->name = strdup(name);
344 		dso->btf = btf__new_empty();
345 	}
346 
347 	tmp = realloc(dso->ranges, (dso->range_sz + 1) * sizeof(*dso->ranges));
348 	if (!tmp)
349 		return -1;
350 	dso->ranges = tmp;
351 	dso->ranges[dso->range_sz].start = map->start_addr;
352 	dso->ranges[dso->range_sz].end = map->end_addr;
353 	dso->ranges[dso->range_sz].file_off = map->file_off;
354 	dso->range_sz++;
355 	type = get_elf_type(name);
356 	if (type == ET_EXEC) {
357 		dso->type = EXEC;
358 	} else if (type == ET_DYN) {
359 		dso->type = DYN;
360 		if (get_elf_text_scn_info(name, &dso->sh_addr, &dso->sh_offset) < 0)
361 			return -1;
362 	} else if (is_perf_map(name)) {
363 		dso->type = PERF_MAP;
364 	} else if (is_vdso(name)) {
365 		dso->type = VDSO;
366 	} else {
367 		dso->type = UNKNOWN;
368 	}
369 	return 0;
370 }
371 
syms__find_dso(const struct syms * syms,unsigned long addr,uint64_t * offset)372 static struct dso *syms__find_dso(const struct syms *syms, unsigned long addr,
373 				  uint64_t *offset)
374 {
375 	struct load_range *range;
376 	struct dso *dso;
377 	int i, j;
378 
379 	for (i = 0; i < syms->dso_sz; i++) {
380 		dso = &syms->dsos[i];
381 		for (j = 0; j < dso->range_sz; j++) {
382 			range = &dso->ranges[j];
383 			if (addr <= range->start || addr >= range->end)
384 				continue;
385 			if (dso->type == DYN || dso->type == VDSO) {
386 				/* Offset within the mmap */
387 				*offset = addr - range->start + range->file_off;
388 				/* Offset within the ELF for dyn symbol lookup */
389 				*offset += dso->sh_addr - dso->sh_offset;
390 			} else {
391 				*offset = addr;
392 			}
393 
394 			return dso;
395 		}
396 	}
397 
398 	return NULL;
399 }
400 
dso__load_sym_table_from_perf_map(struct dso * dso)401 static int dso__load_sym_table_from_perf_map(struct dso *dso)
402 {
403 	return -1;
404 }
405 
dso__add_sym(struct dso * dso,const char * name,uint64_t start,uint64_t size)406 static int dso__add_sym(struct dso *dso, const char *name, uint64_t start,
407 			uint64_t size)
408 {
409 	struct sym *sym;
410 	size_t new_cap;
411 	void *tmp;
412 	int off;
413 
414 	off = btf__add_str(dso->btf, name);
415 	if (off < 0)
416 		return off;
417 
418 	if (dso->syms_sz + 1 > dso->syms_cap) {
419 		new_cap = dso->syms_cap * 4 / 3;
420 		if (new_cap < 1024)
421 			new_cap = 1024;
422 		tmp = realloc(dso->syms, sizeof(*dso->syms) * new_cap);
423 		if (!tmp)
424 			return -1;
425 		dso->syms = tmp;
426 		dso->syms_cap = new_cap;
427 	}
428 
429 	sym = &dso->syms[dso->syms_sz++];
430 	/* while constructing, re-use pointer as just a plain offset */
431 	sym->name = (void*)(unsigned long)off;
432 	sym->start = start;
433 	sym->size = size;
434 	sym->offset = 0;
435 
436 	return 0;
437 }
438 
sym_cmp(const void * p1,const void * p2)439 static int sym_cmp(const void *p1, const void *p2)
440 {
441 	const struct sym *s1 = p1, *s2 = p2;
442 
443 	if (s1->start == s2->start)
444 		return strcmp(s1->name, s2->name);
445 	return s1->start < s2->start ? -1 : 1;
446 }
447 
dso__add_syms(struct dso * dso,Elf * e,Elf_Scn * section,size_t stridx,size_t symsize)448 static int dso__add_syms(struct dso *dso, Elf *e, Elf_Scn *section,
449 			 size_t stridx, size_t symsize)
450 {
451 	Elf_Data *data = NULL;
452 
453 	while ((data = elf_getdata(section, data)) != 0) {
454 		size_t i, symcount = data->d_size / symsize;
455 
456 		if (data->d_size % symsize)
457 			return -1;
458 
459 		for (i = 0; i < symcount; ++i) {
460 			const char *name;
461 			GElf_Sym sym;
462 
463 			if (!gelf_getsym(data, (int)i, &sym))
464 				continue;
465 			if (!(name = elf_strptr(e, stridx, sym.st_name)))
466 				continue;
467 			if (name[0] == '\0')
468 				continue;
469 
470 			if (sym.st_value == 0)
471 				continue;
472 
473 			if (dso__add_sym(dso, name, sym.st_value, sym.st_size))
474 				goto err_out;
475 		}
476 	}
477 
478 	return 0;
479 
480 err_out:
481 	return -1;
482 }
483 
dso__free_fields(struct dso * dso)484 static void dso__free_fields(struct dso *dso)
485 {
486 	if (!dso)
487 		return;
488 
489 	free(dso->name);
490 	free(dso->ranges);
491 	free(dso->syms);
492 	btf__free(dso->btf);
493 }
494 
dso__load_sym_table_from_elf(struct dso * dso,int fd)495 static int dso__load_sym_table_from_elf(struct dso *dso, int fd)
496 {
497 	Elf_Scn *section = NULL;
498 	Elf *e;
499 	int i;
500 
501 	e = fd > 0 ? open_elf_by_fd(fd) : open_elf(dso->name, &fd);
502 	if (!e)
503 		return -1;
504 
505 	while ((section = elf_nextscn(e, section)) != 0) {
506 		GElf_Shdr header;
507 
508 		if (!gelf_getshdr(section, &header))
509 			continue;
510 
511 		if (header.sh_type != SHT_SYMTAB &&
512 		    header.sh_type != SHT_DYNSYM)
513 			continue;
514 
515 		if (dso__add_syms(dso, e, section, header.sh_link,
516 				  header.sh_entsize))
517 			goto err_out;
518 	}
519 
520 	/* now when strings are finalized, adjust pointers properly */
521 	for (i = 0; i < dso->syms_sz; i++)
522 		dso->syms[i].name =
523 			btf__name_by_offset(dso->btf,
524 					    (unsigned long)dso->syms[i].name);
525 
526 	qsort(dso->syms, dso->syms_sz, sizeof(*dso->syms), sym_cmp);
527 
528 	close_elf(e, fd);
529 	return 0;
530 
531 err_out:
532 	dso__free_fields(dso);
533 	close_elf(e, fd);
534 	return -1;
535 }
536 
create_tmp_vdso_image(struct dso * dso)537 static int create_tmp_vdso_image(struct dso *dso)
538 {
539 	uint64_t start_addr, end_addr;
540 	long pid = getpid();
541 	char buf[PATH_MAX];
542 	void *image = NULL;
543 	char tmpfile[128];
544 	int ret, fd = -1;
545 	uint64_t sz;
546 	char *name;
547 	FILE *f;
548 
549 	snprintf(tmpfile, sizeof(tmpfile), "/proc/%ld/maps", pid);
550 	f = fopen(tmpfile, "r");
551 	if (!f)
552 		return -1;
553 
554 	while (true) {
555 		ret = fscanf(f, "%lx-%lx %*s %*x %*x:%*x %*u%[^\n]",
556 			     &start_addr, &end_addr, buf);
557 		if (ret == EOF && feof(f))
558 			break;
559 		if (ret != 3)
560 			goto err_out;
561 
562 		name = buf;
563 		while (isspace(*name))
564 			name++;
565 		if (!is_file_backed(name))
566 			continue;
567 		if (is_vdso(name))
568 			break;
569 	}
570 
571 	sz = end_addr - start_addr;
572 	image = malloc(sz);
573 	if (!image)
574 		goto err_out;
575 	memcpy(image, (void *)start_addr, sz);
576 
577 	snprintf(tmpfile, sizeof(tmpfile),
578 		 "/tmp/libbpf_%ld_vdso_image_XXXXXX", pid);
579 	fd = mkostemp(tmpfile, O_CLOEXEC);
580 	if (fd < 0) {
581 		fprintf(stderr, "failed to create temp file: %s\n",
582 			strerror(errno));
583 		goto err_out;
584 	}
585 	/* Unlink the file to avoid leaking */
586 	if (unlink(tmpfile) == -1)
587 		fprintf(stderr, "failed to unlink %s: %s\n", tmpfile,
588 			strerror(errno));
589 	if (write(fd, image, sz) == -1) {
590 		fprintf(stderr, "failed to write to vDSO image: %s\n",
591 			strerror(errno));
592 		close(fd);
593 		fd = -1;
594 		goto err_out;
595 	}
596 
597 err_out:
598 	fclose(f);
599 	free(image);
600 	return fd;
601 }
602 
dso__load_sym_table_from_vdso_image(struct dso * dso)603 static int dso__load_sym_table_from_vdso_image(struct dso *dso)
604 {
605 	int fd = create_tmp_vdso_image(dso);
606 
607 	if (fd < 0)
608 		return -1;
609 	return dso__load_sym_table_from_elf(dso, fd);
610 }
611 
dso__load_sym_table(struct dso * dso)612 static int dso__load_sym_table(struct dso *dso)
613 {
614 	if (dso->type == UNKNOWN)
615 		return -1;
616 	if (dso->type == PERF_MAP)
617 		return dso__load_sym_table_from_perf_map(dso);
618 	if (dso->type == EXEC || dso->type == DYN)
619 		return dso__load_sym_table_from_elf(dso, 0);
620 	if (dso->type == VDSO)
621 		return dso__load_sym_table_from_vdso_image(dso);
622 	return -1;
623 }
624 
dso__find_sym(struct dso * dso,uint64_t offset)625 static struct sym *dso__find_sym(struct dso *dso, uint64_t offset)
626 {
627 	unsigned long sym_addr;
628 	int start, end, mid;
629 
630 	if (!dso->syms && dso__load_sym_table(dso))
631 		return NULL;
632 
633 	start = 0;
634 	end = dso->syms_sz - 1;
635 
636 	/* find largest sym_addr <= addr using binary search */
637 	while (start < end) {
638 		mid = start + (end - start + 1) / 2;
639 		sym_addr = dso->syms[mid].start;
640 
641 		if (sym_addr <= offset)
642 			start = mid;
643 		else
644 			end = mid - 1;
645 	}
646 
647 	if (start == end && dso->syms[start].start <= offset) {
648 		(dso->syms[start]).offset = offset - dso->syms[start].start;
649 		return &dso->syms[start];
650 	}
651 	return NULL;
652 }
653 
syms__load_file(const char * fname)654 struct syms *syms__load_file(const char *fname)
655 {
656 	char buf[PATH_MAX], perm[5];
657 	struct syms *syms;
658 	struct map map;
659 	char *name;
660 	FILE *f;
661 	int ret;
662 
663 	f = fopen(fname, "r");
664 	if (!f)
665 		return NULL;
666 
667 	syms = calloc(1, sizeof(*syms));
668 	if (!syms)
669 		goto err_out;
670 
671 	while (true) {
672 		ret = fscanf(f, "%lx-%lx %4s %lx %lx:%lx %lu%[^\n]",
673 			     &map.start_addr, &map.end_addr, perm,
674 			     &map.file_off, &map.dev_major,
675 			     &map.dev_minor, &map.inode, buf);
676 		if (ret == EOF && feof(f))
677 			break;
678 		if (ret != 8)	/* perf-<PID>.map */
679 			goto err_out;
680 
681 		if (perm[2] != 'x')
682 			continue;
683 
684 		name = buf;
685 		while (isspace(*name))
686 			name++;
687 		if (!is_file_backed(name))
688 			continue;
689 
690 		if (syms__add_dso(syms, &map, name))
691 			goto err_out;
692 	}
693 
694 	fclose(f);
695 	return syms;
696 
697 err_out:
698 	syms__free(syms);
699 	fclose(f);
700 	return NULL;
701 }
702 
syms__load_pid(pid_t tgid)703 struct syms *syms__load_pid(pid_t tgid)
704 {
705 	char fname[128];
706 
707 	snprintf(fname, sizeof(fname), "/proc/%ld/maps", (long)tgid);
708 	return syms__load_file(fname);
709 }
710 
syms__free(struct syms * syms)711 void syms__free(struct syms *syms)
712 {
713 	int i;
714 
715 	if (!syms)
716 		return;
717 
718 	for (i = 0; i < syms->dso_sz; i++)
719 		dso__free_fields(&syms->dsos[i]);
720 	free(syms->dsos);
721 	free(syms);
722 }
723 
syms__map_addr(const struct syms * syms,unsigned long addr)724 const struct sym *syms__map_addr(const struct syms *syms, unsigned long addr)
725 {
726 	struct dso *dso;
727 	uint64_t offset;
728 
729 	dso = syms__find_dso(syms, addr, &offset);
730 	if (!dso)
731 		return NULL;
732 	return dso__find_sym(dso, offset);
733 }
734 
syms__map_addr_dso(const struct syms * syms,unsigned long addr,char ** dso_name,unsigned long * dso_offset)735 const struct sym *syms__map_addr_dso(const struct syms *syms, unsigned long addr,
736 				     char **dso_name, unsigned long *dso_offset)
737 {
738 	struct dso *dso;
739 	uint64_t offset;
740 
741 	dso = syms__find_dso(syms, addr, &offset);
742 	if (!dso)
743 		return NULL;
744 
745 	*dso_name = dso->name;
746 	*dso_offset = offset;
747 
748 	return dso__find_sym(dso, offset);
749 }
750 
751 struct syms_cache {
752 	struct {
753 		struct syms *syms;
754 		int tgid;
755 	} *data;
756 	int nr;
757 };
758 
syms_cache__new(int nr)759 struct syms_cache *syms_cache__new(int nr)
760 {
761 	struct syms_cache *syms_cache;
762 
763 	syms_cache = calloc(1, sizeof(*syms_cache));
764 	if (!syms_cache)
765 		return NULL;
766 	if (nr > 0)
767 		syms_cache->data = calloc(nr, sizeof(*syms_cache->data));
768 	return syms_cache;
769 }
770 
syms_cache__free(struct syms_cache * syms_cache)771 void syms_cache__free(struct syms_cache *syms_cache)
772 {
773 	int i;
774 
775 	if (!syms_cache)
776 		return;
777 
778 	for (i = 0; i < syms_cache->nr; i++)
779 		syms__free(syms_cache->data[i].syms);
780 	free(syms_cache->data);
781 	free(syms_cache);
782 }
783 
syms_cache__get_syms(struct syms_cache * syms_cache,int tgid)784 struct syms *syms_cache__get_syms(struct syms_cache *syms_cache, int tgid)
785 {
786 	void *tmp;
787 	int i;
788 
789 	for (i = 0; i < syms_cache->nr; i++) {
790 		if (syms_cache->data[i].tgid == tgid)
791 			return syms_cache->data[i].syms;
792 	}
793 
794 	tmp = realloc(syms_cache->data, (syms_cache->nr + 1) *
795 		      sizeof(*syms_cache->data));
796 	if (!tmp)
797 		return NULL;
798 	syms_cache->data = tmp;
799 	syms_cache->data[syms_cache->nr].syms = syms__load_pid(tgid);
800 	syms_cache->data[syms_cache->nr].tgid = tgid;
801 	return syms_cache->data[syms_cache->nr++].syms;
802 }
803 
804 struct partitions {
805 	struct partition *items;
806 	int sz;
807 };
808 
partitions__add_partition(struct partitions * partitions,const char * name,unsigned int dev)809 static int partitions__add_partition(struct partitions *partitions,
810 				     const char *name, unsigned int dev)
811 {
812 	struct partition *partition;
813 	void *tmp;
814 
815 	tmp = realloc(partitions->items, (partitions->sz + 1) *
816 		sizeof(*partitions->items));
817 	if (!tmp)
818 		return -1;
819 	partitions->items = tmp;
820 	partition = &partitions->items[partitions->sz];
821 	partition->name = strdup(name);
822 	partition->dev = dev;
823 	partitions->sz++;
824 
825 	return 0;
826 }
827 
partitions__load(void)828 struct partitions *partitions__load(void)
829 {
830 	char part_name[DISK_NAME_LEN];
831 	unsigned int devmaj, devmin;
832 	unsigned long long nop;
833 	struct partitions *partitions;
834 	char buf[64];
835 	FILE *f;
836 
837 	f = fopen("/proc/partitions", "r");
838 	if (!f)
839 		return NULL;
840 
841 	partitions = calloc(1, sizeof(*partitions));
842 	if (!partitions)
843 		goto err_out;
844 
845 	while (fgets(buf, sizeof(buf), f) != NULL) {
846 		/* skip heading */
847 		if (buf[0] != ' ' || buf[0] == '\n')
848 			continue;
849 		if (sscanf(buf, "%u %u %llu %s", &devmaj, &devmin, &nop,
850 				part_name) != 4)
851 			goto err_out;
852 		if (partitions__add_partition(partitions, part_name,
853 						MKDEV(devmaj, devmin)))
854 			goto err_out;
855 	}
856 
857 	fclose(f);
858 	return partitions;
859 
860 err_out:
861 	partitions__free(partitions);
862 	fclose(f);
863 	return NULL;
864 }
865 
partitions__free(struct partitions * partitions)866 void partitions__free(struct partitions *partitions)
867 {
868 	int i;
869 
870 	if (!partitions)
871 		return;
872 
873 	for (i = 0; i < partitions->sz; i++)
874 		free(partitions->items[i].name);
875 	free(partitions->items);
876 	free(partitions);
877 }
878 
879 const struct partition *
partitions__get_by_dev(const struct partitions * partitions,unsigned int dev)880 partitions__get_by_dev(const struct partitions *partitions, unsigned int dev)
881 {
882 	int i;
883 
884 	for (i = 0; i < partitions->sz; i++) {
885 		if (partitions->items[i].dev == dev)
886 			return &partitions->items[i];
887 	}
888 
889 	return NULL;
890 }
891 
892 const struct partition *
partitions__get_by_name(const struct partitions * partitions,const char * name)893 partitions__get_by_name(const struct partitions *partitions, const char *name)
894 {
895 	int i;
896 
897 	for (i = 0; i < partitions->sz; i++) {
898 		if (strcmp(partitions->items[i].name, name) == 0)
899 			return &partitions->items[i];
900 	}
901 
902 	return NULL;
903 }
904 
print_stars(unsigned int val,unsigned int val_max,int width)905 static void print_stars(unsigned int val, unsigned int val_max, int width)
906 {
907 	int num_stars, num_spaces, i;
908 	bool need_plus;
909 
910 	num_stars = min(val, val_max) * width / val_max;
911 	num_spaces = width - num_stars;
912 	need_plus = val > val_max;
913 
914 	for (i = 0; i < num_stars; i++)
915 		printf("*");
916 	for (i = 0; i < num_spaces; i++)
917 		printf(" ");
918 	if (need_plus)
919 		printf("+");
920 }
921 
print_log2_hist(unsigned int * vals,int vals_size,const char * val_type)922 void print_log2_hist(unsigned int *vals, int vals_size, const char *val_type)
923 {
924 	int stars_max = 40, idx_max = -1;
925 	unsigned int val, val_max = 0;
926 	unsigned long long low, high;
927 	int stars, width, i;
928 
929 	for (i = 0; i < vals_size; i++) {
930 		val = vals[i];
931 		if (val > 0)
932 			idx_max = i;
933 		if (val > val_max)
934 			val_max = val;
935 	}
936 
937 	if (idx_max < 0)
938 		return;
939 
940 	printf("%*s%-*s : count    distribution\n", idx_max <= 32 ? 5 : 15, "",
941 		idx_max <= 32 ? 19 : 29, val_type);
942 
943 	if (idx_max <= 32)
944 		stars = stars_max;
945 	else
946 		stars = stars_max / 2;
947 
948 	for (i = 0; i <= idx_max; i++) {
949 		low = (1ULL << (i + 1)) >> 1;
950 		high = (1ULL << (i + 1)) - 1;
951 		if (low == high)
952 			low -= 1;
953 		val = vals[i];
954 		width = idx_max <= 32 ? 10 : 20;
955 		printf("%*lld -> %-*lld : %-8d |", width, low, width, high, val);
956 		print_stars(val, val_max, stars);
957 		printf("|\n");
958 	}
959 }
960 
print_linear_hist(unsigned int * vals,int vals_size,unsigned int base,unsigned int step,const char * val_type)961 void print_linear_hist(unsigned int *vals, int vals_size, unsigned int base,
962 		       unsigned int step, const char *val_type)
963 {
964 	int i, stars_max = 40, idx_min = -1, idx_max = -1;
965 	unsigned int val, val_max = 0;
966 
967 	for (i = 0; i < vals_size; i++) {
968 		val = vals[i];
969 		if (val > 0) {
970 			idx_max = i;
971 			if (idx_min < 0)
972 				idx_min = i;
973 		}
974 		if (val > val_max)
975 			val_max = val;
976 	}
977 
978 	if (idx_max < 0)
979 		return;
980 
981 	printf("     %-13s : count     distribution\n", val_type);
982 	for (i = idx_min; i <= idx_max; i++) {
983 		val = vals[i];
984 		if (!val)
985 			continue;
986 		printf("        %-10d : %-8d |", base + i * step, val);
987 		print_stars(val, val_max, stars_max);
988 		printf("|\n");
989 	}
990 }
991 
get_ktime_ns(void)992 unsigned long long get_ktime_ns(void)
993 {
994 	struct timespec ts;
995 
996 	clock_gettime(CLOCK_MONOTONIC, &ts);
997 	return ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec;
998 }
999 
is_kernel_module(const char * name)1000 bool is_kernel_module(const char *name)
1001 {
1002 	bool found = false;
1003 	char buf[64];
1004 	FILE *f;
1005 
1006 	f = fopen("/proc/modules", "r");
1007 	if (!f)
1008 		return false;
1009 
1010 	while (fgets(buf, sizeof(buf), f) != NULL) {
1011 		if (sscanf(buf, "%s %*s\n", buf) != 1)
1012 			break;
1013 		if (!strcmp(buf, name)) {
1014 			found = true;
1015 			break;
1016 		}
1017 	}
1018 
1019 	fclose(f);
1020 	return found;
1021 }
1022 
fentry_try_attach(int id)1023 static bool fentry_try_attach(int id)
1024 {
1025 	int prog_fd, attach_fd;
1026 	char error[4096];
1027 	struct bpf_insn insns[] = {
1028 		{ .code = BPF_ALU64 | BPF_MOV | BPF_K, .dst_reg = BPF_REG_0, .imm = 0 },
1029 		{ .code = BPF_JMP | BPF_EXIT },
1030 	};
1031 	LIBBPF_OPTS(bpf_prog_load_opts, opts,
1032 			.expected_attach_type = BPF_TRACE_FENTRY,
1033 			.attach_btf_id = id,
1034 			.log_buf = error,
1035 			.log_size = sizeof(error),
1036 	);
1037 
1038 	prog_fd = bpf_prog_load(BPF_PROG_TYPE_TRACING, "test", "GPL", insns,
1039 			sizeof(insns) / sizeof(struct bpf_insn), &opts);
1040 	if (prog_fd < 0)
1041 		return false;
1042 
1043 	attach_fd = bpf_raw_tracepoint_open(NULL, prog_fd);
1044 	if (attach_fd >= 0)
1045 		close(attach_fd);
1046 
1047 	close(prog_fd);
1048 	return attach_fd >= 0;
1049 }
1050 
fentry_can_attach(const char * name,const char * mod)1051 bool fentry_can_attach(const char *name, const char *mod)
1052 {
1053 	struct btf *btf, *vmlinux_btf, *module_btf = NULL;
1054 	int err, id;
1055 
1056 	vmlinux_btf = btf__load_vmlinux_btf();
1057 	err = libbpf_get_error(vmlinux_btf);
1058 	if (err)
1059 		return false;
1060 
1061 	btf = vmlinux_btf;
1062 
1063 	if (mod) {
1064 		module_btf = btf__load_module_btf(mod, vmlinux_btf);
1065 		err = libbpf_get_error(module_btf);
1066 		if (!err)
1067 			btf = module_btf;
1068 	}
1069 
1070 	id = btf__find_by_name_kind(btf, name, BTF_KIND_FUNC);
1071 
1072 	btf__free(module_btf);
1073 	btf__free(vmlinux_btf);
1074 	return id > 0 && fentry_try_attach(id);
1075 }
1076 
1077 #define DEBUGFS "/sys/kernel/debug/tracing"
1078 #define TRACEFS "/sys/kernel/tracing"
1079 
use_debugfs(void)1080 static bool use_debugfs(void)
1081 {
1082 	static int has_debugfs = -1;
1083 
1084 	if (has_debugfs < 0)
1085 		has_debugfs = faccessat(AT_FDCWD, DEBUGFS, F_OK, AT_EACCESS) == 0;
1086 
1087 	return has_debugfs == 1;
1088 }
1089 
tracefs_path(void)1090 static const char *tracefs_path(void)
1091 {
1092 	return use_debugfs() ? DEBUGFS : TRACEFS;
1093 }
1094 
tracefs_available_filter_functions(void)1095 static const char *tracefs_available_filter_functions(void)
1096 {
1097 	return use_debugfs() ? DEBUGFS"/available_filter_functions" :
1098 			       TRACEFS"/available_filter_functions";
1099 }
1100 
kprobe_exists(const char * name)1101 bool kprobe_exists(const char *name)
1102 {
1103 	char addr_range[256];
1104 	char sym_name[256];
1105 	FILE *f;
1106 	int ret;
1107 
1108 	f = fopen("/sys/kernel/debug/kprobes/blacklist", "r");
1109 	if (!f)
1110 		goto avail_filter;
1111 
1112 	while (true) {
1113 		ret = fscanf(f, "%s %s%*[^\n]\n", addr_range, sym_name);
1114 		if (ret == EOF && feof(f))
1115 			break;
1116 		if (ret != 2) {
1117 			fprintf(stderr, "failed to read symbol from kprobe blacklist\n");
1118 			break;
1119 		}
1120 		if (!strcmp(name, sym_name)) {
1121 			fclose(f);
1122 			return false;
1123 		}
1124 	}
1125 	fclose(f);
1126 
1127 avail_filter:
1128 	f = fopen(tracefs_available_filter_functions(), "r");
1129 	if (!f)
1130 		goto slow_path;
1131 
1132 	while (true) {
1133 		ret = fscanf(f, "%s%*[^\n]\n", sym_name);
1134 		if (ret == EOF && feof(f))
1135 			break;
1136 		if (ret != 1) {
1137 			fprintf(stderr, "failed to read symbol from available_filter_functions\n");
1138 			break;
1139 		}
1140 		if (!strcmp(name, sym_name)) {
1141 			fclose(f);
1142 			return true;
1143 		}
1144 	}
1145 
1146 	fclose(f);
1147 	return false;
1148 
1149 slow_path:
1150 	f = fopen("/proc/kallsyms", "r");
1151 	if (!f)
1152 		return false;
1153 
1154 	while (true) {
1155 		ret = fscanf(f, "%*x %*c %s%*[^\n]\n", sym_name);
1156 		if (ret == EOF && feof(f))
1157 			break;
1158 		if (ret != 1) {
1159 			fprintf(stderr, "failed to read symbol from kallsyms\n");
1160 			break;
1161 		}
1162 		if (!strcmp(name, sym_name)) {
1163 			fclose(f);
1164 			return true;
1165 		}
1166 	}
1167 
1168 	fclose(f);
1169 	return false;
1170 }
1171 
tracepoint_exists(const char * category,const char * event)1172 bool tracepoint_exists(const char *category, const char *event)
1173 {
1174 	char path[PATH_MAX];
1175 
1176 	snprintf(path, sizeof(path), "%s/events/%s/%s/format", tracefs_path(), category, event);
1177 	if (!access(path, F_OK))
1178 		return true;
1179 	return false;
1180 }
1181 
vmlinux_btf_exists(void)1182 bool vmlinux_btf_exists(void)
1183 {
1184 	struct btf *btf;
1185 	int err;
1186 
1187 	btf = btf__load_vmlinux_btf();
1188 	err = libbpf_get_error(btf);
1189 	if (err)
1190 		return false;
1191 
1192 	btf__free(btf);
1193 	return true;
1194 }
1195 
module_btf_exists(const char * mod)1196 bool module_btf_exists(const char *mod)
1197 {
1198 	char sysfs_mod[80];
1199 
1200 	if (mod) {
1201 		snprintf(sysfs_mod, sizeof(sysfs_mod), "/sys/kernel/btf/%s", mod);
1202 		if (!access(sysfs_mod, R_OK))
1203 			return true;
1204 	}
1205 	return false;
1206 }
1207 
probe_tp_btf(const char * name)1208 bool probe_tp_btf(const char *name)
1209 {
1210 	LIBBPF_OPTS(bpf_prog_load_opts, opts, .expected_attach_type = BPF_TRACE_RAW_TP);
1211 	struct bpf_insn insns[] = {
1212 		{ .code = BPF_ALU64 | BPF_MOV | BPF_K, .dst_reg = BPF_REG_0, .imm = 0 },
1213 		{ .code = BPF_JMP | BPF_EXIT },
1214 	};
1215 	int fd, insn_cnt = sizeof(insns) / sizeof(struct bpf_insn);
1216 
1217 	opts.attach_btf_id = libbpf_find_vmlinux_btf_id(name, BPF_TRACE_RAW_TP);
1218 	fd = bpf_prog_load(BPF_PROG_TYPE_TRACING, NULL, "GPL", insns, insn_cnt, &opts);
1219 	if (fd >= 0)
1220 		close(fd);
1221 	return fd >= 0;
1222 }
1223 
probe_ringbuf()1224 bool probe_ringbuf()
1225 {
1226 	int map_fd;
1227 
1228 	map_fd = bpf_map_create(BPF_MAP_TYPE_RINGBUF, NULL, 0, 0, getpagesize(), NULL);
1229 	if (map_fd < 0)
1230 		return false;
1231 
1232 	close(map_fd);
1233 	return true;
1234 }
1235