aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndi Kleen <ak@linux.intel.com>2019-03-11 10:44:58 -0400
committerArnaldo Carvalho de Melo <acme@redhat.com>2019-03-11 15:33:19 -0400
commit4968ac8fb7c378e2bc40b7e9bd97768fa8c7aa32 (patch)
tree24c72d13c3dbba441c0cefae5a1c076c9c2d62e8
parent6f3da20e151f4121548cf598730ae0f9559ae45d (diff)
perf report: Implement browsing of individual samples
Now 'perf report' can show whole time periods with 'perf script', but the user still has to find individual samples of interest manually. It would be expensive and complicated to search for the right samples in the whole perf file. Typically users only need to look at a small number of samples for useful analysis. Also the full scripts tend to show samples of all CPUs and all threads mixed up, which can be very confusing on larger systems. Add a new --samples option to save a small random number of samples per hist entry. Use a reservoir sample technique to select a representatve number of samples. Then allow browsing the samples using 'perf script' as part of the hist entry context menu. This automatically adds the right filters, so only the thread or cpu of the sample is displayed. Then we use less' search functionality to directly jump the to the time stamp of the selected sample. It uses different menus for assembler and source display. Assembler needs xed installed and source needs debuginfo. Currently it only supports as many samples as fit on the screen due to some limitations in the slang ui code. Signed-off-by: Andi Kleen <ak@linux.intel.com> Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com> Acked-by: Jiri Olsa <jolsa@kernel.org> Link: http://lkml.kernel.org/r/20190311174605.GA29294@tassilo.jf.intel.com Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
-rw-r--r--tools/perf/Documentation/perf-config.txt6
-rw-r--r--tools/perf/Documentation/perf-report.txt4
-rw-r--r--tools/perf/builtin-report.c2
-rw-r--r--tools/perf/ui/browsers/Build1
-rw-r--r--tools/perf/ui/browsers/hists.c47
-rw-r--r--tools/perf/ui/browsers/res_sample.c94
-rw-r--r--tools/perf/ui/browsers/scripts.c2
-rw-r--r--tools/perf/util/hist.c39
-rw-r--r--tools/perf/util/hist.h22
-rw-r--r--tools/perf/util/sort.h8
-rw-r--r--tools/perf/util/symbol.c1
-rw-r--r--tools/perf/util/symbol_conf.h1
12 files changed, 226 insertions, 1 deletions
diff --git a/tools/perf/Documentation/perf-config.txt b/tools/perf/Documentation/perf-config.txt
index 86f3dcc15f83..2d0fb7613134 100644
--- a/tools/perf/Documentation/perf-config.txt
+++ b/tools/perf/Documentation/perf-config.txt
@@ -584,6 +584,12 @@ llvm.*::
584 llvm.opts:: 584 llvm.opts::
585 Options passed to llc. 585 Options passed to llc.
586 586
587samples.*::
588
589 samples.context::
590 Define how many ns worth of time to show
591 around samples in perf report sample context browser.
592
587SEE ALSO 593SEE ALSO
588-------- 594--------
589linkperf:perf[1] 595linkperf:perf[1]
diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt
index 546d87221ad8..f441baa794ce 100644
--- a/tools/perf/Documentation/perf-report.txt
+++ b/tools/perf/Documentation/perf-report.txt
@@ -461,6 +461,10 @@ include::itrace.txt[]
461--socket-filter:: 461--socket-filter::
462 Only report the samples on the processor socket that match with this filter 462 Only report the samples on the processor socket that match with this filter
463 463
464--samples=N::
465 Save N individual samples for each histogram entry to show context in perf
466 report tui browser.
467
464--raw-trace:: 468--raw-trace::
465 When displaying traceevent output, do not use print fmt or plugins. 469 When displaying traceevent output, do not use print fmt or plugins.
466 470
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 05c8dd41106c..1921aaa9cece 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -1159,6 +1159,8 @@ int cmd_report(int argc, const char **argv)
1159 OPT_BOOLEAN(0, "demangle-kernel", &symbol_conf.demangle_kernel, 1159 OPT_BOOLEAN(0, "demangle-kernel", &symbol_conf.demangle_kernel,
1160 "Enable kernel symbol demangling"), 1160 "Enable kernel symbol demangling"),
1161 OPT_BOOLEAN(0, "mem-mode", &report.mem_mode, "mem access profile"), 1161 OPT_BOOLEAN(0, "mem-mode", &report.mem_mode, "mem access profile"),
1162 OPT_INTEGER(0, "samples", &symbol_conf.res_sample,
1163 "Number of samples to save per histogram entry for individual browsing"),
1162 OPT_CALLBACK(0, "percent-limit", &report, "percent", 1164 OPT_CALLBACK(0, "percent-limit", &report, "percent",
1163 "Don't show entries under that percent", parse_percent_limit), 1165 "Don't show entries under that percent", parse_percent_limit),
1164 OPT_CALLBACK(0, "percentage", NULL, "relative|absolute", 1166 OPT_CALLBACK(0, "percentage", NULL, "relative|absolute",
diff --git a/tools/perf/ui/browsers/Build b/tools/perf/ui/browsers/Build
index 8fee56b46502..fdf86f7981ca 100644
--- a/tools/perf/ui/browsers/Build
+++ b/tools/perf/ui/browsers/Build
@@ -3,6 +3,7 @@ perf-y += hists.o
3perf-y += map.o 3perf-y += map.o
4perf-y += scripts.o 4perf-y += scripts.o
5perf-y += header.o 5perf-y += header.o
6perf-y += res_sample.o
6 7
7CFLAGS_annotate.o += -DENABLE_SLFUTURE_CONST 8CFLAGS_annotate.o += -DENABLE_SLFUTURE_CONST
8CFLAGS_hists.o += -DENABLE_SLFUTURE_CONST 9CFLAGS_hists.o += -DENABLE_SLFUTURE_CONST
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c
index fb4430f8982c..3421ecbdd3f0 100644
--- a/tools/perf/ui/browsers/hists.c
+++ b/tools/perf/ui/browsers/hists.c
@@ -1226,6 +1226,8 @@ void hist_browser__init_hpp(void)
1226 hist_browser__hpp_color_overhead_guest_us; 1226 hist_browser__hpp_color_overhead_guest_us;
1227 perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color = 1227 perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
1228 hist_browser__hpp_color_overhead_acc; 1228 hist_browser__hpp_color_overhead_acc;
1229
1230 res_sample_init();
1229} 1231}
1230 1232
1231static int hist_browser__show_entry(struct hist_browser *browser, 1233static int hist_browser__show_entry(struct hist_browser *browser,
@@ -2345,6 +2347,7 @@ struct popup_action {
2345 struct map_symbol ms; 2347 struct map_symbol ms;
2346 int socket; 2348 int socket;
2347 struct perf_evsel *evsel; 2349 struct perf_evsel *evsel;
2350 enum rstype rstype;
2348 2351
2349 int (*fn)(struct hist_browser *browser, struct popup_action *act); 2352 int (*fn)(struct hist_browser *browser, struct popup_action *act);
2350}; 2353};
@@ -2573,6 +2576,17 @@ do_run_script(struct hist_browser *browser __maybe_unused,
2573} 2576}
2574 2577
2575static int 2578static int
2579do_res_sample_script(struct hist_browser *browser __maybe_unused,
2580 struct popup_action *act)
2581{
2582 struct hist_entry *he;
2583
2584 he = hist_browser__selected_entry(browser);
2585 res_sample_browse(he->res_samples, he->num_res, act->evsel, act->rstype);
2586 return 0;
2587}
2588
2589static int
2576add_script_opt_2(struct hist_browser *browser __maybe_unused, 2590add_script_opt_2(struct hist_browser *browser __maybe_unused,
2577 struct popup_action *act, char **optstr, 2591 struct popup_action *act, char **optstr,
2578 struct thread *thread, struct symbol *sym, 2592 struct thread *thread, struct symbol *sym,
@@ -2630,6 +2644,27 @@ add_script_opt(struct hist_browser *browser,
2630} 2644}
2631 2645
2632static int 2646static int
2647add_res_sample_opt(struct hist_browser *browser __maybe_unused,
2648 struct popup_action *act, char **optstr,
2649 struct res_sample *res_sample,
2650 struct perf_evsel *evsel,
2651 enum rstype type)
2652{
2653 if (!res_sample)
2654 return 0;
2655
2656 if (asprintf(optstr, "Show context for individual samples %s",
2657 type == A_ASM ? "with assembler" :
2658 type == A_SOURCE ? "with source" : "") < 0)
2659 return 0;
2660
2661 act->fn = do_res_sample_script;
2662 act->evsel = evsel;
2663 act->rstype = type;
2664 return 1;
2665}
2666
2667static int
2633do_switch_data(struct hist_browser *browser __maybe_unused, 2668do_switch_data(struct hist_browser *browser __maybe_unused,
2634 struct popup_action *act __maybe_unused) 2669 struct popup_action *act __maybe_unused)
2635{ 2670{
@@ -3115,6 +3150,18 @@ skip_annotation:
3115 } 3150 }
3116 nr_options += add_script_opt(browser, &actions[nr_options], 3151 nr_options += add_script_opt(browser, &actions[nr_options],
3117 &options[nr_options], NULL, NULL, evsel); 3152 &options[nr_options], NULL, NULL, evsel);
3153 nr_options += add_res_sample_opt(browser, &actions[nr_options],
3154 &options[nr_options],
3155 hist_browser__selected_entry(browser)->res_samples,
3156 evsel, A_NORMAL);
3157 nr_options += add_res_sample_opt(browser, &actions[nr_options],
3158 &options[nr_options],
3159 hist_browser__selected_entry(browser)->res_samples,
3160 evsel, A_ASM);
3161 nr_options += add_res_sample_opt(browser, &actions[nr_options],
3162 &options[nr_options],
3163 hist_browser__selected_entry(browser)->res_samples,
3164 evsel, A_SOURCE);
3118 nr_options += add_switch_opt(browser, &actions[nr_options], 3165 nr_options += add_switch_opt(browser, &actions[nr_options],
3119 &options[nr_options]); 3166 &options[nr_options]);
3120skip_scripting: 3167skip_scripting:
diff --git a/tools/perf/ui/browsers/res_sample.c b/tools/perf/ui/browsers/res_sample.c
new file mode 100644
index 000000000000..884ef2a92c15
--- /dev/null
+++ b/tools/perf/ui/browsers/res_sample.c
@@ -0,0 +1,94 @@
1// SPDX-License-Identifier: GPL-2.0
2/* Display a menu with individual samples to browse with perf script */
3#include "util.h"
4#include "hist.h"
5#include "evsel.h"
6#include "hists.h"
7#include "sort.h"
8#include "config.h"
9#include "time-utils.h"
10#include <linux/time64.h>
11
12static u64 context_len = 10 * NSEC_PER_MSEC;
13
14static int res_sample_config(const char *var, const char *value, void *data __maybe_unused)
15{
16 if (!strcmp(var, "samples.context"))
17 return perf_config_u64(&context_len, var, value);
18 return 0;
19}
20
21void res_sample_init(void)
22{
23 perf_config(res_sample_config, NULL);
24}
25
26int res_sample_browse(struct res_sample *res_samples, int num_res,
27 struct perf_evsel *evsel, enum rstype rstype)
28{
29 char **names;
30 int i, n;
31 int choice;
32 char *cmd;
33 char pbuf[256], tidbuf[32], cpubuf[32];
34 const char *perf = perf_exe(pbuf, sizeof pbuf);
35 char trange[128], tsample[64];
36 struct res_sample *r;
37 char extra_format[256];
38
39 /* For now since ui__popup_menu doesn't like lists that don't fit */
40 num_res = max(min(SLtt_Screen_Rows - 4, num_res), 0);
41
42 names = calloc(num_res, sizeof(char *));
43 if (!names)
44 return -1;
45 for (i = 0; i < num_res; i++) {
46 char tbuf[64];
47
48 timestamp__scnprintf_nsec(res_samples[i].time, tbuf, sizeof tbuf);
49 if (asprintf(&names[i], "%s: CPU %d tid %d", tbuf,
50 res_samples[i].cpu, res_samples[i].tid) < 0) {
51 while (--i >= 0)
52 free(names[i]);
53 free(names);
54 return -1;
55 }
56 }
57 choice = ui__popup_menu(num_res, names);
58 for (i = 0; i < num_res; i++)
59 free(names[i]);
60 free(names);
61
62 if (choice < 0 || choice >= num_res)
63 return -1;
64 r = &res_samples[choice];
65
66 n = timestamp__scnprintf_nsec(r->time - context_len, trange, sizeof trange);
67 trange[n++] = ',';
68 timestamp__scnprintf_nsec(r->time + context_len, trange + n, sizeof trange - n);
69
70 timestamp__scnprintf_nsec(r->time, tsample, sizeof tsample);
71
72 attr_to_script(extra_format, &evsel->attr);
73
74 if (asprintf(&cmd, "%s script %s%s --time %s %s%s %s%s --ns %s %s %s %s %s | less +/%s",
75 perf,
76 input_name ? "-i " : "",
77 input_name ? input_name : "",
78 trange,
79 r->cpu >= 0 ? "--cpu " : "",
80 r->cpu >= 0 ? (sprintf(cpubuf, "%d", r->cpu), cpubuf) : "",
81 r->tid ? "--tid " : "",
82 r->tid ? (sprintf(tidbuf, "%d", r->tid), tidbuf) : "",
83 extra_format,
84 rstype == A_ASM ? "-F +insn --xed" :
85 rstype == A_SOURCE ? "-F +srcline,+srccode" : "",
86 symbol_conf.inline_name ? "--inline" : "",
87 "--show-lost-events ",
88 r->tid ? "--show-switch-events --show-task-events " : "",
89 tsample) < 0)
90 return -1;
91 run_script(cmd);
92 free(cmd);
93 return 0;
94}
diff --git a/tools/perf/ui/browsers/scripts.c b/tools/perf/ui/browsers/scripts.c
index 9e5f87558af6..cdba58447b85 100644
--- a/tools/perf/ui/browsers/scripts.c
+++ b/tools/perf/ui/browsers/scripts.c
@@ -125,7 +125,7 @@ out:
125 return ret; 125 return ret;
126} 126}
127 127
128static void run_script(char *cmd) 128void run_script(char *cmd)
129{ 129{
130 pr_debug("Running %s\n", cmd); 130 pr_debug("Running %s\n", cmd);
131 SLang_reset_tty(); 131 SLang_reset_tty();
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index 34c0f00c68d1..1f230285d78a 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -436,6 +436,13 @@ static int hist_entry__init(struct hist_entry *he,
436 goto err_rawdata; 436 goto err_rawdata;
437 } 437 }
438 438
439 if (symbol_conf.res_sample) {
440 he->res_samples = calloc(sizeof(struct res_sample),
441 symbol_conf.res_sample);
442 if (!he->res_samples)
443 goto err_srcline;
444 }
445
439 INIT_LIST_HEAD(&he->pairs.node); 446 INIT_LIST_HEAD(&he->pairs.node);
440 thread__get(he->thread); 447 thread__get(he->thread);
441 he->hroot_in = RB_ROOT_CACHED; 448 he->hroot_in = RB_ROOT_CACHED;
@@ -446,6 +453,9 @@ static int hist_entry__init(struct hist_entry *he,
446 453
447 return 0; 454 return 0;
448 455
456err_srcline:
457 free(he->srcline);
458
449err_rawdata: 459err_rawdata:
450 free(he->raw_data); 460 free(he->raw_data);
451 461
@@ -603,6 +613,32 @@ out:
603 return he; 613 return he;
604} 614}
605 615
616static unsigned random_max(unsigned high)
617{
618 unsigned thresh = -high % high;
619 for (;;) {
620 unsigned r = random();
621 if (r >= thresh)
622 return r % high;
623 }
624}
625
626static void hists__res_sample(struct hist_entry *he, struct perf_sample *sample)
627{
628 struct res_sample *r;
629 int j;
630
631 if (he->num_res < symbol_conf.res_sample) {
632 j = he->num_res++;
633 } else {
634 j = random_max(symbol_conf.res_sample);
635 }
636 r = &he->res_samples[j];
637 r->time = sample->time;
638 r->cpu = sample->cpu;
639 r->tid = sample->tid;
640}
641
606static struct hist_entry* 642static struct hist_entry*
607__hists__add_entry(struct hists *hists, 643__hists__add_entry(struct hists *hists,
608 struct addr_location *al, 644 struct addr_location *al,
@@ -650,6 +686,8 @@ __hists__add_entry(struct hists *hists,
650 686
651 if (!hists->has_callchains && he && he->callchain_size != 0) 687 if (!hists->has_callchains && he && he->callchain_size != 0)
652 hists->has_callchains = true; 688 hists->has_callchains = true;
689 if (he && symbol_conf.res_sample)
690 hists__res_sample(he, sample);
653 return he; 691 return he;
654} 692}
655 693
@@ -1173,6 +1211,7 @@ void hist_entry__delete(struct hist_entry *he)
1173 mem_info__zput(he->mem_info); 1211 mem_info__zput(he->mem_info);
1174 } 1212 }
1175 1213
1214 zfree(&he->res_samples);
1176 zfree(&he->stat_acc); 1215 zfree(&he->stat_acc);
1177 free_srcline(he->srcline); 1216 free_srcline(he->srcline);
1178 if (he->srcfile && he->srcfile[0]) 1217 if (he->srcfile && he->srcfile[0])
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h
index 2113a6639cea..76ff6c6d03b8 100644
--- a/tools/perf/util/hist.h
+++ b/tools/perf/util/hist.h
@@ -433,6 +433,13 @@ struct hist_browser_timer {
433}; 433};
434 434
435struct annotation_options; 435struct annotation_options;
436struct res_sample;
437
438enum rstype {
439 A_NORMAL,
440 A_ASM,
441 A_SOURCE
442};
436 443
437#ifdef HAVE_SLANG_SUPPORT 444#ifdef HAVE_SLANG_SUPPORT
438#include "../ui/keysyms.h" 445#include "../ui/keysyms.h"
@@ -454,6 +461,11 @@ int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
454 struct annotation_options *annotation_options); 461 struct annotation_options *annotation_options);
455 462
456int script_browse(const char *script_opt, struct perf_evsel *evsel); 463int script_browse(const char *script_opt, struct perf_evsel *evsel);
464
465void run_script(char *cmd);
466int res_sample_browse(struct res_sample *res_samples, int num_res,
467 struct perf_evsel *evsel, enum rstype rstype);
468void res_sample_init(void);
457#else 469#else
458static inline 470static inline
459int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __maybe_unused, 471int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __maybe_unused,
@@ -488,6 +500,16 @@ static inline int script_browse(const char *script_opt __maybe_unused,
488 return 0; 500 return 0;
489} 501}
490 502
503static inline int res_sample_browse(struct res_sample *res_samples __maybe_unused,
504 int num_res __maybe_unused,
505 struct perf_evsel *evsel __maybe_unused,
506 enum rstype rstype __maybe_unused)
507{
508 return 0;
509}
510
511static inline void res_sample_init(void) {}
512
491#define K_LEFT -1000 513#define K_LEFT -1000
492#define K_RIGHT -2000 514#define K_RIGHT -2000
493#define K_SWITCH_INPUT_DATA -3000 515#define K_SWITCH_INPUT_DATA -3000
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h
index 19dceb7f6145..bb9442ab7a0c 100644
--- a/tools/perf/util/sort.h
+++ b/tools/perf/util/sort.h
@@ -47,6 +47,12 @@ extern struct sort_entry sort_srcline;
47extern enum sort_type sort__first_dimension; 47extern enum sort_type sort__first_dimension;
48extern const char default_mem_sort_order[]; 48extern const char default_mem_sort_order[];
49 49
50struct res_sample {
51 u64 time;
52 int cpu;
53 int tid;
54};
55
50struct he_stat { 56struct he_stat {
51 u64 period; 57 u64 period;
52 u64 period_sys; 58 u64 period_sys;
@@ -140,6 +146,8 @@ struct hist_entry {
140 struct mem_info *mem_info; 146 struct mem_info *mem_info;
141 void *raw_data; 147 void *raw_data;
142 u32 raw_size; 148 u32 raw_size;
149 int num_res;
150 struct res_sample *res_samples;
143 void *trace_output; 151 void *trace_output;
144 struct perf_hpp_list *hpp_list; 152 struct perf_hpp_list *hpp_list;
145 struct hist_entry *parent_he; 153 struct hist_entry *parent_he;
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 6b73a0eeb6a1..58442ca5e3c4 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -51,6 +51,7 @@ struct symbol_conf symbol_conf = {
51 .symfs = "", 51 .symfs = "",
52 .event_group = true, 52 .event_group = true,
53 .inline_name = true, 53 .inline_name = true,
54 .res_sample = 0,
54}; 55};
55 56
56static enum dso_binary_type binary_type_symtab[] = { 57static enum dso_binary_type binary_type_symtab[] = {
diff --git a/tools/perf/util/symbol_conf.h b/tools/perf/util/symbol_conf.h
index a5684a71b78e..6c55fa6fccec 100644
--- a/tools/perf/util/symbol_conf.h
+++ b/tools/perf/util/symbol_conf.h
@@ -68,6 +68,7 @@ struct symbol_conf {
68 struct intlist *pid_list, 68 struct intlist *pid_list,
69 *tid_list; 69 *tid_list;
70 const char *symfs; 70 const char *symfs;
71 int res_sample;
71}; 72};
72 73
73extern struct symbol_conf symbol_conf; 74extern struct symbol_conf symbol_conf;