diff options
Diffstat (limited to 'tools/perf/builtin-top.c')
-rw-r--r-- | tools/perf/builtin-top.c | 135 |
1 files changed, 83 insertions, 52 deletions
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 377971dc89a3..0aa7747ff139 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
@@ -59,7 +59,7 @@ | |||
59 | 59 | ||
60 | #include <sys/syscall.h> | 60 | #include <sys/syscall.h> |
61 | #include <sys/ioctl.h> | 61 | #include <sys/ioctl.h> |
62 | #include <sys/poll.h> | 62 | #include <poll.h> |
63 | #include <sys/prctl.h> | 63 | #include <sys/prctl.h> |
64 | #include <sys/wait.h> | 64 | #include <sys/wait.h> |
65 | #include <sys/uio.h> | 65 | #include <sys/uio.h> |
@@ -251,6 +251,7 @@ static void perf_top__print_sym_table(struct perf_top *top) | |||
251 | char bf[160]; | 251 | char bf[160]; |
252 | int printed = 0; | 252 | int printed = 0; |
253 | const int win_width = top->winsize.ws_col - 1; | 253 | const int win_width = top->winsize.ws_col - 1; |
254 | struct hists *hists = evsel__hists(top->sym_evsel); | ||
254 | 255 | ||
255 | puts(CONSOLE_CLEAR); | 256 | puts(CONSOLE_CLEAR); |
256 | 257 | ||
@@ -261,13 +262,13 @@ static void perf_top__print_sym_table(struct perf_top *top) | |||
261 | 262 | ||
262 | printf("%-*.*s\n", win_width, win_width, graph_dotted_line); | 263 | printf("%-*.*s\n", win_width, win_width, graph_dotted_line); |
263 | 264 | ||
264 | if (top->sym_evsel->hists.stats.nr_lost_warned != | 265 | if (hists->stats.nr_lost_warned != |
265 | top->sym_evsel->hists.stats.nr_events[PERF_RECORD_LOST]) { | 266 | hists->stats.nr_events[PERF_RECORD_LOST]) { |
266 | top->sym_evsel->hists.stats.nr_lost_warned = | 267 | hists->stats.nr_lost_warned = |
267 | top->sym_evsel->hists.stats.nr_events[PERF_RECORD_LOST]; | 268 | hists->stats.nr_events[PERF_RECORD_LOST]; |
268 | color_fprintf(stdout, PERF_COLOR_RED, | 269 | color_fprintf(stdout, PERF_COLOR_RED, |
269 | "WARNING: LOST %d chunks, Check IO/CPU overload", | 270 | "WARNING: LOST %d chunks, Check IO/CPU overload", |
270 | top->sym_evsel->hists.stats.nr_lost_warned); | 271 | hists->stats.nr_lost_warned); |
271 | ++printed; | 272 | ++printed; |
272 | } | 273 | } |
273 | 274 | ||
@@ -276,16 +277,19 @@ static void perf_top__print_sym_table(struct perf_top *top) | |||
276 | return; | 277 | return; |
277 | } | 278 | } |
278 | 279 | ||
279 | hists__collapse_resort(&top->sym_evsel->hists, NULL); | 280 | if (top->zero) { |
280 | hists__output_resort(&top->sym_evsel->hists); | 281 | hists__delete_entries(hists); |
281 | hists__decay_entries(&top->sym_evsel->hists, | 282 | } else { |
282 | top->hide_user_symbols, | 283 | hists__decay_entries(hists, top->hide_user_symbols, |
283 | top->hide_kernel_symbols); | 284 | top->hide_kernel_symbols); |
284 | hists__output_recalc_col_len(&top->sym_evsel->hists, | 285 | } |
285 | top->print_entries - printed); | 286 | |
287 | hists__collapse_resort(hists, NULL); | ||
288 | hists__output_resort(hists); | ||
289 | |||
290 | hists__output_recalc_col_len(hists, top->print_entries - printed); | ||
286 | putchar('\n'); | 291 | putchar('\n'); |
287 | hists__fprintf(&top->sym_evsel->hists, false, | 292 | hists__fprintf(hists, false, top->print_entries - printed, win_width, |
288 | top->print_entries - printed, win_width, | ||
289 | top->min_percent, stdout); | 293 | top->min_percent, stdout); |
290 | } | 294 | } |
291 | 295 | ||
@@ -328,6 +332,7 @@ static void perf_top__prompt_symbol(struct perf_top *top, const char *msg) | |||
328 | { | 332 | { |
329 | char *buf = malloc(0), *p; | 333 | char *buf = malloc(0), *p; |
330 | struct hist_entry *syme = top->sym_filter_entry, *n, *found = NULL; | 334 | struct hist_entry *syme = top->sym_filter_entry, *n, *found = NULL; |
335 | struct hists *hists = evsel__hists(top->sym_evsel); | ||
331 | struct rb_node *next; | 336 | struct rb_node *next; |
332 | size_t dummy = 0; | 337 | size_t dummy = 0; |
333 | 338 | ||
@@ -345,7 +350,7 @@ static void perf_top__prompt_symbol(struct perf_top *top, const char *msg) | |||
345 | if (p) | 350 | if (p) |
346 | *p = 0; | 351 | *p = 0; |
347 | 352 | ||
348 | next = rb_first(&top->sym_evsel->hists.entries); | 353 | next = rb_first(&hists->entries); |
349 | while (next) { | 354 | while (next) { |
350 | n = rb_entry(next, struct hist_entry, rb_node); | 355 | n = rb_entry(next, struct hist_entry, rb_node); |
351 | if (n->ms.sym && !strcmp(buf, n->ms.sym->name)) { | 356 | if (n->ms.sym && !strcmp(buf, n->ms.sym->name)) { |
@@ -427,18 +432,13 @@ static bool perf_top__handle_keypress(struct perf_top *top, int c) | |||
427 | 432 | ||
428 | if (!perf_top__key_mapped(top, c)) { | 433 | if (!perf_top__key_mapped(top, c)) { |
429 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; | 434 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; |
430 | struct termios tc, save; | 435 | struct termios save; |
431 | 436 | ||
432 | perf_top__print_mapped_keys(top); | 437 | perf_top__print_mapped_keys(top); |
433 | fprintf(stdout, "\nEnter selection, or unmapped key to continue: "); | 438 | fprintf(stdout, "\nEnter selection, or unmapped key to continue: "); |
434 | fflush(stdout); | 439 | fflush(stdout); |
435 | 440 | ||
436 | tcgetattr(0, &save); | 441 | set_term_quiet_input(&save); |
437 | tc = save; | ||
438 | tc.c_lflag &= ~(ICANON | ECHO); | ||
439 | tc.c_cc[VMIN] = 0; | ||
440 | tc.c_cc[VTIME] = 0; | ||
441 | tcsetattr(0, TCSANOW, &tc); | ||
442 | 442 | ||
443 | poll(&stdin_poll, 1, -1); | 443 | poll(&stdin_poll, 1, -1); |
444 | c = getc(stdin); | 444 | c = getc(stdin); |
@@ -537,16 +537,24 @@ static bool perf_top__handle_keypress(struct perf_top *top, int c) | |||
537 | static void perf_top__sort_new_samples(void *arg) | 537 | static void perf_top__sort_new_samples(void *arg) |
538 | { | 538 | { |
539 | struct perf_top *t = arg; | 539 | struct perf_top *t = arg; |
540 | struct hists *hists; | ||
541 | |||
540 | perf_top__reset_sample_counters(t); | 542 | perf_top__reset_sample_counters(t); |
541 | 543 | ||
542 | if (t->evlist->selected != NULL) | 544 | if (t->evlist->selected != NULL) |
543 | t->sym_evsel = t->evlist->selected; | 545 | t->sym_evsel = t->evlist->selected; |
544 | 546 | ||
545 | hists__collapse_resort(&t->sym_evsel->hists, NULL); | 547 | hists = evsel__hists(t->sym_evsel); |
546 | hists__output_resort(&t->sym_evsel->hists); | 548 | |
547 | hists__decay_entries(&t->sym_evsel->hists, | 549 | if (t->zero) { |
548 | t->hide_user_symbols, | 550 | hists__delete_entries(hists); |
549 | t->hide_kernel_symbols); | 551 | } else { |
552 | hists__decay_entries(hists, t->hide_user_symbols, | ||
553 | t->hide_kernel_symbols); | ||
554 | } | ||
555 | |||
556 | hists__collapse_resort(hists, NULL); | ||
557 | hists__output_resort(hists); | ||
550 | } | 558 | } |
551 | 559 | ||
552 | static void *display_thread_tui(void *arg) | 560 | static void *display_thread_tui(void *arg) |
@@ -567,8 +575,10 @@ static void *display_thread_tui(void *arg) | |||
567 | * Zooming in/out UIDs. For now juse use whatever the user passed | 575 | * Zooming in/out UIDs. For now juse use whatever the user passed |
568 | * via --uid. | 576 | * via --uid. |
569 | */ | 577 | */ |
570 | evlist__for_each(top->evlist, pos) | 578 | evlist__for_each(top->evlist, pos) { |
571 | pos->hists.uid_filter_str = top->record_opts.target.uid_str; | 579 | struct hists *hists = evsel__hists(pos); |
580 | hists->uid_filter_str = top->record_opts.target.uid_str; | ||
581 | } | ||
572 | 582 | ||
573 | perf_evlist__tui_browse_hists(top->evlist, help, &hbt, top->min_percent, | 583 | perf_evlist__tui_browse_hists(top->evlist, help, &hbt, top->min_percent, |
574 | &top->session->header.env); | 584 | &top->session->header.env); |
@@ -577,23 +587,32 @@ static void *display_thread_tui(void *arg) | |||
577 | return NULL; | 587 | return NULL; |
578 | } | 588 | } |
579 | 589 | ||
590 | static void display_sig(int sig __maybe_unused) | ||
591 | { | ||
592 | done = 1; | ||
593 | } | ||
594 | |||
595 | static void display_setup_sig(void) | ||
596 | { | ||
597 | signal(SIGSEGV, display_sig); | ||
598 | signal(SIGFPE, display_sig); | ||
599 | signal(SIGINT, display_sig); | ||
600 | signal(SIGQUIT, display_sig); | ||
601 | signal(SIGTERM, display_sig); | ||
602 | } | ||
603 | |||
580 | static void *display_thread(void *arg) | 604 | static void *display_thread(void *arg) |
581 | { | 605 | { |
582 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; | 606 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; |
583 | struct termios tc, save; | 607 | struct termios save; |
584 | struct perf_top *top = arg; | 608 | struct perf_top *top = arg; |
585 | int delay_msecs, c; | 609 | int delay_msecs, c; |
586 | 610 | ||
587 | tcgetattr(0, &save); | 611 | display_setup_sig(); |
588 | tc = save; | ||
589 | tc.c_lflag &= ~(ICANON | ECHO); | ||
590 | tc.c_cc[VMIN] = 0; | ||
591 | tc.c_cc[VTIME] = 0; | ||
592 | |||
593 | pthread__unblock_sigwinch(); | 612 | pthread__unblock_sigwinch(); |
594 | repeat: | 613 | repeat: |
595 | delay_msecs = top->delay_secs * 1000; | 614 | delay_msecs = top->delay_secs * 1000; |
596 | tcsetattr(0, TCSANOW, &tc); | 615 | set_term_quiet_input(&save); |
597 | /* trash return*/ | 616 | /* trash return*/ |
598 | getc(stdin); | 617 | getc(stdin); |
599 | 618 | ||
@@ -620,13 +639,16 @@ repeat: | |||
620 | } | 639 | } |
621 | } | 640 | } |
622 | 641 | ||
642 | tcsetattr(0, TCSAFLUSH, &save); | ||
623 | return NULL; | 643 | return NULL; |
624 | } | 644 | } |
625 | 645 | ||
626 | static int symbol_filter(struct map *map __maybe_unused, struct symbol *sym) | 646 | static int symbol_filter(struct map *map, struct symbol *sym) |
627 | { | 647 | { |
628 | const char *name = sym->name; | 648 | const char *name = sym->name; |
629 | 649 | ||
650 | if (!map->dso->kernel) | ||
651 | return 0; | ||
630 | /* | 652 | /* |
631 | * ppc64 uses function descriptors and appends a '.' to the | 653 | * ppc64 uses function descriptors and appends a '.' to the |
632 | * start of every instruction address. Remove it. | 654 | * start of every instruction address. Remove it. |
@@ -750,6 +772,7 @@ static void perf_event__process_sample(struct perf_tool *tool, | |||
750 | } | 772 | } |
751 | 773 | ||
752 | if (al.sym == NULL || !al.sym->ignore) { | 774 | if (al.sym == NULL || !al.sym->ignore) { |
775 | struct hists *hists = evsel__hists(evsel); | ||
753 | struct hist_entry_iter iter = { | 776 | struct hist_entry_iter iter = { |
754 | .add_entry_cb = hist_iter__top_callback, | 777 | .add_entry_cb = hist_iter__top_callback, |
755 | }; | 778 | }; |
@@ -759,14 +782,14 @@ static void perf_event__process_sample(struct perf_tool *tool, | |||
759 | else | 782 | else |
760 | iter.ops = &hist_iter_normal; | 783 | iter.ops = &hist_iter_normal; |
761 | 784 | ||
762 | pthread_mutex_lock(&evsel->hists.lock); | 785 | pthread_mutex_lock(&hists->lock); |
763 | 786 | ||
764 | err = hist_entry_iter__add(&iter, &al, evsel, sample, | 787 | err = hist_entry_iter__add(&iter, &al, evsel, sample, |
765 | top->max_stack, top); | 788 | top->max_stack, top); |
766 | if (err < 0) | 789 | if (err < 0) |
767 | pr_err("Problem incrementing symbol period, skipping event\n"); | 790 | pr_err("Problem incrementing symbol period, skipping event\n"); |
768 | 791 | ||
769 | pthread_mutex_unlock(&evsel->hists.lock); | 792 | pthread_mutex_unlock(&hists->lock); |
770 | } | 793 | } |
771 | 794 | ||
772 | return; | 795 | return; |
@@ -831,7 +854,7 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx) | |||
831 | perf_event__process_sample(&top->tool, event, evsel, | 854 | perf_event__process_sample(&top->tool, event, evsel, |
832 | &sample, machine); | 855 | &sample, machine); |
833 | } else if (event->header.type < PERF_RECORD_MAX) { | 856 | } else if (event->header.type < PERF_RECORD_MAX) { |
834 | hists__inc_nr_events(&evsel->hists, event->header.type); | 857 | hists__inc_nr_events(evsel__hists(evsel), event->header.type); |
835 | machine__process_event(machine, event, &sample); | 858 | machine__process_event(machine, event, &sample); |
836 | } else | 859 | } else |
837 | ++session->stats.nr_unknown_events; | 860 | ++session->stats.nr_unknown_events; |
@@ -876,7 +899,7 @@ try_again: | |||
876 | 899 | ||
877 | if (perf_evlist__mmap(evlist, opts->mmap_pages, false) < 0) { | 900 | if (perf_evlist__mmap(evlist, opts->mmap_pages, false) < 0) { |
878 | ui__error("Failed to mmap with %d (%s)\n", | 901 | ui__error("Failed to mmap with %d (%s)\n", |
879 | errno, strerror(errno)); | 902 | errno, strerror_r(errno, msg, sizeof(msg))); |
880 | goto out_err; | 903 | goto out_err; |
881 | } | 904 | } |
882 | 905 | ||
@@ -911,7 +934,7 @@ static int __cmd_top(struct perf_top *top) | |||
911 | 934 | ||
912 | top->session = perf_session__new(NULL, false, NULL); | 935 | top->session = perf_session__new(NULL, false, NULL); |
913 | if (top->session == NULL) | 936 | if (top->session == NULL) |
914 | return -ENOMEM; | 937 | return -1; |
915 | 938 | ||
916 | machines__set_symbol_filter(&top->session->machines, symbol_filter); | 939 | machines__set_symbol_filter(&top->session->machines, symbol_filter); |
917 | 940 | ||
@@ -946,7 +969,7 @@ static int __cmd_top(struct perf_top *top) | |||
946 | perf_evlist__enable(top->evlist); | 969 | perf_evlist__enable(top->evlist); |
947 | 970 | ||
948 | /* Wait for a minimal set of events before starting the snapshot */ | 971 | /* Wait for a minimal set of events before starting the snapshot */ |
949 | poll(top->evlist->pollfd, top->evlist->nr_fds, 100); | 972 | perf_evlist__poll(top->evlist, 100); |
950 | 973 | ||
951 | perf_top__mmap_read(top); | 974 | perf_top__mmap_read(top); |
952 | 975 | ||
@@ -963,7 +986,7 @@ static int __cmd_top(struct perf_top *top) | |||
963 | param.sched_priority = top->realtime_prio; | 986 | param.sched_priority = top->realtime_prio; |
964 | if (sched_setscheduler(0, SCHED_FIFO, ¶m)) { | 987 | if (sched_setscheduler(0, SCHED_FIFO, ¶m)) { |
965 | ui__error("Could not set realtime priority.\n"); | 988 | ui__error("Could not set realtime priority.\n"); |
966 | goto out_delete; | 989 | goto out_join; |
967 | } | 990 | } |
968 | } | 991 | } |
969 | 992 | ||
@@ -973,10 +996,12 @@ static int __cmd_top(struct perf_top *top) | |||
973 | perf_top__mmap_read(top); | 996 | perf_top__mmap_read(top); |
974 | 997 | ||
975 | if (hits == top->samples) | 998 | if (hits == top->samples) |
976 | ret = poll(top->evlist->pollfd, top->evlist->nr_fds, 100); | 999 | ret = perf_evlist__poll(top->evlist, 100); |
977 | } | 1000 | } |
978 | 1001 | ||
979 | ret = 0; | 1002 | ret = 0; |
1003 | out_join: | ||
1004 | pthread_join(thread, NULL); | ||
980 | out_delete: | 1005 | out_delete: |
981 | perf_session__delete(top->session); | 1006 | perf_session__delete(top->session); |
982 | top->session = NULL; | 1007 | top->session = NULL; |
@@ -1000,10 +1025,8 @@ parse_callchain_opt(const struct option *opt, const char *arg, int unset) | |||
1000 | 1025 | ||
1001 | static int perf_top_config(const char *var, const char *value, void *cb) | 1026 | static int perf_top_config(const char *var, const char *value, void *cb) |
1002 | { | 1027 | { |
1003 | struct perf_top *top = cb; | ||
1004 | |||
1005 | if (!strcmp(var, "top.call-graph")) | 1028 | if (!strcmp(var, "top.call-graph")) |
1006 | return record_parse_callchain(value, &top->record_opts); | 1029 | var = "call-graph.record-mode"; /* fall-through */ |
1007 | if (!strcmp(var, "top.children")) { | 1030 | if (!strcmp(var, "top.children")) { |
1008 | symbol_conf.cumulate_callchain = perf_config_bool(var, value); | 1031 | symbol_conf.cumulate_callchain = perf_config_bool(var, value); |
1009 | return 0; | 1032 | return 0; |
@@ -1024,7 +1047,6 @@ parse_percent_limit(const struct option *opt, const char *arg, | |||
1024 | 1047 | ||
1025 | int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) | 1048 | int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) |
1026 | { | 1049 | { |
1027 | int status = -1; | ||
1028 | char errbuf[BUFSIZ]; | 1050 | char errbuf[BUFSIZ]; |
1029 | struct perf_top top = { | 1051 | struct perf_top top = { |
1030 | .count_filter = 5, | 1052 | .count_filter = 5, |
@@ -1122,6 +1144,8 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1122 | "Interleave source code with assembly code (default)"), | 1144 | "Interleave source code with assembly code (default)"), |
1123 | OPT_BOOLEAN(0, "asm-raw", &symbol_conf.annotate_asm_raw, | 1145 | OPT_BOOLEAN(0, "asm-raw", &symbol_conf.annotate_asm_raw, |
1124 | "Display raw encoding of assembly instructions (default)"), | 1146 | "Display raw encoding of assembly instructions (default)"), |
1147 | OPT_BOOLEAN(0, "demangle-kernel", &symbol_conf.demangle_kernel, | ||
1148 | "Enable kernel symbol demangling"), | ||
1125 | OPT_STRING(0, "objdump", &objdump_path, "path", | 1149 | OPT_STRING(0, "objdump", &objdump_path, "path", |
1126 | "objdump binary to use for disassembly and annotations"), | 1150 | "objdump binary to use for disassembly and annotations"), |
1127 | OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style", | 1151 | OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style", |
@@ -1131,12 +1155,19 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1131 | "Don't show entries under that percent", parse_percent_limit), | 1155 | "Don't show entries under that percent", parse_percent_limit), |
1132 | OPT_CALLBACK(0, "percentage", NULL, "relative|absolute", | 1156 | OPT_CALLBACK(0, "percentage", NULL, "relative|absolute", |
1133 | "How to display percentage of filtered entries", parse_filter_percentage), | 1157 | "How to display percentage of filtered entries", parse_filter_percentage), |
1158 | OPT_STRING('w', "column-widths", &symbol_conf.col_width_list_str, | ||
1159 | "width[,width...]", | ||
1160 | "don't try to adjust column width, use these fixed values"), | ||
1134 | OPT_END() | 1161 | OPT_END() |
1135 | }; | 1162 | }; |
1136 | const char * const top_usage[] = { | 1163 | const char * const top_usage[] = { |
1137 | "perf top [<options>]", | 1164 | "perf top [<options>]", |
1138 | NULL | 1165 | NULL |
1139 | }; | 1166 | }; |
1167 | int status = hists__init(); | ||
1168 | |||
1169 | if (status < 0) | ||
1170 | return status; | ||
1140 | 1171 | ||
1141 | top.evlist = perf_evlist__new(); | 1172 | top.evlist = perf_evlist__new(); |
1142 | if (top.evlist == NULL) | 1173 | if (top.evlist == NULL) |
@@ -1217,7 +1248,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1217 | symbol_conf.priv_size = sizeof(struct annotation); | 1248 | symbol_conf.priv_size = sizeof(struct annotation); |
1218 | 1249 | ||
1219 | symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL); | 1250 | symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL); |
1220 | if (symbol__init() < 0) | 1251 | if (symbol__init(NULL) < 0) |
1221 | return -1; | 1252 | return -1; |
1222 | 1253 | ||
1223 | sort__setup_elide(stdout); | 1254 | sort__setup_elide(stdout); |