diff options
author | Arnaldo Carvalho de Melo <acme@redhat.com> | 2012-12-07 15:39:39 -0500 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2012-12-09 06:46:08 -0500 |
commit | 3f067dcab711c2df7eefcfc5b3aa9a0e2b5f7d42 (patch) | |
tree | e90233f6af32b64f6fa230e5bf270b3ceea418fe | |
parent | fbb6976c2f7a6ab2c4d8511181d686f5f2aaf476 (diff) |
perf machine: Move more machine methods to machine.c
Mechanical, no functional changes.
Cc: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Namhyung Kim <namhyung@gmail.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Link: http://lkml.kernel.org/n/tip-9ib6qtqge1jmms2luwu4udbx@git.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
-rw-r--r-- | tools/perf/util/machine.c | 742 | ||||
-rw-r--r-- | tools/perf/util/machine.h | 3 | ||||
-rw-r--r-- | tools/perf/util/session.c | 210 | ||||
-rw-r--r-- | tools/perf/util/symbol.c | 522 | ||||
-rw-r--r-- | tools/perf/util/symbol.h | 4 | ||||
-rw-r--r-- | tools/perf/util/thread.c | 20 | ||||
-rw-r--r-- | tools/perf/util/thread.h | 1 |
7 files changed, 756 insertions, 746 deletions
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 1f09d0581e6b..71fa90391fe4 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c | |||
@@ -1,10 +1,15 @@ | |||
1 | #include "callchain.h" | ||
1 | #include "debug.h" | 2 | #include "debug.h" |
2 | #include "event.h" | 3 | #include "event.h" |
4 | #include "evsel.h" | ||
5 | #include "hist.h" | ||
3 | #include "machine.h" | 6 | #include "machine.h" |
4 | #include "map.h" | 7 | #include "map.h" |
8 | #include "sort.h" | ||
5 | #include "strlist.h" | 9 | #include "strlist.h" |
6 | #include "thread.h" | 10 | #include "thread.h" |
7 | #include <stdbool.h> | 11 | #include <stdbool.h> |
12 | #include "unwind.h" | ||
8 | 13 | ||
9 | int machine__init(struct machine *machine, const char *root_dir, pid_t pid) | 14 | int machine__init(struct machine *machine, const char *root_dir, pid_t pid) |
10 | { | 15 | { |
@@ -48,6 +53,29 @@ static void dsos__delete(struct list_head *dsos) | |||
48 | } | 53 | } |
49 | } | 54 | } |
50 | 55 | ||
56 | void machine__delete_dead_threads(struct machine *machine) | ||
57 | { | ||
58 | struct thread *n, *t; | ||
59 | |||
60 | list_for_each_entry_safe(t, n, &machine->dead_threads, node) { | ||
61 | list_del(&t->node); | ||
62 | thread__delete(t); | ||
63 | } | ||
64 | } | ||
65 | |||
66 | void machine__delete_threads(struct machine *machine) | ||
67 | { | ||
68 | struct rb_node *nd = rb_first(&machine->threads); | ||
69 | |||
70 | while (nd) { | ||
71 | struct thread *t = rb_entry(nd, struct thread, rb_node); | ||
72 | |||
73 | rb_erase(&t->rb_node, &machine->threads); | ||
74 | nd = rb_next(nd); | ||
75 | thread__delete(t); | ||
76 | } | ||
77 | } | ||
78 | |||
51 | void machine__exit(struct machine *machine) | 79 | void machine__exit(struct machine *machine) |
52 | { | 80 | { |
53 | map_groups__exit(&machine->kmaps); | 81 | map_groups__exit(&machine->kmaps); |
@@ -264,6 +292,534 @@ int machine__process_lost_event(struct machine *machine __maybe_unused, | |||
264 | return 0; | 292 | return 0; |
265 | } | 293 | } |
266 | 294 | ||
295 | struct map *machine__new_module(struct machine *machine, u64 start, | ||
296 | const char *filename) | ||
297 | { | ||
298 | struct map *map; | ||
299 | struct dso *dso = __dsos__findnew(&machine->kernel_dsos, filename); | ||
300 | |||
301 | if (dso == NULL) | ||
302 | return NULL; | ||
303 | |||
304 | map = map__new2(start, dso, MAP__FUNCTION); | ||
305 | if (map == NULL) | ||
306 | return NULL; | ||
307 | |||
308 | if (machine__is_host(machine)) | ||
309 | dso->symtab_type = DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE; | ||
310 | else | ||
311 | dso->symtab_type = DSO_BINARY_TYPE__GUEST_KMODULE; | ||
312 | map_groups__insert(&machine->kmaps, map); | ||
313 | return map; | ||
314 | } | ||
315 | |||
316 | size_t machines__fprintf_dsos(struct rb_root *machines, FILE *fp) | ||
317 | { | ||
318 | struct rb_node *nd; | ||
319 | size_t ret = 0; | ||
320 | |||
321 | for (nd = rb_first(machines); nd; nd = rb_next(nd)) { | ||
322 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | ||
323 | ret += __dsos__fprintf(&pos->kernel_dsos, fp); | ||
324 | ret += __dsos__fprintf(&pos->user_dsos, fp); | ||
325 | } | ||
326 | |||
327 | return ret; | ||
328 | } | ||
329 | |||
330 | size_t machine__fprintf_dsos_buildid(struct machine *machine, FILE *fp, | ||
331 | bool (skip)(struct dso *dso, int parm), int parm) | ||
332 | { | ||
333 | return __dsos__fprintf_buildid(&machine->kernel_dsos, fp, skip, parm) + | ||
334 | __dsos__fprintf_buildid(&machine->user_dsos, fp, skip, parm); | ||
335 | } | ||
336 | |||
337 | size_t machines__fprintf_dsos_buildid(struct rb_root *machines, FILE *fp, | ||
338 | bool (skip)(struct dso *dso, int parm), int parm) | ||
339 | { | ||
340 | struct rb_node *nd; | ||
341 | size_t ret = 0; | ||
342 | |||
343 | for (nd = rb_first(machines); nd; nd = rb_next(nd)) { | ||
344 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | ||
345 | ret += machine__fprintf_dsos_buildid(pos, fp, skip, parm); | ||
346 | } | ||
347 | return ret; | ||
348 | } | ||
349 | |||
350 | size_t machine__fprintf_vmlinux_path(struct machine *machine, FILE *fp) | ||
351 | { | ||
352 | int i; | ||
353 | size_t printed = 0; | ||
354 | struct dso *kdso = machine->vmlinux_maps[MAP__FUNCTION]->dso; | ||
355 | |||
356 | if (kdso->has_build_id) { | ||
357 | char filename[PATH_MAX]; | ||
358 | if (dso__build_id_filename(kdso, filename, sizeof(filename))) | ||
359 | printed += fprintf(fp, "[0] %s\n", filename); | ||
360 | } | ||
361 | |||
362 | for (i = 0; i < vmlinux_path__nr_entries; ++i) | ||
363 | printed += fprintf(fp, "[%d] %s\n", | ||
364 | i + kdso->has_build_id, vmlinux_path[i]); | ||
365 | |||
366 | return printed; | ||
367 | } | ||
368 | |||
369 | size_t machine__fprintf(struct machine *machine, FILE *fp) | ||
370 | { | ||
371 | size_t ret = 0; | ||
372 | struct rb_node *nd; | ||
373 | |||
374 | for (nd = rb_first(&machine->threads); nd; nd = rb_next(nd)) { | ||
375 | struct thread *pos = rb_entry(nd, struct thread, rb_node); | ||
376 | |||
377 | ret += thread__fprintf(pos, fp); | ||
378 | } | ||
379 | |||
380 | return ret; | ||
381 | } | ||
382 | |||
383 | static struct dso *machine__get_kernel(struct machine *machine) | ||
384 | { | ||
385 | const char *vmlinux_name = NULL; | ||
386 | struct dso *kernel; | ||
387 | |||
388 | if (machine__is_host(machine)) { | ||
389 | vmlinux_name = symbol_conf.vmlinux_name; | ||
390 | if (!vmlinux_name) | ||
391 | vmlinux_name = "[kernel.kallsyms]"; | ||
392 | |||
393 | kernel = dso__kernel_findnew(machine, vmlinux_name, | ||
394 | "[kernel]", | ||
395 | DSO_TYPE_KERNEL); | ||
396 | } else { | ||
397 | char bf[PATH_MAX]; | ||
398 | |||
399 | if (machine__is_default_guest(machine)) | ||
400 | vmlinux_name = symbol_conf.default_guest_vmlinux_name; | ||
401 | if (!vmlinux_name) | ||
402 | vmlinux_name = machine__mmap_name(machine, bf, | ||
403 | sizeof(bf)); | ||
404 | |||
405 | kernel = dso__kernel_findnew(machine, vmlinux_name, | ||
406 | "[guest.kernel]", | ||
407 | DSO_TYPE_GUEST_KERNEL); | ||
408 | } | ||
409 | |||
410 | if (kernel != NULL && (!kernel->has_build_id)) | ||
411 | dso__read_running_kernel_build_id(kernel, machine); | ||
412 | |||
413 | return kernel; | ||
414 | } | ||
415 | |||
416 | struct process_args { | ||
417 | u64 start; | ||
418 | }; | ||
419 | |||
420 | static int symbol__in_kernel(void *arg, const char *name, | ||
421 | char type __maybe_unused, u64 start) | ||
422 | { | ||
423 | struct process_args *args = arg; | ||
424 | |||
425 | if (strchr(name, '[')) | ||
426 | return 0; | ||
427 | |||
428 | args->start = start; | ||
429 | return 1; | ||
430 | } | ||
431 | |||
432 | /* Figure out the start address of kernel map from /proc/kallsyms */ | ||
433 | static u64 machine__get_kernel_start_addr(struct machine *machine) | ||
434 | { | ||
435 | const char *filename; | ||
436 | char path[PATH_MAX]; | ||
437 | struct process_args args; | ||
438 | |||
439 | if (machine__is_host(machine)) { | ||
440 | filename = "/proc/kallsyms"; | ||
441 | } else { | ||
442 | if (machine__is_default_guest(machine)) | ||
443 | filename = (char *)symbol_conf.default_guest_kallsyms; | ||
444 | else { | ||
445 | sprintf(path, "%s/proc/kallsyms", machine->root_dir); | ||
446 | filename = path; | ||
447 | } | ||
448 | } | ||
449 | |||
450 | if (symbol__restricted_filename(filename, "/proc/kallsyms")) | ||
451 | return 0; | ||
452 | |||
453 | if (kallsyms__parse(filename, &args, symbol__in_kernel) <= 0) | ||
454 | return 0; | ||
455 | |||
456 | return args.start; | ||
457 | } | ||
458 | |||
459 | int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel) | ||
460 | { | ||
461 | enum map_type type; | ||
462 | u64 start = machine__get_kernel_start_addr(machine); | ||
463 | |||
464 | for (type = 0; type < MAP__NR_TYPES; ++type) { | ||
465 | struct kmap *kmap; | ||
466 | |||
467 | machine->vmlinux_maps[type] = map__new2(start, kernel, type); | ||
468 | if (machine->vmlinux_maps[type] == NULL) | ||
469 | return -1; | ||
470 | |||
471 | machine->vmlinux_maps[type]->map_ip = | ||
472 | machine->vmlinux_maps[type]->unmap_ip = | ||
473 | identity__map_ip; | ||
474 | kmap = map__kmap(machine->vmlinux_maps[type]); | ||
475 | kmap->kmaps = &machine->kmaps; | ||
476 | map_groups__insert(&machine->kmaps, | ||
477 | machine->vmlinux_maps[type]); | ||
478 | } | ||
479 | |||
480 | return 0; | ||
481 | } | ||
482 | |||
483 | void machine__destroy_kernel_maps(struct machine *machine) | ||
484 | { | ||
485 | enum map_type type; | ||
486 | |||
487 | for (type = 0; type < MAP__NR_TYPES; ++type) { | ||
488 | struct kmap *kmap; | ||
489 | |||
490 | if (machine->vmlinux_maps[type] == NULL) | ||
491 | continue; | ||
492 | |||
493 | kmap = map__kmap(machine->vmlinux_maps[type]); | ||
494 | map_groups__remove(&machine->kmaps, | ||
495 | machine->vmlinux_maps[type]); | ||
496 | if (kmap->ref_reloc_sym) { | ||
497 | /* | ||
498 | * ref_reloc_sym is shared among all maps, so free just | ||
499 | * on one of them. | ||
500 | */ | ||
501 | if (type == MAP__FUNCTION) { | ||
502 | free((char *)kmap->ref_reloc_sym->name); | ||
503 | kmap->ref_reloc_sym->name = NULL; | ||
504 | free(kmap->ref_reloc_sym); | ||
505 | } | ||
506 | kmap->ref_reloc_sym = NULL; | ||
507 | } | ||
508 | |||
509 | map__delete(machine->vmlinux_maps[type]); | ||
510 | machine->vmlinux_maps[type] = NULL; | ||
511 | } | ||
512 | } | ||
513 | |||
514 | int machines__create_guest_kernel_maps(struct rb_root *machines) | ||
515 | { | ||
516 | int ret = 0; | ||
517 | struct dirent **namelist = NULL; | ||
518 | int i, items = 0; | ||
519 | char path[PATH_MAX]; | ||
520 | pid_t pid; | ||
521 | char *endp; | ||
522 | |||
523 | if (symbol_conf.default_guest_vmlinux_name || | ||
524 | symbol_conf.default_guest_modules || | ||
525 | symbol_conf.default_guest_kallsyms) { | ||
526 | machines__create_kernel_maps(machines, DEFAULT_GUEST_KERNEL_ID); | ||
527 | } | ||
528 | |||
529 | if (symbol_conf.guestmount) { | ||
530 | items = scandir(symbol_conf.guestmount, &namelist, NULL, NULL); | ||
531 | if (items <= 0) | ||
532 | return -ENOENT; | ||
533 | for (i = 0; i < items; i++) { | ||
534 | if (!isdigit(namelist[i]->d_name[0])) { | ||
535 | /* Filter out . and .. */ | ||
536 | continue; | ||
537 | } | ||
538 | pid = (pid_t)strtol(namelist[i]->d_name, &endp, 10); | ||
539 | if ((*endp != '\0') || | ||
540 | (endp == namelist[i]->d_name) || | ||
541 | (errno == ERANGE)) { | ||
542 | pr_debug("invalid directory (%s). Skipping.\n", | ||
543 | namelist[i]->d_name); | ||
544 | continue; | ||
545 | } | ||
546 | sprintf(path, "%s/%s/proc/kallsyms", | ||
547 | symbol_conf.guestmount, | ||
548 | namelist[i]->d_name); | ||
549 | ret = access(path, R_OK); | ||
550 | if (ret) { | ||
551 | pr_debug("Can't access file %s\n", path); | ||
552 | goto failure; | ||
553 | } | ||
554 | machines__create_kernel_maps(machines, pid); | ||
555 | } | ||
556 | failure: | ||
557 | free(namelist); | ||
558 | } | ||
559 | |||
560 | return ret; | ||
561 | } | ||
562 | |||
563 | void machines__destroy_guest_kernel_maps(struct rb_root *machines) | ||
564 | { | ||
565 | struct rb_node *next = rb_first(machines); | ||
566 | |||
567 | while (next) { | ||
568 | struct machine *pos = rb_entry(next, struct machine, rb_node); | ||
569 | |||
570 | next = rb_next(&pos->rb_node); | ||
571 | rb_erase(&pos->rb_node, machines); | ||
572 | machine__delete(pos); | ||
573 | } | ||
574 | } | ||
575 | |||
576 | int machines__create_kernel_maps(struct rb_root *machines, pid_t pid) | ||
577 | { | ||
578 | struct machine *machine = machines__findnew(machines, pid); | ||
579 | |||
580 | if (machine == NULL) | ||
581 | return -1; | ||
582 | |||
583 | return machine__create_kernel_maps(machine); | ||
584 | } | ||
585 | |||
586 | int machine__load_kallsyms(struct machine *machine, const char *filename, | ||
587 | enum map_type type, symbol_filter_t filter) | ||
588 | { | ||
589 | struct map *map = machine->vmlinux_maps[type]; | ||
590 | int ret = dso__load_kallsyms(map->dso, filename, map, filter); | ||
591 | |||
592 | if (ret > 0) { | ||
593 | dso__set_loaded(map->dso, type); | ||
594 | /* | ||
595 | * Since /proc/kallsyms will have multiple sessions for the | ||
596 | * kernel, with modules between them, fixup the end of all | ||
597 | * sections. | ||
598 | */ | ||
599 | __map_groups__fixup_end(&machine->kmaps, type); | ||
600 | } | ||
601 | |||
602 | return ret; | ||
603 | } | ||
604 | |||
605 | int machine__load_vmlinux_path(struct machine *machine, enum map_type type, | ||
606 | symbol_filter_t filter) | ||
607 | { | ||
608 | struct map *map = machine->vmlinux_maps[type]; | ||
609 | int ret = dso__load_vmlinux_path(map->dso, map, filter); | ||
610 | |||
611 | if (ret > 0) { | ||
612 | dso__set_loaded(map->dso, type); | ||
613 | map__reloc_vmlinux(map); | ||
614 | } | ||
615 | |||
616 | return ret; | ||
617 | } | ||
618 | |||
619 | static void map_groups__fixup_end(struct map_groups *mg) | ||
620 | { | ||
621 | int i; | ||
622 | for (i = 0; i < MAP__NR_TYPES; ++i) | ||
623 | __map_groups__fixup_end(mg, i); | ||
624 | } | ||
625 | |||
626 | static char *get_kernel_version(const char *root_dir) | ||
627 | { | ||
628 | char version[PATH_MAX]; | ||
629 | FILE *file; | ||
630 | char *name, *tmp; | ||
631 | const char *prefix = "Linux version "; | ||
632 | |||
633 | sprintf(version, "%s/proc/version", root_dir); | ||
634 | file = fopen(version, "r"); | ||
635 | if (!file) | ||
636 | return NULL; | ||
637 | |||
638 | version[0] = '\0'; | ||
639 | tmp = fgets(version, sizeof(version), file); | ||
640 | fclose(file); | ||
641 | |||
642 | name = strstr(version, prefix); | ||
643 | if (!name) | ||
644 | return NULL; | ||
645 | name += strlen(prefix); | ||
646 | tmp = strchr(name, ' '); | ||
647 | if (tmp) | ||
648 | *tmp = '\0'; | ||
649 | |||
650 | return strdup(name); | ||
651 | } | ||
652 | |||
653 | static int map_groups__set_modules_path_dir(struct map_groups *mg, | ||
654 | const char *dir_name) | ||
655 | { | ||
656 | struct dirent *dent; | ||
657 | DIR *dir = opendir(dir_name); | ||
658 | int ret = 0; | ||
659 | |||
660 | if (!dir) { | ||
661 | pr_debug("%s: cannot open %s dir\n", __func__, dir_name); | ||
662 | return -1; | ||
663 | } | ||
664 | |||
665 | while ((dent = readdir(dir)) != NULL) { | ||
666 | char path[PATH_MAX]; | ||
667 | struct stat st; | ||
668 | |||
669 | /*sshfs might return bad dent->d_type, so we have to stat*/ | ||
670 | snprintf(path, sizeof(path), "%s/%s", dir_name, dent->d_name); | ||
671 | if (stat(path, &st)) | ||
672 | continue; | ||
673 | |||
674 | if (S_ISDIR(st.st_mode)) { | ||
675 | if (!strcmp(dent->d_name, ".") || | ||
676 | !strcmp(dent->d_name, "..")) | ||
677 | continue; | ||
678 | |||
679 | ret = map_groups__set_modules_path_dir(mg, path); | ||
680 | if (ret < 0) | ||
681 | goto out; | ||
682 | } else { | ||
683 | char *dot = strrchr(dent->d_name, '.'), | ||
684 | dso_name[PATH_MAX]; | ||
685 | struct map *map; | ||
686 | char *long_name; | ||
687 | |||
688 | if (dot == NULL || strcmp(dot, ".ko")) | ||
689 | continue; | ||
690 | snprintf(dso_name, sizeof(dso_name), "[%.*s]", | ||
691 | (int)(dot - dent->d_name), dent->d_name); | ||
692 | |||
693 | strxfrchar(dso_name, '-', '_'); | ||
694 | map = map_groups__find_by_name(mg, MAP__FUNCTION, | ||
695 | dso_name); | ||
696 | if (map == NULL) | ||
697 | continue; | ||
698 | |||
699 | long_name = strdup(path); | ||
700 | if (long_name == NULL) { | ||
701 | ret = -1; | ||
702 | goto out; | ||
703 | } | ||
704 | dso__set_long_name(map->dso, long_name); | ||
705 | map->dso->lname_alloc = 1; | ||
706 | dso__kernel_module_get_build_id(map->dso, ""); | ||
707 | } | ||
708 | } | ||
709 | |||
710 | out: | ||
711 | closedir(dir); | ||
712 | return ret; | ||
713 | } | ||
714 | |||
715 | static int machine__set_modules_path(struct machine *machine) | ||
716 | { | ||
717 | char *version; | ||
718 | char modules_path[PATH_MAX]; | ||
719 | |||
720 | version = get_kernel_version(machine->root_dir); | ||
721 | if (!version) | ||
722 | return -1; | ||
723 | |||
724 | snprintf(modules_path, sizeof(modules_path), "%s/lib/modules/%s/kernel", | ||
725 | machine->root_dir, version); | ||
726 | free(version); | ||
727 | |||
728 | return map_groups__set_modules_path_dir(&machine->kmaps, modules_path); | ||
729 | } | ||
730 | |||
731 | static int machine__create_modules(struct machine *machine) | ||
732 | { | ||
733 | char *line = NULL; | ||
734 | size_t n; | ||
735 | FILE *file; | ||
736 | struct map *map; | ||
737 | const char *modules; | ||
738 | char path[PATH_MAX]; | ||
739 | |||
740 | if (machine__is_default_guest(machine)) | ||
741 | modules = symbol_conf.default_guest_modules; | ||
742 | else { | ||
743 | sprintf(path, "%s/proc/modules", machine->root_dir); | ||
744 | modules = path; | ||
745 | } | ||
746 | |||
747 | if (symbol__restricted_filename(path, "/proc/modules")) | ||
748 | return -1; | ||
749 | |||
750 | file = fopen(modules, "r"); | ||
751 | if (file == NULL) | ||
752 | return -1; | ||
753 | |||
754 | while (!feof(file)) { | ||
755 | char name[PATH_MAX]; | ||
756 | u64 start; | ||
757 | char *sep; | ||
758 | int line_len; | ||
759 | |||
760 | line_len = getline(&line, &n, file); | ||
761 | if (line_len < 0) | ||
762 | break; | ||
763 | |||
764 | if (!line) | ||
765 | goto out_failure; | ||
766 | |||
767 | line[--line_len] = '\0'; /* \n */ | ||
768 | |||
769 | sep = strrchr(line, 'x'); | ||
770 | if (sep == NULL) | ||
771 | continue; | ||
772 | |||
773 | hex2u64(sep + 1, &start); | ||
774 | |||
775 | sep = strchr(line, ' '); | ||
776 | if (sep == NULL) | ||
777 | continue; | ||
778 | |||
779 | *sep = '\0'; | ||
780 | |||
781 | snprintf(name, sizeof(name), "[%s]", line); | ||
782 | map = machine__new_module(machine, start, name); | ||
783 | if (map == NULL) | ||
784 | goto out_delete_line; | ||
785 | dso__kernel_module_get_build_id(map->dso, machine->root_dir); | ||
786 | } | ||
787 | |||
788 | free(line); | ||
789 | fclose(file); | ||
790 | |||
791 | return machine__set_modules_path(machine); | ||
792 | |||
793 | out_delete_line: | ||
794 | free(line); | ||
795 | out_failure: | ||
796 | return -1; | ||
797 | } | ||
798 | |||
799 | int machine__create_kernel_maps(struct machine *machine) | ||
800 | { | ||
801 | struct dso *kernel = machine__get_kernel(machine); | ||
802 | |||
803 | if (kernel == NULL || | ||
804 | __machine__create_kernel_maps(machine, kernel) < 0) | ||
805 | return -1; | ||
806 | |||
807 | if (symbol_conf.use_modules && machine__create_modules(machine) < 0) { | ||
808 | if (machine__is_host(machine)) | ||
809 | pr_debug("Problems creating module maps, " | ||
810 | "continuing anyway...\n"); | ||
811 | else | ||
812 | pr_debug("Problems creating module maps for guest %d, " | ||
813 | "continuing anyway...\n", machine->pid); | ||
814 | } | ||
815 | |||
816 | /* | ||
817 | * Now that we have all the maps created, just set the ->end of them: | ||
818 | */ | ||
819 | map_groups__fixup_end(&machine->kmaps); | ||
820 | return 0; | ||
821 | } | ||
822 | |||
267 | static void machine__set_kernel_mmap_len(struct machine *machine, | 823 | static void machine__set_kernel_mmap_len(struct machine *machine, |
268 | union perf_event *event) | 824 | union perf_event *event) |
269 | { | 825 | { |
@@ -462,3 +1018,189 @@ int machine__process_event(struct machine *machine, union perf_event *event) | |||
462 | 1018 | ||
463 | return ret; | 1019 | return ret; |
464 | } | 1020 | } |
1021 | |||
1022 | void machine__remove_thread(struct machine *machine, struct thread *th) | ||
1023 | { | ||
1024 | machine->last_match = NULL; | ||
1025 | rb_erase(&th->rb_node, &machine->threads); | ||
1026 | /* | ||
1027 | * We may have references to this thread, for instance in some hist_entry | ||
1028 | * instances, so just move them to a separate list. | ||
1029 | */ | ||
1030 | list_add_tail(&th->node, &machine->dead_threads); | ||
1031 | } | ||
1032 | |||
1033 | static bool symbol__match_parent_regex(struct symbol *sym) | ||
1034 | { | ||
1035 | if (sym->name && !regexec(&parent_regex, sym->name, 0, NULL, 0)) | ||
1036 | return 1; | ||
1037 | |||
1038 | return 0; | ||
1039 | } | ||
1040 | |||
1041 | static const u8 cpumodes[] = { | ||
1042 | PERF_RECORD_MISC_USER, | ||
1043 | PERF_RECORD_MISC_KERNEL, | ||
1044 | PERF_RECORD_MISC_GUEST_USER, | ||
1045 | PERF_RECORD_MISC_GUEST_KERNEL | ||
1046 | }; | ||
1047 | #define NCPUMODES (sizeof(cpumodes)/sizeof(u8)) | ||
1048 | |||
1049 | static void ip__resolve_ams(struct machine *machine, struct thread *thread, | ||
1050 | struct addr_map_symbol *ams, | ||
1051 | u64 ip) | ||
1052 | { | ||
1053 | struct addr_location al; | ||
1054 | size_t i; | ||
1055 | u8 m; | ||
1056 | |||
1057 | memset(&al, 0, sizeof(al)); | ||
1058 | |||
1059 | for (i = 0; i < NCPUMODES; i++) { | ||
1060 | m = cpumodes[i]; | ||
1061 | /* | ||
1062 | * We cannot use the header.misc hint to determine whether a | ||
1063 | * branch stack address is user, kernel, guest, hypervisor. | ||
1064 | * Branches may straddle the kernel/user/hypervisor boundaries. | ||
1065 | * Thus, we have to try consecutively until we find a match | ||
1066 | * or else, the symbol is unknown | ||
1067 | */ | ||
1068 | thread__find_addr_location(thread, machine, m, MAP__FUNCTION, | ||
1069 | ip, &al, NULL); | ||
1070 | if (al.sym) | ||
1071 | goto found; | ||
1072 | } | ||
1073 | found: | ||
1074 | ams->addr = ip; | ||
1075 | ams->al_addr = al.addr; | ||
1076 | ams->sym = al.sym; | ||
1077 | ams->map = al.map; | ||
1078 | } | ||
1079 | |||
1080 | struct branch_info *machine__resolve_bstack(struct machine *machine, | ||
1081 | struct thread *thr, | ||
1082 | struct branch_stack *bs) | ||
1083 | { | ||
1084 | struct branch_info *bi; | ||
1085 | unsigned int i; | ||
1086 | |||
1087 | bi = calloc(bs->nr, sizeof(struct branch_info)); | ||
1088 | if (!bi) | ||
1089 | return NULL; | ||
1090 | |||
1091 | for (i = 0; i < bs->nr; i++) { | ||
1092 | ip__resolve_ams(machine, thr, &bi[i].to, bs->entries[i].to); | ||
1093 | ip__resolve_ams(machine, thr, &bi[i].from, bs->entries[i].from); | ||
1094 | bi[i].flags = bs->entries[i].flags; | ||
1095 | } | ||
1096 | return bi; | ||
1097 | } | ||
1098 | |||
1099 | static int machine__resolve_callchain_sample(struct machine *machine, | ||
1100 | struct thread *thread, | ||
1101 | struct ip_callchain *chain, | ||
1102 | struct symbol **parent) | ||
1103 | |||
1104 | { | ||
1105 | u8 cpumode = PERF_RECORD_MISC_USER; | ||
1106 | unsigned int i; | ||
1107 | int err; | ||
1108 | |||
1109 | callchain_cursor_reset(&callchain_cursor); | ||
1110 | |||
1111 | if (chain->nr > PERF_MAX_STACK_DEPTH) { | ||
1112 | pr_warning("corrupted callchain. skipping...\n"); | ||
1113 | return 0; | ||
1114 | } | ||
1115 | |||
1116 | for (i = 0; i < chain->nr; i++) { | ||
1117 | u64 ip; | ||
1118 | struct addr_location al; | ||
1119 | |||
1120 | if (callchain_param.order == ORDER_CALLEE) | ||
1121 | ip = chain->ips[i]; | ||
1122 | else | ||
1123 | ip = chain->ips[chain->nr - i - 1]; | ||
1124 | |||
1125 | if (ip >= PERF_CONTEXT_MAX) { | ||
1126 | switch (ip) { | ||
1127 | case PERF_CONTEXT_HV: | ||
1128 | cpumode = PERF_RECORD_MISC_HYPERVISOR; | ||
1129 | break; | ||
1130 | case PERF_CONTEXT_KERNEL: | ||
1131 | cpumode = PERF_RECORD_MISC_KERNEL; | ||
1132 | break; | ||
1133 | case PERF_CONTEXT_USER: | ||
1134 | cpumode = PERF_RECORD_MISC_USER; | ||
1135 | break; | ||
1136 | default: | ||
1137 | pr_debug("invalid callchain context: " | ||
1138 | "%"PRId64"\n", (s64) ip); | ||
1139 | /* | ||
1140 | * It seems the callchain is corrupted. | ||
1141 | * Discard all. | ||
1142 | */ | ||
1143 | callchain_cursor_reset(&callchain_cursor); | ||
1144 | return 0; | ||
1145 | } | ||
1146 | continue; | ||
1147 | } | ||
1148 | |||
1149 | al.filtered = false; | ||
1150 | thread__find_addr_location(thread, machine, cpumode, | ||
1151 | MAP__FUNCTION, ip, &al, NULL); | ||
1152 | if (al.sym != NULL) { | ||
1153 | if (sort__has_parent && !*parent && | ||
1154 | symbol__match_parent_regex(al.sym)) | ||
1155 | *parent = al.sym; | ||
1156 | if (!symbol_conf.use_callchain) | ||
1157 | break; | ||
1158 | } | ||
1159 | |||
1160 | err = callchain_cursor_append(&callchain_cursor, | ||
1161 | ip, al.map, al.sym); | ||
1162 | if (err) | ||
1163 | return err; | ||
1164 | } | ||
1165 | |||
1166 | return 0; | ||
1167 | } | ||
1168 | |||
1169 | static int unwind_entry(struct unwind_entry *entry, void *arg) | ||
1170 | { | ||
1171 | struct callchain_cursor *cursor = arg; | ||
1172 | return callchain_cursor_append(cursor, entry->ip, | ||
1173 | entry->map, entry->sym); | ||
1174 | } | ||
1175 | |||
1176 | int machine__resolve_callchain(struct machine *machine, | ||
1177 | struct perf_evsel *evsel, | ||
1178 | struct thread *thread, | ||
1179 | struct perf_sample *sample, | ||
1180 | struct symbol **parent) | ||
1181 | |||
1182 | { | ||
1183 | int ret; | ||
1184 | |||
1185 | callchain_cursor_reset(&callchain_cursor); | ||
1186 | |||
1187 | ret = machine__resolve_callchain_sample(machine, thread, | ||
1188 | sample->callchain, parent); | ||
1189 | if (ret) | ||
1190 | return ret; | ||
1191 | |||
1192 | /* Can we do dwarf post unwind? */ | ||
1193 | if (!((evsel->attr.sample_type & PERF_SAMPLE_REGS_USER) && | ||
1194 | (evsel->attr.sample_type & PERF_SAMPLE_STACK_USER))) | ||
1195 | return 0; | ||
1196 | |||
1197 | /* Bail out if nothing was captured. */ | ||
1198 | if ((!sample->user_regs.regs) || | ||
1199 | (!sample->user_stack.size)) | ||
1200 | return 0; | ||
1201 | |||
1202 | return unwind__get_entries(unwind_entry, &callchain_cursor, machine, | ||
1203 | thread, evsel->attr.sample_regs_user, | ||
1204 | sample); | ||
1205 | |||
1206 | } | ||
diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index 646ad13005d3..e11236878ec1 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h | |||
@@ -61,9 +61,10 @@ char *machine__mmap_name(struct machine *machine, char *bf, size_t size); | |||
61 | 61 | ||
62 | int machine__init(struct machine *machine, const char *root_dir, pid_t pid); | 62 | int machine__init(struct machine *machine, const char *root_dir, pid_t pid); |
63 | void machine__exit(struct machine *machine); | 63 | void machine__exit(struct machine *machine); |
64 | void machine__delete_dead_threads(struct machine *machine); | ||
65 | void machine__delete_threads(struct machine *machine); | ||
64 | void machine__delete(struct machine *machine); | 66 | void machine__delete(struct machine *machine); |
65 | 67 | ||
66 | |||
67 | struct branch_info *machine__resolve_bstack(struct machine *machine, | 68 | struct branch_info *machine__resolve_bstack(struct machine *machine, |
68 | struct thread *thread, | 69 | struct thread *thread, |
69 | struct branch_stack *bs); | 70 | struct branch_stack *bs); |
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 8d04dfbc205c..76d6e257b8a4 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
@@ -16,7 +16,6 @@ | |||
16 | #include "cpumap.h" | 16 | #include "cpumap.h" |
17 | #include "event-parse.h" | 17 | #include "event-parse.h" |
18 | #include "perf_regs.h" | 18 | #include "perf_regs.h" |
19 | #include "unwind.h" | ||
20 | #include "vdso.h" | 19 | #include "vdso.h" |
21 | 20 | ||
22 | static int perf_session__open(struct perf_session *self, bool force) | 21 | static int perf_session__open(struct perf_session *self, bool force) |
@@ -162,34 +161,11 @@ out_delete: | |||
162 | return NULL; | 161 | return NULL; |
163 | } | 162 | } |
164 | 163 | ||
165 | static void machine__delete_dead_threads(struct machine *machine) | ||
166 | { | ||
167 | struct thread *n, *t; | ||
168 | |||
169 | list_for_each_entry_safe(t, n, &machine->dead_threads, node) { | ||
170 | list_del(&t->node); | ||
171 | thread__delete(t); | ||
172 | } | ||
173 | } | ||
174 | |||
175 | static void perf_session__delete_dead_threads(struct perf_session *session) | 164 | static void perf_session__delete_dead_threads(struct perf_session *session) |
176 | { | 165 | { |
177 | machine__delete_dead_threads(&session->host_machine); | 166 | machine__delete_dead_threads(&session->host_machine); |
178 | } | 167 | } |
179 | 168 | ||
180 | static void machine__delete_threads(struct machine *self) | ||
181 | { | ||
182 | struct rb_node *nd = rb_first(&self->threads); | ||
183 | |||
184 | while (nd) { | ||
185 | struct thread *t = rb_entry(nd, struct thread, rb_node); | ||
186 | |||
187 | rb_erase(&t->rb_node, &self->threads); | ||
188 | nd = rb_next(nd); | ||
189 | thread__delete(t); | ||
190 | } | ||
191 | } | ||
192 | |||
193 | static void perf_session__delete_threads(struct perf_session *session) | 169 | static void perf_session__delete_threads(struct perf_session *session) |
194 | { | 170 | { |
195 | machine__delete_threads(&session->host_machine); | 171 | machine__delete_threads(&session->host_machine); |
@@ -223,192 +199,6 @@ void perf_session__delete(struct perf_session *self) | |||
223 | vdso__exit(); | 199 | vdso__exit(); |
224 | } | 200 | } |
225 | 201 | ||
226 | void machine__remove_thread(struct machine *self, struct thread *th) | ||
227 | { | ||
228 | self->last_match = NULL; | ||
229 | rb_erase(&th->rb_node, &self->threads); | ||
230 | /* | ||
231 | * We may have references to this thread, for instance in some hist_entry | ||
232 | * instances, so just move them to a separate list. | ||
233 | */ | ||
234 | list_add_tail(&th->node, &self->dead_threads); | ||
235 | } | ||
236 | |||
237 | static bool symbol__match_parent_regex(struct symbol *sym) | ||
238 | { | ||
239 | if (sym->name && !regexec(&parent_regex, sym->name, 0, NULL, 0)) | ||
240 | return 1; | ||
241 | |||
242 | return 0; | ||
243 | } | ||
244 | |||
245 | static const u8 cpumodes[] = { | ||
246 | PERF_RECORD_MISC_USER, | ||
247 | PERF_RECORD_MISC_KERNEL, | ||
248 | PERF_RECORD_MISC_GUEST_USER, | ||
249 | PERF_RECORD_MISC_GUEST_KERNEL | ||
250 | }; | ||
251 | #define NCPUMODES (sizeof(cpumodes)/sizeof(u8)) | ||
252 | |||
253 | static void ip__resolve_ams(struct machine *self, struct thread *thread, | ||
254 | struct addr_map_symbol *ams, | ||
255 | u64 ip) | ||
256 | { | ||
257 | struct addr_location al; | ||
258 | size_t i; | ||
259 | u8 m; | ||
260 | |||
261 | memset(&al, 0, sizeof(al)); | ||
262 | |||
263 | for (i = 0; i < NCPUMODES; i++) { | ||
264 | m = cpumodes[i]; | ||
265 | /* | ||
266 | * We cannot use the header.misc hint to determine whether a | ||
267 | * branch stack address is user, kernel, guest, hypervisor. | ||
268 | * Branches may straddle the kernel/user/hypervisor boundaries. | ||
269 | * Thus, we have to try consecutively until we find a match | ||
270 | * or else, the symbol is unknown | ||
271 | */ | ||
272 | thread__find_addr_location(thread, self, m, MAP__FUNCTION, | ||
273 | ip, &al, NULL); | ||
274 | if (al.sym) | ||
275 | goto found; | ||
276 | } | ||
277 | found: | ||
278 | ams->addr = ip; | ||
279 | ams->al_addr = al.addr; | ||
280 | ams->sym = al.sym; | ||
281 | ams->map = al.map; | ||
282 | } | ||
283 | |||
284 | struct branch_info *machine__resolve_bstack(struct machine *self, | ||
285 | struct thread *thr, | ||
286 | struct branch_stack *bs) | ||
287 | { | ||
288 | struct branch_info *bi; | ||
289 | unsigned int i; | ||
290 | |||
291 | bi = calloc(bs->nr, sizeof(struct branch_info)); | ||
292 | if (!bi) | ||
293 | return NULL; | ||
294 | |||
295 | for (i = 0; i < bs->nr; i++) { | ||
296 | ip__resolve_ams(self, thr, &bi[i].to, bs->entries[i].to); | ||
297 | ip__resolve_ams(self, thr, &bi[i].from, bs->entries[i].from); | ||
298 | bi[i].flags = bs->entries[i].flags; | ||
299 | } | ||
300 | return bi; | ||
301 | } | ||
302 | |||
303 | static int machine__resolve_callchain_sample(struct machine *machine, | ||
304 | struct thread *thread, | ||
305 | struct ip_callchain *chain, | ||
306 | struct symbol **parent) | ||
307 | |||
308 | { | ||
309 | u8 cpumode = PERF_RECORD_MISC_USER; | ||
310 | unsigned int i; | ||
311 | int err; | ||
312 | |||
313 | callchain_cursor_reset(&callchain_cursor); | ||
314 | |||
315 | if (chain->nr > PERF_MAX_STACK_DEPTH) { | ||
316 | pr_warning("corrupted callchain. skipping...\n"); | ||
317 | return 0; | ||
318 | } | ||
319 | |||
320 | for (i = 0; i < chain->nr; i++) { | ||
321 | u64 ip; | ||
322 | struct addr_location al; | ||
323 | |||
324 | if (callchain_param.order == ORDER_CALLEE) | ||
325 | ip = chain->ips[i]; | ||
326 | else | ||
327 | ip = chain->ips[chain->nr - i - 1]; | ||
328 | |||
329 | if (ip >= PERF_CONTEXT_MAX) { | ||
330 | switch (ip) { | ||
331 | case PERF_CONTEXT_HV: | ||
332 | cpumode = PERF_RECORD_MISC_HYPERVISOR; | ||
333 | break; | ||
334 | case PERF_CONTEXT_KERNEL: | ||
335 | cpumode = PERF_RECORD_MISC_KERNEL; | ||
336 | break; | ||
337 | case PERF_CONTEXT_USER: | ||
338 | cpumode = PERF_RECORD_MISC_USER; | ||
339 | break; | ||
340 | default: | ||
341 | pr_debug("invalid callchain context: " | ||
342 | "%"PRId64"\n", (s64) ip); | ||
343 | /* | ||
344 | * It seems the callchain is corrupted. | ||
345 | * Discard all. | ||
346 | */ | ||
347 | callchain_cursor_reset(&callchain_cursor); | ||
348 | return 0; | ||
349 | } | ||
350 | continue; | ||
351 | } | ||
352 | |||
353 | al.filtered = false; | ||
354 | thread__find_addr_location(thread, machine, cpumode, | ||
355 | MAP__FUNCTION, ip, &al, NULL); | ||
356 | if (al.sym != NULL) { | ||
357 | if (sort__has_parent && !*parent && | ||
358 | symbol__match_parent_regex(al.sym)) | ||
359 | *parent = al.sym; | ||
360 | if (!symbol_conf.use_callchain) | ||
361 | break; | ||
362 | } | ||
363 | |||
364 | err = callchain_cursor_append(&callchain_cursor, | ||
365 | ip, al.map, al.sym); | ||
366 | if (err) | ||
367 | return err; | ||
368 | } | ||
369 | |||
370 | return 0; | ||
371 | } | ||
372 | |||
373 | static int unwind_entry(struct unwind_entry *entry, void *arg) | ||
374 | { | ||
375 | struct callchain_cursor *cursor = arg; | ||
376 | return callchain_cursor_append(cursor, entry->ip, | ||
377 | entry->map, entry->sym); | ||
378 | } | ||
379 | |||
380 | int machine__resolve_callchain(struct machine *machine, | ||
381 | struct perf_evsel *evsel, | ||
382 | struct thread *thread, | ||
383 | struct perf_sample *sample, | ||
384 | struct symbol **parent) | ||
385 | |||
386 | { | ||
387 | int ret; | ||
388 | |||
389 | callchain_cursor_reset(&callchain_cursor); | ||
390 | |||
391 | ret = machine__resolve_callchain_sample(machine, thread, | ||
392 | sample->callchain, parent); | ||
393 | if (ret) | ||
394 | return ret; | ||
395 | |||
396 | /* Can we do dwarf post unwind? */ | ||
397 | if (!((evsel->attr.sample_type & PERF_SAMPLE_REGS_USER) && | ||
398 | (evsel->attr.sample_type & PERF_SAMPLE_STACK_USER))) | ||
399 | return 0; | ||
400 | |||
401 | /* Bail out if nothing was captured. */ | ||
402 | if ((!sample->user_regs.regs) || | ||
403 | (!sample->user_stack.size)) | ||
404 | return 0; | ||
405 | |||
406 | return unwind__get_entries(unwind_entry, &callchain_cursor, machine, | ||
407 | thread, evsel->attr.sample_regs_user, | ||
408 | sample); | ||
409 | |||
410 | } | ||
411 | |||
412 | static int process_event_synth_tracing_data_stub(union perf_event *event | 202 | static int process_event_synth_tracing_data_stub(union perf_event *event |
413 | __maybe_unused, | 203 | __maybe_unused, |
414 | struct perf_session *session | 204 | struct perf_session *session |
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index e5ba9840ac22..2960284d6ae1 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
@@ -28,8 +28,8 @@ static int dso__load_kernel_sym(struct dso *dso, struct map *map, | |||
28 | symbol_filter_t filter); | 28 | symbol_filter_t filter); |
29 | static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map, | 29 | static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map, |
30 | symbol_filter_t filter); | 30 | symbol_filter_t filter); |
31 | static int vmlinux_path__nr_entries; | 31 | int vmlinux_path__nr_entries; |
32 | static char **vmlinux_path; | 32 | char **vmlinux_path; |
33 | 33 | ||
34 | struct symbol_conf symbol_conf = { | 34 | struct symbol_conf symbol_conf = { |
35 | .exclude_other = true, | 35 | .exclude_other = true, |
@@ -202,13 +202,6 @@ void __map_groups__fixup_end(struct map_groups *mg, enum map_type type) | |||
202 | curr->end = ~0ULL; | 202 | curr->end = ~0ULL; |
203 | } | 203 | } |
204 | 204 | ||
205 | static void map_groups__fixup_end(struct map_groups *mg) | ||
206 | { | ||
207 | int i; | ||
208 | for (i = 0; i < MAP__NR_TYPES; ++i) | ||
209 | __map_groups__fixup_end(mg, i); | ||
210 | } | ||
211 | |||
212 | struct symbol *symbol__new(u64 start, u64 len, u8 binding, const char *name) | 205 | struct symbol *symbol__new(u64 start, u64 len, u8 binding, const char *name) |
213 | { | 206 | { |
214 | size_t namelen = strlen(name) + 1; | 207 | size_t namelen = strlen(name) + 1; |
@@ -652,8 +645,8 @@ discard_symbol: rb_erase(&pos->rb_node, root); | |||
652 | return count + moved; | 645 | return count + moved; |
653 | } | 646 | } |
654 | 647 | ||
655 | static bool symbol__restricted_filename(const char *filename, | 648 | bool symbol__restricted_filename(const char *filename, |
656 | const char *restricted_filename) | 649 | const char *restricted_filename) |
657 | { | 650 | { |
658 | bool restricted = false; | 651 | bool restricted = false; |
659 | 652 | ||
@@ -887,200 +880,6 @@ struct map *map_groups__find_by_name(struct map_groups *mg, | |||
887 | return NULL; | 880 | return NULL; |
888 | } | 881 | } |
889 | 882 | ||
890 | static int map_groups__set_modules_path_dir(struct map_groups *mg, | ||
891 | const char *dir_name) | ||
892 | { | ||
893 | struct dirent *dent; | ||
894 | DIR *dir = opendir(dir_name); | ||
895 | int ret = 0; | ||
896 | |||
897 | if (!dir) { | ||
898 | pr_debug("%s: cannot open %s dir\n", __func__, dir_name); | ||
899 | return -1; | ||
900 | } | ||
901 | |||
902 | while ((dent = readdir(dir)) != NULL) { | ||
903 | char path[PATH_MAX]; | ||
904 | struct stat st; | ||
905 | |||
906 | /*sshfs might return bad dent->d_type, so we have to stat*/ | ||
907 | snprintf(path, sizeof(path), "%s/%s", dir_name, dent->d_name); | ||
908 | if (stat(path, &st)) | ||
909 | continue; | ||
910 | |||
911 | if (S_ISDIR(st.st_mode)) { | ||
912 | if (!strcmp(dent->d_name, ".") || | ||
913 | !strcmp(dent->d_name, "..")) | ||
914 | continue; | ||
915 | |||
916 | ret = map_groups__set_modules_path_dir(mg, path); | ||
917 | if (ret < 0) | ||
918 | goto out; | ||
919 | } else { | ||
920 | char *dot = strrchr(dent->d_name, '.'), | ||
921 | dso_name[PATH_MAX]; | ||
922 | struct map *map; | ||
923 | char *long_name; | ||
924 | |||
925 | if (dot == NULL || strcmp(dot, ".ko")) | ||
926 | continue; | ||
927 | snprintf(dso_name, sizeof(dso_name), "[%.*s]", | ||
928 | (int)(dot - dent->d_name), dent->d_name); | ||
929 | |||
930 | strxfrchar(dso_name, '-', '_'); | ||
931 | map = map_groups__find_by_name(mg, MAP__FUNCTION, | ||
932 | dso_name); | ||
933 | if (map == NULL) | ||
934 | continue; | ||
935 | |||
936 | long_name = strdup(path); | ||
937 | if (long_name == NULL) { | ||
938 | ret = -1; | ||
939 | goto out; | ||
940 | } | ||
941 | dso__set_long_name(map->dso, long_name); | ||
942 | map->dso->lname_alloc = 1; | ||
943 | dso__kernel_module_get_build_id(map->dso, ""); | ||
944 | } | ||
945 | } | ||
946 | |||
947 | out: | ||
948 | closedir(dir); | ||
949 | return ret; | ||
950 | } | ||
951 | |||
952 | static char *get_kernel_version(const char *root_dir) | ||
953 | { | ||
954 | char version[PATH_MAX]; | ||
955 | FILE *file; | ||
956 | char *name, *tmp; | ||
957 | const char *prefix = "Linux version "; | ||
958 | |||
959 | sprintf(version, "%s/proc/version", root_dir); | ||
960 | file = fopen(version, "r"); | ||
961 | if (!file) | ||
962 | return NULL; | ||
963 | |||
964 | version[0] = '\0'; | ||
965 | tmp = fgets(version, sizeof(version), file); | ||
966 | fclose(file); | ||
967 | |||
968 | name = strstr(version, prefix); | ||
969 | if (!name) | ||
970 | return NULL; | ||
971 | name += strlen(prefix); | ||
972 | tmp = strchr(name, ' '); | ||
973 | if (tmp) | ||
974 | *tmp = '\0'; | ||
975 | |||
976 | return strdup(name); | ||
977 | } | ||
978 | |||
979 | static int machine__set_modules_path(struct machine *machine) | ||
980 | { | ||
981 | char *version; | ||
982 | char modules_path[PATH_MAX]; | ||
983 | |||
984 | version = get_kernel_version(machine->root_dir); | ||
985 | if (!version) | ||
986 | return -1; | ||
987 | |||
988 | snprintf(modules_path, sizeof(modules_path), "%s/lib/modules/%s/kernel", | ||
989 | machine->root_dir, version); | ||
990 | free(version); | ||
991 | |||
992 | return map_groups__set_modules_path_dir(&machine->kmaps, modules_path); | ||
993 | } | ||
994 | |||
995 | struct map *machine__new_module(struct machine *machine, u64 start, | ||
996 | const char *filename) | ||
997 | { | ||
998 | struct map *map; | ||
999 | struct dso *dso = __dsos__findnew(&machine->kernel_dsos, filename); | ||
1000 | |||
1001 | if (dso == NULL) | ||
1002 | return NULL; | ||
1003 | |||
1004 | map = map__new2(start, dso, MAP__FUNCTION); | ||
1005 | if (map == NULL) | ||
1006 | return NULL; | ||
1007 | |||
1008 | if (machine__is_host(machine)) | ||
1009 | dso->symtab_type = DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE; | ||
1010 | else | ||
1011 | dso->symtab_type = DSO_BINARY_TYPE__GUEST_KMODULE; | ||
1012 | map_groups__insert(&machine->kmaps, map); | ||
1013 | return map; | ||
1014 | } | ||
1015 | |||
1016 | static int machine__create_modules(struct machine *machine) | ||
1017 | { | ||
1018 | char *line = NULL; | ||
1019 | size_t n; | ||
1020 | FILE *file; | ||
1021 | struct map *map; | ||
1022 | const char *modules; | ||
1023 | char path[PATH_MAX]; | ||
1024 | |||
1025 | if (machine__is_default_guest(machine)) | ||
1026 | modules = symbol_conf.default_guest_modules; | ||
1027 | else { | ||
1028 | sprintf(path, "%s/proc/modules", machine->root_dir); | ||
1029 | modules = path; | ||
1030 | } | ||
1031 | |||
1032 | if (symbol__restricted_filename(path, "/proc/modules")) | ||
1033 | return -1; | ||
1034 | |||
1035 | file = fopen(modules, "r"); | ||
1036 | if (file == NULL) | ||
1037 | return -1; | ||
1038 | |||
1039 | while (!feof(file)) { | ||
1040 | char name[PATH_MAX]; | ||
1041 | u64 start; | ||
1042 | char *sep; | ||
1043 | int line_len; | ||
1044 | |||
1045 | line_len = getline(&line, &n, file); | ||
1046 | if (line_len < 0) | ||
1047 | break; | ||
1048 | |||
1049 | if (!line) | ||
1050 | goto out_failure; | ||
1051 | |||
1052 | line[--line_len] = '\0'; /* \n */ | ||
1053 | |||
1054 | sep = strrchr(line, 'x'); | ||
1055 | if (sep == NULL) | ||
1056 | continue; | ||
1057 | |||
1058 | hex2u64(sep + 1, &start); | ||
1059 | |||
1060 | sep = strchr(line, ' '); | ||
1061 | if (sep == NULL) | ||
1062 | continue; | ||
1063 | |||
1064 | *sep = '\0'; | ||
1065 | |||
1066 | snprintf(name, sizeof(name), "[%s]", line); | ||
1067 | map = machine__new_module(machine, start, name); | ||
1068 | if (map == NULL) | ||
1069 | goto out_delete_line; | ||
1070 | dso__kernel_module_get_build_id(map->dso, machine->root_dir); | ||
1071 | } | ||
1072 | |||
1073 | free(line); | ||
1074 | fclose(file); | ||
1075 | |||
1076 | return machine__set_modules_path(machine); | ||
1077 | |||
1078 | out_delete_line: | ||
1079 | free(line); | ||
1080 | out_failure: | ||
1081 | return -1; | ||
1082 | } | ||
1083 | |||
1084 | int dso__load_vmlinux(struct dso *dso, struct map *map, | 883 | int dso__load_vmlinux(struct dso *dso, struct map *map, |
1085 | const char *vmlinux, symbol_filter_t filter) | 884 | const char *vmlinux, symbol_filter_t filter) |
1086 | { | 885 | { |
@@ -1300,195 +1099,6 @@ out_try_fixup: | |||
1300 | return err; | 1099 | return err; |
1301 | } | 1100 | } |
1302 | 1101 | ||
1303 | size_t machines__fprintf_dsos(struct rb_root *machines, FILE *fp) | ||
1304 | { | ||
1305 | struct rb_node *nd; | ||
1306 | size_t ret = 0; | ||
1307 | |||
1308 | for (nd = rb_first(machines); nd; nd = rb_next(nd)) { | ||
1309 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | ||
1310 | ret += __dsos__fprintf(&pos->kernel_dsos, fp); | ||
1311 | ret += __dsos__fprintf(&pos->user_dsos, fp); | ||
1312 | } | ||
1313 | |||
1314 | return ret; | ||
1315 | } | ||
1316 | |||
1317 | size_t machine__fprintf_dsos_buildid(struct machine *machine, FILE *fp, | ||
1318 | bool (skip)(struct dso *dso, int parm), int parm) | ||
1319 | { | ||
1320 | return __dsos__fprintf_buildid(&machine->kernel_dsos, fp, skip, parm) + | ||
1321 | __dsos__fprintf_buildid(&machine->user_dsos, fp, skip, parm); | ||
1322 | } | ||
1323 | |||
1324 | size_t machines__fprintf_dsos_buildid(struct rb_root *machines, FILE *fp, | ||
1325 | bool (skip)(struct dso *dso, int parm), int parm) | ||
1326 | { | ||
1327 | struct rb_node *nd; | ||
1328 | size_t ret = 0; | ||
1329 | |||
1330 | for (nd = rb_first(machines); nd; nd = rb_next(nd)) { | ||
1331 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | ||
1332 | ret += machine__fprintf_dsos_buildid(pos, fp, skip, parm); | ||
1333 | } | ||
1334 | return ret; | ||
1335 | } | ||
1336 | |||
1337 | static struct dso *machine__get_kernel(struct machine *machine) | ||
1338 | { | ||
1339 | const char *vmlinux_name = NULL; | ||
1340 | struct dso *kernel; | ||
1341 | |||
1342 | if (machine__is_host(machine)) { | ||
1343 | vmlinux_name = symbol_conf.vmlinux_name; | ||
1344 | if (!vmlinux_name) | ||
1345 | vmlinux_name = "[kernel.kallsyms]"; | ||
1346 | |||
1347 | kernel = dso__kernel_findnew(machine, vmlinux_name, | ||
1348 | "[kernel]", | ||
1349 | DSO_TYPE_KERNEL); | ||
1350 | } else { | ||
1351 | char bf[PATH_MAX]; | ||
1352 | |||
1353 | if (machine__is_default_guest(machine)) | ||
1354 | vmlinux_name = symbol_conf.default_guest_vmlinux_name; | ||
1355 | if (!vmlinux_name) | ||
1356 | vmlinux_name = machine__mmap_name(machine, bf, | ||
1357 | sizeof(bf)); | ||
1358 | |||
1359 | kernel = dso__kernel_findnew(machine, vmlinux_name, | ||
1360 | "[guest.kernel]", | ||
1361 | DSO_TYPE_GUEST_KERNEL); | ||
1362 | } | ||
1363 | |||
1364 | if (kernel != NULL && (!kernel->has_build_id)) | ||
1365 | dso__read_running_kernel_build_id(kernel, machine); | ||
1366 | |||
1367 | return kernel; | ||
1368 | } | ||
1369 | |||
1370 | struct process_args { | ||
1371 | u64 start; | ||
1372 | }; | ||
1373 | |||
1374 | static int symbol__in_kernel(void *arg, const char *name, | ||
1375 | char type __maybe_unused, u64 start) | ||
1376 | { | ||
1377 | struct process_args *args = arg; | ||
1378 | |||
1379 | if (strchr(name, '[')) | ||
1380 | return 0; | ||
1381 | |||
1382 | args->start = start; | ||
1383 | return 1; | ||
1384 | } | ||
1385 | |||
1386 | /* Figure out the start address of kernel map from /proc/kallsyms */ | ||
1387 | static u64 machine__get_kernel_start_addr(struct machine *machine) | ||
1388 | { | ||
1389 | const char *filename; | ||
1390 | char path[PATH_MAX]; | ||
1391 | struct process_args args; | ||
1392 | |||
1393 | if (machine__is_host(machine)) { | ||
1394 | filename = "/proc/kallsyms"; | ||
1395 | } else { | ||
1396 | if (machine__is_default_guest(machine)) | ||
1397 | filename = (char *)symbol_conf.default_guest_kallsyms; | ||
1398 | else { | ||
1399 | sprintf(path, "%s/proc/kallsyms", machine->root_dir); | ||
1400 | filename = path; | ||
1401 | } | ||
1402 | } | ||
1403 | |||
1404 | if (symbol__restricted_filename(filename, "/proc/kallsyms")) | ||
1405 | return 0; | ||
1406 | |||
1407 | if (kallsyms__parse(filename, &args, symbol__in_kernel) <= 0) | ||
1408 | return 0; | ||
1409 | |||
1410 | return args.start; | ||
1411 | } | ||
1412 | |||
1413 | int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel) | ||
1414 | { | ||
1415 | enum map_type type; | ||
1416 | u64 start = machine__get_kernel_start_addr(machine); | ||
1417 | |||
1418 | for (type = 0; type < MAP__NR_TYPES; ++type) { | ||
1419 | struct kmap *kmap; | ||
1420 | |||
1421 | machine->vmlinux_maps[type] = map__new2(start, kernel, type); | ||
1422 | if (machine->vmlinux_maps[type] == NULL) | ||
1423 | return -1; | ||
1424 | |||
1425 | machine->vmlinux_maps[type]->map_ip = | ||
1426 | machine->vmlinux_maps[type]->unmap_ip = | ||
1427 | identity__map_ip; | ||
1428 | kmap = map__kmap(machine->vmlinux_maps[type]); | ||
1429 | kmap->kmaps = &machine->kmaps; | ||
1430 | map_groups__insert(&machine->kmaps, | ||
1431 | machine->vmlinux_maps[type]); | ||
1432 | } | ||
1433 | |||
1434 | return 0; | ||
1435 | } | ||
1436 | |||
1437 | void machine__destroy_kernel_maps(struct machine *machine) | ||
1438 | { | ||
1439 | enum map_type type; | ||
1440 | |||
1441 | for (type = 0; type < MAP__NR_TYPES; ++type) { | ||
1442 | struct kmap *kmap; | ||
1443 | |||
1444 | if (machine->vmlinux_maps[type] == NULL) | ||
1445 | continue; | ||
1446 | |||
1447 | kmap = map__kmap(machine->vmlinux_maps[type]); | ||
1448 | map_groups__remove(&machine->kmaps, | ||
1449 | machine->vmlinux_maps[type]); | ||
1450 | if (kmap->ref_reloc_sym) { | ||
1451 | /* | ||
1452 | * ref_reloc_sym is shared among all maps, so free just | ||
1453 | * on one of them. | ||
1454 | */ | ||
1455 | if (type == MAP__FUNCTION) { | ||
1456 | free((char *)kmap->ref_reloc_sym->name); | ||
1457 | kmap->ref_reloc_sym->name = NULL; | ||
1458 | free(kmap->ref_reloc_sym); | ||
1459 | } | ||
1460 | kmap->ref_reloc_sym = NULL; | ||
1461 | } | ||
1462 | |||
1463 | map__delete(machine->vmlinux_maps[type]); | ||
1464 | machine->vmlinux_maps[type] = NULL; | ||
1465 | } | ||
1466 | } | ||
1467 | |||
1468 | int machine__create_kernel_maps(struct machine *machine) | ||
1469 | { | ||
1470 | struct dso *kernel = machine__get_kernel(machine); | ||
1471 | |||
1472 | if (kernel == NULL || | ||
1473 | __machine__create_kernel_maps(machine, kernel) < 0) | ||
1474 | return -1; | ||
1475 | |||
1476 | if (symbol_conf.use_modules && machine__create_modules(machine) < 0) { | ||
1477 | if (machine__is_host(machine)) | ||
1478 | pr_debug("Problems creating module maps, " | ||
1479 | "continuing anyway...\n"); | ||
1480 | else | ||
1481 | pr_debug("Problems creating module maps for guest %d, " | ||
1482 | "continuing anyway...\n", machine->pid); | ||
1483 | } | ||
1484 | |||
1485 | /* | ||
1486 | * Now that we have all the maps created, just set the ->end of them: | ||
1487 | */ | ||
1488 | map_groups__fixup_end(&machine->kmaps); | ||
1489 | return 0; | ||
1490 | } | ||
1491 | |||
1492 | static void vmlinux_path__exit(void) | 1102 | static void vmlinux_path__exit(void) |
1493 | { | 1103 | { |
1494 | while (--vmlinux_path__nr_entries >= 0) { | 1104 | while (--vmlinux_path__nr_entries >= 0) { |
@@ -1549,25 +1159,6 @@ out_fail: | |||
1549 | return -1; | 1159 | return -1; |
1550 | } | 1160 | } |
1551 | 1161 | ||
1552 | size_t machine__fprintf_vmlinux_path(struct machine *machine, FILE *fp) | ||
1553 | { | ||
1554 | int i; | ||
1555 | size_t printed = 0; | ||
1556 | struct dso *kdso = machine->vmlinux_maps[MAP__FUNCTION]->dso; | ||
1557 | |||
1558 | if (kdso->has_build_id) { | ||
1559 | char filename[PATH_MAX]; | ||
1560 | if (dso__build_id_filename(kdso, filename, sizeof(filename))) | ||
1561 | printed += fprintf(fp, "[0] %s\n", filename); | ||
1562 | } | ||
1563 | |||
1564 | for (i = 0; i < vmlinux_path__nr_entries; ++i) | ||
1565 | printed += fprintf(fp, "[%d] %s\n", | ||
1566 | i + kdso->has_build_id, vmlinux_path[i]); | ||
1567 | |||
1568 | return printed; | ||
1569 | } | ||
1570 | |||
1571 | static int setup_list(struct strlist **list, const char *list_str, | 1162 | static int setup_list(struct strlist **list, const char *list_str, |
1572 | const char *list_name) | 1163 | const char *list_name) |
1573 | { | 1164 | { |
@@ -1671,108 +1262,3 @@ void symbol__exit(void) | |||
1671 | symbol_conf.sym_list = symbol_conf.dso_list = symbol_conf.comm_list = NULL; | 1262 | symbol_conf.sym_list = symbol_conf.dso_list = symbol_conf.comm_list = NULL; |
1672 | symbol_conf.initialized = false; | 1263 | symbol_conf.initialized = false; |
1673 | } | 1264 | } |
1674 | |||
1675 | int machines__create_kernel_maps(struct rb_root *machines, pid_t pid) | ||
1676 | { | ||
1677 | struct machine *machine = machines__findnew(machines, pid); | ||
1678 | |||
1679 | if (machine == NULL) | ||
1680 | return -1; | ||
1681 | |||
1682 | return machine__create_kernel_maps(machine); | ||
1683 | } | ||
1684 | |||
1685 | int machines__create_guest_kernel_maps(struct rb_root *machines) | ||
1686 | { | ||
1687 | int ret = 0; | ||
1688 | struct dirent **namelist = NULL; | ||
1689 | int i, items = 0; | ||
1690 | char path[PATH_MAX]; | ||
1691 | pid_t pid; | ||
1692 | char *endp; | ||
1693 | |||
1694 | if (symbol_conf.default_guest_vmlinux_name || | ||
1695 | symbol_conf.default_guest_modules || | ||
1696 | symbol_conf.default_guest_kallsyms) { | ||
1697 | machines__create_kernel_maps(machines, DEFAULT_GUEST_KERNEL_ID); | ||
1698 | } | ||
1699 | |||
1700 | if (symbol_conf.guestmount) { | ||
1701 | items = scandir(symbol_conf.guestmount, &namelist, NULL, NULL); | ||
1702 | if (items <= 0) | ||
1703 | return -ENOENT; | ||
1704 | for (i = 0; i < items; i++) { | ||
1705 | if (!isdigit(namelist[i]->d_name[0])) { | ||
1706 | /* Filter out . and .. */ | ||
1707 | continue; | ||
1708 | } | ||
1709 | pid = (pid_t)strtol(namelist[i]->d_name, &endp, 10); | ||
1710 | if ((*endp != '\0') || | ||
1711 | (endp == namelist[i]->d_name) || | ||
1712 | (errno == ERANGE)) { | ||
1713 | pr_debug("invalid directory (%s). Skipping.\n", | ||
1714 | namelist[i]->d_name); | ||
1715 | continue; | ||
1716 | } | ||
1717 | sprintf(path, "%s/%s/proc/kallsyms", | ||
1718 | symbol_conf.guestmount, | ||
1719 | namelist[i]->d_name); | ||
1720 | ret = access(path, R_OK); | ||
1721 | if (ret) { | ||
1722 | pr_debug("Can't access file %s\n", path); | ||
1723 | goto failure; | ||
1724 | } | ||
1725 | machines__create_kernel_maps(machines, pid); | ||
1726 | } | ||
1727 | failure: | ||
1728 | free(namelist); | ||
1729 | } | ||
1730 | |||
1731 | return ret; | ||
1732 | } | ||
1733 | |||
1734 | void machines__destroy_guest_kernel_maps(struct rb_root *machines) | ||
1735 | { | ||
1736 | struct rb_node *next = rb_first(machines); | ||
1737 | |||
1738 | while (next) { | ||
1739 | struct machine *pos = rb_entry(next, struct machine, rb_node); | ||
1740 | |||
1741 | next = rb_next(&pos->rb_node); | ||
1742 | rb_erase(&pos->rb_node, machines); | ||
1743 | machine__delete(pos); | ||
1744 | } | ||
1745 | } | ||
1746 | |||
1747 | int machine__load_kallsyms(struct machine *machine, const char *filename, | ||
1748 | enum map_type type, symbol_filter_t filter) | ||
1749 | { | ||
1750 | struct map *map = machine->vmlinux_maps[type]; | ||
1751 | int ret = dso__load_kallsyms(map->dso, filename, map, filter); | ||
1752 | |||
1753 | if (ret > 0) { | ||
1754 | dso__set_loaded(map->dso, type); | ||
1755 | /* | ||
1756 | * Since /proc/kallsyms will have multiple sessions for the | ||
1757 | * kernel, with modules between them, fixup the end of all | ||
1758 | * sections. | ||
1759 | */ | ||
1760 | __map_groups__fixup_end(&machine->kmaps, type); | ||
1761 | } | ||
1762 | |||
1763 | return ret; | ||
1764 | } | ||
1765 | |||
1766 | int machine__load_vmlinux_path(struct machine *machine, enum map_type type, | ||
1767 | symbol_filter_t filter) | ||
1768 | { | ||
1769 | struct map *map = machine->vmlinux_maps[type]; | ||
1770 | int ret = dso__load_vmlinux_path(map->dso, map, filter); | ||
1771 | |||
1772 | if (ret > 0) { | ||
1773 | dso__set_loaded(map->dso, type); | ||
1774 | map__reloc_vmlinux(map); | ||
1775 | } | ||
1776 | |||
1777 | return ret; | ||
1778 | } | ||
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index de68f98b236d..ec7b2405c377 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
@@ -120,6 +120,8 @@ struct symbol_conf { | |||
120 | }; | 120 | }; |
121 | 121 | ||
122 | extern struct symbol_conf symbol_conf; | 122 | extern struct symbol_conf symbol_conf; |
123 | extern int vmlinux_path__nr_entries; | ||
124 | extern char **vmlinux_path; | ||
123 | 125 | ||
124 | static inline void *symbol__priv(struct symbol *sym) | 126 | static inline void *symbol__priv(struct symbol *sym) |
125 | { | 127 | { |
@@ -223,6 +225,8 @@ size_t symbol__fprintf_symname_offs(const struct symbol *sym, | |||
223 | size_t symbol__fprintf_symname(const struct symbol *sym, FILE *fp); | 225 | size_t symbol__fprintf_symname(const struct symbol *sym, FILE *fp); |
224 | size_t symbol__fprintf(struct symbol *sym, FILE *fp); | 226 | size_t symbol__fprintf(struct symbol *sym, FILE *fp); |
225 | bool symbol_type__is_a(char symbol_type, enum map_type map_type); | 227 | bool symbol_type__is_a(char symbol_type, enum map_type map_type); |
228 | bool symbol__restricted_filename(const char *filename, | ||
229 | const char *restricted_filename); | ||
226 | 230 | ||
227 | int dso__load_sym(struct dso *dso, struct map *map, struct symsrc *syms_ss, | 231 | int dso__load_sym(struct dso *dso, struct map *map, struct symsrc *syms_ss, |
228 | struct symsrc *runtime_ss, symbol_filter_t filter, | 232 | struct symsrc *runtime_ss, symbol_filter_t filter, |
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index df59623ac763..632e40e5ceca 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c | |||
@@ -54,10 +54,10 @@ int thread__comm_len(struct thread *self) | |||
54 | return self->comm_len; | 54 | return self->comm_len; |
55 | } | 55 | } |
56 | 56 | ||
57 | static size_t thread__fprintf(struct thread *self, FILE *fp) | 57 | size_t thread__fprintf(struct thread *thread, FILE *fp) |
58 | { | 58 | { |
59 | return fprintf(fp, "Thread %d %s\n", self->pid, self->comm) + | 59 | return fprintf(fp, "Thread %d %s\n", thread->pid, thread->comm) + |
60 | map_groups__fprintf(&self->mg, verbose, fp); | 60 | map_groups__fprintf(&thread->mg, verbose, fp); |
61 | } | 61 | } |
62 | 62 | ||
63 | void thread__insert_map(struct thread *self, struct map *map) | 63 | void thread__insert_map(struct thread *self, struct map *map) |
@@ -84,17 +84,3 @@ int thread__fork(struct thread *self, struct thread *parent) | |||
84 | return -ENOMEM; | 84 | return -ENOMEM; |
85 | return 0; | 85 | return 0; |
86 | } | 86 | } |
87 | |||
88 | size_t machine__fprintf(struct machine *machine, FILE *fp) | ||
89 | { | ||
90 | size_t ret = 0; | ||
91 | struct rb_node *nd; | ||
92 | |||
93 | for (nd = rb_first(&machine->threads); nd; nd = rb_next(nd)) { | ||
94 | struct thread *pos = rb_entry(nd, struct thread, rb_node); | ||
95 | |||
96 | ret += thread__fprintf(pos, fp); | ||
97 | } | ||
98 | |||
99 | return ret; | ||
100 | } | ||
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index f2fa17caa7d5..5ad266403098 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h | |||
@@ -30,6 +30,7 @@ int thread__set_comm(struct thread *self, const char *comm); | |||
30 | int thread__comm_len(struct thread *self); | 30 | int thread__comm_len(struct thread *self); |
31 | void thread__insert_map(struct thread *self, struct map *map); | 31 | void thread__insert_map(struct thread *self, struct map *map); |
32 | int thread__fork(struct thread *self, struct thread *parent); | 32 | int thread__fork(struct thread *self, struct thread *parent); |
33 | size_t thread__fprintf(struct thread *thread, FILE *fp); | ||
33 | 34 | ||
34 | static inline struct map *thread__find_map(struct thread *self, | 35 | static inline struct map *thread__find_map(struct thread *self, |
35 | enum map_type type, u64 addr) | 36 | enum map_type type, u64 addr) |