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