diff options
Diffstat (limited to 'tools')
30 files changed, 1407 insertions, 316 deletions
diff --git a/tools/perf/Documentation/perf-kvm.txt b/tools/perf/Documentation/perf-kvm.txt new file mode 100644 index 000000000000..93400a0f17f0 --- /dev/null +++ b/tools/perf/Documentation/perf-kvm.txt | |||
@@ -0,0 +1,67 @@ | |||
1 | perf-kvm(1) | ||
2 | ============== | ||
3 | |||
4 | NAME | ||
5 | ---- | ||
6 | perf-kvm - Tool to trace/measure kvm guest os | ||
7 | |||
8 | SYNOPSIS | ||
9 | -------- | ||
10 | [verse] | ||
11 | 'perf kvm' [--host] [--guest] [--guestmount=<path> | ||
12 | [--guestkallsyms=<path> --guestmodules=<path> | --guestvmlinux=<path>]] | ||
13 | {top|record|report|diff|buildid-list} | ||
14 | 'perf kvm' [--host] [--guest] [--guestkallsyms=<path> --guestmodules=<path> | ||
15 | | --guestvmlinux=<path>] {top|record|report|diff|buildid-list} | ||
16 | |||
17 | DESCRIPTION | ||
18 | ----------- | ||
19 | There are a couple of variants of perf kvm: | ||
20 | |||
21 | 'perf kvm [options] top <command>' to generates and displays | ||
22 | a performance counter profile of guest os in realtime | ||
23 | of an arbitrary workload. | ||
24 | |||
25 | 'perf kvm record <command>' to record the performance couinter profile | ||
26 | of an arbitrary workload and save it into a perf data file. If both | ||
27 | --host and --guest are input, the perf data file name is perf.data.kvm. | ||
28 | If there is no --host but --guest, the file name is perf.data.guest. | ||
29 | If there is no --guest but --host, the file name is perf.data.host. | ||
30 | |||
31 | 'perf kvm report' to display the performance counter profile information | ||
32 | recorded via perf kvm record. | ||
33 | |||
34 | 'perf kvm diff' to displays the performance difference amongst two perf.data | ||
35 | files captured via perf record. | ||
36 | |||
37 | 'perf kvm buildid-list' to display the buildids found in a perf data file, | ||
38 | so that other tools can be used to fetch packages with matching symbol tables | ||
39 | for use by perf report. | ||
40 | |||
41 | OPTIONS | ||
42 | ------- | ||
43 | --host=:: | ||
44 | Collect host side perforamnce profile. | ||
45 | --guest=:: | ||
46 | Collect guest side perforamnce profile. | ||
47 | --guestmount=<path>:: | ||
48 | Guest os root file system mount directory. Users mounts guest os | ||
49 | root directories under <path> by a specific filesystem access method, | ||
50 | typically, sshfs. For example, start 2 guest os. The one's pid is 8888 | ||
51 | and the other's is 9999. | ||
52 | #mkdir ~/guestmount; cd ~/guestmount | ||
53 | #sshfs -o allow_other,direct_io -p 5551 localhost:/ 8888/ | ||
54 | #sshfs -o allow_other,direct_io -p 5552 localhost:/ 9999/ | ||
55 | #perf kvm --host --guest --guestmount=~/guestmount top | ||
56 | --guestkallsyms=<path>:: | ||
57 | Guest os /proc/kallsyms file copy. 'perf' kvm' reads it to get guest | ||
58 | kernel symbols. Users copy it out from guest os. | ||
59 | --guestmodules=<path>:: | ||
60 | Guest os /proc/modules file copy. 'perf' kvm' reads it to get guest | ||
61 | kernel module information. Users copy it out from guest os. | ||
62 | --guestvmlinux=<path>:: | ||
63 | Guest os kernel vmlinux. | ||
64 | |||
65 | SEE ALSO | ||
66 | -------- | ||
67 | linkperf:perf-top[1] perf-record[1] perf-report[1] perf-diff[1] perf-buildid-list[1] | ||
diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 57b3569716dd..3cb3449a9645 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile | |||
@@ -472,6 +472,7 @@ BUILTIN_OBJS += $(OUTPUT)builtin-trace.o | |||
472 | BUILTIN_OBJS += $(OUTPUT)builtin-probe.o | 472 | BUILTIN_OBJS += $(OUTPUT)builtin-probe.o |
473 | BUILTIN_OBJS += $(OUTPUT)builtin-kmem.o | 473 | BUILTIN_OBJS += $(OUTPUT)builtin-kmem.o |
474 | BUILTIN_OBJS += $(OUTPUT)builtin-lock.o | 474 | BUILTIN_OBJS += $(OUTPUT)builtin-lock.o |
475 | BUILTIN_OBJS += $(OUTPUT)builtin-kvm.o | ||
475 | 476 | ||
476 | PERFLIBS = $(LIB_FILE) | 477 | PERFLIBS = $(LIB_FILE) |
477 | 478 | ||
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 06eaebe10d04..f924b4332be6 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c | |||
@@ -571,7 +571,7 @@ static int __cmd_annotate(void) | |||
571 | perf_session__fprintf(session, stdout); | 571 | perf_session__fprintf(session, stdout); |
572 | 572 | ||
573 | if (verbose > 2) | 573 | if (verbose > 2) |
574 | dsos__fprintf(stdout); | 574 | dsos__fprintf(&session->kerninfo_root, stdout); |
575 | 575 | ||
576 | perf_session__collapse_resort(&session->hists); | 576 | perf_session__collapse_resort(&session->hists); |
577 | perf_session__output_resort(&session->hists, session->event_total[0]); | 577 | perf_session__output_resort(&session->hists, session->event_total[0]); |
diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c index af2ad8b92f76..623afe3fdcb8 100644 --- a/tools/perf/builtin-buildid-list.c +++ b/tools/perf/builtin-buildid-list.c | |||
@@ -46,7 +46,7 @@ static int __cmd_buildid_list(void) | |||
46 | if (with_hits) | 46 | if (with_hits) |
47 | perf_session__process_events(session, &build_id__mark_dso_hit_ops); | 47 | perf_session__process_events(session, &build_id__mark_dso_hit_ops); |
48 | 48 | ||
49 | dsos__fprintf_buildid(stdout, with_hits); | 49 | dsos__fprintf_buildid(&session->kerninfo_root, stdout, with_hits); |
50 | 50 | ||
51 | perf_session__delete(session); | 51 | perf_session__delete(session); |
52 | return err; | 52 | return err; |
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 3a1d94d75dce..207e860591e2 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c | |||
@@ -33,7 +33,7 @@ static int perf_session__add_hist_entry(struct perf_session *self, | |||
33 | return -ENOMEM; | 33 | return -ENOMEM; |
34 | 34 | ||
35 | if (hit) | 35 | if (hit) |
36 | he->count += count; | 36 | __perf_session__add_count(he, al, count); |
37 | 37 | ||
38 | return 0; | 38 | return 0; |
39 | } | 39 | } |
@@ -225,6 +225,10 @@ int cmd_diff(int argc, const char **argv, const char *prefix __used) | |||
225 | input_new = argv[1]; | 225 | input_new = argv[1]; |
226 | } else | 226 | } else |
227 | input_new = argv[0]; | 227 | input_new = argv[0]; |
228 | } else if (symbol_conf.default_guest_vmlinux_name || | ||
229 | symbol_conf.default_guest_kallsyms) { | ||
230 | input_old = "perf.data.host"; | ||
231 | input_new = "perf.data.guest"; | ||
228 | } | 232 | } |
229 | 233 | ||
230 | symbol_conf.exclude_other = false; | 234 | symbol_conf.exclude_other = false; |
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index 513aa8a55db6..db474bbf3322 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c | |||
@@ -351,6 +351,7 @@ static void __print_result(struct rb_root *root, struct perf_session *session, | |||
351 | int n_lines, int is_caller) | 351 | int n_lines, int is_caller) |
352 | { | 352 | { |
353 | struct rb_node *next; | 353 | struct rb_node *next; |
354 | struct kernel_info *kerninfo; | ||
354 | 355 | ||
355 | printf("%.102s\n", graph_dotted_line); | 356 | printf("%.102s\n", graph_dotted_line); |
356 | printf(" %-34s |", is_caller ? "Callsite": "Alloc Ptr"); | 357 | printf(" %-34s |", is_caller ? "Callsite": "Alloc Ptr"); |
@@ -359,10 +360,16 @@ static void __print_result(struct rb_root *root, struct perf_session *session, | |||
359 | 360 | ||
360 | next = rb_first(root); | 361 | next = rb_first(root); |
361 | 362 | ||
363 | kerninfo = kerninfo__findhost(&session->kerninfo_root); | ||
364 | if (!kerninfo) { | ||
365 | pr_err("__print_result: couldn't find kernel information\n"); | ||
366 | return; | ||
367 | } | ||
362 | while (next && n_lines--) { | 368 | while (next && n_lines--) { |
363 | struct alloc_stat *data = rb_entry(next, struct alloc_stat, | 369 | struct alloc_stat *data = rb_entry(next, struct alloc_stat, |
364 | node); | 370 | node); |
365 | struct symbol *sym = NULL; | 371 | struct symbol *sym = NULL; |
372 | struct map_groups *kmaps = &kerninfo->kmaps; | ||
366 | struct map *map; | 373 | struct map *map; |
367 | char buf[BUFSIZ]; | 374 | char buf[BUFSIZ]; |
368 | u64 addr; | 375 | u64 addr; |
@@ -370,8 +377,8 @@ static void __print_result(struct rb_root *root, struct perf_session *session, | |||
370 | if (is_caller) { | 377 | if (is_caller) { |
371 | addr = data->call_site; | 378 | addr = data->call_site; |
372 | if (!raw_ip) | 379 | if (!raw_ip) |
373 | sym = map_groups__find_function(&session->kmaps, | 380 | sym = map_groups__find_function(kmaps, addr, |
374 | addr, &map, NULL); | 381 | &map, NULL); |
375 | } else | 382 | } else |
376 | addr = data->ptr; | 383 | addr = data->ptr; |
377 | 384 | ||
diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c new file mode 100644 index 000000000000..a4c7cae45024 --- /dev/null +++ b/tools/perf/builtin-kvm.c | |||
@@ -0,0 +1,144 @@ | |||
1 | #include "builtin.h" | ||
2 | #include "perf.h" | ||
3 | |||
4 | #include "util/util.h" | ||
5 | #include "util/cache.h" | ||
6 | #include "util/symbol.h" | ||
7 | #include "util/thread.h" | ||
8 | #include "util/header.h" | ||
9 | #include "util/session.h" | ||
10 | |||
11 | #include "util/parse-options.h" | ||
12 | #include "util/trace-event.h" | ||
13 | |||
14 | #include "util/debug.h" | ||
15 | |||
16 | #include <sys/prctl.h> | ||
17 | |||
18 | #include <semaphore.h> | ||
19 | #include <pthread.h> | ||
20 | #include <math.h> | ||
21 | |||
22 | static char *file_name; | ||
23 | static char name_buffer[256]; | ||
24 | |||
25 | int perf_host = 1; | ||
26 | int perf_guest; | ||
27 | |||
28 | static const char * const kvm_usage[] = { | ||
29 | "perf kvm [<options>] {top|record|report|diff|buildid-list}", | ||
30 | NULL | ||
31 | }; | ||
32 | |||
33 | static const struct option kvm_options[] = { | ||
34 | OPT_STRING('i', "input", &file_name, "file", | ||
35 | "Input file name"), | ||
36 | OPT_STRING('o', "output", &file_name, "file", | ||
37 | "Output file name"), | ||
38 | OPT_BOOLEAN(0, "guest", &perf_guest, | ||
39 | "Collect guest os data"), | ||
40 | OPT_BOOLEAN(0, "host", &perf_host, | ||
41 | "Collect guest os data"), | ||
42 | OPT_STRING(0, "guestmount", &symbol_conf.guestmount, "directory", | ||
43 | "guest mount directory under which every guest os" | ||
44 | " instance has a subdir"), | ||
45 | OPT_STRING(0, "guestvmlinux", &symbol_conf.default_guest_vmlinux_name, | ||
46 | "file", "file saving guest os vmlinux"), | ||
47 | OPT_STRING(0, "guestkallsyms", &symbol_conf.default_guest_kallsyms, | ||
48 | "file", "file saving guest os /proc/kallsyms"), | ||
49 | OPT_STRING(0, "guestmodules", &symbol_conf.default_guest_modules, | ||
50 | "file", "file saving guest os /proc/modules"), | ||
51 | OPT_END() | ||
52 | }; | ||
53 | |||
54 | static int __cmd_record(int argc, const char **argv) | ||
55 | { | ||
56 | int rec_argc, i = 0, j; | ||
57 | const char **rec_argv; | ||
58 | |||
59 | rec_argc = argc + 2; | ||
60 | rec_argv = calloc(rec_argc + 1, sizeof(char *)); | ||
61 | rec_argv[i++] = strdup("record"); | ||
62 | rec_argv[i++] = strdup("-o"); | ||
63 | rec_argv[i++] = strdup(file_name); | ||
64 | for (j = 1; j < argc; j++, i++) | ||
65 | rec_argv[i] = argv[j]; | ||
66 | |||
67 | BUG_ON(i != rec_argc); | ||
68 | |||
69 | return cmd_record(i, rec_argv, NULL); | ||
70 | } | ||
71 | |||
72 | static int __cmd_report(int argc, const char **argv) | ||
73 | { | ||
74 | int rec_argc, i = 0, j; | ||
75 | const char **rec_argv; | ||
76 | |||
77 | rec_argc = argc + 2; | ||
78 | rec_argv = calloc(rec_argc + 1, sizeof(char *)); | ||
79 | rec_argv[i++] = strdup("report"); | ||
80 | rec_argv[i++] = strdup("-i"); | ||
81 | rec_argv[i++] = strdup(file_name); | ||
82 | for (j = 1; j < argc; j++, i++) | ||
83 | rec_argv[i] = argv[j]; | ||
84 | |||
85 | BUG_ON(i != rec_argc); | ||
86 | |||
87 | return cmd_report(i, rec_argv, NULL); | ||
88 | } | ||
89 | |||
90 | static int __cmd_buildid_list(int argc, const char **argv) | ||
91 | { | ||
92 | int rec_argc, i = 0, j; | ||
93 | const char **rec_argv; | ||
94 | |||
95 | rec_argc = argc + 2; | ||
96 | rec_argv = calloc(rec_argc + 1, sizeof(char *)); | ||
97 | rec_argv[i++] = strdup("buildid-list"); | ||
98 | rec_argv[i++] = strdup("-i"); | ||
99 | rec_argv[i++] = strdup(file_name); | ||
100 | for (j = 1; j < argc; j++, i++) | ||
101 | rec_argv[i] = argv[j]; | ||
102 | |||
103 | BUG_ON(i != rec_argc); | ||
104 | |||
105 | return cmd_buildid_list(i, rec_argv, NULL); | ||
106 | } | ||
107 | |||
108 | int cmd_kvm(int argc, const char **argv, const char *prefix __used) | ||
109 | { | ||
110 | perf_host = perf_guest = 0; | ||
111 | |||
112 | argc = parse_options(argc, argv, kvm_options, kvm_usage, | ||
113 | PARSE_OPT_STOP_AT_NON_OPTION); | ||
114 | if (!argc) | ||
115 | usage_with_options(kvm_usage, kvm_options); | ||
116 | |||
117 | if (!perf_host) | ||
118 | perf_guest = 1; | ||
119 | |||
120 | if (!file_name) { | ||
121 | if (perf_host && !perf_guest) | ||
122 | sprintf(name_buffer, "perf.data.host"); | ||
123 | else if (!perf_host && perf_guest) | ||
124 | sprintf(name_buffer, "perf.data.guest"); | ||
125 | else | ||
126 | sprintf(name_buffer, "perf.data.kvm"); | ||
127 | file_name = name_buffer; | ||
128 | } | ||
129 | |||
130 | if (!strncmp(argv[0], "rec", 3)) | ||
131 | return __cmd_record(argc, argv); | ||
132 | else if (!strncmp(argv[0], "rep", 3)) | ||
133 | return __cmd_report(argc, argv); | ||
134 | else if (!strncmp(argv[0], "diff", 4)) | ||
135 | return cmd_diff(argc, argv, NULL); | ||
136 | else if (!strncmp(argv[0], "top", 3)) | ||
137 | return cmd_top(argc, argv, NULL); | ||
138 | else if (!strncmp(argv[0], "buildid-list", 12)) | ||
139 | return __cmd_buildid_list(argc, argv); | ||
140 | else | ||
141 | usage_with_options(kvm_usage, kvm_options); | ||
142 | |||
143 | return 0; | ||
144 | } | ||
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index a1b99eeac3c0..27f992aca8b5 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
@@ -456,6 +456,52 @@ static void atexit_header(void) | |||
456 | } | 456 | } |
457 | } | 457 | } |
458 | 458 | ||
459 | static void event__synthesize_guest_os(struct kernel_info *kerninfo, | ||
460 | void *data __attribute__((unused))) | ||
461 | { | ||
462 | int err; | ||
463 | char *guest_kallsyms; | ||
464 | char path[PATH_MAX]; | ||
465 | |||
466 | if (is_host_kernel(kerninfo)) | ||
467 | return; | ||
468 | |||
469 | /* | ||
470 | *As for guest kernel when processing subcommand record&report, | ||
471 | *we arrange module mmap prior to guest kernel mmap and trigger | ||
472 | *a preload dso because default guest module symbols are loaded | ||
473 | *from guest kallsyms instead of /lib/modules/XXX/XXX. This | ||
474 | *method is used to avoid symbol missing when the first addr is | ||
475 | *in module instead of in guest kernel. | ||
476 | */ | ||
477 | err = event__synthesize_modules(process_synthesized_event, | ||
478 | session, | ||
479 | kerninfo); | ||
480 | if (err < 0) | ||
481 | pr_err("Couldn't record guest kernel [%d]'s reference" | ||
482 | " relocation symbol.\n", kerninfo->pid); | ||
483 | |||
484 | if (is_default_guest(kerninfo)) | ||
485 | guest_kallsyms = (char *) symbol_conf.default_guest_kallsyms; | ||
486 | else { | ||
487 | sprintf(path, "%s/proc/kallsyms", kerninfo->root_dir); | ||
488 | guest_kallsyms = path; | ||
489 | } | ||
490 | |||
491 | /* | ||
492 | * We use _stext for guest kernel because guest kernel's /proc/kallsyms | ||
493 | * have no _text sometimes. | ||
494 | */ | ||
495 | err = event__synthesize_kernel_mmap(process_synthesized_event, | ||
496 | session, kerninfo, "_text"); | ||
497 | if (err < 0) | ||
498 | err = event__synthesize_kernel_mmap(process_synthesized_event, | ||
499 | session, kerninfo, "_stext"); | ||
500 | if (err < 0) | ||
501 | pr_err("Couldn't record guest kernel [%d]'s reference" | ||
502 | " relocation symbol.\n", kerninfo->pid); | ||
503 | } | ||
504 | |||
459 | static int __cmd_record(int argc, const char **argv) | 505 | static int __cmd_record(int argc, const char **argv) |
460 | { | 506 | { |
461 | int i, counter; | 507 | int i, counter; |
@@ -467,6 +513,7 @@ static int __cmd_record(int argc, const char **argv) | |||
467 | int child_ready_pipe[2], go_pipe[2]; | 513 | int child_ready_pipe[2], go_pipe[2]; |
468 | const bool forks = argc > 0; | 514 | const bool forks = argc > 0; |
469 | char buf; | 515 | char buf; |
516 | struct kernel_info *kerninfo; | ||
470 | 517 | ||
471 | page_size = sysconf(_SC_PAGE_SIZE); | 518 | page_size = sysconf(_SC_PAGE_SIZE); |
472 | 519 | ||
@@ -635,21 +682,31 @@ static int __cmd_record(int argc, const char **argv) | |||
635 | advance_output(err); | 682 | advance_output(err); |
636 | } | 683 | } |
637 | 684 | ||
685 | kerninfo = kerninfo__findhost(&session->kerninfo_root); | ||
686 | if (!kerninfo) { | ||
687 | pr_err("Couldn't find native kernel information.\n"); | ||
688 | return -1; | ||
689 | } | ||
690 | |||
638 | err = event__synthesize_kernel_mmap(process_synthesized_event, | 691 | err = event__synthesize_kernel_mmap(process_synthesized_event, |
639 | session, "_text"); | 692 | session, kerninfo, "_text"); |
640 | if (err < 0) | 693 | if (err < 0) |
641 | err = event__synthesize_kernel_mmap(process_synthesized_event, | 694 | err = event__synthesize_kernel_mmap(process_synthesized_event, |
642 | session, "_stext"); | 695 | session, kerninfo, "_stext"); |
643 | if (err < 0) { | 696 | if (err < 0) { |
644 | pr_err("Couldn't record kernel reference relocation symbol.\n"); | 697 | pr_err("Couldn't record kernel reference relocation symbol.\n"); |
645 | return err; | 698 | return err; |
646 | } | 699 | } |
647 | 700 | ||
648 | err = event__synthesize_modules(process_synthesized_event, session); | 701 | err = event__synthesize_modules(process_synthesized_event, |
702 | session, kerninfo); | ||
649 | if (err < 0) { | 703 | if (err < 0) { |
650 | pr_err("Couldn't record kernel reference relocation symbol.\n"); | 704 | pr_err("Couldn't record kernel reference relocation symbol.\n"); |
651 | return err; | 705 | return err; |
652 | } | 706 | } |
707 | if (perf_guest) | ||
708 | kerninfo__process_allkernels(&session->kerninfo_root, | ||
709 | event__synthesize_guest_os, session); | ||
653 | 710 | ||
654 | if (!system_wide && profile_cpu == -1) | 711 | if (!system_wide && profile_cpu == -1) |
655 | event__synthesize_thread(target_tid, process_synthesized_event, | 712 | event__synthesize_thread(target_tid, process_synthesized_event, |
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 7da5fb365264..816edae7c5b2 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
@@ -108,7 +108,7 @@ static int perf_session__add_hist_entry(struct perf_session *self, | |||
108 | return -ENOMEM; | 108 | return -ENOMEM; |
109 | 109 | ||
110 | if (hit) | 110 | if (hit) |
111 | he->count += data->period; | 111 | __perf_session__add_count(he, al, data->period); |
112 | 112 | ||
113 | if (symbol_conf.use_callchain) { | 113 | if (symbol_conf.use_callchain) { |
114 | if (!hit) | 114 | if (!hit) |
@@ -313,7 +313,7 @@ static int __cmd_report(void) | |||
313 | perf_session__fprintf(session, stdout); | 313 | perf_session__fprintf(session, stdout); |
314 | 314 | ||
315 | if (verbose > 2) | 315 | if (verbose > 2) |
316 | dsos__fprintf(stdout); | 316 | dsos__fprintf(&session->kerninfo_root, stdout); |
317 | 317 | ||
318 | next = rb_first(&session->stats_by_id); | 318 | next = rb_first(&session->stats_by_id); |
319 | while (next) { | 319 | while (next) { |
@@ -450,6 +450,8 @@ static const struct option options[] = { | |||
450 | "sort by key(s): pid, comm, dso, symbol, parent"), | 450 | "sort by key(s): pid, comm, dso, symbol, parent"), |
451 | OPT_BOOLEAN('P', "full-paths", &symbol_conf.full_paths, | 451 | OPT_BOOLEAN('P', "full-paths", &symbol_conf.full_paths, |
452 | "Don't shorten the pathnames taking into account the cwd"), | 452 | "Don't shorten the pathnames taking into account the cwd"), |
453 | OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization, | ||
454 | "Show sample percentage for different cpu modes"), | ||
453 | OPT_STRING('p', "parent", &parent_pattern, "regex", | 455 | OPT_STRING('p', "parent", &parent_pattern, "regex", |
454 | "regex filter to identify parent, see: '--sort parent'"), | 456 | "regex filter to identify parent, see: '--sort parent'"), |
455 | OPT_BOOLEAN('x', "exclude-other", &symbol_conf.exclude_other, | 457 | OPT_BOOLEAN('x', "exclude-other", &symbol_conf.exclude_other, |
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 40f24dd46ef4..dfd7ea7dabdd 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
@@ -420,8 +420,9 @@ static double sym_weight(const struct sym_entry *sym) | |||
420 | } | 420 | } |
421 | 421 | ||
422 | static long samples; | 422 | static long samples; |
423 | static long userspace_samples; | 423 | static long kernel_samples, us_samples; |
424 | static long exact_samples; | 424 | static long exact_samples; |
425 | static long guest_us_samples, guest_kernel_samples; | ||
425 | static const char CONSOLE_CLEAR[] = "[H[2J"; | 426 | static const char CONSOLE_CLEAR[] = "[H[2J"; |
426 | 427 | ||
427 | static void __list_insert_active_sym(struct sym_entry *syme) | 428 | static void __list_insert_active_sym(struct sym_entry *syme) |
@@ -461,7 +462,10 @@ static void print_sym_table(void) | |||
461 | int printed = 0, j; | 462 | int printed = 0, j; |
462 | int counter, snap = !display_weighted ? sym_counter : 0; | 463 | int counter, snap = !display_weighted ? sym_counter : 0; |
463 | float samples_per_sec = samples/delay_secs; | 464 | float samples_per_sec = samples/delay_secs; |
464 | float ksamples_per_sec = (samples-userspace_samples)/delay_secs; | 465 | float ksamples_per_sec = kernel_samples/delay_secs; |
466 | float us_samples_per_sec = (us_samples)/delay_secs; | ||
467 | float guest_kernel_samples_per_sec = (guest_kernel_samples)/delay_secs; | ||
468 | float guest_us_samples_per_sec = (guest_us_samples)/delay_secs; | ||
465 | float esamples_percent = (100.0*exact_samples)/samples; | 469 | float esamples_percent = (100.0*exact_samples)/samples; |
466 | float sum_ksamples = 0.0; | 470 | float sum_ksamples = 0.0; |
467 | struct sym_entry *syme, *n; | 471 | struct sym_entry *syme, *n; |
@@ -470,7 +474,8 @@ static void print_sym_table(void) | |||
470 | int sym_width = 0, dso_width = 0, dso_short_width = 0; | 474 | int sym_width = 0, dso_width = 0, dso_short_width = 0; |
471 | const int win_width = winsize.ws_col - 1; | 475 | const int win_width = winsize.ws_col - 1; |
472 | 476 | ||
473 | samples = userspace_samples = exact_samples = 0; | 477 | samples = us_samples = kernel_samples = exact_samples = 0; |
478 | guest_kernel_samples = guest_us_samples = 0; | ||
474 | 479 | ||
475 | /* Sort the active symbols */ | 480 | /* Sort the active symbols */ |
476 | pthread_mutex_lock(&active_symbols_lock); | 481 | pthread_mutex_lock(&active_symbols_lock); |
@@ -501,10 +506,30 @@ static void print_sym_table(void) | |||
501 | puts(CONSOLE_CLEAR); | 506 | puts(CONSOLE_CLEAR); |
502 | 507 | ||
503 | printf("%-*.*s\n", win_width, win_width, graph_dotted_line); | 508 | printf("%-*.*s\n", win_width, win_width, graph_dotted_line); |
504 | printf( " PerfTop:%8.0f irqs/sec kernel:%4.1f%% exact: %4.1f%% [", | 509 | if (!perf_guest) { |
505 | samples_per_sec, | 510 | printf(" PerfTop:%8.0f irqs/sec kernel:%4.1f%%" |
506 | 100.0 - (100.0*((samples_per_sec-ksamples_per_sec)/samples_per_sec)), | 511 | " exact: %4.1f%% [", |
507 | esamples_percent); | 512 | samples_per_sec, |
513 | 100.0 - (100.0 * ((samples_per_sec - ksamples_per_sec) / | ||
514 | samples_per_sec)), | ||
515 | esamples_percent); | ||
516 | } else { | ||
517 | printf(" PerfTop:%8.0f irqs/sec kernel:%4.1f%% us:%4.1f%%" | ||
518 | " guest kernel:%4.1f%% guest us:%4.1f%%" | ||
519 | " exact: %4.1f%% [", | ||
520 | samples_per_sec, | ||
521 | 100.0 - (100.0 * ((samples_per_sec-ksamples_per_sec) / | ||
522 | samples_per_sec)), | ||
523 | 100.0 - (100.0 * ((samples_per_sec-us_samples_per_sec) / | ||
524 | samples_per_sec)), | ||
525 | 100.0 - (100.0 * ((samples_per_sec - | ||
526 | guest_kernel_samples_per_sec) / | ||
527 | samples_per_sec)), | ||
528 | 100.0 - (100.0 * ((samples_per_sec - | ||
529 | guest_us_samples_per_sec) / | ||
530 | samples_per_sec)), | ||
531 | esamples_percent); | ||
532 | } | ||
508 | 533 | ||
509 | if (nr_counters == 1 || !display_weighted) { | 534 | if (nr_counters == 1 || !display_weighted) { |
510 | printf("%Ld", (u64)attrs[0].sample_period); | 535 | printf("%Ld", (u64)attrs[0].sample_period); |
@@ -597,7 +622,6 @@ static void print_sym_table(void) | |||
597 | 622 | ||
598 | syme = rb_entry(nd, struct sym_entry, rb_node); | 623 | syme = rb_entry(nd, struct sym_entry, rb_node); |
599 | sym = sym_entry__symbol(syme); | 624 | sym = sym_entry__symbol(syme); |
600 | |||
601 | if (++printed > print_entries || (int)syme->snap_count < count_filter) | 625 | if (++printed > print_entries || (int)syme->snap_count < count_filter) |
602 | continue; | 626 | continue; |
603 | 627 | ||
@@ -761,7 +785,7 @@ static int key_mapped(int c) | |||
761 | return 0; | 785 | return 0; |
762 | } | 786 | } |
763 | 787 | ||
764 | static void handle_keypress(int c) | 788 | static void handle_keypress(struct perf_session *session, int c) |
765 | { | 789 | { |
766 | if (!key_mapped(c)) { | 790 | if (!key_mapped(c)) { |
767 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; | 791 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; |
@@ -830,7 +854,7 @@ static void handle_keypress(int c) | |||
830 | case 'Q': | 854 | case 'Q': |
831 | printf("exiting.\n"); | 855 | printf("exiting.\n"); |
832 | if (dump_symtab) | 856 | if (dump_symtab) |
833 | dsos__fprintf(stderr); | 857 | dsos__fprintf(&session->kerninfo_root, stderr); |
834 | exit(0); | 858 | exit(0); |
835 | case 's': | 859 | case 's': |
836 | prompt_symbol(&sym_filter_entry, "Enter details symbol"); | 860 | prompt_symbol(&sym_filter_entry, "Enter details symbol"); |
@@ -866,6 +890,7 @@ static void *display_thread(void *arg __used) | |||
866 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; | 890 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; |
867 | struct termios tc, save; | 891 | struct termios tc, save; |
868 | int delay_msecs, c; | 892 | int delay_msecs, c; |
893 | struct perf_session *session = (struct perf_session *) arg; | ||
869 | 894 | ||
870 | tcgetattr(0, &save); | 895 | tcgetattr(0, &save); |
871 | tc = save; | 896 | tc = save; |
@@ -886,7 +911,7 @@ repeat: | |||
886 | c = getc(stdin); | 911 | c = getc(stdin); |
887 | tcsetattr(0, TCSAFLUSH, &save); | 912 | tcsetattr(0, TCSAFLUSH, &save); |
888 | 913 | ||
889 | handle_keypress(c); | 914 | handle_keypress(session, c); |
890 | goto repeat; | 915 | goto repeat; |
891 | 916 | ||
892 | return NULL; | 917 | return NULL; |
@@ -957,24 +982,46 @@ static void event__process_sample(const event_t *self, | |||
957 | u64 ip = self->ip.ip; | 982 | u64 ip = self->ip.ip; |
958 | struct sym_entry *syme; | 983 | struct sym_entry *syme; |
959 | struct addr_location al; | 984 | struct addr_location al; |
985 | struct kernel_info *kerninfo; | ||
960 | u8 origin = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | 986 | u8 origin = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
961 | 987 | ||
962 | ++samples; | 988 | ++samples; |
963 | 989 | ||
964 | switch (origin) { | 990 | switch (origin) { |
965 | case PERF_RECORD_MISC_USER: | 991 | case PERF_RECORD_MISC_USER: |
966 | ++userspace_samples; | 992 | ++us_samples; |
967 | if (hide_user_symbols) | 993 | if (hide_user_symbols) |
968 | return; | 994 | return; |
995 | kerninfo = kerninfo__findhost(&session->kerninfo_root); | ||
969 | break; | 996 | break; |
970 | case PERF_RECORD_MISC_KERNEL: | 997 | case PERF_RECORD_MISC_KERNEL: |
998 | ++kernel_samples; | ||
971 | if (hide_kernel_symbols) | 999 | if (hide_kernel_symbols) |
972 | return; | 1000 | return; |
1001 | kerninfo = kerninfo__findhost(&session->kerninfo_root); | ||
973 | break; | 1002 | break; |
1003 | case PERF_RECORD_MISC_GUEST_KERNEL: | ||
1004 | ++guest_kernel_samples; | ||
1005 | kerninfo = kerninfo__find(&session->kerninfo_root, | ||
1006 | self->ip.pid); | ||
1007 | break; | ||
1008 | case PERF_RECORD_MISC_GUEST_USER: | ||
1009 | ++guest_us_samples; | ||
1010 | /* | ||
1011 | * TODO: we don't process guest user from host side | ||
1012 | * except simple counting. | ||
1013 | */ | ||
1014 | return; | ||
974 | default: | 1015 | default: |
975 | return; | 1016 | return; |
976 | } | 1017 | } |
977 | 1018 | ||
1019 | if (!kerninfo && perf_guest) { | ||
1020 | pr_err("Can't find guest [%d]'s kernel information\n", | ||
1021 | self->ip.pid); | ||
1022 | return; | ||
1023 | } | ||
1024 | |||
978 | if (self->header.misc & PERF_RECORD_MISC_EXACT) | 1025 | if (self->header.misc & PERF_RECORD_MISC_EXACT) |
979 | exact_samples++; | 1026 | exact_samples++; |
980 | 1027 | ||
@@ -994,7 +1041,7 @@ static void event__process_sample(const event_t *self, | |||
994 | * --hide-kernel-symbols, even if the user specifies an | 1041 | * --hide-kernel-symbols, even if the user specifies an |
995 | * invalid --vmlinux ;-) | 1042 | * invalid --vmlinux ;-) |
996 | */ | 1043 | */ |
997 | if (al.map == session->vmlinux_maps[MAP__FUNCTION] && | 1044 | if (al.map == kerninfo->vmlinux_maps[MAP__FUNCTION] && |
998 | RB_EMPTY_ROOT(&al.map->dso->symbols[MAP__FUNCTION])) { | 1045 | RB_EMPTY_ROOT(&al.map->dso->symbols[MAP__FUNCTION])) { |
999 | pr_err("The %s file can't be used\n", | 1046 | pr_err("The %s file can't be used\n", |
1000 | symbol_conf.vmlinux_name); | 1047 | symbol_conf.vmlinux_name); |
@@ -1261,7 +1308,7 @@ static int __cmd_top(void) | |||
1261 | 1308 | ||
1262 | perf_session__mmap_read(session); | 1309 | perf_session__mmap_read(session); |
1263 | 1310 | ||
1264 | if (pthread_create(&thread, NULL, display_thread, NULL)) { | 1311 | if (pthread_create(&thread, NULL, display_thread, session)) { |
1265 | printf("Could not create display thread.\n"); | 1312 | printf("Could not create display thread.\n"); |
1266 | exit(-1); | 1313 | exit(-1); |
1267 | } | 1314 | } |
diff --git a/tools/perf/builtin.h b/tools/perf/builtin.h index 10fe49e7048a..ab28bca92e52 100644 --- a/tools/perf/builtin.h +++ b/tools/perf/builtin.h | |||
@@ -32,5 +32,6 @@ extern int cmd_version(int argc, const char **argv, const char *prefix); | |||
32 | extern int cmd_probe(int argc, const char **argv, const char *prefix); | 32 | extern int cmd_probe(int argc, const char **argv, const char *prefix); |
33 | extern int cmd_kmem(int argc, const char **argv, const char *prefix); | 33 | extern int cmd_kmem(int argc, const char **argv, const char *prefix); |
34 | extern int cmd_lock(int argc, const char **argv, const char *prefix); | 34 | extern int cmd_lock(int argc, const char **argv, const char *prefix); |
35 | extern int cmd_kvm(int argc, const char **argv, const char *prefix); | ||
35 | 36 | ||
36 | #endif | 37 | #endif |
diff --git a/tools/perf/command-list.txt b/tools/perf/command-list.txt index db6ee94d4a8e..2a1162d413a8 100644 --- a/tools/perf/command-list.txt +++ b/tools/perf/command-list.txt | |||
@@ -19,3 +19,4 @@ perf-trace mainporcelain common | |||
19 | perf-probe mainporcelain common | 19 | perf-probe mainporcelain common |
20 | perf-kmem mainporcelain common | 20 | perf-kmem mainporcelain common |
21 | perf-lock mainporcelain common | 21 | perf-lock mainporcelain common |
22 | perf-kvm mainporcelain common | ||
diff --git a/tools/perf/perf.c b/tools/perf/perf.c index d4be55b6cd34..985cdb4bd005 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c | |||
@@ -307,6 +307,7 @@ static void handle_internal_command(int argc, const char **argv) | |||
307 | { "probe", cmd_probe, 0 }, | 307 | { "probe", cmd_probe, 0 }, |
308 | { "kmem", cmd_kmem, 0 }, | 308 | { "kmem", cmd_kmem, 0 }, |
309 | { "lock", cmd_lock, 0 }, | 309 | { "lock", cmd_lock, 0 }, |
310 | { "kvm", cmd_kvm, 0 }, | ||
310 | }; | 311 | }; |
311 | unsigned int i; | 312 | unsigned int i; |
312 | static const char ext[] = STRIP_EXTENSION; | 313 | static const char ext[] = STRIP_EXTENSION; |
diff --git a/tools/perf/perf.h b/tools/perf/perf.h index ec212748d651..02821febb704 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h | |||
@@ -131,4 +131,6 @@ struct ip_callchain { | |||
131 | u64 ips[0]; | 131 | u64 ips[0]; |
132 | }; | 132 | }; |
133 | 133 | ||
134 | extern int perf_host, perf_guest; | ||
135 | |||
134 | #endif | 136 | #endif |
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index 04904b35ba81..0f60a3906808 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c | |||
@@ -24,7 +24,7 @@ static int build_id__mark_dso_hit(event_t *event, struct perf_session *session) | |||
24 | } | 24 | } |
25 | 25 | ||
26 | thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION, | 26 | thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION, |
27 | event->ip.ip, &al); | 27 | event->ip.pid, event->ip.ip, &al); |
28 | 28 | ||
29 | if (al.map != NULL) | 29 | if (al.map != NULL) |
30 | al.map->dso->hit = 1; | 30 | al.map->dso->hit = 1; |
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 571fb25f7eb9..e3fa8d3d11b4 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c | |||
@@ -112,7 +112,11 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, | |||
112 | event_t ev = { | 112 | event_t ev = { |
113 | .header = { | 113 | .header = { |
114 | .type = PERF_RECORD_MMAP, | 114 | .type = PERF_RECORD_MMAP, |
115 | .misc = 0, /* Just like the kernel, see kernel/perf_event.c __perf_event_mmap */ | 115 | /* |
116 | * Just like the kernel, see __perf_event_mmap | ||
117 | * in kernel/perf_event.c | ||
118 | */ | ||
119 | .misc = PERF_RECORD_MISC_USER, | ||
116 | }, | 120 | }, |
117 | }; | 121 | }; |
118 | int n; | 122 | int n; |
@@ -167,11 +171,23 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, | |||
167 | } | 171 | } |
168 | 172 | ||
169 | int event__synthesize_modules(event__handler_t process, | 173 | int event__synthesize_modules(event__handler_t process, |
170 | struct perf_session *session) | 174 | struct perf_session *session, |
175 | struct kernel_info *kerninfo) | ||
171 | { | 176 | { |
172 | struct rb_node *nd; | 177 | struct rb_node *nd; |
178 | struct map_groups *kmaps = &kerninfo->kmaps; | ||
179 | u16 misc; | ||
173 | 180 | ||
174 | for (nd = rb_first(&session->kmaps.maps[MAP__FUNCTION]); | 181 | /* |
182 | * kernel uses 0 for user space maps, see kernel/perf_event.c | ||
183 | * __perf_event_mmap | ||
184 | */ | ||
185 | if (is_host_kernel(kerninfo)) | ||
186 | misc = PERF_RECORD_MISC_KERNEL; | ||
187 | else | ||
188 | misc = PERF_RECORD_MISC_GUEST_KERNEL; | ||
189 | |||
190 | for (nd = rb_first(&kmaps->maps[MAP__FUNCTION]); | ||
175 | nd; nd = rb_next(nd)) { | 191 | nd; nd = rb_next(nd)) { |
176 | event_t ev; | 192 | event_t ev; |
177 | size_t size; | 193 | size_t size; |
@@ -182,12 +198,13 @@ int event__synthesize_modules(event__handler_t process, | |||
182 | 198 | ||
183 | size = ALIGN(pos->dso->long_name_len + 1, sizeof(u64)); | 199 | size = ALIGN(pos->dso->long_name_len + 1, sizeof(u64)); |
184 | memset(&ev, 0, sizeof(ev)); | 200 | memset(&ev, 0, sizeof(ev)); |
185 | ev.mmap.header.misc = 1; /* kernel uses 0 for user space maps, see kernel/perf_event.c __perf_event_mmap */ | 201 | ev.mmap.header.misc = misc; |
186 | ev.mmap.header.type = PERF_RECORD_MMAP; | 202 | ev.mmap.header.type = PERF_RECORD_MMAP; |
187 | ev.mmap.header.size = (sizeof(ev.mmap) - | 203 | ev.mmap.header.size = (sizeof(ev.mmap) - |
188 | (sizeof(ev.mmap.filename) - size)); | 204 | (sizeof(ev.mmap.filename) - size)); |
189 | ev.mmap.start = pos->start; | 205 | ev.mmap.start = pos->start; |
190 | ev.mmap.len = pos->end - pos->start; | 206 | ev.mmap.len = pos->end - pos->start; |
207 | ev.mmap.pid = kerninfo->pid; | ||
191 | 208 | ||
192 | memcpy(ev.mmap.filename, pos->dso->long_name, | 209 | memcpy(ev.mmap.filename, pos->dso->long_name, |
193 | pos->dso->long_name_len + 1); | 210 | pos->dso->long_name_len + 1); |
@@ -250,13 +267,18 @@ static int find_symbol_cb(void *arg, const char *name, char type, u64 start) | |||
250 | 267 | ||
251 | int event__synthesize_kernel_mmap(event__handler_t process, | 268 | int event__synthesize_kernel_mmap(event__handler_t process, |
252 | struct perf_session *session, | 269 | struct perf_session *session, |
270 | struct kernel_info *kerninfo, | ||
253 | const char *symbol_name) | 271 | const char *symbol_name) |
254 | { | 272 | { |
255 | size_t size; | 273 | size_t size; |
274 | const char *filename, *mmap_name; | ||
275 | char path[PATH_MAX]; | ||
276 | char name_buff[PATH_MAX]; | ||
277 | struct map *map; | ||
278 | |||
256 | event_t ev = { | 279 | event_t ev = { |
257 | .header = { | 280 | .header = { |
258 | .type = PERF_RECORD_MMAP, | 281 | .type = PERF_RECORD_MMAP, |
259 | .misc = 1, /* kernel uses 0 for user space maps, see kernel/perf_event.c __perf_event_mmap */ | ||
260 | }, | 282 | }, |
261 | }; | 283 | }; |
262 | /* | 284 | /* |
@@ -266,16 +288,37 @@ int event__synthesize_kernel_mmap(event__handler_t process, | |||
266 | */ | 288 | */ |
267 | struct process_symbol_args args = { .name = symbol_name, }; | 289 | struct process_symbol_args args = { .name = symbol_name, }; |
268 | 290 | ||
269 | if (kallsyms__parse("/proc/kallsyms", &args, find_symbol_cb) <= 0) | 291 | mmap_name = kern_mmap_name(kerninfo, name_buff); |
292 | if (is_host_kernel(kerninfo)) { | ||
293 | /* | ||
294 | * kernel uses PERF_RECORD_MISC_USER for user space maps, | ||
295 | * see kernel/perf_event.c __perf_event_mmap | ||
296 | */ | ||
297 | ev.header.misc = PERF_RECORD_MISC_KERNEL; | ||
298 | filename = "/proc/kallsyms"; | ||
299 | } else { | ||
300 | ev.header.misc = PERF_RECORD_MISC_GUEST_KERNEL; | ||
301 | if (is_default_guest(kerninfo)) | ||
302 | filename = (char *) symbol_conf.default_guest_kallsyms; | ||
303 | else { | ||
304 | sprintf(path, "%s/proc/kallsyms", kerninfo->root_dir); | ||
305 | filename = path; | ||
306 | } | ||
307 | } | ||
308 | |||
309 | if (kallsyms__parse(filename, &args, find_symbol_cb) <= 0) | ||
270 | return -ENOENT; | 310 | return -ENOENT; |
271 | 311 | ||
312 | map = kerninfo->vmlinux_maps[MAP__FUNCTION]; | ||
272 | size = snprintf(ev.mmap.filename, sizeof(ev.mmap.filename), | 313 | size = snprintf(ev.mmap.filename, sizeof(ev.mmap.filename), |
273 | "[kernel.kallsyms.%s]", symbol_name) + 1; | 314 | "%s%s", mmap_name, symbol_name) + 1; |
274 | size = ALIGN(size, sizeof(u64)); | 315 | size = ALIGN(size, sizeof(u64)); |
275 | ev.mmap.header.size = (sizeof(ev.mmap) - (sizeof(ev.mmap.filename) - size)); | 316 | ev.mmap.header.size = (sizeof(ev.mmap) - |
317 | (sizeof(ev.mmap.filename) - size)); | ||
276 | ev.mmap.pgoff = args.start; | 318 | ev.mmap.pgoff = args.start; |
277 | ev.mmap.start = session->vmlinux_maps[MAP__FUNCTION]->start; | 319 | ev.mmap.start = map->start; |
278 | ev.mmap.len = session->vmlinux_maps[MAP__FUNCTION]->end - ev.mmap.start ; | 320 | ev.mmap.len = map->end - ev.mmap.start; |
321 | ev.mmap.pid = kerninfo->pid; | ||
279 | 322 | ||
280 | return process(&ev, session); | 323 | return process(&ev, session); |
281 | } | 324 | } |
@@ -329,22 +372,50 @@ int event__process_lost(event_t *self, struct perf_session *session) | |||
329 | return 0; | 372 | return 0; |
330 | } | 373 | } |
331 | 374 | ||
332 | int event__process_mmap(event_t *self, struct perf_session *session) | 375 | static void event_set_kernel_mmap_len(struct map **maps, event_t *self) |
376 | { | ||
377 | maps[MAP__FUNCTION]->start = self->mmap.start; | ||
378 | maps[MAP__FUNCTION]->end = self->mmap.start + self->mmap.len; | ||
379 | /* | ||
380 | * Be a bit paranoid here, some perf.data file came with | ||
381 | * a zero sized synthesized MMAP event for the kernel. | ||
382 | */ | ||
383 | if (maps[MAP__FUNCTION]->end == 0) | ||
384 | maps[MAP__FUNCTION]->end = ~0UL; | ||
385 | } | ||
386 | |||
387 | static int event__process_kernel_mmap(event_t *self, | ||
388 | struct perf_session *session) | ||
333 | { | 389 | { |
334 | struct thread *thread; | ||
335 | struct map *map; | 390 | struct map *map; |
391 | char kmmap_prefix[PATH_MAX]; | ||
392 | struct kernel_info *kerninfo; | ||
393 | enum dso_kernel_type kernel_type; | ||
394 | bool is_kernel_mmap; | ||
395 | |||
396 | kerninfo = kerninfo__findnew(&session->kerninfo_root, self->mmap.pid); | ||
397 | if (!kerninfo) { | ||
398 | pr_err("Can't find id %d's kerninfo\n", self->mmap.pid); | ||
399 | goto out_problem; | ||
400 | } | ||
336 | 401 | ||
337 | dump_printf(" %d/%d: [%#Lx(%#Lx) @ %#Lx]: %s\n", | 402 | kern_mmap_name(kerninfo, kmmap_prefix); |
338 | self->mmap.pid, self->mmap.tid, self->mmap.start, | 403 | if (is_host_kernel(kerninfo)) |
339 | self->mmap.len, self->mmap.pgoff, self->mmap.filename); | 404 | kernel_type = DSO_TYPE_KERNEL; |
405 | else | ||
406 | kernel_type = DSO_TYPE_GUEST_KERNEL; | ||
340 | 407 | ||
341 | if (self->mmap.pid == 0) { | 408 | is_kernel_mmap = memcmp(self->mmap.filename, |
342 | static const char kmmap_prefix[] = "[kernel.kallsyms."; | 409 | kmmap_prefix, |
410 | strlen(kmmap_prefix)) == 0; | ||
411 | if (self->mmap.filename[0] == '/' || | ||
412 | (!is_kernel_mmap && self->mmap.filename[0] == '[')) { | ||
343 | 413 | ||
344 | if (self->mmap.filename[0] == '/') { | 414 | char short_module_name[1024]; |
345 | char short_module_name[1024]; | 415 | char *name, *dot; |
346 | char *name = strrchr(self->mmap.filename, '/'), *dot; | ||
347 | 416 | ||
417 | if (self->mmap.filename[0] == '/') { | ||
418 | name = strrchr(self->mmap.filename, '/'); | ||
348 | if (name == NULL) | 419 | if (name == NULL) |
349 | goto out_problem; | 420 | goto out_problem; |
350 | 421 | ||
@@ -352,59 +423,86 @@ int event__process_mmap(event_t *self, struct perf_session *session) | |||
352 | dot = strrchr(name, '.'); | 423 | dot = strrchr(name, '.'); |
353 | if (dot == NULL) | 424 | if (dot == NULL) |
354 | goto out_problem; | 425 | goto out_problem; |
355 | |||
356 | snprintf(short_module_name, sizeof(short_module_name), | 426 | snprintf(short_module_name, sizeof(short_module_name), |
357 | "[%.*s]", (int)(dot - name), name); | 427 | "[%.*s]", (int)(dot - name), name); |
358 | strxfrchar(short_module_name, '-', '_'); | 428 | strxfrchar(short_module_name, '-', '_'); |
359 | 429 | } else | |
360 | map = perf_session__new_module_map(session, | 430 | strcpy(short_module_name, self->mmap.filename); |
361 | self->mmap.start, | 431 | |
362 | self->mmap.filename); | 432 | map = map_groups__new_module(&kerninfo->kmaps, |
363 | if (map == NULL) | 433 | self->mmap.start, |
364 | goto out_problem; | 434 | self->mmap.filename, |
365 | 435 | kerninfo); | |
366 | name = strdup(short_module_name); | 436 | if (map == NULL) |
367 | if (name == NULL) | 437 | goto out_problem; |
368 | goto out_problem; | 438 | |
369 | 439 | name = strdup(short_module_name); | |
370 | map->dso->short_name = name; | 440 | if (name == NULL) |
371 | map->end = map->start + self->mmap.len; | 441 | goto out_problem; |
372 | } else if (memcmp(self->mmap.filename, kmmap_prefix, | 442 | |
373 | sizeof(kmmap_prefix) - 1) == 0) { | 443 | map->dso->short_name = name; |
374 | const char *symbol_name = (self->mmap.filename + | 444 | map->end = map->start + self->mmap.len; |
375 | sizeof(kmmap_prefix) - 1); | 445 | } else if (is_kernel_mmap) { |
446 | const char *symbol_name = (self->mmap.filename + | ||
447 | strlen(kmmap_prefix)); | ||
448 | /* | ||
449 | * Should be there already, from the build-id table in | ||
450 | * the header. | ||
451 | */ | ||
452 | struct dso *kernel = __dsos__findnew(&kerninfo->dsos__kernel, | ||
453 | kmmap_prefix); | ||
454 | if (kernel == NULL) | ||
455 | goto out_problem; | ||
456 | |||
457 | kernel->kernel = kernel_type; | ||
458 | if (__map_groups__create_kernel_maps(&kerninfo->kmaps, | ||
459 | kerninfo->vmlinux_maps, kernel) < 0) | ||
460 | goto out_problem; | ||
461 | |||
462 | event_set_kernel_mmap_len(kerninfo->vmlinux_maps, self); | ||
463 | perf_session__set_kallsyms_ref_reloc_sym(kerninfo->vmlinux_maps, | ||
464 | symbol_name, | ||
465 | self->mmap.pgoff); | ||
466 | if (is_default_guest(kerninfo)) { | ||
376 | /* | 467 | /* |
377 | * Should be there already, from the build-id table in | 468 | * preload dso of guest kernel and modules |
378 | * the header. | ||
379 | */ | 469 | */ |
380 | struct dso *kernel = __dsos__findnew(&dsos__kernel, | 470 | dso__load(kernel, |
381 | "[kernel.kallsyms]"); | 471 | kerninfo->vmlinux_maps[MAP__FUNCTION], |
382 | if (kernel == NULL) | 472 | NULL); |
383 | goto out_problem; | 473 | } |
384 | 474 | } | |
385 | kernel->kernel = 1; | 475 | return 0; |
386 | if (__perf_session__create_kernel_maps(session, kernel) < 0) | 476 | out_problem: |
387 | goto out_problem; | 477 | return -1; |
478 | } | ||
388 | 479 | ||
389 | session->vmlinux_maps[MAP__FUNCTION]->start = self->mmap.start; | 480 | int event__process_mmap(event_t *self, struct perf_session *session) |
390 | session->vmlinux_maps[MAP__FUNCTION]->end = self->mmap.start + self->mmap.len; | 481 | { |
391 | /* | 482 | struct kernel_info *kerninfo; |
392 | * Be a bit paranoid here, some perf.data file came with | 483 | struct thread *thread; |
393 | * a zero sized synthesized MMAP event for the kernel. | 484 | struct map *map; |
394 | */ | 485 | u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
395 | if (session->vmlinux_maps[MAP__FUNCTION]->end == 0) | 486 | int ret = 0; |
396 | session->vmlinux_maps[MAP__FUNCTION]->end = ~0UL; | ||
397 | 487 | ||
398 | perf_session__set_kallsyms_ref_reloc_sym(session, symbol_name, | 488 | dump_printf(" %d/%d: [%#Lx(%#Lx) @ %#Lx]: %s\n", |
399 | self->mmap.pgoff); | 489 | self->mmap.pid, self->mmap.tid, self->mmap.start, |
400 | } | 490 | self->mmap.len, self->mmap.pgoff, self->mmap.filename); |
491 | |||
492 | if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL || | ||
493 | cpumode == PERF_RECORD_MISC_KERNEL) { | ||
494 | ret = event__process_kernel_mmap(self, session); | ||
495 | if (ret < 0) | ||
496 | goto out_problem; | ||
401 | return 0; | 497 | return 0; |
402 | } | 498 | } |
403 | 499 | ||
404 | thread = perf_session__findnew(session, self->mmap.pid); | 500 | thread = perf_session__findnew(session, self->mmap.pid); |
405 | map = map__new(self->mmap.start, self->mmap.len, self->mmap.pgoff, | 501 | kerninfo = kerninfo__findhost(&session->kerninfo_root); |
406 | self->mmap.pid, self->mmap.filename, MAP__FUNCTION, | 502 | map = map__new(&kerninfo->dsos__user, self->mmap.start, |
407 | session->cwd, session->cwdlen); | 503 | self->mmap.len, self->mmap.pgoff, |
504 | self->mmap.pid, self->mmap.filename, | ||
505 | MAP__FUNCTION, session->cwd, session->cwdlen); | ||
408 | 506 | ||
409 | if (thread == NULL || map == NULL) | 507 | if (thread == NULL || map == NULL) |
410 | goto out_problem; | 508 | goto out_problem; |
@@ -444,22 +542,52 @@ int event__process_task(event_t *self, struct perf_session *session) | |||
444 | 542 | ||
445 | void thread__find_addr_map(struct thread *self, | 543 | void thread__find_addr_map(struct thread *self, |
446 | struct perf_session *session, u8 cpumode, | 544 | struct perf_session *session, u8 cpumode, |
447 | enum map_type type, u64 addr, | 545 | enum map_type type, pid_t pid, u64 addr, |
448 | struct addr_location *al) | 546 | struct addr_location *al) |
449 | { | 547 | { |
450 | struct map_groups *mg = &self->mg; | 548 | struct map_groups *mg = &self->mg; |
549 | struct kernel_info *kerninfo = NULL; | ||
451 | 550 | ||
452 | al->thread = self; | 551 | al->thread = self; |
453 | al->addr = addr; | 552 | al->addr = addr; |
553 | al->cpumode = cpumode; | ||
554 | al->filtered = false; | ||
454 | 555 | ||
455 | if (cpumode == PERF_RECORD_MISC_KERNEL) { | 556 | if (cpumode == PERF_RECORD_MISC_KERNEL && perf_host) { |
456 | al->level = 'k'; | 557 | al->level = 'k'; |
457 | mg = &session->kmaps; | 558 | kerninfo = kerninfo__findhost(&session->kerninfo_root); |
458 | } else if (cpumode == PERF_RECORD_MISC_USER) | 559 | mg = &kerninfo->kmaps; |
560 | } else if (cpumode == PERF_RECORD_MISC_USER && perf_host) { | ||
459 | al->level = '.'; | 561 | al->level = '.'; |
460 | else { | 562 | kerninfo = kerninfo__findhost(&session->kerninfo_root); |
461 | al->level = 'H'; | 563 | } else if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL && perf_guest) { |
564 | al->level = 'g'; | ||
565 | kerninfo = kerninfo__find(&session->kerninfo_root, pid); | ||
566 | if (!kerninfo) { | ||
567 | al->map = NULL; | ||
568 | return; | ||
569 | } | ||
570 | mg = &kerninfo->kmaps; | ||
571 | } else { | ||
572 | /* | ||
573 | * 'u' means guest os user space. | ||
574 | * TODO: We don't support guest user space. Might support late. | ||
575 | */ | ||
576 | if (cpumode == PERF_RECORD_MISC_GUEST_USER && perf_guest) | ||
577 | al->level = 'u'; | ||
578 | else | ||
579 | al->level = 'H'; | ||
462 | al->map = NULL; | 580 | al->map = NULL; |
581 | |||
582 | if ((cpumode == PERF_RECORD_MISC_GUEST_USER || | ||
583 | cpumode == PERF_RECORD_MISC_GUEST_KERNEL) && | ||
584 | !perf_guest) | ||
585 | al->filtered = true; | ||
586 | if ((cpumode == PERF_RECORD_MISC_USER || | ||
587 | cpumode == PERF_RECORD_MISC_KERNEL) && | ||
588 | !perf_host) | ||
589 | al->filtered = true; | ||
590 | |||
463 | return; | 591 | return; |
464 | } | 592 | } |
465 | try_again: | 593 | try_again: |
@@ -474,8 +602,11 @@ try_again: | |||
474 | * "[vdso]" dso, but for now lets use the old trick of looking | 602 | * "[vdso]" dso, but for now lets use the old trick of looking |
475 | * in the whole kernel symbol list. | 603 | * in the whole kernel symbol list. |
476 | */ | 604 | */ |
477 | if ((long long)al->addr < 0 && mg != &session->kmaps) { | 605 | if ((long long)al->addr < 0 && |
478 | mg = &session->kmaps; | 606 | cpumode == PERF_RECORD_MISC_KERNEL && |
607 | kerninfo && | ||
608 | mg != &kerninfo->kmaps) { | ||
609 | mg = &kerninfo->kmaps; | ||
479 | goto try_again; | 610 | goto try_again; |
480 | } | 611 | } |
481 | } else | 612 | } else |
@@ -484,11 +615,11 @@ try_again: | |||
484 | 615 | ||
485 | void thread__find_addr_location(struct thread *self, | 616 | void thread__find_addr_location(struct thread *self, |
486 | struct perf_session *session, u8 cpumode, | 617 | struct perf_session *session, u8 cpumode, |
487 | enum map_type type, u64 addr, | 618 | enum map_type type, pid_t pid, u64 addr, |
488 | struct addr_location *al, | 619 | struct addr_location *al, |
489 | symbol_filter_t filter) | 620 | symbol_filter_t filter) |
490 | { | 621 | { |
491 | thread__find_addr_map(self, session, cpumode, type, addr, al); | 622 | thread__find_addr_map(self, session, cpumode, type, pid, addr, al); |
492 | if (al->map != NULL) | 623 | if (al->map != NULL) |
493 | al->sym = map__find_symbol(al->map, al->addr, filter); | 624 | al->sym = map__find_symbol(al->map, al->addr, filter); |
494 | else | 625 | else |
@@ -524,7 +655,7 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session, | |||
524 | dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); | 655 | dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); |
525 | 656 | ||
526 | thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION, | 657 | thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION, |
527 | self->ip.ip, al); | 658 | self->ip.pid, self->ip.ip, al); |
528 | dump_printf(" ...... dso: %s\n", | 659 | dump_printf(" ...... dso: %s\n", |
529 | al->map ? al->map->dso->long_name : | 660 | al->map ? al->map->dso->long_name : |
530 | al->level == 'H' ? "[hypervisor]" : "<not found>"); | 661 | al->level == 'H' ? "[hypervisor]" : "<not found>"); |
@@ -554,7 +685,6 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session, | |||
554 | !strlist__has_entry(symbol_conf.sym_list, al->sym->name)) | 685 | !strlist__has_entry(symbol_conf.sym_list, al->sym->name)) |
555 | goto out_filtered; | 686 | goto out_filtered; |
556 | 687 | ||
557 | al->filtered = false; | ||
558 | return 0; | 688 | return 0; |
559 | 689 | ||
560 | out_filtered: | 690 | out_filtered: |
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index e5740ea140ab..4af2ed5d48ad 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h | |||
@@ -79,6 +79,7 @@ struct sample_data { | |||
79 | 79 | ||
80 | struct build_id_event { | 80 | struct build_id_event { |
81 | struct perf_event_header header; | 81 | struct perf_event_header header; |
82 | pid_t pid; | ||
82 | u8 build_id[ALIGN(BUILD_ID_SIZE, sizeof(u64))]; | 83 | u8 build_id[ALIGN(BUILD_ID_SIZE, sizeof(u64))]; |
83 | char filename[]; | 84 | char filename[]; |
84 | }; | 85 | }; |
@@ -154,10 +155,13 @@ int event__synthesize_thread(pid_t pid, event__handler_t process, | |||
154 | void event__synthesize_threads(event__handler_t process, | 155 | void event__synthesize_threads(event__handler_t process, |
155 | struct perf_session *session); | 156 | struct perf_session *session); |
156 | int event__synthesize_kernel_mmap(event__handler_t process, | 157 | int event__synthesize_kernel_mmap(event__handler_t process, |
157 | struct perf_session *session, | 158 | struct perf_session *session, |
158 | const char *symbol_name); | 159 | struct kernel_info *kerninfo, |
160 | const char *symbol_name); | ||
161 | |||
159 | int event__synthesize_modules(event__handler_t process, | 162 | int event__synthesize_modules(event__handler_t process, |
160 | struct perf_session *session); | 163 | struct perf_session *session, |
164 | struct kernel_info *kerninfo); | ||
161 | 165 | ||
162 | int event__process_comm(event_t *self, struct perf_session *session); | 166 | int event__process_comm(event_t *self, struct perf_session *session); |
163 | int event__process_lost(event_t *self, struct perf_session *session); | 167 | int event__process_lost(event_t *self, struct perf_session *session); |
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 628173ba689e..75d016768021 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
@@ -190,7 +190,8 @@ static int write_padded(int fd, const void *bf, size_t count, | |||
190 | continue; \ | 190 | continue; \ |
191 | else | 191 | else |
192 | 192 | ||
193 | static int __dsos__write_buildid_table(struct list_head *head, u16 misc, int fd) | 193 | static int __dsos__write_buildid_table(struct list_head *head, pid_t pid, |
194 | u16 misc, int fd) | ||
194 | { | 195 | { |
195 | struct dso *pos; | 196 | struct dso *pos; |
196 | 197 | ||
@@ -205,6 +206,7 @@ static int __dsos__write_buildid_table(struct list_head *head, u16 misc, int fd) | |||
205 | len = ALIGN(len, NAME_ALIGN); | 206 | len = ALIGN(len, NAME_ALIGN); |
206 | memset(&b, 0, sizeof(b)); | 207 | memset(&b, 0, sizeof(b)); |
207 | memcpy(&b.build_id, pos->build_id, sizeof(pos->build_id)); | 208 | memcpy(&b.build_id, pos->build_id, sizeof(pos->build_id)); |
209 | b.pid = pid; | ||
208 | b.header.misc = misc; | 210 | b.header.misc = misc; |
209 | b.header.size = sizeof(b) + len; | 211 | b.header.size = sizeof(b) + len; |
210 | err = do_write(fd, &b, sizeof(b)); | 212 | err = do_write(fd, &b, sizeof(b)); |
@@ -219,13 +221,33 @@ static int __dsos__write_buildid_table(struct list_head *head, u16 misc, int fd) | |||
219 | return 0; | 221 | return 0; |
220 | } | 222 | } |
221 | 223 | ||
222 | static int dsos__write_buildid_table(int fd) | 224 | static int dsos__write_buildid_table(struct perf_header *header, int fd) |
223 | { | 225 | { |
224 | int err = __dsos__write_buildid_table(&dsos__kernel, | 226 | struct perf_session *session = container_of(header, |
225 | PERF_RECORD_MISC_KERNEL, fd); | 227 | struct perf_session, header); |
226 | if (err == 0) | 228 | struct rb_node *nd; |
227 | err = __dsos__write_buildid_table(&dsos__user, | 229 | int err = 0; |
228 | PERF_RECORD_MISC_USER, fd); | 230 | u16 kmisc, umisc; |
231 | |||
232 | for (nd = rb_first(&session->kerninfo_root); nd; nd = rb_next(nd)) { | ||
233 | struct kernel_info *pos = rb_entry(nd, struct kernel_info, | ||
234 | rb_node); | ||
235 | if (is_host_kernel(pos)) { | ||
236 | kmisc = PERF_RECORD_MISC_KERNEL; | ||
237 | umisc = PERF_RECORD_MISC_USER; | ||
238 | } else { | ||
239 | kmisc = PERF_RECORD_MISC_GUEST_KERNEL; | ||
240 | umisc = PERF_RECORD_MISC_GUEST_USER; | ||
241 | } | ||
242 | |||
243 | err = __dsos__write_buildid_table(&pos->dsos__kernel, pos->pid, | ||
244 | kmisc, fd); | ||
245 | if (err == 0) | ||
246 | err = __dsos__write_buildid_table(&pos->dsos__user, | ||
247 | pos->pid, umisc, fd); | ||
248 | if (err) | ||
249 | break; | ||
250 | } | ||
229 | return err; | 251 | return err; |
230 | } | 252 | } |
231 | 253 | ||
@@ -342,9 +364,12 @@ static int __dsos__cache_build_ids(struct list_head *head, const char *debugdir) | |||
342 | return err; | 364 | return err; |
343 | } | 365 | } |
344 | 366 | ||
345 | static int dsos__cache_build_ids(void) | 367 | static int dsos__cache_build_ids(struct perf_header *self) |
346 | { | 368 | { |
347 | int err_kernel, err_user; | 369 | struct perf_session *session = container_of(self, |
370 | struct perf_session, header); | ||
371 | struct rb_node *nd; | ||
372 | int ret = 0; | ||
348 | char debugdir[PATH_MAX]; | 373 | char debugdir[PATH_MAX]; |
349 | 374 | ||
350 | snprintf(debugdir, sizeof(debugdir), "%s/%s", getenv("HOME"), | 375 | snprintf(debugdir, sizeof(debugdir), "%s/%s", getenv("HOME"), |
@@ -353,9 +378,30 @@ static int dsos__cache_build_ids(void) | |||
353 | if (mkdir(debugdir, 0755) != 0 && errno != EEXIST) | 378 | if (mkdir(debugdir, 0755) != 0 && errno != EEXIST) |
354 | return -1; | 379 | return -1; |
355 | 380 | ||
356 | err_kernel = __dsos__cache_build_ids(&dsos__kernel, debugdir); | 381 | for (nd = rb_first(&session->kerninfo_root); nd; nd = rb_next(nd)) { |
357 | err_user = __dsos__cache_build_ids(&dsos__user, debugdir); | 382 | struct kernel_info *pos = rb_entry(nd, struct kernel_info, |
358 | return err_kernel || err_user ? -1 : 0; | 383 | rb_node); |
384 | ret |= __dsos__cache_build_ids(&pos->dsos__kernel, debugdir); | ||
385 | ret |= __dsos__cache_build_ids(&pos->dsos__user, debugdir); | ||
386 | } | ||
387 | return ret ? -1 : 0; | ||
388 | } | ||
389 | |||
390 | static bool dsos__read_build_ids(struct perf_header *self, bool with_hits) | ||
391 | { | ||
392 | bool ret = false; | ||
393 | struct perf_session *session = container_of(self, | ||
394 | struct perf_session, header); | ||
395 | struct rb_node *nd; | ||
396 | |||
397 | for (nd = rb_first(&session->kerninfo_root); nd; nd = rb_next(nd)) { | ||
398 | struct kernel_info *pos = rb_entry(nd, struct kernel_info, | ||
399 | rb_node); | ||
400 | ret |= __dsos__read_build_ids(&pos->dsos__kernel, with_hits); | ||
401 | ret |= __dsos__read_build_ids(&pos->dsos__user, with_hits); | ||
402 | } | ||
403 | |||
404 | return ret; | ||
359 | } | 405 | } |
360 | 406 | ||
361 | static int perf_header__adds_write(struct perf_header *self, int fd) | 407 | static int perf_header__adds_write(struct perf_header *self, int fd) |
@@ -366,7 +412,7 @@ static int perf_header__adds_write(struct perf_header *self, int fd) | |||
366 | u64 sec_start; | 412 | u64 sec_start; |
367 | int idx = 0, err; | 413 | int idx = 0, err; |
368 | 414 | ||
369 | if (dsos__read_build_ids(true)) | 415 | if (dsos__read_build_ids(self, true)) |
370 | perf_header__set_feat(self, HEADER_BUILD_ID); | 416 | perf_header__set_feat(self, HEADER_BUILD_ID); |
371 | 417 | ||
372 | nr_sections = bitmap_weight(self->adds_features, HEADER_FEAT_BITS); | 418 | nr_sections = bitmap_weight(self->adds_features, HEADER_FEAT_BITS); |
@@ -401,14 +447,14 @@ static int perf_header__adds_write(struct perf_header *self, int fd) | |||
401 | 447 | ||
402 | /* Write build-ids */ | 448 | /* Write build-ids */ |
403 | buildid_sec->offset = lseek(fd, 0, SEEK_CUR); | 449 | buildid_sec->offset = lseek(fd, 0, SEEK_CUR); |
404 | err = dsos__write_buildid_table(fd); | 450 | err = dsos__write_buildid_table(self, fd); |
405 | if (err < 0) { | 451 | if (err < 0) { |
406 | pr_debug("failed to write buildid table\n"); | 452 | pr_debug("failed to write buildid table\n"); |
407 | goto out_free; | 453 | goto out_free; |
408 | } | 454 | } |
409 | buildid_sec->size = lseek(fd, 0, SEEK_CUR) - | 455 | buildid_sec->size = lseek(fd, 0, SEEK_CUR) - |
410 | buildid_sec->offset; | 456 | buildid_sec->offset; |
411 | dsos__cache_build_ids(); | 457 | dsos__cache_build_ids(self); |
412 | } | 458 | } |
413 | 459 | ||
414 | lseek(fd, sec_start, SEEK_SET); | 460 | lseek(fd, sec_start, SEEK_SET); |
@@ -633,6 +679,85 @@ int perf_file_header__read(struct perf_file_header *self, | |||
633 | return 0; | 679 | return 0; |
634 | } | 680 | } |
635 | 681 | ||
682 | static int __event_process_build_id(struct build_id_event *bev, | ||
683 | char *filename, | ||
684 | struct perf_session *session) | ||
685 | { | ||
686 | int err = -1; | ||
687 | struct list_head *head; | ||
688 | struct kernel_info *kerninfo; | ||
689 | u16 misc; | ||
690 | struct dso *dso; | ||
691 | enum dso_kernel_type dso_type; | ||
692 | |||
693 | kerninfo = kerninfo__findnew(&session->kerninfo_root, bev->pid); | ||
694 | if (!kerninfo) | ||
695 | goto out; | ||
696 | |||
697 | misc = bev->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | ||
698 | |||
699 | switch (misc) { | ||
700 | case PERF_RECORD_MISC_KERNEL: | ||
701 | dso_type = DSO_TYPE_KERNEL; | ||
702 | head = &kerninfo->dsos__kernel; | ||
703 | break; | ||
704 | case PERF_RECORD_MISC_GUEST_KERNEL: | ||
705 | dso_type = DSO_TYPE_GUEST_KERNEL; | ||
706 | head = &kerninfo->dsos__kernel; | ||
707 | break; | ||
708 | case PERF_RECORD_MISC_USER: | ||
709 | case PERF_RECORD_MISC_GUEST_USER: | ||
710 | dso_type = DSO_TYPE_USER; | ||
711 | head = &kerninfo->dsos__user; | ||
712 | break; | ||
713 | default: | ||
714 | goto out; | ||
715 | } | ||
716 | |||
717 | dso = __dsos__findnew(head, filename); | ||
718 | if (dso != NULL) { | ||
719 | dso__set_build_id(dso, &bev->build_id); | ||
720 | if (filename[0] == '[') | ||
721 | dso->kernel = dso_type; | ||
722 | } | ||
723 | |||
724 | err = 0; | ||
725 | out: | ||
726 | return err; | ||
727 | } | ||
728 | |||
729 | static int perf_header__read_build_ids(struct perf_header *self, | ||
730 | int input, u64 offset, u64 size) | ||
731 | { | ||
732 | struct perf_session *session = container_of(self, | ||
733 | struct perf_session, header); | ||
734 | struct build_id_event bev; | ||
735 | char filename[PATH_MAX]; | ||
736 | u64 limit = offset + size; | ||
737 | int err = -1; | ||
738 | |||
739 | while (offset < limit) { | ||
740 | ssize_t len; | ||
741 | |||
742 | if (read(input, &bev, sizeof(bev)) != sizeof(bev)) | ||
743 | goto out; | ||
744 | |||
745 | if (self->needs_swap) | ||
746 | perf_event_header__bswap(&bev.header); | ||
747 | |||
748 | len = bev.header.size - sizeof(bev); | ||
749 | if (read(input, filename, len) != len) | ||
750 | goto out; | ||
751 | |||
752 | __event_process_build_id(&bev, filename, session); | ||
753 | |||
754 | offset += bev.header.size; | ||
755 | } | ||
756 | err = 0; | ||
757 | out: | ||
758 | return err; | ||
759 | } | ||
760 | |||
636 | static int perf_file_section__process(struct perf_file_section *self, | 761 | static int perf_file_section__process(struct perf_file_section *self, |
637 | struct perf_header *ph, | 762 | struct perf_header *ph, |
638 | int feat, int fd) | 763 | int feat, int fd) |
@@ -989,6 +1114,7 @@ int event__process_tracing_data(event_t *self, | |||
989 | 1114 | ||
990 | int event__synthesize_build_id(struct dso *pos, u16 misc, | 1115 | int event__synthesize_build_id(struct dso *pos, u16 misc, |
991 | event__handler_t process, | 1116 | event__handler_t process, |
1117 | struct kernel_info *kerninfo, | ||
992 | struct perf_session *session) | 1118 | struct perf_session *session) |
993 | { | 1119 | { |
994 | event_t ev; | 1120 | event_t ev; |
@@ -1005,6 +1131,7 @@ int event__synthesize_build_id(struct dso *pos, u16 misc, | |||
1005 | memcpy(&ev.build_id.build_id, pos->build_id, sizeof(pos->build_id)); | 1131 | memcpy(&ev.build_id.build_id, pos->build_id, sizeof(pos->build_id)); |
1006 | ev.build_id.header.type = PERF_RECORD_HEADER_BUILD_ID; | 1132 | ev.build_id.header.type = PERF_RECORD_HEADER_BUILD_ID; |
1007 | ev.build_id.header.misc = misc; | 1133 | ev.build_id.header.misc = misc; |
1134 | ev.build_id.pid = kerninfo->pid; | ||
1008 | ev.build_id.header.size = sizeof(ev.build_id) + len; | 1135 | ev.build_id.header.size = sizeof(ev.build_id) + len; |
1009 | memcpy(&ev.build_id.filename, pos->long_name, pos->long_name_len); | 1136 | memcpy(&ev.build_id.filename, pos->long_name, pos->long_name_len); |
1010 | 1137 | ||
@@ -1015,6 +1142,7 @@ int event__synthesize_build_id(struct dso *pos, u16 misc, | |||
1015 | 1142 | ||
1016 | static int __event_synthesize_build_ids(struct list_head *head, u16 misc, | 1143 | static int __event_synthesize_build_ids(struct list_head *head, u16 misc, |
1017 | event__handler_t process, | 1144 | event__handler_t process, |
1145 | struct kernel_info *kerninfo, | ||
1018 | struct perf_session *session) | 1146 | struct perf_session *session) |
1019 | { | 1147 | { |
1020 | struct dso *pos; | 1148 | struct dso *pos; |
@@ -1024,7 +1152,8 @@ static int __event_synthesize_build_ids(struct list_head *head, u16 misc, | |||
1024 | if (!pos->hit) | 1152 | if (!pos->hit) |
1025 | continue; | 1153 | continue; |
1026 | 1154 | ||
1027 | err = event__synthesize_build_id(pos, misc, process, session); | 1155 | err = event__synthesize_build_id(pos, misc, process, |
1156 | kerninfo, session); | ||
1028 | if (err < 0) | 1157 | if (err < 0) |
1029 | return err; | 1158 | return err; |
1030 | } | 1159 | } |
@@ -1035,44 +1164,48 @@ static int __event_synthesize_build_ids(struct list_head *head, u16 misc, | |||
1035 | int event__synthesize_build_ids(event__handler_t process, | 1164 | int event__synthesize_build_ids(event__handler_t process, |
1036 | struct perf_session *session) | 1165 | struct perf_session *session) |
1037 | { | 1166 | { |
1038 | int err; | 1167 | int err = 0; |
1168 | u16 kmisc, umisc; | ||
1169 | struct kernel_info *pos; | ||
1170 | struct rb_node *nd; | ||
1039 | 1171 | ||
1040 | if (!dsos__read_build_ids(true)) | 1172 | if (!dsos__read_build_ids(&session->header, true)) |
1041 | return 0; | 1173 | return 0; |
1042 | 1174 | ||
1043 | err = __event_synthesize_build_ids(&dsos__kernel, | 1175 | for (nd = rb_first(&session->kerninfo_root); nd; nd = rb_next(nd)) { |
1044 | PERF_RECORD_MISC_KERNEL, | 1176 | pos = rb_entry(nd, struct kernel_info, rb_node); |
1045 | process, session); | 1177 | if (is_host_kernel(pos)) { |
1046 | if (err == 0) | 1178 | kmisc = PERF_RECORD_MISC_KERNEL; |
1047 | err = __event_synthesize_build_ids(&dsos__user, | 1179 | umisc = PERF_RECORD_MISC_USER; |
1048 | PERF_RECORD_MISC_USER, | 1180 | } else { |
1049 | process, session); | 1181 | kmisc = PERF_RECORD_MISC_GUEST_KERNEL; |
1182 | umisc = PERF_RECORD_MISC_GUEST_USER; | ||
1183 | } | ||
1184 | |||
1185 | err = __event_synthesize_build_ids(&pos->dsos__kernel, | ||
1186 | kmisc, process, pos, session); | ||
1187 | if (err == 0) | ||
1188 | err = __event_synthesize_build_ids(&pos->dsos__user, | ||
1189 | umisc, process, pos, session); | ||
1190 | if (err) | ||
1191 | break; | ||
1192 | } | ||
1050 | 1193 | ||
1051 | if (err < 0) { | 1194 | if (err < 0) { |
1052 | pr_debug("failed to synthesize build ids\n"); | 1195 | pr_debug("failed to synthesize build ids\n"); |
1053 | return err; | 1196 | return err; |
1054 | } | 1197 | } |
1055 | 1198 | ||
1056 | dsos__cache_build_ids(); | 1199 | dsos__cache_build_ids(&session->header); |
1057 | 1200 | ||
1058 | return 0; | 1201 | return 0; |
1059 | } | 1202 | } |
1060 | 1203 | ||
1061 | int event__process_build_id(event_t *self, | 1204 | int event__process_build_id(event_t *self, |
1062 | struct perf_session *session __unused) | 1205 | struct perf_session *session) |
1063 | { | 1206 | { |
1064 | struct list_head *head = &dsos__user; | 1207 | __event_process_build_id(&self->build_id, |
1065 | struct dso *dso; | 1208 | self->build_id.filename, |
1066 | 1209 | session); | |
1067 | if (self->build_id.header.misc & PERF_RECORD_MISC_KERNEL) | ||
1068 | head = &dsos__kernel; | ||
1069 | |||
1070 | dso = __dsos__findnew(head, self->build_id.filename); | ||
1071 | if (dso != NULL) { | ||
1072 | dso__set_build_id(dso, &self->build_id.build_id); | ||
1073 | if (head == &dsos__kernel && self->build_id.filename[0] == '[') | ||
1074 | dso->kernel = 1; | ||
1075 | } | ||
1076 | |||
1077 | return 0; | 1210 | return 0; |
1078 | } | 1211 | } |
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 4214e2375650..275915458148 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h | |||
@@ -120,6 +120,7 @@ int event__process_tracing_data(event_t *self, | |||
120 | 120 | ||
121 | int event__synthesize_build_id(struct dso *pos, u16 misc, | 121 | int event__synthesize_build_id(struct dso *pos, u16 misc, |
122 | event__handler_t process, | 122 | event__handler_t process, |
123 | struct kernel_info *kerninfo, | ||
123 | struct perf_session *session); | 124 | struct perf_session *session); |
124 | int event__synthesize_build_ids(event__handler_t process, | 125 | int event__synthesize_build_ids(event__handler_t process, |
125 | struct perf_session *session); | 126 | struct perf_session *session); |
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 9c2b8743cef6..ad6b22dde27f 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
@@ -8,6 +8,30 @@ struct callchain_param callchain_param = { | |||
8 | .min_percent = 0.5 | 8 | .min_percent = 0.5 |
9 | }; | 9 | }; |
10 | 10 | ||
11 | void __perf_session__add_count(struct hist_entry *he, | ||
12 | struct addr_location *al, | ||
13 | u64 count) | ||
14 | { | ||
15 | he->count += count; | ||
16 | |||
17 | switch (al->cpumode) { | ||
18 | case PERF_RECORD_MISC_KERNEL: | ||
19 | he->count_sys += count; | ||
20 | break; | ||
21 | case PERF_RECORD_MISC_USER: | ||
22 | he->count_us += count; | ||
23 | break; | ||
24 | case PERF_RECORD_MISC_GUEST_KERNEL: | ||
25 | he->count_guest_sys += count; | ||
26 | break; | ||
27 | case PERF_RECORD_MISC_GUEST_USER: | ||
28 | he->count_guest_us += count; | ||
29 | break; | ||
30 | default: | ||
31 | break; | ||
32 | } | ||
33 | } | ||
34 | |||
11 | /* | 35 | /* |
12 | * histogram, sorted on item, collects counts | 36 | * histogram, sorted on item, collects counts |
13 | */ | 37 | */ |
@@ -464,7 +488,7 @@ int hist_entry__snprintf(struct hist_entry *self, | |||
464 | u64 session_total) | 488 | u64 session_total) |
465 | { | 489 | { |
466 | struct sort_entry *se; | 490 | struct sort_entry *se; |
467 | u64 count, total; | 491 | u64 count, total, count_sys, count_us, count_guest_sys, count_guest_us; |
468 | const char *sep = symbol_conf.field_sep; | 492 | const char *sep = symbol_conf.field_sep; |
469 | int ret; | 493 | int ret; |
470 | 494 | ||
@@ -474,9 +498,17 @@ int hist_entry__snprintf(struct hist_entry *self, | |||
474 | if (pair_session) { | 498 | if (pair_session) { |
475 | count = self->pair ? self->pair->count : 0; | 499 | count = self->pair ? self->pair->count : 0; |
476 | total = pair_session->events_stats.total; | 500 | total = pair_session->events_stats.total; |
501 | count_sys = self->pair ? self->pair->count_sys : 0; | ||
502 | count_us = self->pair ? self->pair->count_us : 0; | ||
503 | count_guest_sys = self->pair ? self->pair->count_guest_sys : 0; | ||
504 | count_guest_us = self->pair ? self->pair->count_guest_us : 0; | ||
477 | } else { | 505 | } else { |
478 | count = self->count; | 506 | count = self->count; |
479 | total = session_total; | 507 | total = session_total; |
508 | count_sys = self->count_sys; | ||
509 | count_us = self->count_us; | ||
510 | count_guest_sys = self->count_guest_sys; | ||
511 | count_guest_us = self->count_guest_us; | ||
480 | } | 512 | } |
481 | 513 | ||
482 | if (total) { | 514 | if (total) { |
@@ -487,6 +519,26 @@ int hist_entry__snprintf(struct hist_entry *self, | |||
487 | else | 519 | else |
488 | ret = snprintf(s, size, sep ? "%.2f" : " %6.2f%%", | 520 | ret = snprintf(s, size, sep ? "%.2f" : " %6.2f%%", |
489 | (count * 100.0) / total); | 521 | (count * 100.0) / total); |
522 | if (symbol_conf.show_cpu_utilization) { | ||
523 | ret += percent_color_snprintf(s + ret, size - ret, | ||
524 | sep ? "%.2f" : " %6.2f%%", | ||
525 | (count_sys * 100.0) / total); | ||
526 | ret += percent_color_snprintf(s + ret, size - ret, | ||
527 | sep ? "%.2f" : " %6.2f%%", | ||
528 | (count_us * 100.0) / total); | ||
529 | if (perf_guest) { | ||
530 | ret += percent_color_snprintf(s + ret, | ||
531 | size - ret, | ||
532 | sep ? "%.2f" : " %6.2f%%", | ||
533 | (count_guest_sys * 100.0) / | ||
534 | total); | ||
535 | ret += percent_color_snprintf(s + ret, | ||
536 | size - ret, | ||
537 | sep ? "%.2f" : " %6.2f%%", | ||
538 | (count_guest_us * 100.0) / | ||
539 | total); | ||
540 | } | ||
541 | } | ||
490 | } else | 542 | } else |
491 | ret = snprintf(s, size, sep ? "%lld" : "%12lld ", count); | 543 | ret = snprintf(s, size, sep ? "%lld" : "%12lld ", count); |
492 | 544 | ||
@@ -597,6 +649,24 @@ size_t perf_session__fprintf_hists(struct rb_root *hists, | |||
597 | fputs(" Samples ", fp); | 649 | fputs(" Samples ", fp); |
598 | } | 650 | } |
599 | 651 | ||
652 | if (symbol_conf.show_cpu_utilization) { | ||
653 | if (sep) { | ||
654 | ret += fprintf(fp, "%csys", *sep); | ||
655 | ret += fprintf(fp, "%cus", *sep); | ||
656 | if (perf_guest) { | ||
657 | ret += fprintf(fp, "%cguest sys", *sep); | ||
658 | ret += fprintf(fp, "%cguest us", *sep); | ||
659 | } | ||
660 | } else { | ||
661 | ret += fprintf(fp, " sys "); | ||
662 | ret += fprintf(fp, " us "); | ||
663 | if (perf_guest) { | ||
664 | ret += fprintf(fp, " guest sys "); | ||
665 | ret += fprintf(fp, " guest us "); | ||
666 | } | ||
667 | } | ||
668 | } | ||
669 | |||
600 | if (pair) { | 670 | if (pair) { |
601 | if (sep) | 671 | if (sep) |
602 | ret += fprintf(fp, "%cDelta", *sep); | 672 | ret += fprintf(fp, "%cDelta", *sep); |
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index ad17f0ad798b..9df1c340ec92 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h | |||
@@ -12,6 +12,9 @@ struct addr_location; | |||
12 | struct symbol; | 12 | struct symbol; |
13 | struct rb_root; | 13 | struct rb_root; |
14 | 14 | ||
15 | void __perf_session__add_count(struct hist_entry *he, | ||
16 | struct addr_location *al, | ||
17 | u64 count); | ||
15 | struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists, | 18 | struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists, |
16 | struct addr_location *al, | 19 | struct addr_location *al, |
17 | struct symbol *parent, | 20 | struct symbol *parent, |
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 37913b241bdf..7facd016ec97 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c | |||
@@ -4,6 +4,7 @@ | |||
4 | #include <stdlib.h> | 4 | #include <stdlib.h> |
5 | #include <string.h> | 5 | #include <string.h> |
6 | #include <stdio.h> | 6 | #include <stdio.h> |
7 | #include <unistd.h> | ||
7 | #include "map.h" | 8 | #include "map.h" |
8 | 9 | ||
9 | const char *map_type__name[MAP__NR_TYPES] = { | 10 | const char *map_type__name[MAP__NR_TYPES] = { |
@@ -37,9 +38,11 @@ void map__init(struct map *self, enum map_type type, | |||
37 | self->map_ip = map__map_ip; | 38 | self->map_ip = map__map_ip; |
38 | self->unmap_ip = map__unmap_ip; | 39 | self->unmap_ip = map__unmap_ip; |
39 | RB_CLEAR_NODE(&self->rb_node); | 40 | RB_CLEAR_NODE(&self->rb_node); |
41 | self->groups = NULL; | ||
40 | } | 42 | } |
41 | 43 | ||
42 | struct map *map__new(u64 start, u64 len, u64 pgoff, u32 pid, char *filename, | 44 | struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, |
45 | u64 pgoff, u32 pid, char *filename, | ||
43 | enum map_type type, char *cwd, int cwdlen) | 46 | enum map_type type, char *cwd, int cwdlen) |
44 | { | 47 | { |
45 | struct map *self = malloc(sizeof(*self)); | 48 | struct map *self = malloc(sizeof(*self)); |
@@ -66,7 +69,7 @@ struct map *map__new(u64 start, u64 len, u64 pgoff, u32 pid, char *filename, | |||
66 | filename = newfilename; | 69 | filename = newfilename; |
67 | } | 70 | } |
68 | 71 | ||
69 | dso = dsos__findnew(filename); | 72 | dso = __dsos__findnew(dsos__list, filename); |
70 | if (dso == NULL) | 73 | if (dso == NULL) |
71 | goto out_delete; | 74 | goto out_delete; |
72 | 75 | ||
@@ -242,6 +245,7 @@ void map_groups__init(struct map_groups *self) | |||
242 | self->maps[i] = RB_ROOT; | 245 | self->maps[i] = RB_ROOT; |
243 | INIT_LIST_HEAD(&self->removed_maps[i]); | 246 | INIT_LIST_HEAD(&self->removed_maps[i]); |
244 | } | 247 | } |
248 | self->this_kerninfo = NULL; | ||
245 | } | 249 | } |
246 | 250 | ||
247 | void map_groups__flush(struct map_groups *self) | 251 | void map_groups__flush(struct map_groups *self) |
@@ -508,3 +512,134 @@ struct map *maps__find(struct rb_root *maps, u64 ip) | |||
508 | 512 | ||
509 | return NULL; | 513 | return NULL; |
510 | } | 514 | } |
515 | |||
516 | struct kernel_info *add_new_kernel_info(struct rb_root *kerninfo_root, | ||
517 | pid_t pid, const char *root_dir) | ||
518 | { | ||
519 | struct rb_node **p = &kerninfo_root->rb_node; | ||
520 | struct rb_node *parent = NULL; | ||
521 | struct kernel_info *kerninfo, *pos; | ||
522 | |||
523 | kerninfo = malloc(sizeof(struct kernel_info)); | ||
524 | if (!kerninfo) | ||
525 | return NULL; | ||
526 | |||
527 | kerninfo->pid = pid; | ||
528 | map_groups__init(&kerninfo->kmaps); | ||
529 | kerninfo->root_dir = strdup(root_dir); | ||
530 | RB_CLEAR_NODE(&kerninfo->rb_node); | ||
531 | INIT_LIST_HEAD(&kerninfo->dsos__user); | ||
532 | INIT_LIST_HEAD(&kerninfo->dsos__kernel); | ||
533 | kerninfo->kmaps.this_kerninfo = kerninfo; | ||
534 | |||
535 | while (*p != NULL) { | ||
536 | parent = *p; | ||
537 | pos = rb_entry(parent, struct kernel_info, rb_node); | ||
538 | if (pid < pos->pid) | ||
539 | p = &(*p)->rb_left; | ||
540 | else | ||
541 | p = &(*p)->rb_right; | ||
542 | } | ||
543 | |||
544 | rb_link_node(&kerninfo->rb_node, parent, p); | ||
545 | rb_insert_color(&kerninfo->rb_node, kerninfo_root); | ||
546 | |||
547 | return kerninfo; | ||
548 | } | ||
549 | |||
550 | struct kernel_info *kerninfo__find(struct rb_root *kerninfo_root, pid_t pid) | ||
551 | { | ||
552 | struct rb_node **p = &kerninfo_root->rb_node; | ||
553 | struct rb_node *parent = NULL; | ||
554 | struct kernel_info *kerninfo; | ||
555 | struct kernel_info *default_kerninfo = NULL; | ||
556 | |||
557 | while (*p != NULL) { | ||
558 | parent = *p; | ||
559 | kerninfo = rb_entry(parent, struct kernel_info, rb_node); | ||
560 | if (pid < kerninfo->pid) | ||
561 | p = &(*p)->rb_left; | ||
562 | else if (pid > kerninfo->pid) | ||
563 | p = &(*p)->rb_right; | ||
564 | else | ||
565 | return kerninfo; | ||
566 | if (!kerninfo->pid) | ||
567 | default_kerninfo = kerninfo; | ||
568 | } | ||
569 | |||
570 | return default_kerninfo; | ||
571 | } | ||
572 | |||
573 | struct kernel_info *kerninfo__findhost(struct rb_root *kerninfo_root) | ||
574 | { | ||
575 | struct rb_node **p = &kerninfo_root->rb_node; | ||
576 | struct rb_node *parent = NULL; | ||
577 | struct kernel_info *kerninfo; | ||
578 | pid_t pid = HOST_KERNEL_ID; | ||
579 | |||
580 | while (*p != NULL) { | ||
581 | parent = *p; | ||
582 | kerninfo = rb_entry(parent, struct kernel_info, rb_node); | ||
583 | if (pid < kerninfo->pid) | ||
584 | p = &(*p)->rb_left; | ||
585 | else if (pid > kerninfo->pid) | ||
586 | p = &(*p)->rb_right; | ||
587 | else | ||
588 | return kerninfo; | ||
589 | } | ||
590 | |||
591 | return NULL; | ||
592 | } | ||
593 | |||
594 | struct kernel_info *kerninfo__findnew(struct rb_root *kerninfo_root, pid_t pid) | ||
595 | { | ||
596 | char path[PATH_MAX]; | ||
597 | const char *root_dir; | ||
598 | int ret; | ||
599 | struct kernel_info *kerninfo = kerninfo__find(kerninfo_root, pid); | ||
600 | |||
601 | if (!kerninfo || kerninfo->pid != pid) { | ||
602 | if (pid == HOST_KERNEL_ID || pid == DEFAULT_GUEST_KERNEL_ID) | ||
603 | root_dir = ""; | ||
604 | else { | ||
605 | if (!symbol_conf.guestmount) | ||
606 | goto out; | ||
607 | sprintf(path, "%s/%d", symbol_conf.guestmount, pid); | ||
608 | ret = access(path, R_OK); | ||
609 | if (ret) { | ||
610 | pr_err("Can't access file %s\n", path); | ||
611 | goto out; | ||
612 | } | ||
613 | root_dir = path; | ||
614 | } | ||
615 | kerninfo = add_new_kernel_info(kerninfo_root, pid, root_dir); | ||
616 | } | ||
617 | |||
618 | out: | ||
619 | return kerninfo; | ||
620 | } | ||
621 | |||
622 | void kerninfo__process_allkernels(struct rb_root *kerninfo_root, | ||
623 | process_kernel_info process, | ||
624 | void *data) | ||
625 | { | ||
626 | struct rb_node *nd; | ||
627 | |||
628 | for (nd = rb_first(kerninfo_root); nd; nd = rb_next(nd)) { | ||
629 | struct kernel_info *pos = rb_entry(nd, struct kernel_info, | ||
630 | rb_node); | ||
631 | process(pos, data); | ||
632 | } | ||
633 | } | ||
634 | |||
635 | char *kern_mmap_name(struct kernel_info *kerninfo, char *buff) | ||
636 | { | ||
637 | if (is_host_kernel(kerninfo)) | ||
638 | sprintf(buff, "[%s]", "kernel.kallsyms"); | ||
639 | else if (is_default_guest(kerninfo)) | ||
640 | sprintf(buff, "[%s]", "guest.kernel.kallsyms"); | ||
641 | else | ||
642 | sprintf(buff, "[%s.%d]", "guest.kernel.kallsyms", kerninfo->pid); | ||
643 | |||
644 | return buff; | ||
645 | } | ||
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index 2031278cc06a..30d38d634e09 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h | |||
@@ -19,6 +19,7 @@ extern const char *map_type__name[MAP__NR_TYPES]; | |||
19 | struct dso; | 19 | struct dso; |
20 | struct ref_reloc_sym; | 20 | struct ref_reloc_sym; |
21 | struct map_groups; | 21 | struct map_groups; |
22 | struct kernel_info; | ||
22 | 23 | ||
23 | struct map { | 24 | struct map { |
24 | union { | 25 | union { |
@@ -36,6 +37,7 @@ struct map { | |||
36 | u64 (*unmap_ip)(struct map *, u64); | 37 | u64 (*unmap_ip)(struct map *, u64); |
37 | 38 | ||
38 | struct dso *dso; | 39 | struct dso *dso; |
40 | struct map_groups *groups; | ||
39 | }; | 41 | }; |
40 | 42 | ||
41 | struct kmap { | 43 | struct kmap { |
@@ -43,6 +45,26 @@ struct kmap { | |||
43 | struct map_groups *kmaps; | 45 | struct map_groups *kmaps; |
44 | }; | 46 | }; |
45 | 47 | ||
48 | struct map_groups { | ||
49 | struct rb_root maps[MAP__NR_TYPES]; | ||
50 | struct list_head removed_maps[MAP__NR_TYPES]; | ||
51 | struct kernel_info *this_kerninfo; | ||
52 | }; | ||
53 | |||
54 | /* Native host kernel uses -1 as pid index in kernel_info */ | ||
55 | #define HOST_KERNEL_ID (-1) | ||
56 | #define DEFAULT_GUEST_KERNEL_ID (0) | ||
57 | |||
58 | struct kernel_info { | ||
59 | struct rb_node rb_node; | ||
60 | pid_t pid; | ||
61 | char *root_dir; | ||
62 | struct list_head dsos__user; | ||
63 | struct list_head dsos__kernel; | ||
64 | struct map_groups kmaps; | ||
65 | struct map *vmlinux_maps[MAP__NR_TYPES]; | ||
66 | }; | ||
67 | |||
46 | static inline struct kmap *map__kmap(struct map *self) | 68 | static inline struct kmap *map__kmap(struct map *self) |
47 | { | 69 | { |
48 | return (struct kmap *)(self + 1); | 70 | return (struct kmap *)(self + 1); |
@@ -74,7 +96,8 @@ typedef int (*symbol_filter_t)(struct map *map, struct symbol *sym); | |||
74 | 96 | ||
75 | void map__init(struct map *self, enum map_type type, | 97 | void map__init(struct map *self, enum map_type type, |
76 | u64 start, u64 end, u64 pgoff, struct dso *dso); | 98 | u64 start, u64 end, u64 pgoff, struct dso *dso); |
77 | struct map *map__new(u64 start, u64 len, u64 pgoff, u32 pid, char *filename, | 99 | struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, |
100 | u64 pgoff, u32 pid, char *filename, | ||
78 | enum map_type type, char *cwd, int cwdlen); | 101 | enum map_type type, char *cwd, int cwdlen); |
79 | void map__delete(struct map *self); | 102 | void map__delete(struct map *self); |
80 | struct map *map__clone(struct map *self); | 103 | struct map *map__clone(struct map *self); |
@@ -91,11 +114,6 @@ void map__fixup_end(struct map *self); | |||
91 | 114 | ||
92 | void map__reloc_vmlinux(struct map *self); | 115 | void map__reloc_vmlinux(struct map *self); |
93 | 116 | ||
94 | struct map_groups { | ||
95 | struct rb_root maps[MAP__NR_TYPES]; | ||
96 | struct list_head removed_maps[MAP__NR_TYPES]; | ||
97 | }; | ||
98 | |||
99 | size_t __map_groups__fprintf_maps(struct map_groups *self, | 117 | size_t __map_groups__fprintf_maps(struct map_groups *self, |
100 | enum map_type type, int verbose, FILE *fp); | 118 | enum map_type type, int verbose, FILE *fp); |
101 | void maps__insert(struct rb_root *maps, struct map *map); | 119 | void maps__insert(struct rb_root *maps, struct map *map); |
@@ -106,9 +124,40 @@ int map_groups__clone(struct map_groups *self, | |||
106 | size_t map_groups__fprintf(struct map_groups *self, int verbose, FILE *fp); | 124 | size_t map_groups__fprintf(struct map_groups *self, int verbose, FILE *fp); |
107 | size_t map_groups__fprintf_maps(struct map_groups *self, int verbose, FILE *fp); | 125 | size_t map_groups__fprintf_maps(struct map_groups *self, int verbose, FILE *fp); |
108 | 126 | ||
127 | struct kernel_info *add_new_kernel_info(struct rb_root *kerninfo_root, | ||
128 | pid_t pid, const char *root_dir); | ||
129 | struct kernel_info *kerninfo__find(struct rb_root *kerninfo_root, pid_t pid); | ||
130 | struct kernel_info *kerninfo__findnew(struct rb_root *kerninfo_root, pid_t pid); | ||
131 | struct kernel_info *kerninfo__findhost(struct rb_root *kerninfo_root); | ||
132 | char *kern_mmap_name(struct kernel_info *kerninfo, char *buff); | ||
133 | |||
134 | /* | ||
135 | * Default guest kernel is defined by parameter --guestkallsyms | ||
136 | * and --guestmodules | ||
137 | */ | ||
138 | static inline int is_default_guest(struct kernel_info *kerninfo) | ||
139 | { | ||
140 | if (!kerninfo) | ||
141 | return 0; | ||
142 | return kerninfo->pid == DEFAULT_GUEST_KERNEL_ID; | ||
143 | } | ||
144 | |||
145 | static inline int is_host_kernel(struct kernel_info *kerninfo) | ||
146 | { | ||
147 | if (!kerninfo) | ||
148 | return 0; | ||
149 | return kerninfo->pid == HOST_KERNEL_ID; | ||
150 | } | ||
151 | |||
152 | typedef void (*process_kernel_info)(struct kernel_info *kerninfo, void *data); | ||
153 | void kerninfo__process_allkernels(struct rb_root *kerninfo_root, | ||
154 | process_kernel_info process, | ||
155 | void *data); | ||
156 | |||
109 | static inline void map_groups__insert(struct map_groups *self, struct map *map) | 157 | static inline void map_groups__insert(struct map_groups *self, struct map *map) |
110 | { | 158 | { |
111 | maps__insert(&self->maps[map->type], map); | 159 | maps__insert(&self->maps[map->type], map); |
160 | map->groups = self; | ||
112 | } | 161 | } |
113 | 162 | ||
114 | static inline struct map *map_groups__find(struct map_groups *self, | 163 | static inline struct map *map_groups__find(struct map_groups *self, |
@@ -148,13 +197,11 @@ int map_groups__fixup_overlappings(struct map_groups *self, struct map *map, | |||
148 | 197 | ||
149 | struct map *map_groups__find_by_name(struct map_groups *self, | 198 | struct map *map_groups__find_by_name(struct map_groups *self, |
150 | enum map_type type, const char *name); | 199 | enum map_type type, const char *name); |
151 | int __map_groups__create_kernel_maps(struct map_groups *self, | 200 | struct map *map_groups__new_module(struct map_groups *self, |
152 | struct map *vmlinux_maps[MAP__NR_TYPES], | 201 | u64 start, |
153 | struct dso *kernel); | 202 | const char *filename, |
154 | int map_groups__create_kernel_maps(struct map_groups *self, | 203 | struct kernel_info *kerninfo); |
155 | struct map *vmlinux_maps[MAP__NR_TYPES]); | 204 | |
156 | struct map *map_groups__new_module(struct map_groups *self, u64 start, | ||
157 | const char *filename); | ||
158 | void map_groups__flush(struct map_groups *self); | 205 | void map_groups__flush(struct map_groups *self); |
159 | 206 | ||
160 | #endif /* __PERF_MAP_H */ | 207 | #endif /* __PERF_MAP_H */ |
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 5bf8ab034466..3967f8f63d0d 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
@@ -78,6 +78,7 @@ static struct map *kmaps[MAP__NR_TYPES]; | |||
78 | /* Initialize symbol maps and path of vmlinux */ | 78 | /* Initialize symbol maps and path of vmlinux */ |
79 | static int init_vmlinux(void) | 79 | static int init_vmlinux(void) |
80 | { | 80 | { |
81 | struct dso *kernel; | ||
81 | int ret; | 82 | int ret; |
82 | 83 | ||
83 | symbol_conf.sort_by_name = true; | 84 | symbol_conf.sort_by_name = true; |
@@ -91,8 +92,12 @@ static int init_vmlinux(void) | |||
91 | goto out; | 92 | goto out; |
92 | } | 93 | } |
93 | 94 | ||
95 | kernel = dso__new_kernel(symbol_conf.vmlinux_name); | ||
96 | if (kernel == NULL) | ||
97 | die("Failed to create kernel dso."); | ||
98 | |||
94 | map_groups__init(&kmap_groups); | 99 | map_groups__init(&kmap_groups); |
95 | ret = map_groups__create_kernel_maps(&kmap_groups, kmaps); | 100 | ret = __map_groups__create_kernel_maps(&kmap_groups, kmaps, kernel); |
96 | if (ret < 0) | 101 | if (ret < 0) |
97 | pr_debug("Failed to create kernel maps.\n"); | 102 | pr_debug("Failed to create kernel maps.\n"); |
98 | 103 | ||
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 0fdf3ebef1e9..7d88ae5c270f 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
@@ -67,6 +67,17 @@ void perf_session__update_sample_type(struct perf_session *self) | |||
67 | self->sample_type = perf_header__sample_type(&self->header); | 67 | self->sample_type = perf_header__sample_type(&self->header); |
68 | } | 68 | } |
69 | 69 | ||
70 | int perf_session__create_kernel_maps(struct perf_session *self) | ||
71 | { | ||
72 | int ret; | ||
73 | struct rb_root *root = &self->kerninfo_root; | ||
74 | |||
75 | ret = map_groups__create_kernel_maps(root, HOST_KERNEL_ID); | ||
76 | if (ret >= 0) | ||
77 | ret = map_groups__create_guest_kernel_maps(root); | ||
78 | return ret; | ||
79 | } | ||
80 | |||
70 | struct perf_session *perf_session__new(const char *filename, int mode, bool force) | 81 | struct perf_session *perf_session__new(const char *filename, int mode, bool force) |
71 | { | 82 | { |
72 | size_t len = filename ? strlen(filename) + 1 : 0; | 83 | size_t len = filename ? strlen(filename) + 1 : 0; |
@@ -86,7 +97,7 @@ struct perf_session *perf_session__new(const char *filename, int mode, bool forc | |||
86 | self->cwd = NULL; | 97 | self->cwd = NULL; |
87 | self->cwdlen = 0; | 98 | self->cwdlen = 0; |
88 | self->unknown_events = 0; | 99 | self->unknown_events = 0; |
89 | map_groups__init(&self->kmaps); | 100 | self->kerninfo_root = RB_ROOT; |
90 | 101 | ||
91 | if (mode == O_RDONLY) { | 102 | if (mode == O_RDONLY) { |
92 | if (perf_session__open(self, force) < 0) | 103 | if (perf_session__open(self, force) < 0) |
@@ -157,8 +168,9 @@ struct map_symbol *perf_session__resolve_callchain(struct perf_session *self, | |||
157 | continue; | 168 | continue; |
158 | } | 169 | } |
159 | 170 | ||
171 | al.filtered = false; | ||
160 | thread__find_addr_location(thread, self, cpumode, | 172 | thread__find_addr_location(thread, self, cpumode, |
161 | MAP__FUNCTION, ip, &al, NULL); | 173 | MAP__FUNCTION, thread->pid, ip, &al, NULL); |
162 | if (al.sym != NULL) { | 174 | if (al.sym != NULL) { |
163 | if (sort__has_parent && !*parent && | 175 | if (sort__has_parent && !*parent && |
164 | symbol__match_parent_regex(al.sym)) | 176 | symbol__match_parent_regex(al.sym)) |
@@ -399,46 +411,6 @@ void perf_event_header__bswap(struct perf_event_header *self) | |||
399 | self->size = bswap_16(self->size); | 411 | self->size = bswap_16(self->size); |
400 | } | 412 | } |
401 | 413 | ||
402 | int perf_header__read_build_ids(struct perf_header *self, | ||
403 | int input, u64 offset, u64 size) | ||
404 | { | ||
405 | struct build_id_event bev; | ||
406 | char filename[PATH_MAX]; | ||
407 | u64 limit = offset + size; | ||
408 | int err = -1; | ||
409 | |||
410 | while (offset < limit) { | ||
411 | struct dso *dso; | ||
412 | ssize_t len; | ||
413 | struct list_head *head = &dsos__user; | ||
414 | |||
415 | if (read(input, &bev, sizeof(bev)) != sizeof(bev)) | ||
416 | goto out; | ||
417 | |||
418 | if (self->needs_swap) | ||
419 | perf_event_header__bswap(&bev.header); | ||
420 | |||
421 | len = bev.header.size - sizeof(bev); | ||
422 | if (read(input, filename, len) != len) | ||
423 | goto out; | ||
424 | |||
425 | if (bev.header.misc & PERF_RECORD_MISC_KERNEL) | ||
426 | head = &dsos__kernel; | ||
427 | |||
428 | dso = __dsos__findnew(head, filename); | ||
429 | if (dso != NULL) { | ||
430 | dso__set_build_id(dso, &bev.build_id); | ||
431 | if (head == &dsos__kernel && filename[0] == '[') | ||
432 | dso->kernel = 1; | ||
433 | } | ||
434 | |||
435 | offset += bev.header.size; | ||
436 | } | ||
437 | err = 0; | ||
438 | out: | ||
439 | return err; | ||
440 | } | ||
441 | |||
442 | static struct thread *perf_session__register_idle_thread(struct perf_session *self) | 414 | static struct thread *perf_session__register_idle_thread(struct perf_session *self) |
443 | { | 415 | { |
444 | struct thread *thread = perf_session__findnew(self, 0); | 416 | struct thread *thread = perf_session__findnew(self, 0); |
@@ -690,26 +662,33 @@ bool perf_session__has_traces(struct perf_session *self, const char *msg) | |||
690 | return true; | 662 | return true; |
691 | } | 663 | } |
692 | 664 | ||
693 | int perf_session__set_kallsyms_ref_reloc_sym(struct perf_session *self, | 665 | int perf_session__set_kallsyms_ref_reloc_sym(struct map **maps, |
694 | const char *symbol_name, | 666 | const char *symbol_name, |
695 | u64 addr) | 667 | u64 addr) |
696 | { | 668 | { |
697 | char *bracket; | 669 | char *bracket; |
698 | enum map_type i; | 670 | enum map_type i; |
671 | struct ref_reloc_sym *ref; | ||
672 | |||
673 | ref = zalloc(sizeof(struct ref_reloc_sym)); | ||
674 | if (ref == NULL) | ||
675 | return -ENOMEM; | ||
699 | 676 | ||
700 | self->ref_reloc_sym.name = strdup(symbol_name); | 677 | ref->name = strdup(symbol_name); |
701 | if (self->ref_reloc_sym.name == NULL) | 678 | if (ref->name == NULL) { |
679 | free(ref); | ||
702 | return -ENOMEM; | 680 | return -ENOMEM; |
681 | } | ||
703 | 682 | ||
704 | bracket = strchr(self->ref_reloc_sym.name, ']'); | 683 | bracket = strchr(ref->name, ']'); |
705 | if (bracket) | 684 | if (bracket) |
706 | *bracket = '\0'; | 685 | *bracket = '\0'; |
707 | 686 | ||
708 | self->ref_reloc_sym.addr = addr; | 687 | ref->addr = addr; |
709 | 688 | ||
710 | for (i = 0; i < MAP__NR_TYPES; ++i) { | 689 | for (i = 0; i < MAP__NR_TYPES; ++i) { |
711 | struct kmap *kmap = map__kmap(self->vmlinux_maps[i]); | 690 | struct kmap *kmap = map__kmap(maps[i]); |
712 | kmap->ref_reloc_sym = &self->ref_reloc_sym; | 691 | kmap->ref_reloc_sym = ref; |
713 | } | 692 | } |
714 | 693 | ||
715 | return 0; | 694 | return 0; |
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 0ac14d42dc28..5e47c87b9266 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h | |||
@@ -15,17 +15,15 @@ struct perf_session { | |||
15 | struct perf_header header; | 15 | struct perf_header header; |
16 | unsigned long size; | 16 | unsigned long size; |
17 | unsigned long mmap_window; | 17 | unsigned long mmap_window; |
18 | struct map_groups kmaps; | ||
19 | struct rb_root threads; | 18 | struct rb_root threads; |
20 | struct thread *last_match; | 19 | struct thread *last_match; |
21 | struct map *vmlinux_maps[MAP__NR_TYPES]; | 20 | struct rb_root kerninfo_root; |
22 | struct events_stats events_stats; | 21 | struct events_stats events_stats; |
23 | struct rb_root stats_by_id; | 22 | struct rb_root stats_by_id; |
24 | unsigned long event_total[PERF_RECORD_MAX]; | 23 | unsigned long event_total[PERF_RECORD_MAX]; |
25 | unsigned long unknown_events; | 24 | unsigned long unknown_events; |
26 | struct rb_root hists; | 25 | struct rb_root hists; |
27 | u64 sample_type; | 26 | u64 sample_type; |
28 | struct ref_reloc_sym ref_reloc_sym; | ||
29 | int fd; | 27 | int fd; |
30 | bool fd_pipe; | 28 | bool fd_pipe; |
31 | int cwdlen; | 29 | int cwdlen; |
@@ -69,33 +67,13 @@ struct map_symbol *perf_session__resolve_callchain(struct perf_session *self, | |||
69 | 67 | ||
70 | bool perf_session__has_traces(struct perf_session *self, const char *msg); | 68 | bool perf_session__has_traces(struct perf_session *self, const char *msg); |
71 | 69 | ||
72 | int perf_header__read_build_ids(struct perf_header *self, int input, | 70 | int perf_session__set_kallsyms_ref_reloc_sym(struct map **maps, |
73 | u64 offset, u64 file_size); | ||
74 | |||
75 | int perf_session__set_kallsyms_ref_reloc_sym(struct perf_session *self, | ||
76 | const char *symbol_name, | 71 | const char *symbol_name, |
77 | u64 addr); | 72 | u64 addr); |
78 | 73 | ||
79 | void mem_bswap_64(void *src, int byte_size); | 74 | void mem_bswap_64(void *src, int byte_size); |
80 | 75 | ||
81 | static inline int __perf_session__create_kernel_maps(struct perf_session *self, | 76 | int perf_session__create_kernel_maps(struct perf_session *self); |
82 | struct dso *kernel) | ||
83 | { | ||
84 | return __map_groups__create_kernel_maps(&self->kmaps, | ||
85 | self->vmlinux_maps, kernel); | ||
86 | } | ||
87 | |||
88 | static inline int perf_session__create_kernel_maps(struct perf_session *self) | ||
89 | { | ||
90 | return map_groups__create_kernel_maps(&self->kmaps, self->vmlinux_maps); | ||
91 | } | ||
92 | |||
93 | static inline struct map * | ||
94 | perf_session__new_module_map(struct perf_session *self, | ||
95 | u64 start, const char *filename) | ||
96 | { | ||
97 | return map_groups__new_module(&self->kmaps, start, filename); | ||
98 | } | ||
99 | 77 | ||
100 | int do_read(int fd, void *buf, size_t size); | 78 | int do_read(int fd, void *buf, size_t size); |
101 | void perf_session__update_sample_type(struct perf_session *self); | 79 | void perf_session__update_sample_type(struct perf_session *self); |
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 1d857aa2c01f..b7c54eeed9c9 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h | |||
@@ -44,6 +44,11 @@ extern enum sort_type sort__first_dimension; | |||
44 | struct hist_entry { | 44 | struct hist_entry { |
45 | struct rb_node rb_node; | 45 | struct rb_node rb_node; |
46 | u64 count; | 46 | u64 count; |
47 | u64 count_sys; | ||
48 | u64 count_us; | ||
49 | u64 count_guest_sys; | ||
50 | u64 count_guest_us; | ||
51 | |||
47 | /* | 52 | /* |
48 | * XXX WARNING! | 53 | * XXX WARNING! |
49 | * thread _has_ to come after ms, see | 54 | * thread _has_ to come after ms, see |
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index f3d4151e46a1..e782e7db16c5 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
@@ -28,6 +28,8 @@ static void dsos__add(struct list_head *head, struct dso *dso); | |||
28 | static struct map *map__new2(u64 start, struct dso *dso, enum map_type type); | 28 | static struct map *map__new2(u64 start, struct dso *dso, enum map_type type); |
29 | static int dso__load_kernel_sym(struct dso *self, struct map *map, | 29 | static int dso__load_kernel_sym(struct dso *self, struct map *map, |
30 | symbol_filter_t filter); | 30 | symbol_filter_t filter); |
31 | static int dso__load_guest_kernel_sym(struct dso *self, struct map *map, | ||
32 | symbol_filter_t filter); | ||
31 | static int vmlinux_path__nr_entries; | 33 | static int vmlinux_path__nr_entries; |
32 | static char **vmlinux_path; | 34 | static char **vmlinux_path; |
33 | 35 | ||
@@ -186,6 +188,7 @@ struct dso *dso__new(const char *name) | |||
186 | self->loaded = 0; | 188 | self->loaded = 0; |
187 | self->sorted_by_name = 0; | 189 | self->sorted_by_name = 0; |
188 | self->has_build_id = 0; | 190 | self->has_build_id = 0; |
191 | self->kernel = DSO_TYPE_USER; | ||
189 | } | 192 | } |
190 | 193 | ||
191 | return self; | 194 | return self; |
@@ -402,12 +405,9 @@ int kallsyms__parse(const char *filename, void *arg, | |||
402 | char *symbol_name; | 405 | char *symbol_name; |
403 | 406 | ||
404 | line_len = getline(&line, &n, file); | 407 | line_len = getline(&line, &n, file); |
405 | if (line_len < 0) | 408 | if (line_len < 0 || !line) |
406 | break; | 409 | break; |
407 | 410 | ||
408 | if (!line) | ||
409 | goto out_failure; | ||
410 | |||
411 | line[--line_len] = '\0'; /* \n */ | 411 | line[--line_len] = '\0'; /* \n */ |
412 | 412 | ||
413 | len = hex2u64(line, &start); | 413 | len = hex2u64(line, &start); |
@@ -459,6 +459,7 @@ static int map__process_kallsym_symbol(void *arg, const char *name, | |||
459 | * map__split_kallsyms, when we have split the maps per module | 459 | * map__split_kallsyms, when we have split the maps per module |
460 | */ | 460 | */ |
461 | symbols__insert(root, sym); | 461 | symbols__insert(root, sym); |
462 | |||
462 | return 0; | 463 | return 0; |
463 | } | 464 | } |
464 | 465 | ||
@@ -483,6 +484,7 @@ static int dso__split_kallsyms(struct dso *self, struct map *map, | |||
483 | symbol_filter_t filter) | 484 | symbol_filter_t filter) |
484 | { | 485 | { |
485 | struct map_groups *kmaps = map__kmap(map)->kmaps; | 486 | struct map_groups *kmaps = map__kmap(map)->kmaps; |
487 | struct kernel_info *kerninfo = kmaps->this_kerninfo; | ||
486 | struct map *curr_map = map; | 488 | struct map *curr_map = map; |
487 | struct symbol *pos; | 489 | struct symbol *pos; |
488 | int count = 0; | 490 | int count = 0; |
@@ -504,15 +506,33 @@ static int dso__split_kallsyms(struct dso *self, struct map *map, | |||
504 | *module++ = '\0'; | 506 | *module++ = '\0'; |
505 | 507 | ||
506 | if (strcmp(curr_map->dso->short_name, module)) { | 508 | if (strcmp(curr_map->dso->short_name, module)) { |
507 | curr_map = map_groups__find_by_name(kmaps, map->type, module); | 509 | if (curr_map != map && |
510 | self->kernel == DSO_TYPE_GUEST_KERNEL && | ||
511 | is_default_guest(kerninfo)) { | ||
512 | /* | ||
513 | * We assume all symbols of a module are | ||
514 | * continuous in * kallsyms, so curr_map | ||
515 | * points to a module and all its | ||
516 | * symbols are in its kmap. Mark it as | ||
517 | * loaded. | ||
518 | */ | ||
519 | dso__set_loaded(curr_map->dso, | ||
520 | curr_map->type); | ||
521 | } | ||
522 | |||
523 | curr_map = map_groups__find_by_name(kmaps, | ||
524 | map->type, module); | ||
508 | if (curr_map == NULL) { | 525 | if (curr_map == NULL) { |
509 | pr_debug("/proc/{kallsyms,modules} " | 526 | pr_err("%s/proc/{kallsyms,modules} " |
510 | "inconsistency while looking " | 527 | "inconsistency while looking " |
511 | "for \"%s\" module!\n", module); | 528 | "for \"%s\" module!\n", |
512 | return -1; | 529 | kerninfo->root_dir, module); |
530 | curr_map = map; | ||
531 | goto discard_symbol; | ||
513 | } | 532 | } |
514 | 533 | ||
515 | if (curr_map->dso->loaded) | 534 | if (curr_map->dso->loaded && |
535 | !is_default_guest(kmaps->this_kerninfo)) | ||
516 | goto discard_symbol; | 536 | goto discard_symbol; |
517 | } | 537 | } |
518 | /* | 538 | /* |
@@ -525,13 +545,21 @@ static int dso__split_kallsyms(struct dso *self, struct map *map, | |||
525 | char dso_name[PATH_MAX]; | 545 | char dso_name[PATH_MAX]; |
526 | struct dso *dso; | 546 | struct dso *dso; |
527 | 547 | ||
528 | snprintf(dso_name, sizeof(dso_name), "[kernel].%d", | 548 | if (self->kernel == DSO_TYPE_GUEST_KERNEL) |
529 | kernel_range++); | 549 | snprintf(dso_name, sizeof(dso_name), |
550 | "[guest.kernel].%d", | ||
551 | kernel_range++); | ||
552 | else | ||
553 | snprintf(dso_name, sizeof(dso_name), | ||
554 | "[kernel].%d", | ||
555 | kernel_range++); | ||
530 | 556 | ||
531 | dso = dso__new(dso_name); | 557 | dso = dso__new(dso_name); |
532 | if (dso == NULL) | 558 | if (dso == NULL) |
533 | return -1; | 559 | return -1; |
534 | 560 | ||
561 | dso->kernel = self->kernel; | ||
562 | |||
535 | curr_map = map__new2(pos->start, dso, map->type); | 563 | curr_map = map__new2(pos->start, dso, map->type); |
536 | if (curr_map == NULL) { | 564 | if (curr_map == NULL) { |
537 | dso__delete(dso); | 565 | dso__delete(dso); |
@@ -555,6 +583,12 @@ discard_symbol: rb_erase(&pos->rb_node, root); | |||
555 | } | 583 | } |
556 | } | 584 | } |
557 | 585 | ||
586 | if (curr_map != map && | ||
587 | self->kernel == DSO_TYPE_GUEST_KERNEL && | ||
588 | is_default_guest(kmaps->this_kerninfo)) { | ||
589 | dso__set_loaded(curr_map->dso, curr_map->type); | ||
590 | } | ||
591 | |||
558 | return count; | 592 | return count; |
559 | } | 593 | } |
560 | 594 | ||
@@ -565,7 +599,10 @@ int dso__load_kallsyms(struct dso *self, const char *filename, | |||
565 | return -1; | 599 | return -1; |
566 | 600 | ||
567 | symbols__fixup_end(&self->symbols[map->type]); | 601 | symbols__fixup_end(&self->symbols[map->type]); |
568 | self->origin = DSO__ORIG_KERNEL; | 602 | if (self->kernel == DSO_TYPE_GUEST_KERNEL) |
603 | self->origin = DSO__ORIG_GUEST_KERNEL; | ||
604 | else | ||
605 | self->origin = DSO__ORIG_KERNEL; | ||
569 | 606 | ||
570 | return dso__split_kallsyms(self, map, filter); | 607 | return dso__split_kallsyms(self, map, filter); |
571 | } | 608 | } |
@@ -952,7 +989,7 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name, | |||
952 | nr_syms = shdr.sh_size / shdr.sh_entsize; | 989 | nr_syms = shdr.sh_size / shdr.sh_entsize; |
953 | 990 | ||
954 | memset(&sym, 0, sizeof(sym)); | 991 | memset(&sym, 0, sizeof(sym)); |
955 | if (!self->kernel) { | 992 | if (self->kernel == DSO_TYPE_USER) { |
956 | self->adjust_symbols = (ehdr.e_type == ET_EXEC || | 993 | self->adjust_symbols = (ehdr.e_type == ET_EXEC || |
957 | elf_section_by_name(elf, &ehdr, &shdr, | 994 | elf_section_by_name(elf, &ehdr, &shdr, |
958 | ".gnu.prelink_undo", | 995 | ".gnu.prelink_undo", |
@@ -984,7 +1021,7 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name, | |||
984 | 1021 | ||
985 | section_name = elf_sec__name(&shdr, secstrs); | 1022 | section_name = elf_sec__name(&shdr, secstrs); |
986 | 1023 | ||
987 | if (self->kernel || kmodule) { | 1024 | if (self->kernel != DSO_TYPE_USER || kmodule) { |
988 | char dso_name[PATH_MAX]; | 1025 | char dso_name[PATH_MAX]; |
989 | 1026 | ||
990 | if (strcmp(section_name, | 1027 | if (strcmp(section_name, |
@@ -1011,6 +1048,7 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name, | |||
1011 | curr_dso = dso__new(dso_name); | 1048 | curr_dso = dso__new(dso_name); |
1012 | if (curr_dso == NULL) | 1049 | if (curr_dso == NULL) |
1013 | goto out_elf_end; | 1050 | goto out_elf_end; |
1051 | curr_dso->kernel = self->kernel; | ||
1014 | curr_map = map__new2(start, curr_dso, | 1052 | curr_map = map__new2(start, curr_dso, |
1015 | map->type); | 1053 | map->type); |
1016 | if (curr_map == NULL) { | 1054 | if (curr_map == NULL) { |
@@ -1021,7 +1059,7 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name, | |||
1021 | curr_map->unmap_ip = identity__map_ip; | 1059 | curr_map->unmap_ip = identity__map_ip; |
1022 | curr_dso->origin = self->origin; | 1060 | curr_dso->origin = self->origin; |
1023 | map_groups__insert(kmap->kmaps, curr_map); | 1061 | map_groups__insert(kmap->kmaps, curr_map); |
1024 | dsos__add(&dsos__kernel, curr_dso); | 1062 | dsos__add(&self->node, curr_dso); |
1025 | dso__set_loaded(curr_dso, map->type); | 1063 | dso__set_loaded(curr_dso, map->type); |
1026 | } else | 1064 | } else |
1027 | curr_dso = curr_map->dso; | 1065 | curr_dso = curr_map->dso; |
@@ -1083,7 +1121,7 @@ static bool dso__build_id_equal(const struct dso *self, u8 *build_id) | |||
1083 | return memcmp(self->build_id, build_id, sizeof(self->build_id)) == 0; | 1121 | return memcmp(self->build_id, build_id, sizeof(self->build_id)) == 0; |
1084 | } | 1122 | } |
1085 | 1123 | ||
1086 | static bool __dsos__read_build_ids(struct list_head *head, bool with_hits) | 1124 | bool __dsos__read_build_ids(struct list_head *head, bool with_hits) |
1087 | { | 1125 | { |
1088 | bool have_build_id = false; | 1126 | bool have_build_id = false; |
1089 | struct dso *pos; | 1127 | struct dso *pos; |
@@ -1101,13 +1139,6 @@ static bool __dsos__read_build_ids(struct list_head *head, bool with_hits) | |||
1101 | return have_build_id; | 1139 | return have_build_id; |
1102 | } | 1140 | } |
1103 | 1141 | ||
1104 | bool dsos__read_build_ids(bool with_hits) | ||
1105 | { | ||
1106 | bool kbuildids = __dsos__read_build_ids(&dsos__kernel, with_hits), | ||
1107 | ubuildids = __dsos__read_build_ids(&dsos__user, with_hits); | ||
1108 | return kbuildids || ubuildids; | ||
1109 | } | ||
1110 | |||
1111 | /* | 1142 | /* |
1112 | * Align offset to 4 bytes as needed for note name and descriptor data. | 1143 | * Align offset to 4 bytes as needed for note name and descriptor data. |
1113 | */ | 1144 | */ |
@@ -1242,6 +1273,8 @@ char dso__symtab_origin(const struct dso *self) | |||
1242 | [DSO__ORIG_BUILDID] = 'b', | 1273 | [DSO__ORIG_BUILDID] = 'b', |
1243 | [DSO__ORIG_DSO] = 'd', | 1274 | [DSO__ORIG_DSO] = 'd', |
1244 | [DSO__ORIG_KMODULE] = 'K', | 1275 | [DSO__ORIG_KMODULE] = 'K', |
1276 | [DSO__ORIG_GUEST_KERNEL] = 'g', | ||
1277 | [DSO__ORIG_GUEST_KMODULE] = 'G', | ||
1245 | }; | 1278 | }; |
1246 | 1279 | ||
1247 | if (self == NULL || self->origin == DSO__ORIG_NOT_FOUND) | 1280 | if (self == NULL || self->origin == DSO__ORIG_NOT_FOUND) |
@@ -1257,11 +1290,20 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) | |||
1257 | char build_id_hex[BUILD_ID_SIZE * 2 + 1]; | 1290 | char build_id_hex[BUILD_ID_SIZE * 2 + 1]; |
1258 | int ret = -1; | 1291 | int ret = -1; |
1259 | int fd; | 1292 | int fd; |
1293 | struct kernel_info *kerninfo; | ||
1294 | const char *root_dir; | ||
1260 | 1295 | ||
1261 | dso__set_loaded(self, map->type); | 1296 | dso__set_loaded(self, map->type); |
1262 | 1297 | ||
1263 | if (self->kernel) | 1298 | if (self->kernel == DSO_TYPE_KERNEL) |
1264 | return dso__load_kernel_sym(self, map, filter); | 1299 | return dso__load_kernel_sym(self, map, filter); |
1300 | else if (self->kernel == DSO_TYPE_GUEST_KERNEL) | ||
1301 | return dso__load_guest_kernel_sym(self, map, filter); | ||
1302 | |||
1303 | if (map->groups && map->groups->this_kerninfo) | ||
1304 | kerninfo = map->groups->this_kerninfo; | ||
1305 | else | ||
1306 | kerninfo = NULL; | ||
1265 | 1307 | ||
1266 | name = malloc(size); | 1308 | name = malloc(size); |
1267 | if (!name) | 1309 | if (!name) |
@@ -1315,6 +1357,13 @@ more: | |||
1315 | case DSO__ORIG_DSO: | 1357 | case DSO__ORIG_DSO: |
1316 | snprintf(name, size, "%s", self->long_name); | 1358 | snprintf(name, size, "%s", self->long_name); |
1317 | break; | 1359 | break; |
1360 | case DSO__ORIG_GUEST_KMODULE: | ||
1361 | if (map->groups && map->groups->this_kerninfo) | ||
1362 | root_dir = map->groups->this_kerninfo->root_dir; | ||
1363 | else | ||
1364 | root_dir = ""; | ||
1365 | snprintf(name, size, "%s%s", root_dir, self->long_name); | ||
1366 | break; | ||
1318 | 1367 | ||
1319 | default: | 1368 | default: |
1320 | goto out; | 1369 | goto out; |
@@ -1368,7 +1417,8 @@ struct map *map_groups__find_by_name(struct map_groups *self, | |||
1368 | return NULL; | 1417 | return NULL; |
1369 | } | 1418 | } |
1370 | 1419 | ||
1371 | static int dso__kernel_module_get_build_id(struct dso *self) | 1420 | static int dso__kernel_module_get_build_id(struct dso *self, |
1421 | const char *root_dir) | ||
1372 | { | 1422 | { |
1373 | char filename[PATH_MAX]; | 1423 | char filename[PATH_MAX]; |
1374 | /* | 1424 | /* |
@@ -1378,8 +1428,8 @@ static int dso__kernel_module_get_build_id(struct dso *self) | |||
1378 | const char *name = self->short_name + 1; | 1428 | const char *name = self->short_name + 1; |
1379 | 1429 | ||
1380 | snprintf(filename, sizeof(filename), | 1430 | snprintf(filename, sizeof(filename), |
1381 | "/sys/module/%.*s/notes/.note.gnu.build-id", | 1431 | "%s/sys/module/%.*s/notes/.note.gnu.build-id", |
1382 | (int)strlen(name - 1), name); | 1432 | root_dir, (int)strlen(name) - 1, name); |
1383 | 1433 | ||
1384 | if (sysfs__read_build_id(filename, self->build_id, | 1434 | if (sysfs__read_build_id(filename, self->build_id, |
1385 | sizeof(self->build_id)) == 0) | 1435 | sizeof(self->build_id)) == 0) |
@@ -1388,7 +1438,8 @@ static int dso__kernel_module_get_build_id(struct dso *self) | |||
1388 | return 0; | 1438 | return 0; |
1389 | } | 1439 | } |
1390 | 1440 | ||
1391 | static int map_groups__set_modules_path_dir(struct map_groups *self, char *dir_name) | 1441 | static int map_groups__set_modules_path_dir(struct map_groups *self, |
1442 | const char *dir_name) | ||
1392 | { | 1443 | { |
1393 | struct dirent *dent; | 1444 | struct dirent *dent; |
1394 | DIR *dir = opendir(dir_name); | 1445 | DIR *dir = opendir(dir_name); |
@@ -1400,8 +1451,14 @@ static int map_groups__set_modules_path_dir(struct map_groups *self, char *dir_n | |||
1400 | 1451 | ||
1401 | while ((dent = readdir(dir)) != NULL) { | 1452 | while ((dent = readdir(dir)) != NULL) { |
1402 | char path[PATH_MAX]; | 1453 | char path[PATH_MAX]; |
1454 | struct stat st; | ||
1455 | |||
1456 | /*sshfs might return bad dent->d_type, so we have to stat*/ | ||
1457 | sprintf(path, "%s/%s", dir_name, dent->d_name); | ||
1458 | if (stat(path, &st)) | ||
1459 | continue; | ||
1403 | 1460 | ||
1404 | if (dent->d_type == DT_DIR) { | 1461 | if (S_ISDIR(st.st_mode)) { |
1405 | if (!strcmp(dent->d_name, ".") || | 1462 | if (!strcmp(dent->d_name, ".") || |
1406 | !strcmp(dent->d_name, "..")) | 1463 | !strcmp(dent->d_name, "..")) |
1407 | continue; | 1464 | continue; |
@@ -1433,7 +1490,7 @@ static int map_groups__set_modules_path_dir(struct map_groups *self, char *dir_n | |||
1433 | if (long_name == NULL) | 1490 | if (long_name == NULL) |
1434 | goto failure; | 1491 | goto failure; |
1435 | dso__set_long_name(map->dso, long_name); | 1492 | dso__set_long_name(map->dso, long_name); |
1436 | dso__kernel_module_get_build_id(map->dso); | 1493 | dso__kernel_module_get_build_id(map->dso, ""); |
1437 | } | 1494 | } |
1438 | } | 1495 | } |
1439 | 1496 | ||
@@ -1443,16 +1500,46 @@ failure: | |||
1443 | return -1; | 1500 | return -1; |
1444 | } | 1501 | } |
1445 | 1502 | ||
1446 | static int map_groups__set_modules_path(struct map_groups *self) | 1503 | static char *get_kernel_version(const char *root_dir) |
1447 | { | 1504 | { |
1448 | struct utsname uts; | 1505 | char version[PATH_MAX]; |
1506 | FILE *file; | ||
1507 | char *name, *tmp; | ||
1508 | const char *prefix = "Linux version "; | ||
1509 | |||
1510 | sprintf(version, "%s/proc/version", root_dir); | ||
1511 | file = fopen(version, "r"); | ||
1512 | if (!file) | ||
1513 | return NULL; | ||
1514 | |||
1515 | version[0] = '\0'; | ||
1516 | tmp = fgets(version, sizeof(version), file); | ||
1517 | fclose(file); | ||
1518 | |||
1519 | name = strstr(version, prefix); | ||
1520 | if (!name) | ||
1521 | return NULL; | ||
1522 | name += strlen(prefix); | ||
1523 | tmp = strchr(name, ' '); | ||
1524 | if (tmp) | ||
1525 | *tmp = '\0'; | ||
1526 | |||
1527 | return strdup(name); | ||
1528 | } | ||
1529 | |||
1530 | static int map_groups__set_modules_path(struct map_groups *self, | ||
1531 | const char *root_dir) | ||
1532 | { | ||
1533 | char *version; | ||
1449 | char modules_path[PATH_MAX]; | 1534 | char modules_path[PATH_MAX]; |
1450 | 1535 | ||
1451 | if (uname(&uts) < 0) | 1536 | version = get_kernel_version(root_dir); |
1537 | if (!version) | ||
1452 | return -1; | 1538 | return -1; |
1453 | 1539 | ||
1454 | snprintf(modules_path, sizeof(modules_path), "/lib/modules/%s/kernel", | 1540 | snprintf(modules_path, sizeof(modules_path), "%s/lib/modules/%s/kernel", |
1455 | uts.release); | 1541 | root_dir, version); |
1542 | free(version); | ||
1456 | 1543 | ||
1457 | return map_groups__set_modules_path_dir(self, modules_path); | 1544 | return map_groups__set_modules_path_dir(self, modules_path); |
1458 | } | 1545 | } |
@@ -1477,11 +1564,13 @@ static struct map *map__new2(u64 start, struct dso *dso, enum map_type type) | |||
1477 | } | 1564 | } |
1478 | 1565 | ||
1479 | struct map *map_groups__new_module(struct map_groups *self, u64 start, | 1566 | struct map *map_groups__new_module(struct map_groups *self, u64 start, |
1480 | const char *filename) | 1567 | const char *filename, |
1568 | struct kernel_info *kerninfo) | ||
1481 | { | 1569 | { |
1482 | struct map *map; | 1570 | struct map *map; |
1483 | struct dso *dso = __dsos__findnew(&dsos__kernel, filename); | 1571 | struct dso *dso; |
1484 | 1572 | ||
1573 | dso = __dsos__findnew(&kerninfo->dsos__kernel, filename); | ||
1485 | if (dso == NULL) | 1574 | if (dso == NULL) |
1486 | return NULL; | 1575 | return NULL; |
1487 | 1576 | ||
@@ -1489,21 +1578,37 @@ struct map *map_groups__new_module(struct map_groups *self, u64 start, | |||
1489 | if (map == NULL) | 1578 | if (map == NULL) |
1490 | return NULL; | 1579 | return NULL; |
1491 | 1580 | ||
1492 | dso->origin = DSO__ORIG_KMODULE; | 1581 | if (is_host_kernel(kerninfo)) |
1582 | dso->origin = DSO__ORIG_KMODULE; | ||
1583 | else | ||
1584 | dso->origin = DSO__ORIG_GUEST_KMODULE; | ||
1493 | map_groups__insert(self, map); | 1585 | map_groups__insert(self, map); |
1494 | return map; | 1586 | return map; |
1495 | } | 1587 | } |
1496 | 1588 | ||
1497 | static int map_groups__create_modules(struct map_groups *self) | 1589 | static int map_groups__create_modules(struct kernel_info *kerninfo) |
1498 | { | 1590 | { |
1499 | char *line = NULL; | 1591 | char *line = NULL; |
1500 | size_t n; | 1592 | size_t n; |
1501 | FILE *file = fopen("/proc/modules", "r"); | 1593 | FILE *file; |
1502 | struct map *map; | 1594 | struct map *map; |
1595 | const char *root_dir; | ||
1596 | const char *modules; | ||
1597 | char path[PATH_MAX]; | ||
1598 | |||
1599 | if (is_default_guest(kerninfo)) | ||
1600 | modules = symbol_conf.default_guest_modules; | ||
1601 | else { | ||
1602 | sprintf(path, "%s/proc/modules", kerninfo->root_dir); | ||
1603 | modules = path; | ||
1604 | } | ||
1503 | 1605 | ||
1606 | file = fopen(modules, "r"); | ||
1504 | if (file == NULL) | 1607 | if (file == NULL) |
1505 | return -1; | 1608 | return -1; |
1506 | 1609 | ||
1610 | root_dir = kerninfo->root_dir; | ||
1611 | |||
1507 | while (!feof(file)) { | 1612 | while (!feof(file)) { |
1508 | char name[PATH_MAX]; | 1613 | char name[PATH_MAX]; |
1509 | u64 start; | 1614 | u64 start; |
@@ -1532,16 +1637,17 @@ static int map_groups__create_modules(struct map_groups *self) | |||
1532 | *sep = '\0'; | 1637 | *sep = '\0'; |
1533 | 1638 | ||
1534 | snprintf(name, sizeof(name), "[%s]", line); | 1639 | snprintf(name, sizeof(name), "[%s]", line); |
1535 | map = map_groups__new_module(self, start, name); | 1640 | map = map_groups__new_module(&kerninfo->kmaps, |
1641 | start, name, kerninfo); | ||
1536 | if (map == NULL) | 1642 | if (map == NULL) |
1537 | goto out_delete_line; | 1643 | goto out_delete_line; |
1538 | dso__kernel_module_get_build_id(map->dso); | 1644 | dso__kernel_module_get_build_id(map->dso, root_dir); |
1539 | } | 1645 | } |
1540 | 1646 | ||
1541 | free(line); | 1647 | free(line); |
1542 | fclose(file); | 1648 | fclose(file); |
1543 | 1649 | ||
1544 | return map_groups__set_modules_path(self); | 1650 | return map_groups__set_modules_path(&kerninfo->kmaps, root_dir); |
1545 | 1651 | ||
1546 | out_delete_line: | 1652 | out_delete_line: |
1547 | free(line); | 1653 | free(line); |
@@ -1708,8 +1814,57 @@ out_fixup: | |||
1708 | return err; | 1814 | return err; |
1709 | } | 1815 | } |
1710 | 1816 | ||
1711 | LIST_HEAD(dsos__user); | 1817 | static int dso__load_guest_kernel_sym(struct dso *self, struct map *map, |
1712 | LIST_HEAD(dsos__kernel); | 1818 | symbol_filter_t filter) |
1819 | { | ||
1820 | int err; | ||
1821 | const char *kallsyms_filename = NULL; | ||
1822 | struct kernel_info *kerninfo; | ||
1823 | char path[PATH_MAX]; | ||
1824 | |||
1825 | if (!map->groups) { | ||
1826 | pr_debug("Guest kernel map hasn't the point to groups\n"); | ||
1827 | return -1; | ||
1828 | } | ||
1829 | kerninfo = map->groups->this_kerninfo; | ||
1830 | |||
1831 | if (is_default_guest(kerninfo)) { | ||
1832 | /* | ||
1833 | * if the user specified a vmlinux filename, use it and only | ||
1834 | * it, reporting errors to the user if it cannot be used. | ||
1835 | * Or use file guest_kallsyms inputted by user on commandline | ||
1836 | */ | ||
1837 | if (symbol_conf.default_guest_vmlinux_name != NULL) { | ||
1838 | err = dso__load_vmlinux(self, map, | ||
1839 | symbol_conf.default_guest_vmlinux_name, filter); | ||
1840 | goto out_try_fixup; | ||
1841 | } | ||
1842 | |||
1843 | kallsyms_filename = symbol_conf.default_guest_kallsyms; | ||
1844 | if (!kallsyms_filename) | ||
1845 | return -1; | ||
1846 | } else { | ||
1847 | sprintf(path, "%s/proc/kallsyms", kerninfo->root_dir); | ||
1848 | kallsyms_filename = path; | ||
1849 | } | ||
1850 | |||
1851 | err = dso__load_kallsyms(self, kallsyms_filename, map, filter); | ||
1852 | if (err > 0) | ||
1853 | pr_debug("Using %s for symbols\n", kallsyms_filename); | ||
1854 | |||
1855 | out_try_fixup: | ||
1856 | if (err > 0) { | ||
1857 | if (kallsyms_filename != NULL) { | ||
1858 | kern_mmap_name(kerninfo, path); | ||
1859 | dso__set_long_name(self, | ||
1860 | strdup(path)); | ||
1861 | } | ||
1862 | map__fixup_start(map); | ||
1863 | map__fixup_end(map); | ||
1864 | } | ||
1865 | |||
1866 | return err; | ||
1867 | } | ||
1713 | 1868 | ||
1714 | static void dsos__add(struct list_head *head, struct dso *dso) | 1869 | static void dsos__add(struct list_head *head, struct dso *dso) |
1715 | { | 1870 | { |
@@ -1752,10 +1907,16 @@ static void __dsos__fprintf(struct list_head *head, FILE *fp) | |||
1752 | } | 1907 | } |
1753 | } | 1908 | } |
1754 | 1909 | ||
1755 | void dsos__fprintf(FILE *fp) | 1910 | void dsos__fprintf(struct rb_root *kerninfo_root, FILE *fp) |
1756 | { | 1911 | { |
1757 | __dsos__fprintf(&dsos__kernel, fp); | 1912 | struct rb_node *nd; |
1758 | __dsos__fprintf(&dsos__user, fp); | 1913 | |
1914 | for (nd = rb_first(kerninfo_root); nd; nd = rb_next(nd)) { | ||
1915 | struct kernel_info *pos = rb_entry(nd, struct kernel_info, | ||
1916 | rb_node); | ||
1917 | __dsos__fprintf(&pos->dsos__kernel, fp); | ||
1918 | __dsos__fprintf(&pos->dsos__user, fp); | ||
1919 | } | ||
1759 | } | 1920 | } |
1760 | 1921 | ||
1761 | static size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp, | 1922 | static size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp, |
@@ -1773,10 +1934,21 @@ static size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp, | |||
1773 | return ret; | 1934 | return ret; |
1774 | } | 1935 | } |
1775 | 1936 | ||
1776 | size_t dsos__fprintf_buildid(FILE *fp, bool with_hits) | 1937 | size_t dsos__fprintf_buildid(struct rb_root *kerninfo_root, |
1938 | FILE *fp, bool with_hits) | ||
1777 | { | 1939 | { |
1778 | return (__dsos__fprintf_buildid(&dsos__kernel, fp, with_hits) + | 1940 | struct rb_node *nd; |
1779 | __dsos__fprintf_buildid(&dsos__user, fp, with_hits)); | 1941 | size_t ret = 0; |
1942 | |||
1943 | for (nd = rb_first(kerninfo_root); nd; nd = rb_next(nd)) { | ||
1944 | struct kernel_info *pos = rb_entry(nd, struct kernel_info, | ||
1945 | rb_node); | ||
1946 | ret += __dsos__fprintf_buildid(&pos->dsos__kernel, | ||
1947 | fp, with_hits); | ||
1948 | ret += __dsos__fprintf_buildid(&pos->dsos__user, | ||
1949 | fp, with_hits); | ||
1950 | } | ||
1951 | return ret; | ||
1780 | } | 1952 | } |
1781 | 1953 | ||
1782 | struct dso *dso__new_kernel(const char *name) | 1954 | struct dso *dso__new_kernel(const char *name) |
@@ -1785,28 +1957,59 @@ struct dso *dso__new_kernel(const char *name) | |||
1785 | 1957 | ||
1786 | if (self != NULL) { | 1958 | if (self != NULL) { |
1787 | dso__set_short_name(self, "[kernel]"); | 1959 | dso__set_short_name(self, "[kernel]"); |
1788 | self->kernel = 1; | 1960 | self->kernel = DSO_TYPE_KERNEL; |
1961 | } | ||
1962 | |||
1963 | return self; | ||
1964 | } | ||
1965 | |||
1966 | static struct dso *dso__new_guest_kernel(struct kernel_info *kerninfo, | ||
1967 | const char *name) | ||
1968 | { | ||
1969 | char buff[PATH_MAX]; | ||
1970 | struct dso *self; | ||
1971 | |||
1972 | kern_mmap_name(kerninfo, buff); | ||
1973 | self = dso__new(name ?: buff); | ||
1974 | if (self != NULL) { | ||
1975 | dso__set_short_name(self, "[guest.kernel]"); | ||
1976 | self->kernel = DSO_TYPE_GUEST_KERNEL; | ||
1789 | } | 1977 | } |
1790 | 1978 | ||
1791 | return self; | 1979 | return self; |
1792 | } | 1980 | } |
1793 | 1981 | ||
1794 | void dso__read_running_kernel_build_id(struct dso *self) | 1982 | void dso__read_running_kernel_build_id(struct dso *self, |
1983 | struct kernel_info *kerninfo) | ||
1795 | { | 1984 | { |
1796 | if (sysfs__read_build_id("/sys/kernel/notes", self->build_id, | 1985 | char path[PATH_MAX]; |
1986 | |||
1987 | if (is_default_guest(kerninfo)) | ||
1988 | return; | ||
1989 | sprintf(path, "%s/sys/kernel/notes", kerninfo->root_dir); | ||
1990 | if (sysfs__read_build_id(path, self->build_id, | ||
1797 | sizeof(self->build_id)) == 0) | 1991 | sizeof(self->build_id)) == 0) |
1798 | self->has_build_id = true; | 1992 | self->has_build_id = true; |
1799 | } | 1993 | } |
1800 | 1994 | ||
1801 | static struct dso *dsos__create_kernel(const char *vmlinux) | 1995 | static struct dso *dsos__create_kernel(struct kernel_info *kerninfo) |
1802 | { | 1996 | { |
1803 | struct dso *kernel = dso__new_kernel(vmlinux); | 1997 | const char *vmlinux_name = NULL; |
1998 | struct dso *kernel; | ||
1804 | 1999 | ||
1805 | if (kernel != NULL) { | 2000 | if (is_host_kernel(kerninfo)) { |
1806 | dso__read_running_kernel_build_id(kernel); | 2001 | vmlinux_name = symbol_conf.vmlinux_name; |
1807 | dsos__add(&dsos__kernel, kernel); | 2002 | kernel = dso__new_kernel(vmlinux_name); |
2003 | } else { | ||
2004 | if (is_default_guest(kerninfo)) | ||
2005 | vmlinux_name = symbol_conf.default_guest_vmlinux_name; | ||
2006 | kernel = dso__new_guest_kernel(kerninfo, vmlinux_name); | ||
1808 | } | 2007 | } |
1809 | 2008 | ||
2009 | if (kernel != NULL) { | ||
2010 | dso__read_running_kernel_build_id(kernel, kerninfo); | ||
2011 | dsos__add(&kerninfo->dsos__kernel, kernel); | ||
2012 | } | ||
1810 | return kernel; | 2013 | return kernel; |
1811 | } | 2014 | } |
1812 | 2015 | ||
@@ -1950,23 +2153,29 @@ out_free_comm_list: | |||
1950 | return -1; | 2153 | return -1; |
1951 | } | 2154 | } |
1952 | 2155 | ||
1953 | int map_groups__create_kernel_maps(struct map_groups *self, | 2156 | int map_groups__create_kernel_maps(struct rb_root *kerninfo_root, pid_t pid) |
1954 | struct map *vmlinux_maps[MAP__NR_TYPES]) | ||
1955 | { | 2157 | { |
1956 | struct dso *kernel = dsos__create_kernel(symbol_conf.vmlinux_name); | 2158 | struct kernel_info *kerninfo; |
2159 | struct dso *kernel; | ||
1957 | 2160 | ||
2161 | kerninfo = kerninfo__findnew(kerninfo_root, pid); | ||
2162 | if (kerninfo == NULL) | ||
2163 | return -1; | ||
2164 | kernel = dsos__create_kernel(kerninfo); | ||
1958 | if (kernel == NULL) | 2165 | if (kernel == NULL) |
1959 | return -1; | 2166 | return -1; |
1960 | 2167 | ||
1961 | if (__map_groups__create_kernel_maps(self, vmlinux_maps, kernel) < 0) | 2168 | if (__map_groups__create_kernel_maps(&kerninfo->kmaps, |
2169 | kerninfo->vmlinux_maps, kernel) < 0) | ||
1962 | return -1; | 2170 | return -1; |
1963 | 2171 | ||
1964 | if (symbol_conf.use_modules && map_groups__create_modules(self) < 0) | 2172 | if (symbol_conf.use_modules && |
2173 | map_groups__create_modules(kerninfo) < 0) | ||
1965 | pr_debug("Problems creating module maps, continuing anyway...\n"); | 2174 | pr_debug("Problems creating module maps, continuing anyway...\n"); |
1966 | /* | 2175 | /* |
1967 | * Now that we have all the maps created, just set the ->end of them: | 2176 | * Now that we have all the maps created, just set the ->end of them: |
1968 | */ | 2177 | */ |
1969 | map_groups__fixup_end(self); | 2178 | map_groups__fixup_end(&kerninfo->kmaps); |
1970 | return 0; | 2179 | return 0; |
1971 | } | 2180 | } |
1972 | 2181 | ||
@@ -2012,3 +2221,46 @@ char *strxfrchar(char *s, char from, char to) | |||
2012 | 2221 | ||
2013 | return s; | 2222 | return s; |
2014 | } | 2223 | } |
2224 | |||
2225 | int map_groups__create_guest_kernel_maps(struct rb_root *kerninfo_root) | ||
2226 | { | ||
2227 | int ret = 0; | ||
2228 | struct dirent **namelist = NULL; | ||
2229 | int i, items = 0; | ||
2230 | char path[PATH_MAX]; | ||
2231 | pid_t pid; | ||
2232 | |||
2233 | if (symbol_conf.default_guest_vmlinux_name || | ||
2234 | symbol_conf.default_guest_modules || | ||
2235 | symbol_conf.default_guest_kallsyms) { | ||
2236 | map_groups__create_kernel_maps(kerninfo_root, | ||
2237 | DEFAULT_GUEST_KERNEL_ID); | ||
2238 | } | ||
2239 | |||
2240 | if (symbol_conf.guestmount) { | ||
2241 | items = scandir(symbol_conf.guestmount, &namelist, NULL, NULL); | ||
2242 | if (items <= 0) | ||
2243 | return -ENOENT; | ||
2244 | for (i = 0; i < items; i++) { | ||
2245 | if (!isdigit(namelist[i]->d_name[0])) { | ||
2246 | /* Filter out . and .. */ | ||
2247 | continue; | ||
2248 | } | ||
2249 | pid = atoi(namelist[i]->d_name); | ||
2250 | sprintf(path, "%s/%s/proc/kallsyms", | ||
2251 | symbol_conf.guestmount, | ||
2252 | namelist[i]->d_name); | ||
2253 | ret = access(path, R_OK); | ||
2254 | if (ret) { | ||
2255 | pr_debug("Can't access file %s\n", path); | ||
2256 | goto failure; | ||
2257 | } | ||
2258 | map_groups__create_kernel_maps(kerninfo_root, | ||
2259 | pid); | ||
2260 | } | ||
2261 | failure: | ||
2262 | free(namelist); | ||
2263 | } | ||
2264 | |||
2265 | return ret; | ||
2266 | } | ||
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 757fae3f5ee0..478f5ab37787 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
@@ -69,10 +69,15 @@ struct symbol_conf { | |||
69 | show_nr_samples, | 69 | show_nr_samples, |
70 | use_callchain, | 70 | use_callchain, |
71 | exclude_other, | 71 | exclude_other, |
72 | full_paths; | 72 | full_paths, |
73 | show_cpu_utilization; | ||
73 | const char *vmlinux_name, | 74 | const char *vmlinux_name, |
74 | *field_sep; | 75 | *field_sep; |
75 | char *dso_list_str, | 76 | const char *default_guest_vmlinux_name, |
77 | *default_guest_kallsyms, | ||
78 | *default_guest_modules; | ||
79 | const char *guestmount; | ||
80 | char *dso_list_str, | ||
76 | *comm_list_str, | 81 | *comm_list_str, |
77 | *sym_list_str, | 82 | *sym_list_str, |
78 | *col_width_list_str; | 83 | *col_width_list_str; |
@@ -106,6 +111,13 @@ struct addr_location { | |||
106 | u64 addr; | 111 | u64 addr; |
107 | char level; | 112 | char level; |
108 | bool filtered; | 113 | bool filtered; |
114 | unsigned int cpumode; | ||
115 | }; | ||
116 | |||
117 | enum dso_kernel_type { | ||
118 | DSO_TYPE_USER = 0, | ||
119 | DSO_TYPE_KERNEL, | ||
120 | DSO_TYPE_GUEST_KERNEL | ||
109 | }; | 121 | }; |
110 | 122 | ||
111 | struct dso { | 123 | struct dso { |
@@ -115,7 +127,7 @@ struct dso { | |||
115 | u8 adjust_symbols:1; | 127 | u8 adjust_symbols:1; |
116 | u8 slen_calculated:1; | 128 | u8 slen_calculated:1; |
117 | u8 has_build_id:1; | 129 | u8 has_build_id:1; |
118 | u8 kernel:1; | 130 | enum dso_kernel_type kernel; |
119 | u8 hit:1; | 131 | u8 hit:1; |
120 | u8 annotate_warned:1; | 132 | u8 annotate_warned:1; |
121 | unsigned char origin; | 133 | unsigned char origin; |
@@ -143,34 +155,30 @@ static inline void dso__set_loaded(struct dso *self, enum map_type type) | |||
143 | 155 | ||
144 | void dso__sort_by_name(struct dso *self, enum map_type type); | 156 | void dso__sort_by_name(struct dso *self, enum map_type type); |
145 | 157 | ||
146 | extern struct list_head dsos__user, dsos__kernel; | ||
147 | |||
148 | struct dso *__dsos__findnew(struct list_head *head, const char *name); | 158 | struct dso *__dsos__findnew(struct list_head *head, const char *name); |
149 | 159 | ||
150 | static inline struct dso *dsos__findnew(const char *name) | ||
151 | { | ||
152 | return __dsos__findnew(&dsos__user, name); | ||
153 | } | ||
154 | |||
155 | int dso__load(struct dso *self, struct map *map, symbol_filter_t filter); | 160 | int dso__load(struct dso *self, struct map *map, symbol_filter_t filter); |
156 | int dso__load_vmlinux_path(struct dso *self, struct map *map, | 161 | int dso__load_vmlinux_path(struct dso *self, struct map *map, |
157 | symbol_filter_t filter); | 162 | symbol_filter_t filter); |
158 | int dso__load_kallsyms(struct dso *self, const char *filename, struct map *map, | 163 | int dso__load_kallsyms(struct dso *self, const char *filename, struct map *map, |
159 | symbol_filter_t filter); | 164 | symbol_filter_t filter); |
160 | void dsos__fprintf(FILE *fp); | 165 | void dsos__fprintf(struct rb_root *kerninfo_root, FILE *fp); |
161 | size_t dsos__fprintf_buildid(FILE *fp, bool with_hits); | 166 | size_t dsos__fprintf_buildid(struct rb_root *kerninfo_root, |
167 | FILE *fp, bool with_hits); | ||
162 | 168 | ||
163 | size_t dso__fprintf_buildid(struct dso *self, FILE *fp); | 169 | size_t dso__fprintf_buildid(struct dso *self, FILE *fp); |
164 | size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp); | 170 | size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp); |
165 | 171 | ||
166 | enum dso_origin { | 172 | enum dso_origin { |
167 | DSO__ORIG_KERNEL = 0, | 173 | DSO__ORIG_KERNEL = 0, |
174 | DSO__ORIG_GUEST_KERNEL, | ||
168 | DSO__ORIG_JAVA_JIT, | 175 | DSO__ORIG_JAVA_JIT, |
169 | DSO__ORIG_BUILD_ID_CACHE, | 176 | DSO__ORIG_BUILD_ID_CACHE, |
170 | DSO__ORIG_FEDORA, | 177 | DSO__ORIG_FEDORA, |
171 | DSO__ORIG_UBUNTU, | 178 | DSO__ORIG_UBUNTU, |
172 | DSO__ORIG_BUILDID, | 179 | DSO__ORIG_BUILDID, |
173 | DSO__ORIG_DSO, | 180 | DSO__ORIG_DSO, |
181 | DSO__ORIG_GUEST_KMODULE, | ||
174 | DSO__ORIG_KMODULE, | 182 | DSO__ORIG_KMODULE, |
175 | DSO__ORIG_NOT_FOUND, | 183 | DSO__ORIG_NOT_FOUND, |
176 | }; | 184 | }; |
@@ -178,19 +186,26 @@ enum dso_origin { | |||
178 | char dso__symtab_origin(const struct dso *self); | 186 | char dso__symtab_origin(const struct dso *self); |
179 | void dso__set_long_name(struct dso *self, char *name); | 187 | void dso__set_long_name(struct dso *self, char *name); |
180 | void dso__set_build_id(struct dso *self, void *build_id); | 188 | void dso__set_build_id(struct dso *self, void *build_id); |
181 | void dso__read_running_kernel_build_id(struct dso *self); | 189 | void dso__read_running_kernel_build_id(struct dso *self, |
190 | struct kernel_info *kerninfo); | ||
182 | struct symbol *dso__find_symbol(struct dso *self, enum map_type type, u64 addr); | 191 | struct symbol *dso__find_symbol(struct dso *self, enum map_type type, u64 addr); |
183 | struct symbol *dso__find_symbol_by_name(struct dso *self, enum map_type type, | 192 | struct symbol *dso__find_symbol_by_name(struct dso *self, enum map_type type, |
184 | const char *name); | 193 | const char *name); |
185 | 194 | ||
186 | int filename__read_build_id(const char *filename, void *bf, size_t size); | 195 | int filename__read_build_id(const char *filename, void *bf, size_t size); |
187 | int sysfs__read_build_id(const char *filename, void *bf, size_t size); | 196 | int sysfs__read_build_id(const char *filename, void *bf, size_t size); |
188 | bool dsos__read_build_ids(bool with_hits); | 197 | bool __dsos__read_build_ids(struct list_head *head, bool with_hits); |
189 | int build_id__sprintf(const u8 *self, int len, char *bf); | 198 | int build_id__sprintf(const u8 *self, int len, char *bf); |
190 | int kallsyms__parse(const char *filename, void *arg, | 199 | int kallsyms__parse(const char *filename, void *arg, |
191 | int (*process_symbol)(void *arg, const char *name, | 200 | int (*process_symbol)(void *arg, const char *name, |
192 | char type, u64 start)); | 201 | char type, u64 start)); |
193 | 202 | ||
203 | int __map_groups__create_kernel_maps(struct map_groups *self, | ||
204 | struct map *vmlinux_maps[MAP__NR_TYPES], | ||
205 | struct dso *kernel); | ||
206 | int map_groups__create_kernel_maps(struct rb_root *kerninfo_root, pid_t pid); | ||
207 | int map_groups__create_guest_kernel_maps(struct rb_root *kerninfo_root); | ||
208 | |||
194 | int symbol__init(void); | 209 | int symbol__init(void); |
195 | bool symbol_type__is_a(char symbol_type, enum map_type map_type); | 210 | bool symbol_type__is_a(char symbol_type, enum map_type map_type); |
196 | 211 | ||
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index 9c488fcadec9..1dfd9ff8bdcd 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h | |||
@@ -33,12 +33,12 @@ static inline struct map *thread__find_map(struct thread *self, | |||
33 | 33 | ||
34 | void thread__find_addr_map(struct thread *self, | 34 | void thread__find_addr_map(struct thread *self, |
35 | struct perf_session *session, u8 cpumode, | 35 | struct perf_session *session, u8 cpumode, |
36 | enum map_type type, u64 addr, | 36 | enum map_type type, pid_t pid, u64 addr, |
37 | struct addr_location *al); | 37 | struct addr_location *al); |
38 | 38 | ||
39 | void thread__find_addr_location(struct thread *self, | 39 | void thread__find_addr_location(struct thread *self, |
40 | struct perf_session *session, u8 cpumode, | 40 | struct perf_session *session, u8 cpumode, |
41 | enum map_type type, u64 addr, | 41 | enum map_type type, pid_t pid, u64 addr, |
42 | struct addr_location *al, | 42 | struct addr_location *al, |
43 | symbol_filter_t filter); | 43 | symbol_filter_t filter); |
44 | #endif /* __PERF_THREAD_H */ | 44 | #endif /* __PERF_THREAD_H */ |