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