• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, "[vsyscall]"));
247 }
248 
is_perf_map(const char * path)249 static bool is_perf_map(const char *path)
250 {
251 	return false;
252 }
253 
is_vdso(const char * path)254 static bool is_vdso(const char *path)
255 {
256 	return !strcmp(path, "[vdso]");
257 }
258 
get_elf_type(const char * path)259 static int get_elf_type(const char *path)
260 {
261 	GElf_Ehdr hdr;
262 	void *res;
263 	Elf *e;
264 	int fd;
265 
266 	if (is_vdso(path))
267 		return -1;
268 	e = open_elf(path, &fd);
269 	if (!e)
270 		return -1;
271 	res = gelf_getehdr(e, &hdr);
272 	close_elf(e, fd);
273 	if (!res)
274 		return -1;
275 	return hdr.e_type;
276 }
277 
get_elf_text_scn_info(const char * path,uint64_t * addr,uint64_t * offset)278 static int get_elf_text_scn_info(const char *path, uint64_t *addr,
279 				 uint64_t *offset)
280 {
281 	Elf_Scn *section = NULL;
282 	int fd = -1, err = -1;
283 	GElf_Shdr header;
284 	size_t stridx;
285 	Elf *e = NULL;
286 	char *name;
287 
288 	e = open_elf(path, &fd);
289 	if (!e)
290 		goto err_out;
291 	err = elf_getshdrstrndx(e, &stridx);
292 	if (err < 0)
293 		goto err_out;
294 
295 	err = -1;
296 	while ((section = elf_nextscn(e, section)) != 0) {
297 		if (!gelf_getshdr(section, &header))
298 			continue;
299 
300 		name = elf_strptr(e, stridx, header.sh_name);
301 		if (name && !strcmp(name, ".text")) {
302 			*addr = (uint64_t)header.sh_addr;
303 			*offset = (uint64_t)header.sh_offset;
304 			err = 0;
305 			break;
306 		}
307 	}
308 
309 err_out:
310 	close_elf(e, fd);
311 	return err;
312 }
313 
syms__add_dso(struct syms * syms,struct map * map,const char * name)314 static int syms__add_dso(struct syms *syms, struct map *map, const char *name)
315 {
316 	struct dso *dso = NULL;
317 	int i, type;
318 	void *tmp;
319 
320 	for (i = 0; i < syms->dso_sz; i++) {
321 		if (!strcmp(syms->dsos[i].name, name)) {
322 			dso = &syms->dsos[i];
323 			break;
324 		}
325 	}
326 
327 	if (!dso) {
328 		tmp = realloc(syms->dsos, (syms->dso_sz + 1) *
329 			      sizeof(*syms->dsos));
330 		if (!tmp)
331 			return -1;
332 		syms->dsos = tmp;
333 		dso = &syms->dsos[syms->dso_sz++];
334 		memset(dso, 0, sizeof(*dso));
335 		dso->name = strdup(name);
336 		dso->btf = btf__new_empty();
337 	}
338 
339 	tmp = realloc(dso->ranges, (dso->range_sz + 1) * sizeof(*dso->ranges));
340 	if (!tmp)
341 		return -1;
342 	dso->ranges = tmp;
343 	dso->ranges[dso->range_sz].start = map->start_addr;
344 	dso->ranges[dso->range_sz].end = map->end_addr;
345 	dso->ranges[dso->range_sz].file_off = map->file_off;
346 	dso->range_sz++;
347 	type = get_elf_type(name);
348 	if (type == ET_EXEC) {
349 		dso->type = EXEC;
350 	} else if (type == ET_DYN) {
351 		dso->type = DYN;
352 		if (get_elf_text_scn_info(name, &dso->sh_addr, &dso->sh_offset) < 0)
353 			return -1;
354 	} else if (is_perf_map(name)) {
355 		dso->type = PERF_MAP;
356 	} else if (is_vdso(name)) {
357 		dso->type = VDSO;
358 	} else {
359 		dso->type = UNKNOWN;
360 	}
361 	return 0;
362 }
363 
syms__find_dso(const struct syms * syms,unsigned long addr,uint64_t * offset)364 static struct dso *syms__find_dso(const struct syms *syms, unsigned long addr,
365 				  uint64_t *offset)
366 {
367 	struct load_range *range;
368 	struct dso *dso;
369 	int i, j;
370 
371 	for (i = 0; i < syms->dso_sz; i++) {
372 		dso = &syms->dsos[i];
373 		for (j = 0; j < dso->range_sz; j++) {
374 			range = &dso->ranges[j];
375 			if (addr <= range->start || addr >= range->end)
376 				continue;
377 			if (dso->type == DYN || dso->type == VDSO) {
378 				/* Offset within the mmap */
379 				*offset = addr - range->start + range->file_off;
380 				/* Offset within the ELF for dyn symbol lookup */
381 				*offset += dso->sh_addr - dso->sh_offset;
382 			} else {
383 				*offset = addr;
384 			}
385 
386 			return dso;
387 		}
388 	}
389 
390 	return NULL;
391 }
392 
dso__load_sym_table_from_perf_map(struct dso * dso)393 static int dso__load_sym_table_from_perf_map(struct dso *dso)
394 {
395 	return -1;
396 }
397 
dso__add_sym(struct dso * dso,const char * name,uint64_t start,uint64_t size)398 static int dso__add_sym(struct dso *dso, const char *name, uint64_t start,
399 			uint64_t size)
400 {
401 	struct sym *sym;
402 	size_t new_cap;
403 	void *tmp;
404 	int off;
405 
406 	off = btf__add_str(dso->btf, name);
407 	if (off < 0)
408 		return off;
409 
410 	if (dso->syms_sz + 1 > dso->syms_cap) {
411 		new_cap = dso->syms_cap * 4 / 3;
412 		if (new_cap < 1024)
413 			new_cap = 1024;
414 		tmp = realloc(dso->syms, sizeof(*dso->syms) * new_cap);
415 		if (!tmp)
416 			return -1;
417 		dso->syms = tmp;
418 		dso->syms_cap = new_cap;
419 	}
420 
421 	sym = &dso->syms[dso->syms_sz++];
422 	/* while constructing, re-use pointer as just a plain offset */
423 	sym->name = (void*)(unsigned long)off;
424 	sym->start = start;
425 	sym->size = size;
426 
427 	return 0;
428 }
429 
sym_cmp(const void * p1,const void * p2)430 static int sym_cmp(const void *p1, const void *p2)
431 {
432 	const struct sym *s1 = p1, *s2 = p2;
433 
434 	if (s1->start == s2->start)
435 		return strcmp(s1->name, s2->name);
436 	return s1->start < s2->start ? -1 : 1;
437 }
438 
dso__add_syms(struct dso * dso,Elf * e,Elf_Scn * section,size_t stridx,size_t symsize)439 static int dso__add_syms(struct dso *dso, Elf *e, Elf_Scn *section,
440 			 size_t stridx, size_t symsize)
441 {
442 	Elf_Data *data = NULL;
443 
444 	while ((data = elf_getdata(section, data)) != 0) {
445 		size_t i, symcount = data->d_size / symsize;
446 
447 		if (data->d_size % symsize)
448 			return -1;
449 
450 		for (i = 0; i < symcount; ++i) {
451 			const char *name;
452 			GElf_Sym sym;
453 
454 			if (!gelf_getsym(data, (int)i, &sym))
455 				continue;
456 			if (!(name = elf_strptr(e, stridx, sym.st_name)))
457 				continue;
458 			if (name[0] == '\0')
459 				continue;
460 
461 			if (sym.st_value == 0)
462 				continue;
463 
464 			if (dso__add_sym(dso, name, sym.st_value, sym.st_size))
465 				goto err_out;
466 		}
467 	}
468 
469 	return 0;
470 
471 err_out:
472 	return -1;
473 }
474 
dso__free_fields(struct dso * dso)475 static void dso__free_fields(struct dso *dso)
476 {
477 	if (!dso)
478 		return;
479 
480 	free(dso->name);
481 	free(dso->ranges);
482 	free(dso->syms);
483 	btf__free(dso->btf);
484 }
485 
dso__load_sym_table_from_elf(struct dso * dso,int fd)486 static int dso__load_sym_table_from_elf(struct dso *dso, int fd)
487 {
488 	Elf_Scn *section = NULL;
489 	Elf *e;
490 	int i;
491 
492 	e = fd > 0 ? open_elf_by_fd(fd) : open_elf(dso->name, &fd);
493 	if (!e)
494 		return -1;
495 
496 	while ((section = elf_nextscn(e, section)) != 0) {
497 		GElf_Shdr header;
498 
499 		if (!gelf_getshdr(section, &header))
500 			continue;
501 
502 		if (header.sh_type != SHT_SYMTAB &&
503 		    header.sh_type != SHT_DYNSYM)
504 			continue;
505 
506 		if (dso__add_syms(dso, e, section, header.sh_link,
507 				  header.sh_entsize))
508 			goto err_out;
509 	}
510 
511 	/* now when strings are finalized, adjust pointers properly */
512 	for (i = 0; i < dso->syms_sz; i++)
513 		dso->syms[i].name =
514 			btf__name_by_offset(dso->btf,
515 					    (unsigned long)dso->syms[i].name);
516 
517 	qsort(dso->syms, dso->syms_sz, sizeof(*dso->syms), sym_cmp);
518 
519 	close_elf(e, fd);
520 	return 0;
521 
522 err_out:
523 	dso__free_fields(dso);
524 	close_elf(e, fd);
525 	return -1;
526 }
527 
create_tmp_vdso_image(struct dso * dso)528 static int create_tmp_vdso_image(struct dso *dso)
529 {
530 	uint64_t start_addr, end_addr;
531 	long pid = getpid();
532 	char buf[PATH_MAX];
533 	void *image = NULL;
534 	char tmpfile[128];
535 	int ret, fd = -1;
536 	uint64_t sz;
537 	char *name;
538 	FILE *f;
539 
540 	snprintf(tmpfile, sizeof(tmpfile), "/proc/%ld/maps", pid);
541 	f = fopen(tmpfile, "r");
542 	if (!f)
543 		return -1;
544 
545 	while (true) {
546 		ret = fscanf(f, "%lx-%lx %*s %*x %*x:%*x %*u%[^\n]",
547 			     &start_addr, &end_addr, buf);
548 		if (ret == EOF && feof(f))
549 			break;
550 		if (ret != 3)
551 			goto err_out;
552 
553 		name = buf;
554 		while (isspace(*name))
555 			name++;
556 		if (!is_file_backed(name))
557 			continue;
558 		if (is_vdso(name))
559 			break;
560 	}
561 
562 	sz = end_addr - start_addr;
563 	image = malloc(sz);
564 	if (!image)
565 		goto err_out;
566 	memcpy(image, (void *)start_addr, sz);
567 
568 	snprintf(tmpfile, sizeof(tmpfile),
569 		 "/tmp/libbpf_%ld_vdso_image_XXXXXX", pid);
570 	fd = mkostemp(tmpfile, O_CLOEXEC);
571 	if (fd < 0) {
572 		fprintf(stderr, "failed to create temp file: %s\n",
573 			strerror(errno));
574 		goto err_out;
575 	}
576 	/* Unlink the file to avoid leaking */
577 	if (unlink(tmpfile) == -1)
578 		fprintf(stderr, "failed to unlink %s: %s\n", tmpfile,
579 			strerror(errno));
580 	if (write(fd, image, sz) == -1) {
581 		fprintf(stderr, "failed to write to vDSO image: %s\n",
582 			strerror(errno));
583 		close(fd);
584 		fd = -1;
585 		goto err_out;
586 	}
587 
588 err_out:
589 	fclose(f);
590 	free(image);
591 	return fd;
592 }
593 
dso__load_sym_table_from_vdso_image(struct dso * dso)594 static int dso__load_sym_table_from_vdso_image(struct dso *dso)
595 {
596 	int fd = create_tmp_vdso_image(dso);
597 
598 	if (fd < 0)
599 		return -1;
600 	return dso__load_sym_table_from_elf(dso, fd);
601 }
602 
dso__load_sym_table(struct dso * dso)603 static int dso__load_sym_table(struct dso *dso)
604 {
605 	if (dso->type == UNKNOWN)
606 		return -1;
607 	if (dso->type == PERF_MAP)
608 		return dso__load_sym_table_from_perf_map(dso);
609 	if (dso->type == EXEC || dso->type == DYN)
610 		return dso__load_sym_table_from_elf(dso, 0);
611 	if (dso->type == VDSO)
612 		return dso__load_sym_table_from_vdso_image(dso);
613 	return -1;
614 }
615 
dso__find_sym(struct dso * dso,uint64_t offset)616 static struct sym *dso__find_sym(struct dso *dso, uint64_t offset)
617 {
618 	unsigned long sym_addr;
619 	int start, end, mid;
620 
621 	if (!dso->syms && dso__load_sym_table(dso))
622 		return NULL;
623 
624 	start = 0;
625 	end = dso->syms_sz - 1;
626 
627 	/* find largest sym_addr <= addr using binary search */
628 	while (start < end) {
629 		mid = start + (end - start + 1) / 2;
630 		sym_addr = dso->syms[mid].start;
631 
632 		if (sym_addr <= offset)
633 			start = mid;
634 		else
635 			end = mid - 1;
636 	}
637 
638 	if (start == end && dso->syms[start].start <= offset)
639 		return &dso->syms[start];
640 	return NULL;
641 }
642 
syms__load_file(const char * fname)643 struct syms *syms__load_file(const char *fname)
644 {
645 	char buf[PATH_MAX], perm[5];
646 	struct syms *syms;
647 	struct map map;
648 	char *name;
649 	FILE *f;
650 	int ret;
651 
652 	f = fopen(fname, "r");
653 	if (!f)
654 		return NULL;
655 
656 	syms = calloc(1, sizeof(*syms));
657 	if (!syms)
658 		goto err_out;
659 
660 	while (true) {
661 		ret = fscanf(f, "%lx-%lx %4s %lx %lx:%lx %lu%[^\n]",
662 			     &map.start_addr, &map.end_addr, perm,
663 			     &map.file_off, &map.dev_major,
664 			     &map.dev_minor, &map.inode, buf);
665 		if (ret == EOF && feof(f))
666 			break;
667 		if (ret != 8)	/* perf-<PID>.map */
668 			goto err_out;
669 
670 		if (perm[2] != 'x')
671 			continue;
672 
673 		name = buf;
674 		while (isspace(*name))
675 			name++;
676 		if (!is_file_backed(name))
677 			continue;
678 
679 		if (syms__add_dso(syms, &map, name))
680 			goto err_out;
681 	}
682 
683 	fclose(f);
684 	return syms;
685 
686 err_out:
687 	syms__free(syms);
688 	fclose(f);
689 	return NULL;
690 }
691 
syms__load_pid(pid_t tgid)692 struct syms *syms__load_pid(pid_t tgid)
693 {
694 	char fname[128];
695 
696 	snprintf(fname, sizeof(fname), "/proc/%ld/maps", (long)tgid);
697 	return syms__load_file(fname);
698 }
699 
syms__free(struct syms * syms)700 void syms__free(struct syms *syms)
701 {
702 	int i;
703 
704 	if (!syms)
705 		return;
706 
707 	for (i = 0; i < syms->dso_sz; i++)
708 		dso__free_fields(&syms->dsos[i]);
709 	free(syms->dsos);
710 	free(syms);
711 }
712 
syms__map_addr(const struct syms * syms,unsigned long addr)713 const struct sym *syms__map_addr(const struct syms *syms, unsigned long addr)
714 {
715 	struct dso *dso;
716 	uint64_t offset;
717 
718 	dso = syms__find_dso(syms, addr, &offset);
719 	if (!dso)
720 		return NULL;
721 	return dso__find_sym(dso, offset);
722 }
723 
724 struct syms_cache {
725 	struct {
726 		struct syms *syms;
727 		int tgid;
728 	} *data;
729 	int nr;
730 };
731 
syms_cache__new(int nr)732 struct syms_cache *syms_cache__new(int nr)
733 {
734 	struct syms_cache *syms_cache;
735 
736 	syms_cache = calloc(1, sizeof(*syms_cache));
737 	if (!syms_cache)
738 		return NULL;
739 	if (nr > 0)
740 		syms_cache->data = calloc(nr, sizeof(*syms_cache->data));
741 	return syms_cache;
742 }
743 
syms_cache__free(struct syms_cache * syms_cache)744 void syms_cache__free(struct syms_cache *syms_cache)
745 {
746 	int i;
747 
748 	if (!syms_cache)
749 		return;
750 
751 	for (i = 0; i < syms_cache->nr; i++)
752 		syms__free(syms_cache->data[i].syms);
753 	free(syms_cache->data);
754 	free(syms_cache);
755 }
756 
syms_cache__get_syms(struct syms_cache * syms_cache,int tgid)757 struct syms *syms_cache__get_syms(struct syms_cache *syms_cache, int tgid)
758 {
759 	void *tmp;
760 	int i;
761 
762 	for (i = 0; i < syms_cache->nr; i++) {
763 		if (syms_cache->data[i].tgid == tgid)
764 			return syms_cache->data[i].syms;
765 	}
766 
767 	tmp = realloc(syms_cache->data, (syms_cache->nr + 1) *
768 		      sizeof(*syms_cache->data));
769 	if (!tmp)
770 		return NULL;
771 	syms_cache->data = tmp;
772 	syms_cache->data[syms_cache->nr].syms = syms__load_pid(tgid);
773 	syms_cache->data[syms_cache->nr].tgid = tgid;
774 	return syms_cache->data[syms_cache->nr++].syms;
775 }
776 
777 struct partitions {
778 	struct partition *items;
779 	int sz;
780 };
781 
partitions__add_partition(struct partitions * partitions,const char * name,unsigned int dev)782 static int partitions__add_partition(struct partitions *partitions,
783 				     const char *name, unsigned int dev)
784 {
785 	struct partition *partition;
786 	void *tmp;
787 
788 	tmp = realloc(partitions->items, (partitions->sz + 1) *
789 		sizeof(*partitions->items));
790 	if (!tmp)
791 		return -1;
792 	partitions->items = tmp;
793 	partition = &partitions->items[partitions->sz];
794 	partition->name = strdup(name);
795 	partition->dev = dev;
796 	partitions->sz++;
797 
798 	return 0;
799 }
800 
partitions__load(void)801 struct partitions *partitions__load(void)
802 {
803 	char part_name[DISK_NAME_LEN];
804 	unsigned int devmaj, devmin;
805 	unsigned long long nop;
806 	struct partitions *partitions;
807 	char buf[64];
808 	FILE *f;
809 
810 	f = fopen("/proc/partitions", "r");
811 	if (!f)
812 		return NULL;
813 
814 	partitions = calloc(1, sizeof(*partitions));
815 	if (!partitions)
816 		goto err_out;
817 
818 	while (fgets(buf, sizeof(buf), f) != NULL) {
819 		/* skip heading */
820 		if (buf[0] != ' ' || buf[0] == '\n')
821 			continue;
822 		if (sscanf(buf, "%u %u %llu %s", &devmaj, &devmin, &nop,
823 				part_name) != 4)
824 			goto err_out;
825 		if (partitions__add_partition(partitions, part_name,
826 						MKDEV(devmaj, devmin)))
827 			goto err_out;
828 	}
829 
830 	fclose(f);
831 	return partitions;
832 
833 err_out:
834 	partitions__free(partitions);
835 	fclose(f);
836 	return NULL;
837 }
838 
partitions__free(struct partitions * partitions)839 void partitions__free(struct partitions *partitions)
840 {
841 	int i;
842 
843 	if (!partitions)
844 		return;
845 
846 	for (i = 0; i < partitions->sz; i++)
847 		free(partitions->items[i].name);
848 	free(partitions->items);
849 	free(partitions);
850 }
851 
852 const struct partition *
partitions__get_by_dev(const struct partitions * partitions,unsigned int dev)853 partitions__get_by_dev(const struct partitions *partitions, unsigned int dev)
854 {
855 	int i;
856 
857 	for (i = 0; i < partitions->sz; i++) {
858 		if (partitions->items[i].dev == dev)
859 			return &partitions->items[i];
860 	}
861 
862 	return NULL;
863 }
864 
865 const struct partition *
partitions__get_by_name(const struct partitions * partitions,const char * name)866 partitions__get_by_name(const struct partitions *partitions, const char *name)
867 {
868 	int i;
869 
870 	for (i = 0; i < partitions->sz; i++) {
871 		if (strcmp(partitions->items[i].name, name) == 0)
872 			return &partitions->items[i];
873 	}
874 
875 	return NULL;
876 }
877 
print_stars(unsigned int val,unsigned int val_max,int width)878 static void print_stars(unsigned int val, unsigned int val_max, int width)
879 {
880 	int num_stars, num_spaces, i;
881 	bool need_plus;
882 
883 	num_stars = min(val, val_max) * width / val_max;
884 	num_spaces = width - num_stars;
885 	need_plus = val > val_max;
886 
887 	for (i = 0; i < num_stars; i++)
888 		printf("*");
889 	for (i = 0; i < num_spaces; i++)
890 		printf(" ");
891 	if (need_plus)
892 		printf("+");
893 }
894 
print_log2_hist(unsigned int * vals,int vals_size,const char * val_type)895 void print_log2_hist(unsigned int *vals, int vals_size, const char *val_type)
896 {
897 	int stars_max = 40, idx_max = -1;
898 	unsigned int val, val_max = 0;
899 	unsigned long long low, high;
900 	int stars, width, i;
901 
902 	for (i = 0; i < vals_size; i++) {
903 		val = vals[i];
904 		if (val > 0)
905 			idx_max = i;
906 		if (val > val_max)
907 			val_max = val;
908 	}
909 
910 	if (idx_max < 0)
911 		return;
912 
913 	printf("%*s%-*s : count    distribution\n", idx_max <= 32 ? 5 : 15, "",
914 		idx_max <= 32 ? 19 : 29, val_type);
915 
916 	if (idx_max <= 32)
917 		stars = stars_max;
918 	else
919 		stars = stars_max / 2;
920 
921 	for (i = 0; i <= idx_max; i++) {
922 		low = (1ULL << (i + 1)) >> 1;
923 		high = (1ULL << (i + 1)) - 1;
924 		if (low == high)
925 			low -= 1;
926 		val = vals[i];
927 		width = idx_max <= 32 ? 10 : 20;
928 		printf("%*lld -> %-*lld : %-8d |", width, low, width, high, val);
929 		print_stars(val, val_max, stars);
930 		printf("|\n");
931 	}
932 }
933 
print_linear_hist(unsigned int * vals,int vals_size,unsigned int base,unsigned int step,const char * val_type)934 void print_linear_hist(unsigned int *vals, int vals_size, unsigned int base,
935 		       unsigned int step, const char *val_type)
936 {
937 	int i, stars_max = 40, idx_min = -1, idx_max = -1;
938 	unsigned int val, val_max = 0;
939 
940 	for (i = 0; i < vals_size; i++) {
941 		val = vals[i];
942 		if (val > 0) {
943 			idx_max = i;
944 			if (idx_min < 0)
945 				idx_min = i;
946 		}
947 		if (val > val_max)
948 			val_max = val;
949 	}
950 
951 	if (idx_max < 0)
952 		return;
953 
954 	printf("     %-13s : count     distribution\n", val_type);
955 	for (i = idx_min; i <= idx_max; i++) {
956 		val = vals[i];
957 		printf("        %-10d : %-8d |", base + i * step, val);
958 		print_stars(val, val_max, stars_max);
959 		printf("|\n");
960 	}
961 }
962 
get_ktime_ns(void)963 unsigned long long get_ktime_ns(void)
964 {
965 	struct timespec ts;
966 
967 	clock_gettime(CLOCK_MONOTONIC, &ts);
968 	return ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec;
969 }
970 
is_kernel_module(const char * name)971 bool is_kernel_module(const char *name)
972 {
973 	bool found = false;
974 	char buf[64];
975 	FILE *f;
976 
977 	f = fopen("/proc/modules", "r");
978 	if (!f)
979 		return false;
980 
981 	while (fgets(buf, sizeof(buf), f) != NULL) {
982 		if (sscanf(buf, "%s %*s\n", buf) != 1)
983 			break;
984 		if (!strcmp(buf, name)) {
985 			found = true;
986 			break;
987 		}
988 	}
989 
990 	fclose(f);
991 	return found;
992 }
993 
fentry_try_attach(int id)994 static bool fentry_try_attach(int id)
995 {
996 	struct bpf_insn insns[] = { { .code = BPF_JMP | BPF_EXIT } };
997 	LIBBPF_OPTS(bpf_prog_load_opts, opts);
998 	int prog_fd, attach_fd;
999 
1000 	opts.expected_attach_type = BPF_TRACE_FENTRY;
1001 	opts.attach_btf_id = id,
1002 
1003 	prog_fd = bpf_prog_load(BPF_PROG_TYPE_TRACING, "test", NULL, insns, 1, &opts);
1004 	if (prog_fd < 0)
1005 		return false;
1006 
1007 	attach_fd = bpf_raw_tracepoint_open(NULL, prog_fd);
1008 	if (attach_fd >= 0)
1009 		close(attach_fd);
1010 
1011 	close(prog_fd);
1012 	return attach_fd >= 0;
1013 }
1014 
fentry_can_attach(const char * name,const char * mod)1015 bool fentry_can_attach(const char *name, const char *mod)
1016 {
1017 	const char sysfs_vmlinux[] = "/sys/kernel/btf/vmlinux";
1018 	struct btf *base, *btf = NULL;
1019 	char sysfs_mod[80];
1020 	int id = -1, err;
1021 
1022 	base = btf__parse(sysfs_vmlinux, NULL);
1023 	if (!base) {
1024 		err = -errno;
1025 		fprintf(stderr, "failed to parse vmlinux BTF at '%s': %s\n",
1026 			sysfs_vmlinux, strerror(-err));
1027 		goto err_out;
1028 	}
1029 	if (mod && module_btf_exists(mod)) {
1030 		snprintf(sysfs_mod, sizeof(sysfs_mod), "/sys/kernel/btf/%s", mod);
1031 		btf = btf__parse_split(sysfs_mod, base);
1032 		if (!btf) {
1033 			err = -errno;
1034 			fprintf(stderr, "failed to load BTF from %s: %s\n",
1035 				sysfs_mod, strerror(-err));
1036 			btf = base;
1037 			base = NULL;
1038 		}
1039 	} else {
1040 		btf = base;
1041 		base = NULL;
1042 	}
1043 
1044 	id = btf__find_by_name_kind(btf, name, BTF_KIND_FUNC);
1045 
1046 err_out:
1047 	btf__free(btf);
1048 	btf__free(base);
1049 	return id > 0 && fentry_try_attach(id);
1050 }
1051 
kprobe_exists(const char * name)1052 bool kprobe_exists(const char *name)
1053 {
1054 	char sym_name[256];
1055 	FILE *f;
1056 	int ret;
1057 
1058 	f = fopen("/sys/kernel/debug/tracing/available_filter_functions", "r");
1059 	if (!f)
1060 		goto slow_path;
1061 
1062 	while (true) {
1063 		ret = fscanf(f, "%s%*[^\n]\n", sym_name);
1064 		if (ret == EOF && feof(f))
1065 			break;
1066 		if (ret != 1) {
1067 			fprintf(stderr, "failed to read symbol from available_filter_functions\n");
1068 			break;
1069 		}
1070 		if (!strcmp(name, sym_name)) {
1071 			fclose(f);
1072 			return true;
1073 		}
1074 	}
1075 
1076 	fclose(f);
1077 	return false;
1078 
1079 slow_path:
1080 	f = fopen("/proc/kallsyms", "r");
1081 	if (!f)
1082 		return false;
1083 
1084 	while (true) {
1085 		ret = fscanf(f, "%*x %*c %s%*[^\n]\n", sym_name);
1086 		if (ret == EOF && feof(f))
1087 			break;
1088 		if (ret != 1) {
1089 			fprintf(stderr, "failed to read symbol from kallsyms\n");
1090 			break;
1091 		}
1092 		if (!strcmp(name, sym_name)) {
1093 			fclose(f);
1094 			return true;
1095 		}
1096 	}
1097 
1098 	fclose(f);
1099 	return false;
1100 }
1101 
vmlinux_btf_exists(void)1102 bool vmlinux_btf_exists(void)
1103 {
1104 	if (!access("/sys/kernel/btf/vmlinux", R_OK))
1105 		return true;
1106 	return false;
1107 }
1108 
module_btf_exists(const char * mod)1109 bool module_btf_exists(const char *mod)
1110 {
1111 	char sysfs_mod[80];
1112 
1113 	if (mod) {
1114 		snprintf(sysfs_mod, sizeof(sysfs_mod), "/sys/kernel/btf/%s", mod);
1115 		if (!access(sysfs_mod, R_OK))
1116 			return true;
1117 	}
1118 	return false;
1119 }
1120