diff options
author | Ingo Molnar <mingo@elte.hu> | 2011-10-10 01:10:05 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2011-10-10 01:10:05 -0400 |
commit | 7588badafcd762034aa962ec86b82cacd4f42f74 (patch) | |
tree | 3fbfeb46f771e73e4269d655c24212b488916f6a | |
parent | d48b0e173715f678698d3678fefd40f2893ce798 (diff) | |
parent | 64c6f0c7f8db449e05ee16e35a7083df69addd1d (diff) |
Merge branch 'perf/core' of git://github.com/acmel/linux into perf/core
35 files changed, 2124 insertions, 804 deletions
diff --git a/tools/perf/Documentation/perf-annotate.txt b/tools/perf/Documentation/perf-annotate.txt index 0102d83600db..fe6762ed56bd 100644 --- a/tools/perf/Documentation/perf-annotate.txt +++ b/tools/perf/Documentation/perf-annotate.txt | |||
@@ -73,8 +73,7 @@ OPTIONS | |||
73 | CPUs. | 73 | CPUs. |
74 | 74 | ||
75 | --asm-raw:: | 75 | --asm-raw:: |
76 | Show raw instruction encoding of assembly instructions. They | 76 | Show raw instruction encoding of assembly instructions. |
77 | are displayed by default, disable with --no-asm-raw. | ||
78 | 77 | ||
79 | --source:: | 78 | --source:: |
80 | Interleave source code with assembly code. Enabled by default, | 79 | Interleave source code with assembly code. Enabled by default, |
diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index 6349b6c0e3ec..212f24d672e1 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt | |||
@@ -137,6 +137,21 @@ OPTIONS | |||
137 | -M:: | 137 | -M:: |
138 | --disassembler-style=:: Set disassembler style for objdump. | 138 | --disassembler-style=:: Set disassembler style for objdump. |
139 | 139 | ||
140 | --source:: | ||
141 | Interleave source code with assembly code. Enabled by default, | ||
142 | disable with --no-source. | ||
143 | |||
144 | --asm-raw:: | ||
145 | Show raw instruction encoding of assembly instructions. | ||
146 | |||
147 | --show-total-period:: Show a column with the sum of periods. | ||
148 | |||
149 | -I:: | ||
150 | --show-info:: | ||
151 | Display extended information about the perf.data file. This adds | ||
152 | information which may be very large and thus may clutter the display. | ||
153 | It currently includes: cpu and numa topology of the host system. | ||
154 | |||
140 | SEE ALSO | 155 | SEE ALSO |
141 | -------- | 156 | -------- |
142 | linkperf:perf-stat[1] | 157 | linkperf:perf-stat[1], linkperf:perf-annotate[1] |
diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt index db017867d9e8..dec87ecb530e 100644 --- a/tools/perf/Documentation/perf-script.txt +++ b/tools/perf/Documentation/perf-script.txt | |||
@@ -188,6 +188,13 @@ OPTIONS | |||
188 | CPUs are specified with -: 0-2. Default is to report samples on all | 188 | CPUs are specified with -: 0-2. Default is to report samples on all |
189 | CPUs. | 189 | CPUs. |
190 | 190 | ||
191 | -I:: | ||
192 | --show-info:: | ||
193 | Display extended information about the perf.data file. This adds | ||
194 | information which may be very large and thus may clutter the display. | ||
195 | It currently includes: cpu and numa topology of the host system. | ||
196 | It can only be used with the perf script report mode. | ||
197 | |||
191 | SEE ALSO | 198 | SEE ALSO |
192 | -------- | 199 | -------- |
193 | linkperf:perf-record[1], linkperf:perf-script-perl[1], | 200 | linkperf:perf-record[1], linkperf:perf-script-perl[1], |
diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt index f6eb1cdafb77..b1a5bbbfebef 100644 --- a/tools/perf/Documentation/perf-top.txt +++ b/tools/perf/Documentation/perf-top.txt | |||
@@ -106,6 +106,51 @@ Default is to monitor all CPUS. | |||
106 | --zero:: | 106 | --zero:: |
107 | Zero history across display updates. | 107 | Zero history across display updates. |
108 | 108 | ||
109 | -s:: | ||
110 | --sort:: | ||
111 | Sort by key(s): pid, comm, dso, symbol, parent | ||
112 | |||
113 | -n:: | ||
114 | --show-nr-samples:: | ||
115 | Show a column with the number of samples. | ||
116 | |||
117 | --show-total-period:: | ||
118 | Show a column with the sum of periods. | ||
119 | |||
120 | --dsos:: | ||
121 | Only consider symbols in these dsos. | ||
122 | |||
123 | --comms:: | ||
124 | Only consider symbols in these comms. | ||
125 | |||
126 | --symbols:: | ||
127 | Only consider these symbols. | ||
128 | |||
129 | -M:: | ||
130 | --disassembler-style=:: Set disassembler style for objdump. | ||
131 | |||
132 | --source:: | ||
133 | Interleave source code with assembly code. Enabled by default, | ||
134 | disable with --no-source. | ||
135 | |||
136 | --asm-raw:: | ||
137 | Show raw instruction encoding of assembly instructions. | ||
138 | |||
139 | -G [type,min,order]:: | ||
140 | --call-graph:: | ||
141 | Display call chains using type, min percent threshold and order. | ||
142 | type can be either: | ||
143 | - flat: single column, linear exposure of call chains. | ||
144 | - graph: use a graph tree, displaying absolute overhead rates. | ||
145 | - fractal: like graph, but displays relative rates. Each branch of | ||
146 | the tree is considered as a new profiled object. | ||
147 | |||
148 | order can be either: | ||
149 | - callee: callee based call graph. | ||
150 | - caller: inverted caller based call graph. | ||
151 | |||
152 | Default: fractal,0.5,callee. | ||
153 | |||
109 | INTERACTIVE PROMPTING KEYS | 154 | INTERACTIVE PROMPTING KEYS |
110 | -------------------------- | 155 | -------------------------- |
111 | 156 | ||
@@ -130,9 +175,6 @@ INTERACTIVE PROMPTING KEYS | |||
130 | [S]:: | 175 | [S]:: |
131 | Stop annotation, return to full profile display. | 176 | Stop annotation, return to full profile display. |
132 | 177 | ||
133 | [w]:: | ||
134 | Toggle between weighted sum and individual count[E]r profile. | ||
135 | |||
136 | [z]:: | 178 | [z]:: |
137 | Toggle event count zeroing across display updates. | 179 | Toggle event count zeroing across display updates. |
138 | 180 | ||
diff --git a/tools/perf/Makefile b/tools/perf/Makefile index e9d5c271db69..37fe93019bc6 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile | |||
@@ -466,7 +466,6 @@ else | |||
466 | LIB_OBJS += $(OUTPUT)util/ui/browsers/annotate.o | 466 | LIB_OBJS += $(OUTPUT)util/ui/browsers/annotate.o |
467 | LIB_OBJS += $(OUTPUT)util/ui/browsers/hists.o | 467 | LIB_OBJS += $(OUTPUT)util/ui/browsers/hists.o |
468 | LIB_OBJS += $(OUTPUT)util/ui/browsers/map.o | 468 | LIB_OBJS += $(OUTPUT)util/ui/browsers/map.o |
469 | LIB_OBJS += $(OUTPUT)util/ui/browsers/top.o | ||
470 | LIB_OBJS += $(OUTPUT)util/ui/helpline.o | 469 | LIB_OBJS += $(OUTPUT)util/ui/helpline.o |
471 | LIB_OBJS += $(OUTPUT)util/ui/progress.o | 470 | LIB_OBJS += $(OUTPUT)util/ui/progress.o |
472 | LIB_OBJS += $(OUTPUT)util/ui/util.o | 471 | LIB_OBJS += $(OUTPUT)util/ui/util.o |
@@ -729,9 +728,6 @@ $(OUTPUT)util/ui/browser.o: util/ui/browser.c $(OUTPUT)PERF-CFLAGS | |||
729 | $(OUTPUT)util/ui/browsers/annotate.o: util/ui/browsers/annotate.c $(OUTPUT)PERF-CFLAGS | 728 | $(OUTPUT)util/ui/browsers/annotate.o: util/ui/browsers/annotate.c $(OUTPUT)PERF-CFLAGS |
730 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< | 729 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< |
731 | 730 | ||
732 | $(OUTPUT)util/ui/browsers/top.o: util/ui/browsers/top.c $(OUTPUT)PERF-CFLAGS | ||
733 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< | ||
734 | |||
735 | $(OUTPUT)util/ui/browsers/hists.o: util/ui/browsers/hists.c $(OUTPUT)PERF-CFLAGS | 731 | $(OUTPUT)util/ui/browsers/hists.o: util/ui/browsers/hists.c $(OUTPUT)PERF-CFLAGS |
736 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< | 732 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< |
737 | 733 | ||
diff --git a/tools/perf/arch/powerpc/Makefile b/tools/perf/arch/powerpc/Makefile index 15130b50dfe3..744e629797be 100644 --- a/tools/perf/arch/powerpc/Makefile +++ b/tools/perf/arch/powerpc/Makefile | |||
@@ -2,3 +2,4 @@ ifndef NO_DWARF | |||
2 | PERF_HAVE_DWARF_REGS := 1 | 2 | PERF_HAVE_DWARF_REGS := 1 |
3 | LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o | 3 | LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o |
4 | endif | 4 | endif |
5 | LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/header.o | ||
diff --git a/tools/perf/arch/powerpc/util/header.c b/tools/perf/arch/powerpc/util/header.c new file mode 100644 index 000000000000..eba80c292945 --- /dev/null +++ b/tools/perf/arch/powerpc/util/header.c | |||
@@ -0,0 +1,36 @@ | |||
1 | #include <sys/types.h> | ||
2 | #include <unistd.h> | ||
3 | #include <stdio.h> | ||
4 | #include <stdlib.h> | ||
5 | #include <string.h> | ||
6 | |||
7 | #include "../../util/header.h" | ||
8 | |||
9 | #define __stringify_1(x) #x | ||
10 | #define __stringify(x) __stringify_1(x) | ||
11 | |||
12 | #define mfspr(rn) ({unsigned long rval; \ | ||
13 | asm volatile("mfspr %0," __stringify(rn) \ | ||
14 | : "=r" (rval)); rval; }) | ||
15 | |||
16 | #define SPRN_PVR 0x11F /* Processor Version Register */ | ||
17 | #define PVR_VER(pvr) (((pvr) >> 16) & 0xFFFF) /* Version field */ | ||
18 | #define PVR_REV(pvr) (((pvr) >> 0) & 0xFFFF) /* Revison field */ | ||
19 | |||
20 | int | ||
21 | get_cpuid(char *buffer, size_t sz) | ||
22 | { | ||
23 | unsigned long pvr; | ||
24 | int nb; | ||
25 | |||
26 | pvr = mfspr(SPRN_PVR); | ||
27 | |||
28 | nb = snprintf(buffer, sz, "%lu,%lu$", PVR_VER(pvr), PVR_REV(pvr)); | ||
29 | |||
30 | /* look for end marker to ensure the entire data fit */ | ||
31 | if (strchr(buffer, '$')) { | ||
32 | buffer[nb-1] = '\0'; | ||
33 | return 0; | ||
34 | } | ||
35 | return -1; | ||
36 | } | ||
diff --git a/tools/perf/arch/x86/Makefile b/tools/perf/arch/x86/Makefile index 15130b50dfe3..744e629797be 100644 --- a/tools/perf/arch/x86/Makefile +++ b/tools/perf/arch/x86/Makefile | |||
@@ -2,3 +2,4 @@ ifndef NO_DWARF | |||
2 | PERF_HAVE_DWARF_REGS := 1 | 2 | PERF_HAVE_DWARF_REGS := 1 |
3 | LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o | 3 | LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o |
4 | endif | 4 | endif |
5 | LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/header.o | ||
diff --git a/tools/perf/arch/x86/util/header.c b/tools/perf/arch/x86/util/header.c new file mode 100644 index 000000000000..f94006068d2b --- /dev/null +++ b/tools/perf/arch/x86/util/header.c | |||
@@ -0,0 +1,59 @@ | |||
1 | #include <sys/types.h> | ||
2 | #include <unistd.h> | ||
3 | #include <stdio.h> | ||
4 | #include <stdlib.h> | ||
5 | #include <string.h> | ||
6 | |||
7 | #include "../../util/header.h" | ||
8 | |||
9 | static inline void | ||
10 | cpuid(unsigned int op, unsigned int *a, unsigned int *b, unsigned int *c, | ||
11 | unsigned int *d) | ||
12 | { | ||
13 | __asm__ __volatile__ (".byte 0x53\n\tcpuid\n\t" | ||
14 | "movl %%ebx, %%esi\n\t.byte 0x5b" | ||
15 | : "=a" (*a), | ||
16 | "=S" (*b), | ||
17 | "=c" (*c), | ||
18 | "=d" (*d) | ||
19 | : "a" (op)); | ||
20 | } | ||
21 | |||
22 | int | ||
23 | get_cpuid(char *buffer, size_t sz) | ||
24 | { | ||
25 | unsigned int a, b, c, d, lvl; | ||
26 | int family = -1, model = -1, step = -1; | ||
27 | int nb; | ||
28 | char vendor[16]; | ||
29 | |||
30 | cpuid(0, &lvl, &b, &c, &d); | ||
31 | strncpy(&vendor[0], (char *)(&b), 4); | ||
32 | strncpy(&vendor[4], (char *)(&d), 4); | ||
33 | strncpy(&vendor[8], (char *)(&c), 4); | ||
34 | vendor[12] = '\0'; | ||
35 | |||
36 | if (lvl >= 1) { | ||
37 | cpuid(1, &a, &b, &c, &d); | ||
38 | |||
39 | family = (a >> 8) & 0xf; /* bits 11 - 8 */ | ||
40 | model = (a >> 4) & 0xf; /* Bits 7 - 4 */ | ||
41 | step = a & 0xf; | ||
42 | |||
43 | /* extended family */ | ||
44 | if (family == 0xf) | ||
45 | family += (a >> 20) & 0xff; | ||
46 | |||
47 | /* extended model */ | ||
48 | if (family >= 0x6) | ||
49 | model += ((a >> 16) & 0xf) << 4; | ||
50 | } | ||
51 | nb = snprintf(buffer, sz, "%s,%u,%u,%u$", vendor, family, model, step); | ||
52 | |||
53 | /* look for end marker to ensure the entire data fit */ | ||
54 | if (strchr(buffer, '$')) { | ||
55 | buffer[nb-1] = '\0'; | ||
56 | return 0; | ||
57 | } | ||
58 | return -1; | ||
59 | } | ||
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index cf68819f7453..3ea764a78053 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c | |||
@@ -114,7 +114,8 @@ static int hist_entry__tty_annotate(struct hist_entry *he, int evidx) | |||
114 | print_line, full_paths, 0, 0); | 114 | print_line, full_paths, 0, 0); |
115 | } | 115 | } |
116 | 116 | ||
117 | static void hists__find_annotations(struct hists *self, int evidx) | 117 | static void hists__find_annotations(struct hists *self, int evidx, |
118 | int nr_events) | ||
118 | { | 119 | { |
119 | struct rb_node *nd = rb_first(&self->entries), *next; | 120 | struct rb_node *nd = rb_first(&self->entries), *next; |
120 | int key = KEY_RIGHT; | 121 | int key = KEY_RIGHT; |
@@ -137,7 +138,8 @@ find_next: | |||
137 | } | 138 | } |
138 | 139 | ||
139 | if (use_browser > 0) { | 140 | if (use_browser > 0) { |
140 | key = hist_entry__tui_annotate(he, evidx); | 141 | key = hist_entry__tui_annotate(he, evidx, nr_events, |
142 | NULL, NULL, 0); | ||
141 | switch (key) { | 143 | switch (key) { |
142 | case KEY_RIGHT: | 144 | case KEY_RIGHT: |
143 | next = rb_next(nd); | 145 | next = rb_next(nd); |
@@ -215,7 +217,8 @@ static int __cmd_annotate(void) | |||
215 | total_nr_samples += nr_samples; | 217 | total_nr_samples += nr_samples; |
216 | hists__collapse_resort(hists); | 218 | hists__collapse_resort(hists); |
217 | hists__output_resort(hists); | 219 | hists__output_resort(hists); |
218 | hists__find_annotations(hists, pos->idx); | 220 | hists__find_annotations(hists, pos->idx, |
221 | session->evlist->nr_entries); | ||
219 | } | 222 | } |
220 | } | 223 | } |
221 | 224 | ||
@@ -269,9 +272,9 @@ static const struct option options[] = { | |||
269 | OPT_STRING('c', "cpu", &cpu_list, "cpu", "list of cpus to profile"), | 272 | OPT_STRING('c', "cpu", &cpu_list, "cpu", "list of cpus to profile"), |
270 | OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", | 273 | OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", |
271 | "Look for files with symbols relative to this directory"), | 274 | "Look for files with symbols relative to this directory"), |
272 | OPT_BOOLEAN('0', "source", &symbol_conf.annotate_src, | 275 | OPT_BOOLEAN(0, "source", &symbol_conf.annotate_src, |
273 | "Interleave source code with assembly code (default)"), | 276 | "Interleave source code with assembly code (default)"), |
274 | OPT_BOOLEAN('0', "asm-raw", &symbol_conf.annotate_asm_raw, | 277 | OPT_BOOLEAN(0, "asm-raw", &symbol_conf.annotate_asm_raw, |
275 | "Display raw encoding of assembly instructions (default)"), | 278 | "Display raw encoding of assembly instructions (default)"), |
276 | OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style", | 279 | OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style", |
277 | "Specify disassembler style (e.g. -M intel for intel syntax)"), | 280 | "Specify disassembler style (e.g. -M intel for intel syntax)"), |
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index e8219990f8b8..b39f3a1ee7dc 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c | |||
@@ -162,7 +162,7 @@ static int __cmd_diff(void) | |||
162 | 162 | ||
163 | hists__match(&session[0]->hists, &session[1]->hists); | 163 | hists__match(&session[0]->hists, &session[1]->hists); |
164 | hists__fprintf(&session[1]->hists, &session[0]->hists, | 164 | hists__fprintf(&session[1]->hists, &session[0]->hists, |
165 | show_displacement, stdout); | 165 | show_displacement, true, 0, 0, stdout); |
166 | out_delete: | 166 | out_delete: |
167 | for (i = 0; i < 2; ++i) | 167 | for (i = 0; i < 2; ++i) |
168 | perf_session__delete(session[i]); | 168 | perf_session__delete(session[i]); |
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index dd6467872f60..f82480fa7f27 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
@@ -529,6 +529,19 @@ static int __cmd_record(int argc, const char **argv) | |||
529 | if (have_tracepoints(&evsel_list->entries)) | 529 | if (have_tracepoints(&evsel_list->entries)) |
530 | perf_header__set_feat(&session->header, HEADER_TRACE_INFO); | 530 | perf_header__set_feat(&session->header, HEADER_TRACE_INFO); |
531 | 531 | ||
532 | perf_header__set_feat(&session->header, HEADER_HOSTNAME); | ||
533 | perf_header__set_feat(&session->header, HEADER_OSRELEASE); | ||
534 | perf_header__set_feat(&session->header, HEADER_ARCH); | ||
535 | perf_header__set_feat(&session->header, HEADER_CPUDESC); | ||
536 | perf_header__set_feat(&session->header, HEADER_NRCPUS); | ||
537 | perf_header__set_feat(&session->header, HEADER_EVENT_DESC); | ||
538 | perf_header__set_feat(&session->header, HEADER_CMDLINE); | ||
539 | perf_header__set_feat(&session->header, HEADER_VERSION); | ||
540 | perf_header__set_feat(&session->header, HEADER_CPU_TOPOLOGY); | ||
541 | perf_header__set_feat(&session->header, HEADER_TOTAL_MEM); | ||
542 | perf_header__set_feat(&session->header, HEADER_NUMA_TOPOLOGY); | ||
543 | perf_header__set_feat(&session->header, HEADER_CPUID); | ||
544 | |||
532 | /* 512 kiB: default amount of unprivileged mlocked memory */ | 545 | /* 512 kiB: default amount of unprivileged mlocked memory */ |
533 | if (mmap_pages == UINT_MAX) | 546 | if (mmap_pages == UINT_MAX) |
534 | mmap_pages = (512 * 1024) / page_size; | 547 | mmap_pages = (512 * 1024) / page_size; |
@@ -800,6 +813,8 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) | |||
800 | int err = -ENOMEM; | 813 | int err = -ENOMEM; |
801 | struct perf_evsel *pos; | 814 | struct perf_evsel *pos; |
802 | 815 | ||
816 | perf_header__set_cmdline(argc, argv); | ||
817 | |||
803 | evsel_list = perf_evlist__new(NULL, NULL); | 818 | evsel_list = perf_evlist__new(NULL, NULL); |
804 | if (evsel_list == NULL) | 819 | if (evsel_list == NULL) |
805 | return -ENOMEM; | 820 | return -ENOMEM; |
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 3d58334909a5..4d7c8340c326 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
@@ -40,6 +40,7 @@ static char const *input_name = "perf.data"; | |||
40 | static bool force, use_tui, use_stdio; | 40 | static bool force, use_tui, use_stdio; |
41 | static bool hide_unresolved; | 41 | static bool hide_unresolved; |
42 | static bool dont_use_callchains; | 42 | static bool dont_use_callchains; |
43 | static bool show_full_info; | ||
43 | 44 | ||
44 | static bool show_threads; | 45 | static bool show_threads; |
45 | static struct perf_read_values show_threads_values; | 46 | static struct perf_read_values show_threads_values; |
@@ -232,7 +233,7 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist, | |||
232 | const char *evname = event_name(pos); | 233 | const char *evname = event_name(pos); |
233 | 234 | ||
234 | hists__fprintf_nr_sample_events(hists, evname, stdout); | 235 | hists__fprintf_nr_sample_events(hists, evname, stdout); |
235 | hists__fprintf(hists, NULL, false, stdout); | 236 | hists__fprintf(hists, NULL, false, true, 0, 0, stdout); |
236 | fprintf(stdout, "\n\n"); | 237 | fprintf(stdout, "\n\n"); |
237 | } | 238 | } |
238 | 239 | ||
@@ -273,6 +274,9 @@ static int __cmd_report(void) | |||
273 | goto out_delete; | 274 | goto out_delete; |
274 | } | 275 | } |
275 | 276 | ||
277 | if (use_browser <= 0) | ||
278 | perf_session__fprintf_info(session, stdout, show_full_info); | ||
279 | |||
276 | if (show_threads) | 280 | if (show_threads) |
277 | perf_read_values_init(&show_threads_values); | 281 | perf_read_values_init(&show_threads_values); |
278 | 282 | ||
@@ -327,9 +331,10 @@ static int __cmd_report(void) | |||
327 | goto out_delete; | 331 | goto out_delete; |
328 | } | 332 | } |
329 | 333 | ||
330 | if (use_browser > 0) | 334 | if (use_browser > 0) { |
331 | perf_evlist__tui_browse_hists(session->evlist, help); | 335 | perf_evlist__tui_browse_hists(session->evlist, help, |
332 | else | 336 | NULL, NULL, 0); |
337 | } else | ||
333 | perf_evlist__tty_browse_hists(session->evlist, help); | 338 | perf_evlist__tty_browse_hists(session->evlist, help); |
334 | 339 | ||
335 | out_delete: | 340 | out_delete: |
@@ -484,8 +489,16 @@ static const struct option options[] = { | |||
484 | OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", | 489 | OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", |
485 | "Look for files with symbols relative to this directory"), | 490 | "Look for files with symbols relative to this directory"), |
486 | OPT_STRING('c', "cpu", &cpu_list, "cpu", "list of cpus to profile"), | 491 | OPT_STRING('c', "cpu", &cpu_list, "cpu", "list of cpus to profile"), |
492 | OPT_BOOLEAN('I', "show-info", &show_full_info, | ||
493 | "Display extended information about perf.data file"), | ||
494 | OPT_BOOLEAN(0, "source", &symbol_conf.annotate_src, | ||
495 | "Interleave source code with assembly code (default)"), | ||
496 | OPT_BOOLEAN(0, "asm-raw", &symbol_conf.annotate_asm_raw, | ||
497 | "Display raw encoding of assembly instructions (default)"), | ||
487 | OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style", | 498 | OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style", |
488 | "Specify disassembler style (e.g. -M intel for intel syntax)"), | 499 | "Specify disassembler style (e.g. -M intel for intel syntax)"), |
500 | OPT_BOOLEAN(0, "show-total-period", &symbol_conf.show_total_period, | ||
501 | "Show a column with the sum of periods"), | ||
489 | OPT_END() | 502 | OPT_END() |
490 | }; | 503 | }; |
491 | 504 | ||
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 09024ec2ab2e..2f62a2952269 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c | |||
@@ -22,6 +22,7 @@ static u64 last_timestamp; | |||
22 | static u64 nr_unordered; | 22 | static u64 nr_unordered; |
23 | extern const struct option record_options[]; | 23 | extern const struct option record_options[]; |
24 | static bool no_callchain; | 24 | static bool no_callchain; |
25 | static bool show_full_info; | ||
25 | static const char *cpu_list; | 26 | static const char *cpu_list; |
26 | static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); | 27 | static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); |
27 | 28 | ||
@@ -1083,7 +1084,8 @@ static const struct option options[] = { | |||
1083 | "comma separated output fields prepend with 'type:'. Valid types: hw,sw,trace,raw. Fields: comm,tid,pid,time,cpu,event,trace,ip,sym,dso,addr", | 1084 | "comma separated output fields prepend with 'type:'. Valid types: hw,sw,trace,raw. Fields: comm,tid,pid,time,cpu,event,trace,ip,sym,dso,addr", |
1084 | parse_output_fields), | 1085 | parse_output_fields), |
1085 | OPT_STRING('c', "cpu", &cpu_list, "cpu", "list of cpus to profile"), | 1086 | OPT_STRING('c', "cpu", &cpu_list, "cpu", "list of cpus to profile"), |
1086 | 1087 | OPT_BOOLEAN('I', "show-info", &show_full_info, | |
1088 | "display extended information from perf.data file"), | ||
1087 | OPT_END() | 1089 | OPT_END() |
1088 | }; | 1090 | }; |
1089 | 1091 | ||
@@ -1268,6 +1270,8 @@ int cmd_script(int argc, const char **argv, const char *prefix __used) | |||
1268 | return -1; | 1270 | return -1; |
1269 | } | 1271 | } |
1270 | 1272 | ||
1273 | perf_session__fprintf_info(session, stdout, show_full_info); | ||
1274 | |||
1271 | if (!no_callchain) | 1275 | if (!no_callchain) |
1272 | symbol_conf.use_callchain = true; | 1276 | symbol_conf.use_callchain = true; |
1273 | else | 1277 | else |
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 5ede7d7c9239..c5aebf6eb746 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
@@ -5,6 +5,7 @@ | |||
5 | * any workload, CPU or specific PID. | 5 | * any workload, CPU or specific PID. |
6 | * | 6 | * |
7 | * Copyright (C) 2008, Red Hat Inc, Ingo Molnar <mingo@redhat.com> | 7 | * Copyright (C) 2008, Red Hat Inc, Ingo Molnar <mingo@redhat.com> |
8 | * 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> | ||
8 | * | 9 | * |
9 | * Improvements and fixes by: | 10 | * Improvements and fixes by: |
10 | * | 11 | * |
@@ -36,6 +37,7 @@ | |||
36 | #include "util/parse-events.h" | 37 | #include "util/parse-events.h" |
37 | #include "util/cpumap.h" | 38 | #include "util/cpumap.h" |
38 | #include "util/xyarray.h" | 39 | #include "util/xyarray.h" |
40 | #include "util/sort.h" | ||
39 | 41 | ||
40 | #include "util/debug.h" | 42 | #include "util/debug.h" |
41 | 43 | ||
@@ -65,12 +67,8 @@ | |||
65 | static struct perf_top top = { | 67 | static struct perf_top top = { |
66 | .count_filter = 5, | 68 | .count_filter = 5, |
67 | .delay_secs = 2, | 69 | .delay_secs = 2, |
68 | .display_weighted = -1, | ||
69 | .target_pid = -1, | 70 | .target_pid = -1, |
70 | .target_tid = -1, | 71 | .target_tid = -1, |
71 | .active_symbols = LIST_HEAD_INIT(top.active_symbols), | ||
72 | .active_symbols_lock = PTHREAD_MUTEX_INITIALIZER, | ||
73 | .active_symbols_cond = PTHREAD_COND_INITIALIZER, | ||
74 | .freq = 1000, /* 1 KHz */ | 72 | .freq = 1000, /* 1 KHz */ |
75 | }; | 73 | }; |
76 | 74 | ||
@@ -78,6 +76,12 @@ static bool system_wide = false; | |||
78 | 76 | ||
79 | static bool use_tui, use_stdio; | 77 | static bool use_tui, use_stdio; |
80 | 78 | ||
79 | static bool sort_has_symbols; | ||
80 | |||
81 | static bool dont_use_callchains; | ||
82 | static char callchain_default_opt[] = "fractal,0.5,callee"; | ||
83 | |||
84 | |||
81 | static int default_interval = 0; | 85 | static int default_interval = 0; |
82 | 86 | ||
83 | static bool kptr_restrict_warned; | 87 | static bool kptr_restrict_warned; |
@@ -85,7 +89,6 @@ static bool vmlinux_warned; | |||
85 | static bool inherit = false; | 89 | static bool inherit = false; |
86 | static int realtime_prio = 0; | 90 | static int realtime_prio = 0; |
87 | static bool group = false; | 91 | static bool group = false; |
88 | static unsigned int page_size; | ||
89 | static unsigned int mmap_pages = 128; | 92 | static unsigned int mmap_pages = 128; |
90 | 93 | ||
91 | static bool dump_symtab = false; | 94 | static bool dump_symtab = false; |
@@ -93,7 +96,6 @@ static bool dump_symtab = false; | |||
93 | static struct winsize winsize; | 96 | static struct winsize winsize; |
94 | 97 | ||
95 | static const char *sym_filter = NULL; | 98 | static const char *sym_filter = NULL; |
96 | struct sym_entry *sym_filter_entry_sched = NULL; | ||
97 | static int sym_pcnt_filter = 5; | 99 | static int sym_pcnt_filter = 5; |
98 | 100 | ||
99 | /* | 101 | /* |
@@ -136,18 +138,18 @@ static void sig_winch_handler(int sig __used) | |||
136 | update_print_entries(&winsize); | 138 | update_print_entries(&winsize); |
137 | } | 139 | } |
138 | 140 | ||
139 | static int parse_source(struct sym_entry *syme) | 141 | static int parse_source(struct hist_entry *he) |
140 | { | 142 | { |
141 | struct symbol *sym; | 143 | struct symbol *sym; |
142 | struct annotation *notes; | 144 | struct annotation *notes; |
143 | struct map *map; | 145 | struct map *map; |
144 | int err = -1; | 146 | int err = -1; |
145 | 147 | ||
146 | if (!syme) | 148 | if (!he || !he->ms.sym) |
147 | return -1; | 149 | return -1; |
148 | 150 | ||
149 | sym = sym_entry__symbol(syme); | 151 | sym = he->ms.sym; |
150 | map = syme->map; | 152 | map = he->ms.map; |
151 | 153 | ||
152 | /* | 154 | /* |
153 | * We can't annotate with just /proc/kallsyms | 155 | * We can't annotate with just /proc/kallsyms |
@@ -175,53 +177,62 @@ static int parse_source(struct sym_entry *syme) | |||
175 | return err; | 177 | return err; |
176 | } | 178 | } |
177 | 179 | ||
178 | err = symbol__annotate(sym, syme->map, 0); | 180 | err = symbol__annotate(sym, map, 0); |
179 | if (err == 0) { | 181 | if (err == 0) { |
180 | out_assign: | 182 | out_assign: |
181 | top.sym_filter_entry = syme; | 183 | top.sym_filter_entry = he; |
182 | } | 184 | } |
183 | 185 | ||
184 | pthread_mutex_unlock(¬es->lock); | 186 | pthread_mutex_unlock(¬es->lock); |
185 | return err; | 187 | return err; |
186 | } | 188 | } |
187 | 189 | ||
188 | static void __zero_source_counters(struct sym_entry *syme) | 190 | static void __zero_source_counters(struct hist_entry *he) |
189 | { | 191 | { |
190 | struct symbol *sym = sym_entry__symbol(syme); | 192 | struct symbol *sym = he->ms.sym; |
191 | symbol__annotate_zero_histograms(sym); | 193 | symbol__annotate_zero_histograms(sym); |
192 | } | 194 | } |
193 | 195 | ||
194 | static void record_precise_ip(struct sym_entry *syme, struct map *map, | 196 | static void record_precise_ip(struct hist_entry *he, int counter, u64 ip) |
195 | int counter, u64 ip) | ||
196 | { | 197 | { |
197 | struct annotation *notes; | 198 | struct annotation *notes; |
198 | struct symbol *sym; | 199 | struct symbol *sym; |
199 | 200 | ||
200 | if (syme != top.sym_filter_entry) | 201 | if (he == NULL || he->ms.sym == NULL || |
202 | (he != top.sym_filter_entry && use_browser != 1)) | ||
201 | return; | 203 | return; |
202 | 204 | ||
203 | sym = sym_entry__symbol(syme); | 205 | sym = he->ms.sym; |
204 | notes = symbol__annotation(sym); | 206 | notes = symbol__annotation(sym); |
205 | 207 | ||
206 | if (pthread_mutex_trylock(¬es->lock)) | 208 | if (pthread_mutex_trylock(¬es->lock)) |
207 | return; | 209 | return; |
208 | 210 | ||
209 | ip = map->map_ip(map, ip); | 211 | if (notes->src == NULL && |
210 | symbol__inc_addr_samples(sym, map, counter, ip); | 212 | symbol__alloc_hist(sym, top.evlist->nr_entries) < 0) { |
213 | pthread_mutex_unlock(¬es->lock); | ||
214 | pr_err("Not enough memory for annotating '%s' symbol!\n", | ||
215 | sym->name); | ||
216 | sleep(1); | ||
217 | return; | ||
218 | } | ||
219 | |||
220 | ip = he->ms.map->map_ip(he->ms.map, ip); | ||
221 | symbol__inc_addr_samples(sym, he->ms.map, counter, ip); | ||
211 | 222 | ||
212 | pthread_mutex_unlock(¬es->lock); | 223 | pthread_mutex_unlock(¬es->lock); |
213 | } | 224 | } |
214 | 225 | ||
215 | static void show_details(struct sym_entry *syme) | 226 | static void show_details(struct hist_entry *he) |
216 | { | 227 | { |
217 | struct annotation *notes; | 228 | struct annotation *notes; |
218 | struct symbol *symbol; | 229 | struct symbol *symbol; |
219 | int more; | 230 | int more; |
220 | 231 | ||
221 | if (!syme) | 232 | if (!he) |
222 | return; | 233 | return; |
223 | 234 | ||
224 | symbol = sym_entry__symbol(syme); | 235 | symbol = he->ms.sym; |
225 | notes = symbol__annotation(symbol); | 236 | notes = symbol__annotation(symbol); |
226 | 237 | ||
227 | pthread_mutex_lock(¬es->lock); | 238 | pthread_mutex_lock(¬es->lock); |
@@ -232,7 +243,7 @@ static void show_details(struct sym_entry *syme) | |||
232 | printf("Showing %s for %s\n", event_name(top.sym_evsel), symbol->name); | 243 | printf("Showing %s for %s\n", event_name(top.sym_evsel), symbol->name); |
233 | printf(" Events Pcnt (>=%d%%)\n", sym_pcnt_filter); | 244 | printf(" Events Pcnt (>=%d%%)\n", sym_pcnt_filter); |
234 | 245 | ||
235 | more = symbol__annotate_printf(symbol, syme->map, top.sym_evsel->idx, | 246 | more = symbol__annotate_printf(symbol, he->ms.map, top.sym_evsel->idx, |
236 | 0, sym_pcnt_filter, top.print_entries, 4); | 247 | 0, sym_pcnt_filter, top.print_entries, 4); |
237 | if (top.zero) | 248 | if (top.zero) |
238 | symbol__annotate_zero_histogram(symbol, top.sym_evsel->idx); | 249 | symbol__annotate_zero_histogram(symbol, top.sym_evsel->idx); |
@@ -246,21 +257,28 @@ out_unlock: | |||
246 | 257 | ||
247 | static const char CONSOLE_CLEAR[] = "[H[2J"; | 258 | static const char CONSOLE_CLEAR[] = "[H[2J"; |
248 | 259 | ||
249 | static void __list_insert_active_sym(struct sym_entry *syme) | 260 | static struct hist_entry * |
261 | perf_session__add_hist_entry(struct perf_session *session, | ||
262 | struct addr_location *al, | ||
263 | struct perf_sample *sample, | ||
264 | struct perf_evsel *evsel) | ||
250 | { | 265 | { |
251 | list_add(&syme->node, &top.active_symbols); | 266 | struct hist_entry *he; |
267 | |||
268 | he = __hists__add_entry(&evsel->hists, al, NULL, sample->period); | ||
269 | if (he == NULL) | ||
270 | return NULL; | ||
271 | |||
272 | session->hists.stats.total_period += sample->period; | ||
273 | hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); | ||
274 | return he; | ||
252 | } | 275 | } |
253 | 276 | ||
254 | static void print_sym_table(void) | 277 | static void print_sym_table(void) |
255 | { | 278 | { |
256 | char bf[160]; | 279 | char bf[160]; |
257 | int printed = 0; | 280 | int printed = 0; |
258 | struct rb_node *nd; | ||
259 | struct sym_entry *syme; | ||
260 | struct rb_root tmp = RB_ROOT; | ||
261 | const int win_width = winsize.ws_col - 1; | 281 | const int win_width = winsize.ws_col - 1; |
262 | int sym_width, dso_width, dso_short_width; | ||
263 | float sum_ksamples = perf_top__decay_samples(&top, &tmp); | ||
264 | 282 | ||
265 | puts(CONSOLE_CLEAR); | 283 | puts(CONSOLE_CLEAR); |
266 | 284 | ||
@@ -276,6 +294,7 @@ static void print_sym_table(void) | |||
276 | color_fprintf(stdout, PERF_COLOR_RED, "WARNING:"); | 294 | color_fprintf(stdout, PERF_COLOR_RED, "WARNING:"); |
277 | printf(" LOST %" PRIu64 " events, Check IO/CPU overload\n", | 295 | printf(" LOST %" PRIu64 " events, Check IO/CPU overload\n", |
278 | top.total_lost_warned); | 296 | top.total_lost_warned); |
297 | ++printed; | ||
279 | } | 298 | } |
280 | 299 | ||
281 | if (top.sym_filter_entry) { | 300 | if (top.sym_filter_entry) { |
@@ -283,58 +302,13 @@ static void print_sym_table(void) | |||
283 | return; | 302 | return; |
284 | } | 303 | } |
285 | 304 | ||
286 | perf_top__find_widths(&top, &tmp, &dso_width, &dso_short_width, | 305 | hists__collapse_resort_threaded(&top.sym_evsel->hists); |
287 | &sym_width); | 306 | hists__output_resort_threaded(&top.sym_evsel->hists); |
288 | 307 | hists__decay_entries(&top.sym_evsel->hists); | |
289 | if (sym_width + dso_width > winsize.ws_col - 29) { | 308 | hists__output_recalc_col_len(&top.sym_evsel->hists, winsize.ws_row - 3); |
290 | dso_width = dso_short_width; | ||
291 | if (sym_width + dso_width > winsize.ws_col - 29) | ||
292 | sym_width = winsize.ws_col - dso_width - 29; | ||
293 | } | ||
294 | putchar('\n'); | 309 | putchar('\n'); |
295 | if (top.evlist->nr_entries == 1) | 310 | hists__fprintf(&top.sym_evsel->hists, NULL, false, false, |
296 | printf(" samples pcnt"); | 311 | winsize.ws_row - 4 - printed, win_width, stdout); |
297 | else | ||
298 | printf(" weight samples pcnt"); | ||
299 | |||
300 | if (verbose) | ||
301 | printf(" RIP "); | ||
302 | printf(" %-*.*s DSO\n", sym_width, sym_width, "function"); | ||
303 | printf(" %s _______ _____", | ||
304 | top.evlist->nr_entries == 1 ? " " : "______"); | ||
305 | if (verbose) | ||
306 | printf(" ________________"); | ||
307 | printf(" %-*.*s", sym_width, sym_width, graph_line); | ||
308 | printf(" %-*.*s", dso_width, dso_width, graph_line); | ||
309 | puts("\n"); | ||
310 | |||
311 | for (nd = rb_first(&tmp); nd; nd = rb_next(nd)) { | ||
312 | struct symbol *sym; | ||
313 | double pcnt; | ||
314 | |||
315 | syme = rb_entry(nd, struct sym_entry, rb_node); | ||
316 | sym = sym_entry__symbol(syme); | ||
317 | if (++printed > top.print_entries || | ||
318 | (int)syme->snap_count < top.count_filter) | ||
319 | continue; | ||
320 | |||
321 | pcnt = 100.0 - (100.0 * ((sum_ksamples - syme->snap_count) / | ||
322 | sum_ksamples)); | ||
323 | |||
324 | if (top.evlist->nr_entries == 1 || !top.display_weighted) | ||
325 | printf("%20.2f ", syme->weight); | ||
326 | else | ||
327 | printf("%9.1f %10ld ", syme->weight, syme->snap_count); | ||
328 | |||
329 | percent_color_fprintf(stdout, "%4.1f%%", pcnt); | ||
330 | if (verbose) | ||
331 | printf(" %016" PRIx64, sym->start); | ||
332 | printf(" %-*.*s", sym_width, sym_width, sym->name); | ||
333 | printf(" %-*.*s\n", dso_width, dso_width, | ||
334 | dso_width >= syme->map->dso->long_name_len ? | ||
335 | syme->map->dso->long_name : | ||
336 | syme->map->dso->short_name); | ||
337 | } | ||
338 | } | 312 | } |
339 | 313 | ||
340 | static void prompt_integer(int *target, const char *msg) | 314 | static void prompt_integer(int *target, const char *msg) |
@@ -372,10 +346,11 @@ static void prompt_percent(int *target, const char *msg) | |||
372 | *target = tmp; | 346 | *target = tmp; |
373 | } | 347 | } |
374 | 348 | ||
375 | static void prompt_symbol(struct sym_entry **target, const char *msg) | 349 | static void prompt_symbol(struct hist_entry **target, const char *msg) |
376 | { | 350 | { |
377 | char *buf = malloc(0), *p; | 351 | char *buf = malloc(0), *p; |
378 | struct sym_entry *syme = *target, *n, *found = NULL; | 352 | struct hist_entry *syme = *target, *n, *found = NULL; |
353 | struct rb_node *next; | ||
379 | size_t dummy = 0; | 354 | size_t dummy = 0; |
380 | 355 | ||
381 | /* zero counters of active symbol */ | 356 | /* zero counters of active symbol */ |
@@ -392,17 +367,14 @@ static void prompt_symbol(struct sym_entry **target, const char *msg) | |||
392 | if (p) | 367 | if (p) |
393 | *p = 0; | 368 | *p = 0; |
394 | 369 | ||
395 | pthread_mutex_lock(&top.active_symbols_lock); | 370 | next = rb_first(&top.sym_evsel->hists.entries); |
396 | syme = list_entry(top.active_symbols.next, struct sym_entry, node); | 371 | while (next) { |
397 | pthread_mutex_unlock(&top.active_symbols_lock); | 372 | n = rb_entry(next, struct hist_entry, rb_node); |
398 | 373 | if (n->ms.sym && !strcmp(buf, n->ms.sym->name)) { | |
399 | list_for_each_entry_safe_from(syme, n, &top.active_symbols, node) { | 374 | found = n; |
400 | struct symbol *sym = sym_entry__symbol(syme); | ||
401 | |||
402 | if (!strcmp(buf, sym->name)) { | ||
403 | found = syme; | ||
404 | break; | 375 | break; |
405 | } | 376 | } |
377 | next = rb_next(&n->rb_node); | ||
406 | } | 378 | } |
407 | 379 | ||
408 | if (!found) { | 380 | if (!found) { |
@@ -421,7 +393,7 @@ static void print_mapped_keys(void) | |||
421 | char *name = NULL; | 393 | char *name = NULL; |
422 | 394 | ||
423 | if (top.sym_filter_entry) { | 395 | if (top.sym_filter_entry) { |
424 | struct symbol *sym = sym_entry__symbol(top.sym_filter_entry); | 396 | struct symbol *sym = top.sym_filter_entry->ms.sym; |
425 | name = sym->name; | 397 | name = sym->name; |
426 | } | 398 | } |
427 | 399 | ||
@@ -438,9 +410,6 @@ static void print_mapped_keys(void) | |||
438 | fprintf(stdout, "\t[s] annotate symbol. \t(%s)\n", name?: "NULL"); | 410 | fprintf(stdout, "\t[s] annotate symbol. \t(%s)\n", name?: "NULL"); |
439 | fprintf(stdout, "\t[S] stop annotation.\n"); | 411 | fprintf(stdout, "\t[S] stop annotation.\n"); |
440 | 412 | ||
441 | if (top.evlist->nr_entries > 1) | ||
442 | fprintf(stdout, "\t[w] toggle display weighted/count[E]r. \t(%d)\n", top.display_weighted ? 1 : 0); | ||
443 | |||
444 | fprintf(stdout, | 413 | fprintf(stdout, |
445 | "\t[K] hide kernel_symbols symbols. \t(%s)\n", | 414 | "\t[K] hide kernel_symbols symbols. \t(%s)\n", |
446 | top.hide_kernel_symbols ? "yes" : "no"); | 415 | top.hide_kernel_symbols ? "yes" : "no"); |
@@ -467,8 +436,6 @@ static int key_mapped(int c) | |||
467 | case 'S': | 436 | case 'S': |
468 | return 1; | 437 | return 1; |
469 | case 'E': | 438 | case 'E': |
470 | case 'w': | ||
471 | return top.evlist->nr_entries > 1 ? 1 : 0; | ||
472 | default: | 439 | default: |
473 | break; | 440 | break; |
474 | } | 441 | } |
@@ -561,7 +528,7 @@ static void handle_keypress(int c) | |||
561 | if (!top.sym_filter_entry) | 528 | if (!top.sym_filter_entry) |
562 | break; | 529 | break; |
563 | else { | 530 | else { |
564 | struct sym_entry *syme = top.sym_filter_entry; | 531 | struct hist_entry *syme = top.sym_filter_entry; |
565 | 532 | ||
566 | top.sym_filter_entry = NULL; | 533 | top.sym_filter_entry = NULL; |
567 | __zero_source_counters(syme); | 534 | __zero_source_counters(syme); |
@@ -570,9 +537,6 @@ static void handle_keypress(int c) | |||
570 | case 'U': | 537 | case 'U': |
571 | top.hide_user_symbols = !top.hide_user_symbols; | 538 | top.hide_user_symbols = !top.hide_user_symbols; |
572 | break; | 539 | break; |
573 | case 'w': | ||
574 | top.display_weighted = ~top.display_weighted; | ||
575 | break; | ||
576 | case 'z': | 540 | case 'z': |
577 | top.zero = !top.zero; | 541 | top.zero = !top.zero; |
578 | break; | 542 | break; |
@@ -581,19 +545,29 @@ static void handle_keypress(int c) | |||
581 | } | 545 | } |
582 | } | 546 | } |
583 | 547 | ||
548 | static void perf_top__sort_new_samples(void *arg) | ||
549 | { | ||
550 | struct perf_top *t = arg; | ||
551 | perf_top__reset_sample_counters(t); | ||
552 | |||
553 | if (t->evlist->selected != NULL) | ||
554 | t->sym_evsel = t->evlist->selected; | ||
555 | |||
556 | hists__collapse_resort_threaded(&t->sym_evsel->hists); | ||
557 | hists__output_resort_threaded(&t->sym_evsel->hists); | ||
558 | hists__decay_entries(&t->sym_evsel->hists); | ||
559 | hists__output_recalc_col_len(&t->sym_evsel->hists, winsize.ws_row - 3); | ||
560 | } | ||
561 | |||
584 | static void *display_thread_tui(void *arg __used) | 562 | static void *display_thread_tui(void *arg __used) |
585 | { | 563 | { |
586 | int err = 0; | 564 | const char *help = "For a higher level overview, try: perf top --sort comm,dso"; |
587 | pthread_mutex_lock(&top.active_symbols_lock); | 565 | |
588 | while (list_empty(&top.active_symbols)) { | 566 | perf_top__sort_new_samples(&top); |
589 | err = pthread_cond_wait(&top.active_symbols_cond, | 567 | perf_evlist__tui_browse_hists(top.evlist, help, |
590 | &top.active_symbols_lock); | 568 | perf_top__sort_new_samples, |
591 | if (err) | 569 | &top, top.delay_secs); |
592 | break; | 570 | |
593 | } | ||
594 | pthread_mutex_unlock(&top.active_symbols_lock); | ||
595 | if (!err) | ||
596 | perf_top__tui_browser(&top); | ||
597 | exit_browser(0); | 571 | exit_browser(0); |
598 | exit(0); | 572 | exit(0); |
599 | return NULL; | 573 | return NULL; |
@@ -645,9 +619,8 @@ static const char *skip_symbols[] = { | |||
645 | NULL | 619 | NULL |
646 | }; | 620 | }; |
647 | 621 | ||
648 | static int symbol_filter(struct map *map, struct symbol *sym) | 622 | static int symbol_filter(struct map *map __used, struct symbol *sym) |
649 | { | 623 | { |
650 | struct sym_entry *syme; | ||
651 | const char *name = sym->name; | 624 | const char *name = sym->name; |
652 | int i; | 625 | int i; |
653 | 626 | ||
@@ -667,16 +640,6 @@ static int symbol_filter(struct map *map, struct symbol *sym) | |||
667 | strstr(name, "_text_end")) | 640 | strstr(name, "_text_end")) |
668 | return 1; | 641 | return 1; |
669 | 642 | ||
670 | syme = symbol__priv(sym); | ||
671 | syme->map = map; | ||
672 | symbol__annotate_init(map, sym); | ||
673 | |||
674 | if (!top.sym_filter_entry && sym_filter && !strcmp(name, sym_filter)) { | ||
675 | /* schedule initial sym_filter_entry setup */ | ||
676 | sym_filter_entry_sched = syme; | ||
677 | sym_filter = NULL; | ||
678 | } | ||
679 | |||
680 | for (i = 0; skip_symbols[i]; i++) { | 643 | for (i = 0; skip_symbols[i]; i++) { |
681 | if (!strcmp(skip_symbols[i], name)) { | 644 | if (!strcmp(skip_symbols[i], name)) { |
682 | sym->ignore = true; | 645 | sym->ignore = true; |
@@ -691,10 +654,11 @@ static void perf_event__process_sample(const union perf_event *event, | |||
691 | struct perf_sample *sample, | 654 | struct perf_sample *sample, |
692 | struct perf_session *session) | 655 | struct perf_session *session) |
693 | { | 656 | { |
657 | struct symbol *parent = NULL; | ||
694 | u64 ip = event->ip.ip; | 658 | u64 ip = event->ip.ip; |
695 | struct sym_entry *syme; | ||
696 | struct addr_location al; | 659 | struct addr_location al; |
697 | struct machine *machine; | 660 | struct machine *machine; |
661 | int err; | ||
698 | u8 origin = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | 662 | u8 origin = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
699 | 663 | ||
700 | ++top.samples; | 664 | ++top.samples; |
@@ -783,46 +747,41 @@ static void perf_event__process_sample(const union perf_event *event, | |||
783 | sleep(5); | 747 | sleep(5); |
784 | vmlinux_warned = true; | 748 | vmlinux_warned = true; |
785 | } | 749 | } |
786 | |||
787 | return; | ||
788 | } | ||
789 | |||
790 | /* let's see, whether we need to install initial sym_filter_entry */ | ||
791 | if (sym_filter_entry_sched) { | ||
792 | top.sym_filter_entry = sym_filter_entry_sched; | ||
793 | sym_filter_entry_sched = NULL; | ||
794 | if (parse_source(top.sym_filter_entry) < 0) { | ||
795 | struct symbol *sym = sym_entry__symbol(top.sym_filter_entry); | ||
796 | |||
797 | pr_err("Can't annotate %s", sym->name); | ||
798 | if (top.sym_filter_entry->map->dso->symtab_type == SYMTAB__KALLSYMS) { | ||
799 | pr_err(": No vmlinux file was found in the path:\n"); | ||
800 | machine__fprintf_vmlinux_path(machine, stderr); | ||
801 | } else | ||
802 | pr_err(".\n"); | ||
803 | exit(1); | ||
804 | } | ||
805 | } | 750 | } |
806 | 751 | ||
807 | syme = symbol__priv(al.sym); | 752 | if (al.sym == NULL || !al.sym->ignore) { |
808 | if (!al.sym->ignore) { | ||
809 | struct perf_evsel *evsel; | 753 | struct perf_evsel *evsel; |
754 | struct hist_entry *he; | ||
810 | 755 | ||
811 | evsel = perf_evlist__id2evsel(top.evlist, sample->id); | 756 | evsel = perf_evlist__id2evsel(top.evlist, sample->id); |
812 | assert(evsel != NULL); | 757 | assert(evsel != NULL); |
813 | syme->count[evsel->idx]++; | 758 | |
814 | record_precise_ip(syme, al.map, evsel->idx, ip); | 759 | if ((sort__has_parent || symbol_conf.use_callchain) && |
815 | pthread_mutex_lock(&top.active_symbols_lock); | 760 | sample->callchain) { |
816 | if (list_empty(&syme->node) || !syme->node.next) { | 761 | err = perf_session__resolve_callchain(session, al.thread, |
817 | static bool first = true; | 762 | sample->callchain, &parent); |
818 | __list_insert_active_sym(syme); | 763 | if (err) |
819 | if (first) { | 764 | return; |
820 | pthread_cond_broadcast(&top.active_symbols_cond); | ||
821 | first = false; | ||
822 | } | ||
823 | } | 765 | } |
824 | pthread_mutex_unlock(&top.active_symbols_lock); | 766 | |
767 | he = perf_session__add_hist_entry(session, &al, sample, evsel); | ||
768 | if (he == NULL) { | ||
769 | pr_err("Problem incrementing symbol period, skipping event\n"); | ||
770 | return; | ||
771 | } | ||
772 | |||
773 | if (symbol_conf.use_callchain) { | ||
774 | err = callchain_append(he->callchain, &session->callchain_cursor, | ||
775 | sample->period); | ||
776 | if (err) | ||
777 | return; | ||
778 | } | ||
779 | |||
780 | if (sort_has_symbols) | ||
781 | record_precise_ip(he, evsel->idx, ip); | ||
825 | } | 782 | } |
783 | |||
784 | return; | ||
826 | } | 785 | } |
827 | 786 | ||
828 | static void perf_session__mmap_read_idx(struct perf_session *self, int idx) | 787 | static void perf_session__mmap_read_idx(struct perf_session *self, int idx) |
@@ -873,7 +832,11 @@ static void start_counters(struct perf_evlist *evlist) | |||
873 | attr->read_format |= PERF_FORMAT_ID; | 832 | attr->read_format |= PERF_FORMAT_ID; |
874 | } | 833 | } |
875 | 834 | ||
835 | if (symbol_conf.use_callchain) | ||
836 | attr->sample_type |= PERF_SAMPLE_CALLCHAIN; | ||
837 | |||
876 | attr->mmap = 1; | 838 | attr->mmap = 1; |
839 | attr->comm = 1; | ||
877 | attr->inherit = inherit; | 840 | attr->inherit = inherit; |
878 | try_again: | 841 | try_again: |
879 | if (perf_evsel__open(counter, top.evlist->cpus, | 842 | if (perf_evsel__open(counter, top.evlist->cpus, |
@@ -928,10 +891,27 @@ out_err: | |||
928 | exit(0); | 891 | exit(0); |
929 | } | 892 | } |
930 | 893 | ||
894 | static int setup_sample_type(void) | ||
895 | { | ||
896 | if (!sort_has_symbols) { | ||
897 | if (symbol_conf.use_callchain) { | ||
898 | ui__warning("Selected -g but \"sym\" not present in --sort/-s."); | ||
899 | return -EINVAL; | ||
900 | } | ||
901 | } else if (!dont_use_callchains && callchain_param.mode != CHAIN_NONE) { | ||
902 | if (callchain_register_param(&callchain_param) < 0) { | ||
903 | ui__warning("Can't register callchain params.\n"); | ||
904 | return -EINVAL; | ||
905 | } | ||
906 | } | ||
907 | |||
908 | return 0; | ||
909 | } | ||
910 | |||
931 | static int __cmd_top(void) | 911 | static int __cmd_top(void) |
932 | { | 912 | { |
933 | pthread_t thread; | 913 | pthread_t thread; |
934 | int ret __used; | 914 | int ret; |
935 | /* | 915 | /* |
936 | * FIXME: perf_session__new should allow passing a O_MMAP, so that all this | 916 | * FIXME: perf_session__new should allow passing a O_MMAP, so that all this |
937 | * mmap reading, etc is encapsulated in it. Use O_WRONLY for now. | 917 | * mmap reading, etc is encapsulated in it. Use O_WRONLY for now. |
@@ -940,6 +920,10 @@ static int __cmd_top(void) | |||
940 | if (top.session == NULL) | 920 | if (top.session == NULL) |
941 | return -ENOMEM; | 921 | return -ENOMEM; |
942 | 922 | ||
923 | ret = setup_sample_type(); | ||
924 | if (ret) | ||
925 | goto out_delete; | ||
926 | |||
943 | if (top.target_tid != -1) | 927 | if (top.target_tid != -1) |
944 | perf_event__synthesize_thread_map(top.evlist->threads, | 928 | perf_event__synthesize_thread_map(top.evlist->threads, |
945 | perf_event__process, top.session); | 929 | perf_event__process, top.session); |
@@ -980,6 +964,90 @@ static int __cmd_top(void) | |||
980 | ret = poll(top.evlist->pollfd, top.evlist->nr_fds, 100); | 964 | ret = poll(top.evlist->pollfd, top.evlist->nr_fds, 100); |
981 | } | 965 | } |
982 | 966 | ||
967 | out_delete: | ||
968 | perf_session__delete(top.session); | ||
969 | top.session = NULL; | ||
970 | |||
971 | return 0; | ||
972 | } | ||
973 | |||
974 | static int | ||
975 | parse_callchain_opt(const struct option *opt __used, const char *arg, | ||
976 | int unset) | ||
977 | { | ||
978 | char *tok, *tok2; | ||
979 | char *endptr; | ||
980 | |||
981 | /* | ||
982 | * --no-call-graph | ||
983 | */ | ||
984 | if (unset) { | ||
985 | dont_use_callchains = true; | ||
986 | return 0; | ||
987 | } | ||
988 | |||
989 | symbol_conf.use_callchain = true; | ||
990 | |||
991 | if (!arg) | ||
992 | return 0; | ||
993 | |||
994 | tok = strtok((char *)arg, ","); | ||
995 | if (!tok) | ||
996 | return -1; | ||
997 | |||
998 | /* get the output mode */ | ||
999 | if (!strncmp(tok, "graph", strlen(arg))) | ||
1000 | callchain_param.mode = CHAIN_GRAPH_ABS; | ||
1001 | |||
1002 | else if (!strncmp(tok, "flat", strlen(arg))) | ||
1003 | callchain_param.mode = CHAIN_FLAT; | ||
1004 | |||
1005 | else if (!strncmp(tok, "fractal", strlen(arg))) | ||
1006 | callchain_param.mode = CHAIN_GRAPH_REL; | ||
1007 | |||
1008 | else if (!strncmp(tok, "none", strlen(arg))) { | ||
1009 | callchain_param.mode = CHAIN_NONE; | ||
1010 | symbol_conf.use_callchain = false; | ||
1011 | |||
1012 | return 0; | ||
1013 | } | ||
1014 | |||
1015 | else | ||
1016 | return -1; | ||
1017 | |||
1018 | /* get the min percentage */ | ||
1019 | tok = strtok(NULL, ","); | ||
1020 | if (!tok) | ||
1021 | goto setup; | ||
1022 | |||
1023 | callchain_param.min_percent = strtod(tok, &endptr); | ||
1024 | if (tok == endptr) | ||
1025 | return -1; | ||
1026 | |||
1027 | /* get the print limit */ | ||
1028 | tok2 = strtok(NULL, ","); | ||
1029 | if (!tok2) | ||
1030 | goto setup; | ||
1031 | |||
1032 | if (tok2[0] != 'c') { | ||
1033 | callchain_param.print_limit = strtod(tok2, &endptr); | ||
1034 | tok2 = strtok(NULL, ","); | ||
1035 | if (!tok2) | ||
1036 | goto setup; | ||
1037 | } | ||
1038 | |||
1039 | /* get the call chain order */ | ||
1040 | if (!strcmp(tok2, "caller")) | ||
1041 | callchain_param.order = ORDER_CALLER; | ||
1042 | else if (!strcmp(tok2, "callee")) | ||
1043 | callchain_param.order = ORDER_CALLEE; | ||
1044 | else | ||
1045 | return -1; | ||
1046 | setup: | ||
1047 | if (callchain_register_param(&callchain_param) < 0) { | ||
1048 | fprintf(stderr, "Can't register callchain params\n"); | ||
1049 | return -1; | ||
1050 | } | ||
983 | return 0; | 1051 | return 0; |
984 | } | 1052 | } |
985 | 1053 | ||
@@ -1019,7 +1087,7 @@ static const struct option options[] = { | |||
1019 | "put the counters into a counter group"), | 1087 | "put the counters into a counter group"), |
1020 | OPT_BOOLEAN('i', "inherit", &inherit, | 1088 | OPT_BOOLEAN('i', "inherit", &inherit, |
1021 | "child tasks inherit counters"), | 1089 | "child tasks inherit counters"), |
1022 | OPT_STRING('s', "sym-annotate", &sym_filter, "symbol name", | 1090 | OPT_STRING(0, "sym-annotate", &sym_filter, "symbol name", |
1023 | "symbol to annotate"), | 1091 | "symbol to annotate"), |
1024 | OPT_BOOLEAN('z', "zero", &top.zero, | 1092 | OPT_BOOLEAN('z', "zero", &top.zero, |
1025 | "zero history across updates"), | 1093 | "zero history across updates"), |
@@ -1033,6 +1101,28 @@ static const struct option options[] = { | |||
1033 | OPT_BOOLEAN(0, "stdio", &use_stdio, "Use the stdio interface"), | 1101 | OPT_BOOLEAN(0, "stdio", &use_stdio, "Use the stdio interface"), |
1034 | OPT_INCR('v', "verbose", &verbose, | 1102 | OPT_INCR('v', "verbose", &verbose, |
1035 | "be more verbose (show counter open errors, etc)"), | 1103 | "be more verbose (show counter open errors, etc)"), |
1104 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", | ||
1105 | "sort by key(s): pid, comm, dso, symbol, parent"), | ||
1106 | OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples, | ||
1107 | "Show a column with the number of samples"), | ||
1108 | OPT_CALLBACK_DEFAULT('G', "call-graph", NULL, "output_type,min_percent, call_order", | ||
1109 | "Display callchains using output_type (graph, flat, fractal, or none), min percent threshold and callchain order. " | ||
1110 | "Default: fractal,0.5,callee", &parse_callchain_opt, | ||
1111 | callchain_default_opt), | ||
1112 | OPT_BOOLEAN(0, "show-total-period", &symbol_conf.show_total_period, | ||
1113 | "Show a column with the sum of periods"), | ||
1114 | OPT_STRING(0, "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", | ||
1115 | "only consider symbols in these dsos"), | ||
1116 | OPT_STRING(0, "comms", &symbol_conf.comm_list_str, "comm[,comm...]", | ||
1117 | "only consider symbols in these comms"), | ||
1118 | OPT_STRING(0, "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]", | ||
1119 | "only consider these symbols"), | ||
1120 | OPT_BOOLEAN(0, "source", &symbol_conf.annotate_src, | ||
1121 | "Interleave source code with assembly code (default)"), | ||
1122 | OPT_BOOLEAN(0, "asm-raw", &symbol_conf.annotate_asm_raw, | ||
1123 | "Display raw encoding of assembly instructions (default)"), | ||
1124 | OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style", | ||
1125 | "Specify disassembler style (e.g. -M intel for intel syntax)"), | ||
1036 | OPT_END() | 1126 | OPT_END() |
1037 | }; | 1127 | }; |
1038 | 1128 | ||
@@ -1045,18 +1135,16 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
1045 | if (top.evlist == NULL) | 1135 | if (top.evlist == NULL) |
1046 | return -ENOMEM; | 1136 | return -ENOMEM; |
1047 | 1137 | ||
1048 | page_size = sysconf(_SC_PAGE_SIZE); | 1138 | symbol_conf.exclude_other = false; |
1049 | 1139 | ||
1050 | argc = parse_options(argc, argv, options, top_usage, 0); | 1140 | argc = parse_options(argc, argv, options, top_usage, 0); |
1051 | if (argc) | 1141 | if (argc) |
1052 | usage_with_options(top_usage, options); | 1142 | usage_with_options(top_usage, options); |
1053 | 1143 | ||
1054 | /* | 1144 | if (sort_order == default_sort_order) |
1055 | * XXX For now start disabled, only using TUI if explicitely asked for. | 1145 | sort_order = "dso,symbol"; |
1056 | * Change that when handle_keys equivalent gets written, live annotation | 1146 | |
1057 | * done, etc. | 1147 | setup_sorting(top_usage, options); |
1058 | */ | ||
1059 | use_browser = 0; | ||
1060 | 1148 | ||
1061 | if (use_stdio) | 1149 | if (use_stdio) |
1062 | use_browser = 0; | 1150 | use_browser = 0; |
@@ -1119,13 +1207,22 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
1119 | 1207 | ||
1120 | top.sym_evsel = list_entry(top.evlist->entries.next, struct perf_evsel, node); | 1208 | top.sym_evsel = list_entry(top.evlist->entries.next, struct perf_evsel, node); |
1121 | 1209 | ||
1122 | symbol_conf.priv_size = (sizeof(struct sym_entry) + sizeof(struct annotation) + | 1210 | symbol_conf.priv_size = sizeof(struct annotation); |
1123 | (top.evlist->nr_entries + 1) * sizeof(unsigned long)); | ||
1124 | 1211 | ||
1125 | symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL); | 1212 | symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL); |
1126 | if (symbol__init() < 0) | 1213 | if (symbol__init() < 0) |
1127 | return -1; | 1214 | return -1; |
1128 | 1215 | ||
1216 | sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso", stdout); | ||
1217 | sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, "comm", stdout); | ||
1218 | sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list, "symbol", stdout); | ||
1219 | |||
1220 | /* | ||
1221 | * Avoid annotation data structures overhead when symbols aren't on the | ||
1222 | * sort list. | ||
1223 | */ | ||
1224 | sort_has_symbols = sort_sym.list.next != NULL; | ||
1225 | |||
1129 | get_term_dimensions(&winsize); | 1226 | get_term_dimensions(&winsize); |
1130 | if (top.print_entries == 0) { | 1227 | if (top.print_entries == 0) { |
1131 | update_print_entries(&winsize); | 1228 | update_print_entries(&winsize); |
diff --git a/tools/perf/builtin.h b/tools/perf/builtin.h index 4702e2443a8e..b382bd551aac 100644 --- a/tools/perf/builtin.h +++ b/tools/perf/builtin.h | |||
@@ -4,7 +4,6 @@ | |||
4 | #include "util/util.h" | 4 | #include "util/util.h" |
5 | #include "util/strbuf.h" | 5 | #include "util/strbuf.h" |
6 | 6 | ||
7 | extern const char perf_version_string[]; | ||
8 | extern const char perf_usage_string[]; | 7 | extern const char perf_usage_string[]; |
9 | extern const char perf_more_info_string[]; | 8 | extern const char perf_more_info_string[]; |
10 | 9 | ||
diff --git a/tools/perf/perf.h b/tools/perf/perf.h index a5fc660c1f12..08b0b5e82a44 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h | |||
@@ -9,18 +9,21 @@ void get_term_dimensions(struct winsize *ws); | |||
9 | #include "../../arch/x86/include/asm/unistd.h" | 9 | #include "../../arch/x86/include/asm/unistd.h" |
10 | #define rmb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory") | 10 | #define rmb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory") |
11 | #define cpu_relax() asm volatile("rep; nop" ::: "memory"); | 11 | #define cpu_relax() asm volatile("rep; nop" ::: "memory"); |
12 | #define CPUINFO_PROC "model name" | ||
12 | #endif | 13 | #endif |
13 | 14 | ||
14 | #if defined(__x86_64__) | 15 | #if defined(__x86_64__) |
15 | #include "../../arch/x86/include/asm/unistd.h" | 16 | #include "../../arch/x86/include/asm/unistd.h" |
16 | #define rmb() asm volatile("lfence" ::: "memory") | 17 | #define rmb() asm volatile("lfence" ::: "memory") |
17 | #define cpu_relax() asm volatile("rep; nop" ::: "memory"); | 18 | #define cpu_relax() asm volatile("rep; nop" ::: "memory"); |
19 | #define CPUINFO_PROC "model name" | ||
18 | #endif | 20 | #endif |
19 | 21 | ||
20 | #ifdef __powerpc__ | 22 | #ifdef __powerpc__ |
21 | #include "../../arch/powerpc/include/asm/unistd.h" | 23 | #include "../../arch/powerpc/include/asm/unistd.h" |
22 | #define rmb() asm volatile ("sync" ::: "memory") | 24 | #define rmb() asm volatile ("sync" ::: "memory") |
23 | #define cpu_relax() asm volatile ("" ::: "memory"); | 25 | #define cpu_relax() asm volatile ("" ::: "memory"); |
26 | #define CPUINFO_PROC "cpu" | ||
24 | #endif | 27 | #endif |
25 | 28 | ||
26 | #ifdef __s390__ | 29 | #ifdef __s390__ |
@@ -37,30 +40,35 @@ void get_term_dimensions(struct winsize *ws); | |||
37 | # define rmb() asm volatile("" ::: "memory") | 40 | # define rmb() asm volatile("" ::: "memory") |
38 | #endif | 41 | #endif |
39 | #define cpu_relax() asm volatile("" ::: "memory") | 42 | #define cpu_relax() asm volatile("" ::: "memory") |
43 | #define CPUINFO_PROC "cpu type" | ||
40 | #endif | 44 | #endif |
41 | 45 | ||
42 | #ifdef __hppa__ | 46 | #ifdef __hppa__ |
43 | #include "../../arch/parisc/include/asm/unistd.h" | 47 | #include "../../arch/parisc/include/asm/unistd.h" |
44 | #define rmb() asm volatile("" ::: "memory") | 48 | #define rmb() asm volatile("" ::: "memory") |
45 | #define cpu_relax() asm volatile("" ::: "memory"); | 49 | #define cpu_relax() asm volatile("" ::: "memory"); |
50 | #define CPUINFO_PROC "cpu" | ||
46 | #endif | 51 | #endif |
47 | 52 | ||
48 | #ifdef __sparc__ | 53 | #ifdef __sparc__ |
49 | #include "../../arch/sparc/include/asm/unistd.h" | 54 | #include "../../arch/sparc/include/asm/unistd.h" |
50 | #define rmb() asm volatile("":::"memory") | 55 | #define rmb() asm volatile("":::"memory") |
51 | #define cpu_relax() asm volatile("":::"memory") | 56 | #define cpu_relax() asm volatile("":::"memory") |
57 | #define CPUINFO_PROC "cpu" | ||
52 | #endif | 58 | #endif |
53 | 59 | ||
54 | #ifdef __alpha__ | 60 | #ifdef __alpha__ |
55 | #include "../../arch/alpha/include/asm/unistd.h" | 61 | #include "../../arch/alpha/include/asm/unistd.h" |
56 | #define rmb() asm volatile("mb" ::: "memory") | 62 | #define rmb() asm volatile("mb" ::: "memory") |
57 | #define cpu_relax() asm volatile("" ::: "memory") | 63 | #define cpu_relax() asm volatile("" ::: "memory") |
64 | #define CPUINFO_PROC "cpu model" | ||
58 | #endif | 65 | #endif |
59 | 66 | ||
60 | #ifdef __ia64__ | 67 | #ifdef __ia64__ |
61 | #include "../../arch/ia64/include/asm/unistd.h" | 68 | #include "../../arch/ia64/include/asm/unistd.h" |
62 | #define rmb() asm volatile ("mf" ::: "memory") | 69 | #define rmb() asm volatile ("mf" ::: "memory") |
63 | #define cpu_relax() asm volatile ("hint @pause" ::: "memory") | 70 | #define cpu_relax() asm volatile ("hint @pause" ::: "memory") |
71 | #define CPUINFO_PROC "model name" | ||
64 | #endif | 72 | #endif |
65 | 73 | ||
66 | #ifdef __arm__ | 74 | #ifdef __arm__ |
@@ -71,6 +79,7 @@ void get_term_dimensions(struct winsize *ws); | |||
71 | */ | 79 | */ |
72 | #define rmb() ((void(*)(void))0xffff0fa0)() | 80 | #define rmb() ((void(*)(void))0xffff0fa0)() |
73 | #define cpu_relax() asm volatile("":::"memory") | 81 | #define cpu_relax() asm volatile("":::"memory") |
82 | #define CPUINFO_PROC "Processor" | ||
74 | #endif | 83 | #endif |
75 | 84 | ||
76 | #ifdef __mips__ | 85 | #ifdef __mips__ |
@@ -83,6 +92,7 @@ void get_term_dimensions(struct winsize *ws); | |||
83 | : /* no input */ \ | 92 | : /* no input */ \ |
84 | : "memory") | 93 | : "memory") |
85 | #define cpu_relax() asm volatile("" ::: "memory") | 94 | #define cpu_relax() asm volatile("" ::: "memory") |
95 | #define CPUINFO_PROC "cpu model" | ||
86 | #endif | 96 | #endif |
87 | 97 | ||
88 | #include <time.h> | 98 | #include <time.h> |
@@ -171,5 +181,6 @@ struct ip_callchain { | |||
171 | }; | 181 | }; |
172 | 182 | ||
173 | extern bool perf_host, perf_guest; | 183 | extern bool perf_host, perf_guest; |
184 | extern const char perf_version_string[]; | ||
174 | 185 | ||
175 | #endif | 186 | #endif |
diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index 6ede1286ee71..d9072523d342 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h | |||
@@ -91,13 +91,16 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx, | |||
91 | #ifdef NO_NEWT_SUPPORT | 91 | #ifdef NO_NEWT_SUPPORT |
92 | static inline int symbol__tui_annotate(struct symbol *sym __used, | 92 | static inline int symbol__tui_annotate(struct symbol *sym __used, |
93 | struct map *map __used, | 93 | struct map *map __used, |
94 | int evidx __used, int refresh __used) | 94 | int evidx __used, |
95 | void(*timer)(void *arg) __used, | ||
96 | void *arg __used, int delay_secs __used) | ||
95 | { | 97 | { |
96 | return 0; | 98 | return 0; |
97 | } | 99 | } |
98 | #else | 100 | #else |
99 | int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, | 101 | int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, |
100 | int refresh); | 102 | int nr_events, void(*timer)(void *arg), void *arg, |
103 | int delay_secs); | ||
101 | #endif | 104 | #endif |
102 | 105 | ||
103 | extern const char *disassembler_style; | 106 | extern const char *disassembler_style; |
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 72e9f4886b6d..2f6bc89027da 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c | |||
@@ -533,3 +533,9 @@ bool perf_evlist__sample_id_all(const struct perf_evlist *evlist) | |||
533 | first = list_entry(evlist->entries.next, struct perf_evsel, node); | 533 | first = list_entry(evlist->entries.next, struct perf_evsel, node); |
534 | return first->attr.sample_id_all; | 534 | return first->attr.sample_id_all; |
535 | } | 535 | } |
536 | |||
537 | void perf_evlist__set_selected(struct perf_evlist *evlist, | ||
538 | struct perf_evsel *evsel) | ||
539 | { | ||
540 | evlist->selected = evsel; | ||
541 | } | ||
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index f34915002745..6be71fc57794 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h | |||
@@ -25,6 +25,7 @@ struct perf_evlist { | |||
25 | struct pollfd *pollfd; | 25 | struct pollfd *pollfd; |
26 | struct thread_map *threads; | 26 | struct thread_map *threads; |
27 | struct cpu_map *cpus; | 27 | struct cpu_map *cpus; |
28 | struct perf_evsel *selected; | ||
28 | }; | 29 | }; |
29 | 30 | ||
30 | struct perf_evsel; | 31 | struct perf_evsel; |
@@ -56,6 +57,9 @@ void perf_evlist__munmap(struct perf_evlist *evlist); | |||
56 | void perf_evlist__disable(struct perf_evlist *evlist); | 57 | void perf_evlist__disable(struct perf_evlist *evlist); |
57 | void perf_evlist__enable(struct perf_evlist *evlist); | 58 | void perf_evlist__enable(struct perf_evlist *evlist); |
58 | 59 | ||
60 | void perf_evlist__set_selected(struct perf_evlist *evlist, | ||
61 | struct perf_evsel *evsel); | ||
62 | |||
59 | static inline void perf_evlist__set_maps(struct perf_evlist *evlist, | 63 | static inline void perf_evlist__set_maps(struct perf_evlist *evlist, |
60 | struct cpu_map *cpus, | 64 | struct cpu_map *cpus, |
61 | struct thread_map *threads) | 65 | struct thread_map *threads) |
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index e389815078d3..b46f6e4bff3c 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
@@ -39,6 +39,7 @@ void perf_evsel__init(struct perf_evsel *evsel, | |||
39 | evsel->idx = idx; | 39 | evsel->idx = idx; |
40 | evsel->attr = *attr; | 40 | evsel->attr = *attr; |
41 | INIT_LIST_HEAD(&evsel->node); | 41 | INIT_LIST_HEAD(&evsel->node); |
42 | hists__init(&evsel->hists); | ||
42 | } | 43 | } |
43 | 44 | ||
44 | struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx) | 45 | struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx) |
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index b6c1ad123ca9..f2ceb0f7d669 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
@@ -7,6 +7,7 @@ | |||
7 | #include <stdlib.h> | 7 | #include <stdlib.h> |
8 | #include <linux/list.h> | 8 | #include <linux/list.h> |
9 | #include <linux/kernel.h> | 9 | #include <linux/kernel.h> |
10 | #include <sys/utsname.h> | ||
10 | 11 | ||
11 | #include "evlist.h" | 12 | #include "evlist.h" |
12 | #include "evsel.h" | 13 | #include "evsel.h" |
@@ -17,12 +18,19 @@ | |||
17 | #include "session.h" | 18 | #include "session.h" |
18 | #include "symbol.h" | 19 | #include "symbol.h" |
19 | #include "debug.h" | 20 | #include "debug.h" |
21 | #include "cpumap.h" | ||
20 | 22 | ||
21 | static bool no_buildid_cache = false; | 23 | static bool no_buildid_cache = false; |
22 | 24 | ||
23 | static int event_count; | 25 | static int event_count; |
24 | static struct perf_trace_event_type *events; | 26 | static struct perf_trace_event_type *events; |
25 | 27 | ||
28 | static u32 header_argc; | ||
29 | static const char **header_argv; | ||
30 | |||
31 | static int dsos__write_buildid_table(struct perf_header *header, int fd); | ||
32 | static int perf_session__cache_build_ids(struct perf_session *session); | ||
33 | |||
26 | int perf_header__push_event(u64 id, const char *name) | 34 | int perf_header__push_event(u64 id, const char *name) |
27 | { | 35 | { |
28 | if (strlen(name) > MAX_EVENT_NAME) | 36 | if (strlen(name) > MAX_EVENT_NAME) |
@@ -110,6 +118,1020 @@ static int write_padded(int fd, const void *bf, size_t count, | |||
110 | return err; | 118 | return err; |
111 | } | 119 | } |
112 | 120 | ||
121 | static int do_write_string(int fd, const char *str) | ||
122 | { | ||
123 | u32 len, olen; | ||
124 | int ret; | ||
125 | |||
126 | olen = strlen(str) + 1; | ||
127 | len = ALIGN(olen, NAME_ALIGN); | ||
128 | |||
129 | /* write len, incl. \0 */ | ||
130 | ret = do_write(fd, &len, sizeof(len)); | ||
131 | if (ret < 0) | ||
132 | return ret; | ||
133 | |||
134 | return write_padded(fd, str, olen, len); | ||
135 | } | ||
136 | |||
137 | static char *do_read_string(int fd, struct perf_header *ph) | ||
138 | { | ||
139 | ssize_t sz, ret; | ||
140 | u32 len; | ||
141 | char *buf; | ||
142 | |||
143 | sz = read(fd, &len, sizeof(len)); | ||
144 | if (sz < (ssize_t)sizeof(len)) | ||
145 | return NULL; | ||
146 | |||
147 | if (ph->needs_swap) | ||
148 | len = bswap_32(len); | ||
149 | |||
150 | buf = malloc(len); | ||
151 | if (!buf) | ||
152 | return NULL; | ||
153 | |||
154 | ret = read(fd, buf, len); | ||
155 | if (ret == (ssize_t)len) { | ||
156 | /* | ||
157 | * strings are padded by zeroes | ||
158 | * thus the actual strlen of buf | ||
159 | * may be less than len | ||
160 | */ | ||
161 | return buf; | ||
162 | } | ||
163 | |||
164 | free(buf); | ||
165 | return NULL; | ||
166 | } | ||
167 | |||
168 | int | ||
169 | perf_header__set_cmdline(int argc, const char **argv) | ||
170 | { | ||
171 | int i; | ||
172 | |||
173 | header_argc = (u32)argc; | ||
174 | |||
175 | /* do not include NULL termination */ | ||
176 | header_argv = calloc(argc, sizeof(char *)); | ||
177 | if (!header_argv) | ||
178 | return -ENOMEM; | ||
179 | |||
180 | /* | ||
181 | * must copy argv contents because it gets moved | ||
182 | * around during option parsing | ||
183 | */ | ||
184 | for (i = 0; i < argc ; i++) | ||
185 | header_argv[i] = argv[i]; | ||
186 | |||
187 | return 0; | ||
188 | } | ||
189 | |||
190 | static int write_trace_info(int fd, struct perf_header *h __used, | ||
191 | struct perf_evlist *evlist) | ||
192 | { | ||
193 | return read_tracing_data(fd, &evlist->entries); | ||
194 | } | ||
195 | |||
196 | |||
197 | static int write_build_id(int fd, struct perf_header *h, | ||
198 | struct perf_evlist *evlist __used) | ||
199 | { | ||
200 | struct perf_session *session; | ||
201 | int err; | ||
202 | |||
203 | session = container_of(h, struct perf_session, header); | ||
204 | |||
205 | err = dsos__write_buildid_table(h, fd); | ||
206 | if (err < 0) { | ||
207 | pr_debug("failed to write buildid table\n"); | ||
208 | return err; | ||
209 | } | ||
210 | if (!no_buildid_cache) | ||
211 | perf_session__cache_build_ids(session); | ||
212 | |||
213 | return 0; | ||
214 | } | ||
215 | |||
216 | static int write_hostname(int fd, struct perf_header *h __used, | ||
217 | struct perf_evlist *evlist __used) | ||
218 | { | ||
219 | struct utsname uts; | ||
220 | int ret; | ||
221 | |||
222 | ret = uname(&uts); | ||
223 | if (ret < 0) | ||
224 | return -1; | ||
225 | |||
226 | return do_write_string(fd, uts.nodename); | ||
227 | } | ||
228 | |||
229 | static int write_osrelease(int fd, struct perf_header *h __used, | ||
230 | struct perf_evlist *evlist __used) | ||
231 | { | ||
232 | struct utsname uts; | ||
233 | int ret; | ||
234 | |||
235 | ret = uname(&uts); | ||
236 | if (ret < 0) | ||
237 | return -1; | ||
238 | |||
239 | return do_write_string(fd, uts.release); | ||
240 | } | ||
241 | |||
242 | static int write_arch(int fd, struct perf_header *h __used, | ||
243 | struct perf_evlist *evlist __used) | ||
244 | { | ||
245 | struct utsname uts; | ||
246 | int ret; | ||
247 | |||
248 | ret = uname(&uts); | ||
249 | if (ret < 0) | ||
250 | return -1; | ||
251 | |||
252 | return do_write_string(fd, uts.machine); | ||
253 | } | ||
254 | |||
255 | static int write_version(int fd, struct perf_header *h __used, | ||
256 | struct perf_evlist *evlist __used) | ||
257 | { | ||
258 | return do_write_string(fd, perf_version_string); | ||
259 | } | ||
260 | |||
261 | static int write_cpudesc(int fd, struct perf_header *h __used, | ||
262 | struct perf_evlist *evlist __used) | ||
263 | { | ||
264 | #ifndef CPUINFO_PROC | ||
265 | #define CPUINFO_PROC NULL | ||
266 | #endif | ||
267 | FILE *file; | ||
268 | char *buf = NULL; | ||
269 | char *s, *p; | ||
270 | const char *search = CPUINFO_PROC; | ||
271 | size_t len = 0; | ||
272 | int ret = -1; | ||
273 | |||
274 | if (!search) | ||
275 | return -1; | ||
276 | |||
277 | file = fopen("/proc/cpuinfo", "r"); | ||
278 | if (!file) | ||
279 | return -1; | ||
280 | |||
281 | while (getline(&buf, &len, file) > 0) { | ||
282 | ret = strncmp(buf, search, strlen(search)); | ||
283 | if (!ret) | ||
284 | break; | ||
285 | } | ||
286 | |||
287 | if (ret) | ||
288 | goto done; | ||
289 | |||
290 | s = buf; | ||
291 | |||
292 | p = strchr(buf, ':'); | ||
293 | if (p && *(p+1) == ' ' && *(p+2)) | ||
294 | s = p + 2; | ||
295 | p = strchr(s, '\n'); | ||
296 | if (p) | ||
297 | *p = '\0'; | ||
298 | |||
299 | /* squash extra space characters (branding string) */ | ||
300 | p = s; | ||
301 | while (*p) { | ||
302 | if (isspace(*p)) { | ||
303 | char *r = p + 1; | ||
304 | char *q = r; | ||
305 | *p = ' '; | ||
306 | while (*q && isspace(*q)) | ||
307 | q++; | ||
308 | if (q != (p+1)) | ||
309 | while ((*r++ = *q++)); | ||
310 | } | ||
311 | p++; | ||
312 | } | ||
313 | ret = do_write_string(fd, s); | ||
314 | done: | ||
315 | free(buf); | ||
316 | fclose(file); | ||
317 | return ret; | ||
318 | } | ||
319 | |||
320 | static int write_nrcpus(int fd, struct perf_header *h __used, | ||
321 | struct perf_evlist *evlist __used) | ||
322 | { | ||
323 | long nr; | ||
324 | u32 nrc, nra; | ||
325 | int ret; | ||
326 | |||
327 | nr = sysconf(_SC_NPROCESSORS_CONF); | ||
328 | if (nr < 0) | ||
329 | return -1; | ||
330 | |||
331 | nrc = (u32)(nr & UINT_MAX); | ||
332 | |||
333 | nr = sysconf(_SC_NPROCESSORS_ONLN); | ||
334 | if (nr < 0) | ||
335 | return -1; | ||
336 | |||
337 | nra = (u32)(nr & UINT_MAX); | ||
338 | |||
339 | ret = do_write(fd, &nrc, sizeof(nrc)); | ||
340 | if (ret < 0) | ||
341 | return ret; | ||
342 | |||
343 | return do_write(fd, &nra, sizeof(nra)); | ||
344 | } | ||
345 | |||
346 | static int write_event_desc(int fd, struct perf_header *h __used, | ||
347 | struct perf_evlist *evlist) | ||
348 | { | ||
349 | struct perf_evsel *attr; | ||
350 | u32 nre = 0, nri, sz; | ||
351 | int ret; | ||
352 | |||
353 | list_for_each_entry(attr, &evlist->entries, node) | ||
354 | nre++; | ||
355 | |||
356 | /* | ||
357 | * write number of events | ||
358 | */ | ||
359 | ret = do_write(fd, &nre, sizeof(nre)); | ||
360 | if (ret < 0) | ||
361 | return ret; | ||
362 | |||
363 | /* | ||
364 | * size of perf_event_attr struct | ||
365 | */ | ||
366 | sz = (u32)sizeof(attr->attr); | ||
367 | ret = do_write(fd, &sz, sizeof(sz)); | ||
368 | if (ret < 0) | ||
369 | return ret; | ||
370 | |||
371 | list_for_each_entry(attr, &evlist->entries, node) { | ||
372 | |||
373 | ret = do_write(fd, &attr->attr, sz); | ||
374 | if (ret < 0) | ||
375 | return ret; | ||
376 | /* | ||
377 | * write number of unique id per event | ||
378 | * there is one id per instance of an event | ||
379 | * | ||
380 | * copy into an nri to be independent of the | ||
381 | * type of ids, | ||
382 | */ | ||
383 | nri = attr->ids; | ||
384 | ret = do_write(fd, &nri, sizeof(nri)); | ||
385 | if (ret < 0) | ||
386 | return ret; | ||
387 | |||
388 | /* | ||
389 | * write event string as passed on cmdline | ||
390 | */ | ||
391 | ret = do_write_string(fd, attr->name); | ||
392 | if (ret < 0) | ||
393 | return ret; | ||
394 | /* | ||
395 | * write unique ids for this event | ||
396 | */ | ||
397 | ret = do_write(fd, attr->id, attr->ids * sizeof(u64)); | ||
398 | if (ret < 0) | ||
399 | return ret; | ||
400 | } | ||
401 | return 0; | ||
402 | } | ||
403 | |||
404 | static int write_cmdline(int fd, struct perf_header *h __used, | ||
405 | struct perf_evlist *evlist __used) | ||
406 | { | ||
407 | char buf[MAXPATHLEN]; | ||
408 | char proc[32]; | ||
409 | u32 i, n; | ||
410 | int ret; | ||
411 | |||
412 | /* | ||
413 | * actual atual path to perf binary | ||
414 | */ | ||
415 | sprintf(proc, "/proc/%d/exe", getpid()); | ||
416 | ret = readlink(proc, buf, sizeof(buf)); | ||
417 | if (ret <= 0) | ||
418 | return -1; | ||
419 | |||
420 | /* readlink() does not add null termination */ | ||
421 | buf[ret] = '\0'; | ||
422 | |||
423 | /* account for binary path */ | ||
424 | n = header_argc + 1; | ||
425 | |||
426 | ret = do_write(fd, &n, sizeof(n)); | ||
427 | if (ret < 0) | ||
428 | return ret; | ||
429 | |||
430 | ret = do_write_string(fd, buf); | ||
431 | if (ret < 0) | ||
432 | return ret; | ||
433 | |||
434 | for (i = 0 ; i < header_argc; i++) { | ||
435 | ret = do_write_string(fd, header_argv[i]); | ||
436 | if (ret < 0) | ||
437 | return ret; | ||
438 | } | ||
439 | return 0; | ||
440 | } | ||
441 | |||
442 | #define CORE_SIB_FMT \ | ||
443 | "/sys/devices/system/cpu/cpu%d/topology/core_siblings_list" | ||
444 | #define THRD_SIB_FMT \ | ||
445 | "/sys/devices/system/cpu/cpu%d/topology/thread_siblings_list" | ||
446 | |||
447 | struct cpu_topo { | ||
448 | u32 core_sib; | ||
449 | u32 thread_sib; | ||
450 | char **core_siblings; | ||
451 | char **thread_siblings; | ||
452 | }; | ||
453 | |||
454 | static int build_cpu_topo(struct cpu_topo *tp, int cpu) | ||
455 | { | ||
456 | FILE *fp; | ||
457 | char filename[MAXPATHLEN]; | ||
458 | char *buf = NULL, *p; | ||
459 | size_t len = 0; | ||
460 | u32 i = 0; | ||
461 | int ret = -1; | ||
462 | |||
463 | sprintf(filename, CORE_SIB_FMT, cpu); | ||
464 | fp = fopen(filename, "r"); | ||
465 | if (!fp) | ||
466 | return -1; | ||
467 | |||
468 | if (getline(&buf, &len, fp) <= 0) | ||
469 | goto done; | ||
470 | |||
471 | fclose(fp); | ||
472 | |||
473 | p = strchr(buf, '\n'); | ||
474 | if (p) | ||
475 | *p = '\0'; | ||
476 | |||
477 | for (i = 0; i < tp->core_sib; i++) { | ||
478 | if (!strcmp(buf, tp->core_siblings[i])) | ||
479 | break; | ||
480 | } | ||
481 | if (i == tp->core_sib) { | ||
482 | tp->core_siblings[i] = buf; | ||
483 | tp->core_sib++; | ||
484 | buf = NULL; | ||
485 | len = 0; | ||
486 | } | ||
487 | |||
488 | sprintf(filename, THRD_SIB_FMT, cpu); | ||
489 | fp = fopen(filename, "r"); | ||
490 | if (!fp) | ||
491 | goto done; | ||
492 | |||
493 | if (getline(&buf, &len, fp) <= 0) | ||
494 | goto done; | ||
495 | |||
496 | p = strchr(buf, '\n'); | ||
497 | if (p) | ||
498 | *p = '\0'; | ||
499 | |||
500 | for (i = 0; i < tp->thread_sib; i++) { | ||
501 | if (!strcmp(buf, tp->thread_siblings[i])) | ||
502 | break; | ||
503 | } | ||
504 | if (i == tp->thread_sib) { | ||
505 | tp->thread_siblings[i] = buf; | ||
506 | tp->thread_sib++; | ||
507 | buf = NULL; | ||
508 | } | ||
509 | ret = 0; | ||
510 | done: | ||
511 | if(fp) | ||
512 | fclose(fp); | ||
513 | free(buf); | ||
514 | return ret; | ||
515 | } | ||
516 | |||
517 | static void free_cpu_topo(struct cpu_topo *tp) | ||
518 | { | ||
519 | u32 i; | ||
520 | |||
521 | if (!tp) | ||
522 | return; | ||
523 | |||
524 | for (i = 0 ; i < tp->core_sib; i++) | ||
525 | free(tp->core_siblings[i]); | ||
526 | |||
527 | for (i = 0 ; i < tp->thread_sib; i++) | ||
528 | free(tp->thread_siblings[i]); | ||
529 | |||
530 | free(tp); | ||
531 | } | ||
532 | |||
533 | static struct cpu_topo *build_cpu_topology(void) | ||
534 | { | ||
535 | struct cpu_topo *tp; | ||
536 | void *addr; | ||
537 | u32 nr, i; | ||
538 | size_t sz; | ||
539 | long ncpus; | ||
540 | int ret = -1; | ||
541 | |||
542 | ncpus = sysconf(_SC_NPROCESSORS_CONF); | ||
543 | if (ncpus < 0) | ||
544 | return NULL; | ||
545 | |||
546 | nr = (u32)(ncpus & UINT_MAX); | ||
547 | |||
548 | sz = nr * sizeof(char *); | ||
549 | |||
550 | addr = calloc(1, sizeof(*tp) + 2 * sz); | ||
551 | if (!addr) | ||
552 | return NULL; | ||
553 | |||
554 | tp = addr; | ||
555 | |||
556 | addr += sizeof(*tp); | ||
557 | tp->core_siblings = addr; | ||
558 | addr += sz; | ||
559 | tp->thread_siblings = addr; | ||
560 | |||
561 | for (i = 0; i < nr; i++) { | ||
562 | ret = build_cpu_topo(tp, i); | ||
563 | if (ret < 0) | ||
564 | break; | ||
565 | } | ||
566 | if (ret) { | ||
567 | free_cpu_topo(tp); | ||
568 | tp = NULL; | ||
569 | } | ||
570 | return tp; | ||
571 | } | ||
572 | |||
573 | static int write_cpu_topology(int fd, struct perf_header *h __used, | ||
574 | struct perf_evlist *evlist __used) | ||
575 | { | ||
576 | struct cpu_topo *tp; | ||
577 | u32 i; | ||
578 | int ret; | ||
579 | |||
580 | tp = build_cpu_topology(); | ||
581 | if (!tp) | ||
582 | return -1; | ||
583 | |||
584 | ret = do_write(fd, &tp->core_sib, sizeof(tp->core_sib)); | ||
585 | if (ret < 0) | ||
586 | goto done; | ||
587 | |||
588 | for (i = 0; i < tp->core_sib; i++) { | ||
589 | ret = do_write_string(fd, tp->core_siblings[i]); | ||
590 | if (ret < 0) | ||
591 | goto done; | ||
592 | } | ||
593 | ret = do_write(fd, &tp->thread_sib, sizeof(tp->thread_sib)); | ||
594 | if (ret < 0) | ||
595 | goto done; | ||
596 | |||
597 | for (i = 0; i < tp->thread_sib; i++) { | ||
598 | ret = do_write_string(fd, tp->thread_siblings[i]); | ||
599 | if (ret < 0) | ||
600 | break; | ||
601 | } | ||
602 | done: | ||
603 | free_cpu_topo(tp); | ||
604 | return ret; | ||
605 | } | ||
606 | |||
607 | |||
608 | |||
609 | static int write_total_mem(int fd, struct perf_header *h __used, | ||
610 | struct perf_evlist *evlist __used) | ||
611 | { | ||
612 | char *buf = NULL; | ||
613 | FILE *fp; | ||
614 | size_t len = 0; | ||
615 | int ret = -1, n; | ||
616 | uint64_t mem; | ||
617 | |||
618 | fp = fopen("/proc/meminfo", "r"); | ||
619 | if (!fp) | ||
620 | return -1; | ||
621 | |||
622 | while (getline(&buf, &len, fp) > 0) { | ||
623 | ret = strncmp(buf, "MemTotal:", 9); | ||
624 | if (!ret) | ||
625 | break; | ||
626 | } | ||
627 | if (!ret) { | ||
628 | n = sscanf(buf, "%*s %"PRIu64, &mem); | ||
629 | if (n == 1) | ||
630 | ret = do_write(fd, &mem, sizeof(mem)); | ||
631 | } | ||
632 | free(buf); | ||
633 | fclose(fp); | ||
634 | return ret; | ||
635 | } | ||
636 | |||
637 | static int write_topo_node(int fd, int node) | ||
638 | { | ||
639 | char str[MAXPATHLEN]; | ||
640 | char field[32]; | ||
641 | char *buf = NULL, *p; | ||
642 | size_t len = 0; | ||
643 | FILE *fp; | ||
644 | u64 mem_total, mem_free, mem; | ||
645 | int ret = -1; | ||
646 | |||
647 | sprintf(str, "/sys/devices/system/node/node%d/meminfo", node); | ||
648 | fp = fopen(str, "r"); | ||
649 | if (!fp) | ||
650 | return -1; | ||
651 | |||
652 | while (getline(&buf, &len, fp) > 0) { | ||
653 | /* skip over invalid lines */ | ||
654 | if (!strchr(buf, ':')) | ||
655 | continue; | ||
656 | if (sscanf(buf, "%*s %*d %s %"PRIu64, field, &mem) != 2) | ||
657 | goto done; | ||
658 | if (!strcmp(field, "MemTotal:")) | ||
659 | mem_total = mem; | ||
660 | if (!strcmp(field, "MemFree:")) | ||
661 | mem_free = mem; | ||
662 | } | ||
663 | |||
664 | fclose(fp); | ||
665 | |||
666 | ret = do_write(fd, &mem_total, sizeof(u64)); | ||
667 | if (ret) | ||
668 | goto done; | ||
669 | |||
670 | ret = do_write(fd, &mem_free, sizeof(u64)); | ||
671 | if (ret) | ||
672 | goto done; | ||
673 | |||
674 | ret = -1; | ||
675 | sprintf(str, "/sys/devices/system/node/node%d/cpulist", node); | ||
676 | |||
677 | fp = fopen(str, "r"); | ||
678 | if (!fp) | ||
679 | goto done; | ||
680 | |||
681 | if (getline(&buf, &len, fp) <= 0) | ||
682 | goto done; | ||
683 | |||
684 | p = strchr(buf, '\n'); | ||
685 | if (p) | ||
686 | *p = '\0'; | ||
687 | |||
688 | ret = do_write_string(fd, buf); | ||
689 | done: | ||
690 | free(buf); | ||
691 | fclose(fp); | ||
692 | return ret; | ||
693 | } | ||
694 | |||
695 | static int write_numa_topology(int fd, struct perf_header *h __used, | ||
696 | struct perf_evlist *evlist __used) | ||
697 | { | ||
698 | char *buf = NULL; | ||
699 | size_t len = 0; | ||
700 | FILE *fp; | ||
701 | struct cpu_map *node_map = NULL; | ||
702 | char *c; | ||
703 | u32 nr, i, j; | ||
704 | int ret = -1; | ||
705 | |||
706 | fp = fopen("/sys/devices/system/node/online", "r"); | ||
707 | if (!fp) | ||
708 | return -1; | ||
709 | |||
710 | if (getline(&buf, &len, fp) <= 0) | ||
711 | goto done; | ||
712 | |||
713 | c = strchr(buf, '\n'); | ||
714 | if (c) | ||
715 | *c = '\0'; | ||
716 | |||
717 | node_map = cpu_map__new(buf); | ||
718 | if (!node_map) | ||
719 | goto done; | ||
720 | |||
721 | nr = (u32)node_map->nr; | ||
722 | |||
723 | ret = do_write(fd, &nr, sizeof(nr)); | ||
724 | if (ret < 0) | ||
725 | goto done; | ||
726 | |||
727 | for (i = 0; i < nr; i++) { | ||
728 | j = (u32)node_map->map[i]; | ||
729 | ret = do_write(fd, &j, sizeof(j)); | ||
730 | if (ret < 0) | ||
731 | break; | ||
732 | |||
733 | ret = write_topo_node(fd, i); | ||
734 | if (ret < 0) | ||
735 | break; | ||
736 | } | ||
737 | done: | ||
738 | free(buf); | ||
739 | fclose(fp); | ||
740 | free(node_map); | ||
741 | return ret; | ||
742 | } | ||
743 | |||
744 | /* | ||
745 | * default get_cpuid(): nothing gets recorded | ||
746 | * actual implementation must be in arch/$(ARCH)/util/header.c | ||
747 | */ | ||
748 | int __attribute__((weak)) get_cpuid(char *buffer __used, size_t sz __used) | ||
749 | { | ||
750 | return -1; | ||
751 | } | ||
752 | |||
753 | static int write_cpuid(int fd, struct perf_header *h __used, | ||
754 | struct perf_evlist *evlist __used) | ||
755 | { | ||
756 | char buffer[64]; | ||
757 | int ret; | ||
758 | |||
759 | ret = get_cpuid(buffer, sizeof(buffer)); | ||
760 | if (!ret) | ||
761 | goto write_it; | ||
762 | |||
763 | return -1; | ||
764 | write_it: | ||
765 | return do_write_string(fd, buffer); | ||
766 | } | ||
767 | |||
768 | static void print_hostname(struct perf_header *ph, int fd, FILE *fp) | ||
769 | { | ||
770 | char *str = do_read_string(fd, ph); | ||
771 | fprintf(fp, "# hostname : %s\n", str); | ||
772 | free(str); | ||
773 | } | ||
774 | |||
775 | static void print_osrelease(struct perf_header *ph, int fd, FILE *fp) | ||
776 | { | ||
777 | char *str = do_read_string(fd, ph); | ||
778 | fprintf(fp, "# os release : %s\n", str); | ||
779 | free(str); | ||
780 | } | ||
781 | |||
782 | static void print_arch(struct perf_header *ph, int fd, FILE *fp) | ||
783 | { | ||
784 | char *str = do_read_string(fd, ph); | ||
785 | fprintf(fp, "# arch : %s\n", str); | ||
786 | free(str); | ||
787 | } | ||
788 | |||
789 | static void print_cpudesc(struct perf_header *ph, int fd, FILE *fp) | ||
790 | { | ||
791 | char *str = do_read_string(fd, ph); | ||
792 | fprintf(fp, "# cpudesc : %s\n", str); | ||
793 | free(str); | ||
794 | } | ||
795 | |||
796 | static void print_nrcpus(struct perf_header *ph, int fd, FILE *fp) | ||
797 | { | ||
798 | ssize_t ret; | ||
799 | u32 nr; | ||
800 | |||
801 | ret = read(fd, &nr, sizeof(nr)); | ||
802 | if (ret != (ssize_t)sizeof(nr)) | ||
803 | nr = -1; /* interpreted as error */ | ||
804 | |||
805 | if (ph->needs_swap) | ||
806 | nr = bswap_32(nr); | ||
807 | |||
808 | fprintf(fp, "# nrcpus online : %u\n", nr); | ||
809 | |||
810 | ret = read(fd, &nr, sizeof(nr)); | ||
811 | if (ret != (ssize_t)sizeof(nr)) | ||
812 | nr = -1; /* interpreted as error */ | ||
813 | |||
814 | if (ph->needs_swap) | ||
815 | nr = bswap_32(nr); | ||
816 | |||
817 | fprintf(fp, "# nrcpus avail : %u\n", nr); | ||
818 | } | ||
819 | |||
820 | static void print_version(struct perf_header *ph, int fd, FILE *fp) | ||
821 | { | ||
822 | char *str = do_read_string(fd, ph); | ||
823 | fprintf(fp, "# perf version : %s\n", str); | ||
824 | free(str); | ||
825 | } | ||
826 | |||
827 | static void print_cmdline(struct perf_header *ph, int fd, FILE *fp) | ||
828 | { | ||
829 | ssize_t ret; | ||
830 | char *str; | ||
831 | u32 nr, i; | ||
832 | |||
833 | ret = read(fd, &nr, sizeof(nr)); | ||
834 | if (ret != (ssize_t)sizeof(nr)) | ||
835 | return; | ||
836 | |||
837 | if (ph->needs_swap) | ||
838 | nr = bswap_32(nr); | ||
839 | |||
840 | fprintf(fp, "# cmdline : "); | ||
841 | |||
842 | for (i = 0; i < nr; i++) { | ||
843 | str = do_read_string(fd, ph); | ||
844 | fprintf(fp, "%s ", str); | ||
845 | free(str); | ||
846 | } | ||
847 | fputc('\n', fp); | ||
848 | } | ||
849 | |||
850 | static void print_cpu_topology(struct perf_header *ph, int fd, FILE *fp) | ||
851 | { | ||
852 | ssize_t ret; | ||
853 | u32 nr, i; | ||
854 | char *str; | ||
855 | |||
856 | ret = read(fd, &nr, sizeof(nr)); | ||
857 | if (ret != (ssize_t)sizeof(nr)) | ||
858 | return; | ||
859 | |||
860 | if (ph->needs_swap) | ||
861 | nr = bswap_32(nr); | ||
862 | |||
863 | for (i = 0; i < nr; i++) { | ||
864 | str = do_read_string(fd, ph); | ||
865 | fprintf(fp, "# sibling cores : %s\n", str); | ||
866 | free(str); | ||
867 | } | ||
868 | |||
869 | ret = read(fd, &nr, sizeof(nr)); | ||
870 | if (ret != (ssize_t)sizeof(nr)) | ||
871 | return; | ||
872 | |||
873 | if (ph->needs_swap) | ||
874 | nr = bswap_32(nr); | ||
875 | |||
876 | for (i = 0; i < nr; i++) { | ||
877 | str = do_read_string(fd, ph); | ||
878 | fprintf(fp, "# sibling threads : %s\n", str); | ||
879 | free(str); | ||
880 | } | ||
881 | } | ||
882 | |||
883 | static void print_event_desc(struct perf_header *ph, int fd, FILE *fp) | ||
884 | { | ||
885 | struct perf_event_attr attr; | ||
886 | uint64_t id; | ||
887 | void *buf = NULL; | ||
888 | char *str; | ||
889 | u32 nre, sz, nr, i, j, msz; | ||
890 | int ret; | ||
891 | |||
892 | /* number of events */ | ||
893 | ret = read(fd, &nre, sizeof(nre)); | ||
894 | if (ret != (ssize_t)sizeof(nre)) | ||
895 | goto error; | ||
896 | |||
897 | if (ph->needs_swap) | ||
898 | nre = bswap_32(nre); | ||
899 | |||
900 | ret = read(fd, &sz, sizeof(sz)); | ||
901 | if (ret != (ssize_t)sizeof(sz)) | ||
902 | goto error; | ||
903 | |||
904 | if (ph->needs_swap) | ||
905 | sz = bswap_32(sz); | ||
906 | |||
907 | /* | ||
908 | * ensure it is at least to our ABI rev | ||
909 | */ | ||
910 | if (sz < (u32)sizeof(attr)) | ||
911 | goto error; | ||
912 | |||
913 | memset(&attr, 0, sizeof(attr)); | ||
914 | |||
915 | /* read entire region to sync up to next field */ | ||
916 | buf = malloc(sz); | ||
917 | if (!buf) | ||
918 | goto error; | ||
919 | |||
920 | msz = sizeof(attr); | ||
921 | if (sz < msz) | ||
922 | msz = sz; | ||
923 | |||
924 | for (i = 0 ; i < nre; i++) { | ||
925 | |||
926 | ret = read(fd, buf, sz); | ||
927 | if (ret != (ssize_t)sz) | ||
928 | goto error; | ||
929 | |||
930 | if (ph->needs_swap) | ||
931 | perf_event__attr_swap(buf); | ||
932 | |||
933 | memcpy(&attr, buf, msz); | ||
934 | |||
935 | ret = read(fd, &nr, sizeof(nr)); | ||
936 | if (ret != (ssize_t)sizeof(nr)) | ||
937 | goto error; | ||
938 | |||
939 | if (ph->needs_swap) | ||
940 | nr = bswap_32(nr); | ||
941 | |||
942 | str = do_read_string(fd, ph); | ||
943 | fprintf(fp, "# event : name = %s, ", str); | ||
944 | free(str); | ||
945 | |||
946 | fprintf(fp, "type = %d, config = 0x%"PRIx64 | ||
947 | ", config1 = 0x%"PRIx64", config2 = 0x%"PRIx64, | ||
948 | attr.type, | ||
949 | (u64)attr.config, | ||
950 | (u64)attr.config1, | ||
951 | (u64)attr.config2); | ||
952 | |||
953 | fprintf(fp, ", excl_usr = %d, excl_kern = %d", | ||
954 | attr.exclude_user, | ||
955 | attr.exclude_kernel); | ||
956 | |||
957 | if (nr) | ||
958 | fprintf(fp, ", id = {"); | ||
959 | |||
960 | for (j = 0 ; j < nr; j++) { | ||
961 | ret = read(fd, &id, sizeof(id)); | ||
962 | if (ret != (ssize_t)sizeof(id)) | ||
963 | goto error; | ||
964 | |||
965 | if (ph->needs_swap) | ||
966 | id = bswap_64(id); | ||
967 | |||
968 | if (j) | ||
969 | fputc(',', fp); | ||
970 | |||
971 | fprintf(fp, " %"PRIu64, id); | ||
972 | } | ||
973 | if (nr && j == nr) | ||
974 | fprintf(fp, " }"); | ||
975 | fputc('\n', fp); | ||
976 | } | ||
977 | free(buf); | ||
978 | return; | ||
979 | error: | ||
980 | fprintf(fp, "# event desc: not available or unable to read\n"); | ||
981 | } | ||
982 | |||
983 | static void print_total_mem(struct perf_header *h __used, int fd, FILE *fp) | ||
984 | { | ||
985 | uint64_t mem; | ||
986 | ssize_t ret; | ||
987 | |||
988 | ret = read(fd, &mem, sizeof(mem)); | ||
989 | if (ret != sizeof(mem)) | ||
990 | goto error; | ||
991 | |||
992 | if (h->needs_swap) | ||
993 | mem = bswap_64(mem); | ||
994 | |||
995 | fprintf(fp, "# total memory : %"PRIu64" kB\n", mem); | ||
996 | return; | ||
997 | error: | ||
998 | fprintf(fp, "# total memory : unknown\n"); | ||
999 | } | ||
1000 | |||
1001 | static void print_numa_topology(struct perf_header *h __used, int fd, FILE *fp) | ||
1002 | { | ||
1003 | ssize_t ret; | ||
1004 | u32 nr, c, i; | ||
1005 | char *str; | ||
1006 | uint64_t mem_total, mem_free; | ||
1007 | |||
1008 | /* nr nodes */ | ||
1009 | ret = read(fd, &nr, sizeof(nr)); | ||
1010 | if (ret != (ssize_t)sizeof(nr)) | ||
1011 | goto error; | ||
1012 | |||
1013 | if (h->needs_swap) | ||
1014 | nr = bswap_32(nr); | ||
1015 | |||
1016 | for (i = 0; i < nr; i++) { | ||
1017 | |||
1018 | /* node number */ | ||
1019 | ret = read(fd, &c, sizeof(c)); | ||
1020 | if (ret != (ssize_t)sizeof(c)) | ||
1021 | goto error; | ||
1022 | |||
1023 | if (h->needs_swap) | ||
1024 | c = bswap_32(c); | ||
1025 | |||
1026 | ret = read(fd, &mem_total, sizeof(u64)); | ||
1027 | if (ret != sizeof(u64)) | ||
1028 | goto error; | ||
1029 | |||
1030 | ret = read(fd, &mem_free, sizeof(u64)); | ||
1031 | if (ret != sizeof(u64)) | ||
1032 | goto error; | ||
1033 | |||
1034 | if (h->needs_swap) { | ||
1035 | mem_total = bswap_64(mem_total); | ||
1036 | mem_free = bswap_64(mem_free); | ||
1037 | } | ||
1038 | |||
1039 | fprintf(fp, "# node%u meminfo : total = %"PRIu64" kB," | ||
1040 | " free = %"PRIu64" kB\n", | ||
1041 | c, | ||
1042 | mem_total, | ||
1043 | mem_free); | ||
1044 | |||
1045 | str = do_read_string(fd, h); | ||
1046 | fprintf(fp, "# node%u cpu list : %s\n", c, str); | ||
1047 | free(str); | ||
1048 | } | ||
1049 | return; | ||
1050 | error: | ||
1051 | fprintf(fp, "# numa topology : not available\n"); | ||
1052 | } | ||
1053 | |||
1054 | static void print_cpuid(struct perf_header *ph, int fd, FILE *fp) | ||
1055 | { | ||
1056 | char *str = do_read_string(fd, ph); | ||
1057 | fprintf(fp, "# cpuid : %s\n", str); | ||
1058 | free(str); | ||
1059 | } | ||
1060 | |||
1061 | struct feature_ops { | ||
1062 | int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist); | ||
1063 | void (*print)(struct perf_header *h, int fd, FILE *fp); | ||
1064 | const char *name; | ||
1065 | bool full_only; | ||
1066 | }; | ||
1067 | |||
1068 | #define FEAT_OPA(n, w, p) \ | ||
1069 | [n] = { .name = #n, .write = w, .print = p } | ||
1070 | #define FEAT_OPF(n, w, p) \ | ||
1071 | [n] = { .name = #n, .write = w, .print = p, .full_only = true } | ||
1072 | |||
1073 | static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = { | ||
1074 | FEAT_OPA(HEADER_TRACE_INFO, write_trace_info, NULL), | ||
1075 | FEAT_OPA(HEADER_BUILD_ID, write_build_id, NULL), | ||
1076 | FEAT_OPA(HEADER_HOSTNAME, write_hostname, print_hostname), | ||
1077 | FEAT_OPA(HEADER_OSRELEASE, write_osrelease, print_osrelease), | ||
1078 | FEAT_OPA(HEADER_VERSION, write_version, print_version), | ||
1079 | FEAT_OPA(HEADER_ARCH, write_arch, print_arch), | ||
1080 | FEAT_OPA(HEADER_NRCPUS, write_nrcpus, print_nrcpus), | ||
1081 | FEAT_OPA(HEADER_CPUDESC, write_cpudesc, print_cpudesc), | ||
1082 | FEAT_OPA(HEADER_CPUID, write_cpuid, print_cpuid), | ||
1083 | FEAT_OPA(HEADER_TOTAL_MEM, write_total_mem, print_total_mem), | ||
1084 | FEAT_OPA(HEADER_EVENT_DESC, write_event_desc, print_event_desc), | ||
1085 | FEAT_OPA(HEADER_CMDLINE, write_cmdline, print_cmdline), | ||
1086 | FEAT_OPF(HEADER_CPU_TOPOLOGY, write_cpu_topology, print_cpu_topology), | ||
1087 | FEAT_OPF(HEADER_NUMA_TOPOLOGY, write_numa_topology, print_numa_topology), | ||
1088 | }; | ||
1089 | |||
1090 | struct header_print_data { | ||
1091 | FILE *fp; | ||
1092 | bool full; /* extended list of headers */ | ||
1093 | }; | ||
1094 | |||
1095 | static int perf_file_section__fprintf_info(struct perf_file_section *section, | ||
1096 | struct perf_header *ph, | ||
1097 | int feat, int fd, void *data) | ||
1098 | { | ||
1099 | struct header_print_data *hd = data; | ||
1100 | |||
1101 | if (lseek(fd, section->offset, SEEK_SET) == (off_t)-1) { | ||
1102 | pr_debug("Failed to lseek to %" PRIu64 " offset for feature " | ||
1103 | "%d, continuing...\n", section->offset, feat); | ||
1104 | return 0; | ||
1105 | } | ||
1106 | if (feat < HEADER_TRACE_INFO || feat >= HEADER_LAST_FEATURE) { | ||
1107 | pr_warning("unknown feature %d\n", feat); | ||
1108 | return -1; | ||
1109 | } | ||
1110 | if (!feat_ops[feat].print) | ||
1111 | return 0; | ||
1112 | |||
1113 | if (!feat_ops[feat].full_only || hd->full) | ||
1114 | feat_ops[feat].print(ph, fd, hd->fp); | ||
1115 | else | ||
1116 | fprintf(hd->fp, "# %s info available, use -I to display\n", | ||
1117 | feat_ops[feat].name); | ||
1118 | |||
1119 | return 0; | ||
1120 | } | ||
1121 | |||
1122 | int perf_header__fprintf_info(struct perf_session *session, FILE *fp, bool full) | ||
1123 | { | ||
1124 | struct header_print_data hd; | ||
1125 | struct perf_header *header = &session->header; | ||
1126 | int fd = session->fd; | ||
1127 | hd.fp = fp; | ||
1128 | hd.full = full; | ||
1129 | |||
1130 | perf_header__process_sections(header, fd, &hd, | ||
1131 | perf_file_section__fprintf_info); | ||
1132 | return 0; | ||
1133 | } | ||
1134 | |||
113 | #define dsos__for_each_with_build_id(pos, head) \ | 1135 | #define dsos__for_each_with_build_id(pos, head) \ |
114 | list_for_each_entry(pos, head, node) \ | 1136 | list_for_each_entry(pos, head, node) \ |
115 | if (!pos->has_build_id) \ | 1137 | if (!pos->has_build_id) \ |
@@ -356,15 +1378,41 @@ static bool perf_session__read_build_ids(struct perf_session *session, bool with | |||
356 | return ret; | 1378 | return ret; |
357 | } | 1379 | } |
358 | 1380 | ||
1381 | static int do_write_feat(int fd, struct perf_header *h, int type, | ||
1382 | struct perf_file_section **p, | ||
1383 | struct perf_evlist *evlist) | ||
1384 | { | ||
1385 | int err; | ||
1386 | int ret = 0; | ||
1387 | |||
1388 | if (perf_header__has_feat(h, type)) { | ||
1389 | |||
1390 | (*p)->offset = lseek(fd, 0, SEEK_CUR); | ||
1391 | |||
1392 | err = feat_ops[type].write(fd, h, evlist); | ||
1393 | if (err < 0) { | ||
1394 | pr_debug("failed to write feature %d\n", type); | ||
1395 | |||
1396 | /* undo anything written */ | ||
1397 | lseek(fd, (*p)->offset, SEEK_SET); | ||
1398 | |||
1399 | return -1; | ||
1400 | } | ||
1401 | (*p)->size = lseek(fd, 0, SEEK_CUR) - (*p)->offset; | ||
1402 | (*p)++; | ||
1403 | } | ||
1404 | return ret; | ||
1405 | } | ||
1406 | |||
359 | static int perf_header__adds_write(struct perf_header *header, | 1407 | static int perf_header__adds_write(struct perf_header *header, |
360 | struct perf_evlist *evlist, int fd) | 1408 | struct perf_evlist *evlist, int fd) |
361 | { | 1409 | { |
362 | int nr_sections; | 1410 | int nr_sections; |
363 | struct perf_session *session; | 1411 | struct perf_session *session; |
364 | struct perf_file_section *feat_sec; | 1412 | struct perf_file_section *feat_sec, *p; |
365 | int sec_size; | 1413 | int sec_size; |
366 | u64 sec_start; | 1414 | u64 sec_start; |
367 | int idx = 0, err; | 1415 | int err; |
368 | 1416 | ||
369 | session = container_of(header, struct perf_session, header); | 1417 | session = container_of(header, struct perf_session, header); |
370 | 1418 | ||
@@ -376,7 +1424,7 @@ static int perf_header__adds_write(struct perf_header *header, | |||
376 | if (!nr_sections) | 1424 | if (!nr_sections) |
377 | return 0; | 1425 | return 0; |
378 | 1426 | ||
379 | feat_sec = calloc(sizeof(*feat_sec), nr_sections); | 1427 | feat_sec = p = calloc(sizeof(*feat_sec), nr_sections); |
380 | if (feat_sec == NULL) | 1428 | if (feat_sec == NULL) |
381 | return -ENOMEM; | 1429 | return -ENOMEM; |
382 | 1430 | ||
@@ -385,36 +1433,69 @@ static int perf_header__adds_write(struct perf_header *header, | |||
385 | sec_start = header->data_offset + header->data_size; | 1433 | sec_start = header->data_offset + header->data_size; |
386 | lseek(fd, sec_start + sec_size, SEEK_SET); | 1434 | lseek(fd, sec_start + sec_size, SEEK_SET); |
387 | 1435 | ||
388 | if (perf_header__has_feat(header, HEADER_TRACE_INFO)) { | 1436 | err = do_write_feat(fd, header, HEADER_TRACE_INFO, &p, evlist); |
389 | struct perf_file_section *trace_sec; | 1437 | if (err) |
390 | 1438 | goto out_free; | |
391 | trace_sec = &feat_sec[idx++]; | ||
392 | 1439 | ||
393 | /* Write trace info */ | 1440 | err = do_write_feat(fd, header, HEADER_BUILD_ID, &p, evlist); |
394 | trace_sec->offset = lseek(fd, 0, SEEK_CUR); | 1441 | if (err) { |
395 | read_tracing_data(fd, &evlist->entries); | 1442 | perf_header__clear_feat(header, HEADER_BUILD_ID); |
396 | trace_sec->size = lseek(fd, 0, SEEK_CUR) - trace_sec->offset; | 1443 | goto out_free; |
397 | } | 1444 | } |
398 | 1445 | ||
399 | if (perf_header__has_feat(header, HEADER_BUILD_ID)) { | 1446 | err = do_write_feat(fd, header, HEADER_HOSTNAME, &p, evlist); |
400 | struct perf_file_section *buildid_sec; | 1447 | if (err) |
1448 | perf_header__clear_feat(header, HEADER_HOSTNAME); | ||
401 | 1449 | ||
402 | buildid_sec = &feat_sec[idx++]; | 1450 | err = do_write_feat(fd, header, HEADER_OSRELEASE, &p, evlist); |
1451 | if (err) | ||
1452 | perf_header__clear_feat(header, HEADER_OSRELEASE); | ||
403 | 1453 | ||
404 | /* Write build-ids */ | 1454 | err = do_write_feat(fd, header, HEADER_VERSION, &p, evlist); |
405 | buildid_sec->offset = lseek(fd, 0, SEEK_CUR); | 1455 | if (err) |
406 | err = dsos__write_buildid_table(header, fd); | 1456 | perf_header__clear_feat(header, HEADER_VERSION); |
407 | if (err < 0) { | 1457 | |
408 | pr_debug("failed to write buildid table\n"); | 1458 | err = do_write_feat(fd, header, HEADER_ARCH, &p, evlist); |
409 | goto out_free; | 1459 | if (err) |
410 | } | 1460 | perf_header__clear_feat(header, HEADER_ARCH); |
411 | buildid_sec->size = lseek(fd, 0, SEEK_CUR) - | 1461 | |
412 | buildid_sec->offset; | 1462 | err = do_write_feat(fd, header, HEADER_NRCPUS, &p, evlist); |
413 | if (!no_buildid_cache) | 1463 | if (err) |
414 | perf_session__cache_build_ids(session); | 1464 | perf_header__clear_feat(header, HEADER_NRCPUS); |
415 | } | 1465 | |
1466 | err = do_write_feat(fd, header, HEADER_CPUDESC, &p, evlist); | ||
1467 | if (err) | ||
1468 | perf_header__clear_feat(header, HEADER_CPUDESC); | ||
1469 | |||
1470 | err = do_write_feat(fd, header, HEADER_CPUID, &p, evlist); | ||
1471 | if (err) | ||
1472 | perf_header__clear_feat(header, HEADER_CPUID); | ||
1473 | |||
1474 | err = do_write_feat(fd, header, HEADER_TOTAL_MEM, &p, evlist); | ||
1475 | if (err) | ||
1476 | perf_header__clear_feat(header, HEADER_TOTAL_MEM); | ||
1477 | |||
1478 | err = do_write_feat(fd, header, HEADER_CMDLINE, &p, evlist); | ||
1479 | if (err) | ||
1480 | perf_header__clear_feat(header, HEADER_CMDLINE); | ||
1481 | |||
1482 | err = do_write_feat(fd, header, HEADER_EVENT_DESC, &p, evlist); | ||
1483 | if (err) | ||
1484 | perf_header__clear_feat(header, HEADER_EVENT_DESC); | ||
1485 | |||
1486 | err = do_write_feat(fd, header, HEADER_CPU_TOPOLOGY, &p, evlist); | ||
1487 | if (err) | ||
1488 | perf_header__clear_feat(header, HEADER_CPU_TOPOLOGY); | ||
1489 | |||
1490 | err = do_write_feat(fd, header, HEADER_NUMA_TOPOLOGY, &p, evlist); | ||
1491 | if (err) | ||
1492 | perf_header__clear_feat(header, HEADER_NUMA_TOPOLOGY); | ||
416 | 1493 | ||
417 | lseek(fd, sec_start, SEEK_SET); | 1494 | lseek(fd, sec_start, SEEK_SET); |
1495 | /* | ||
1496 | * may write more than needed due to dropped feature, but | ||
1497 | * this is okay, reader will skip the mising entries | ||
1498 | */ | ||
418 | err = do_write(fd, feat_sec, sec_size); | 1499 | err = do_write(fd, feat_sec, sec_size); |
419 | if (err < 0) | 1500 | if (err < 0) |
420 | pr_debug("failed to write feature section\n"); | 1501 | pr_debug("failed to write feature section\n"); |
@@ -554,9 +1635,10 @@ static int perf_header__getbuffer64(struct perf_header *header, | |||
554 | } | 1635 | } |
555 | 1636 | ||
556 | int perf_header__process_sections(struct perf_header *header, int fd, | 1637 | int perf_header__process_sections(struct perf_header *header, int fd, |
1638 | void *data, | ||
557 | int (*process)(struct perf_file_section *section, | 1639 | int (*process)(struct perf_file_section *section, |
558 | struct perf_header *ph, | 1640 | struct perf_header *ph, |
559 | int feat, int fd)) | 1641 | int feat, int fd, void *data)) |
560 | { | 1642 | { |
561 | struct perf_file_section *feat_sec; | 1643 | struct perf_file_section *feat_sec; |
562 | int nr_sections; | 1644 | int nr_sections; |
@@ -584,7 +1666,7 @@ int perf_header__process_sections(struct perf_header *header, int fd, | |||
584 | if (perf_header__has_feat(header, feat)) { | 1666 | if (perf_header__has_feat(header, feat)) { |
585 | struct perf_file_section *sec = &feat_sec[idx++]; | 1667 | struct perf_file_section *sec = &feat_sec[idx++]; |
586 | 1668 | ||
587 | err = process(sec, header, feat, fd); | 1669 | err = process(sec, header, feat, fd, data); |
588 | if (err < 0) | 1670 | if (err < 0) |
589 | break; | 1671 | break; |
590 | } | 1672 | } |
@@ -796,7 +1878,7 @@ out: | |||
796 | 1878 | ||
797 | static int perf_file_section__process(struct perf_file_section *section, | 1879 | static int perf_file_section__process(struct perf_file_section *section, |
798 | struct perf_header *ph, | 1880 | struct perf_header *ph, |
799 | int feat, int fd) | 1881 | int feat, int fd, void *data __used) |
800 | { | 1882 | { |
801 | if (lseek(fd, section->offset, SEEK_SET) == (off_t)-1) { | 1883 | if (lseek(fd, section->offset, SEEK_SET) == (off_t)-1) { |
802 | pr_debug("Failed to lseek to %" PRIu64 " offset for feature " | 1884 | pr_debug("Failed to lseek to %" PRIu64 " offset for feature " |
@@ -935,7 +2017,8 @@ int perf_session__read_header(struct perf_session *session, int fd) | |||
935 | event_count = f_header.event_types.size / sizeof(struct perf_trace_event_type); | 2017 | event_count = f_header.event_types.size / sizeof(struct perf_trace_event_type); |
936 | } | 2018 | } |
937 | 2019 | ||
938 | perf_header__process_sections(header, fd, perf_file_section__process); | 2020 | perf_header__process_sections(header, fd, NULL, |
2021 | perf_file_section__process); | ||
939 | 2022 | ||
940 | lseek(fd, header->data_offset, SEEK_SET); | 2023 | lseek(fd, header->data_offset, SEEK_SET); |
941 | 2024 | ||
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 1886256768a1..3d5a742f4a2a 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h | |||
@@ -12,6 +12,20 @@ | |||
12 | enum { | 12 | enum { |
13 | HEADER_TRACE_INFO = 1, | 13 | HEADER_TRACE_INFO = 1, |
14 | HEADER_BUILD_ID, | 14 | HEADER_BUILD_ID, |
15 | |||
16 | HEADER_HOSTNAME, | ||
17 | HEADER_OSRELEASE, | ||
18 | HEADER_VERSION, | ||
19 | HEADER_ARCH, | ||
20 | HEADER_NRCPUS, | ||
21 | HEADER_CPUDESC, | ||
22 | HEADER_CPUID, | ||
23 | HEADER_TOTAL_MEM, | ||
24 | HEADER_CMDLINE, | ||
25 | HEADER_EVENT_DESC, | ||
26 | HEADER_CPU_TOPOLOGY, | ||
27 | HEADER_NUMA_TOPOLOGY, | ||
28 | |||
15 | HEADER_LAST_FEATURE, | 29 | HEADER_LAST_FEATURE, |
16 | }; | 30 | }; |
17 | 31 | ||
@@ -68,10 +82,15 @@ void perf_header__set_feat(struct perf_header *header, int feat); | |||
68 | void perf_header__clear_feat(struct perf_header *header, int feat); | 82 | void perf_header__clear_feat(struct perf_header *header, int feat); |
69 | bool perf_header__has_feat(const struct perf_header *header, int feat); | 83 | bool perf_header__has_feat(const struct perf_header *header, int feat); |
70 | 84 | ||
85 | int perf_header__set_cmdline(int argc, const char **argv); | ||
86 | |||
71 | int perf_header__process_sections(struct perf_header *header, int fd, | 87 | int perf_header__process_sections(struct perf_header *header, int fd, |
88 | void *data, | ||
72 | int (*process)(struct perf_file_section *section, | 89 | int (*process)(struct perf_file_section *section, |
73 | struct perf_header *ph, | 90 | struct perf_header *ph, |
74 | int feat, int fd)); | 91 | int feat, int fd, void *data)); |
92 | |||
93 | int perf_header__fprintf_info(struct perf_session *s, FILE *fp, bool full); | ||
75 | 94 | ||
76 | int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, | 95 | int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, |
77 | const char *name, bool is_kallsyms); | 96 | const char *name, bool is_kallsyms); |
@@ -104,4 +123,10 @@ int perf_event__synthesize_build_id(struct dso *pos, u16 misc, | |||
104 | struct perf_session *session); | 123 | struct perf_session *session); |
105 | int perf_event__process_build_id(union perf_event *event, | 124 | int perf_event__process_build_id(union perf_event *event, |
106 | struct perf_session *session); | 125 | struct perf_session *session); |
126 | |||
127 | /* | ||
128 | * arch specific callback | ||
129 | */ | ||
130 | int get_cpuid(char *buffer, size_t sz); | ||
131 | |||
107 | #endif /* __PERF_HEADER_H */ | 132 | #endif /* __PERF_HEADER_H */ |
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 677e1da6bb3e..50c8fece1681 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
@@ -18,56 +18,56 @@ struct callchain_param callchain_param = { | |||
18 | .order = ORDER_CALLEE | 18 | .order = ORDER_CALLEE |
19 | }; | 19 | }; |
20 | 20 | ||
21 | u16 hists__col_len(struct hists *self, enum hist_column col) | 21 | u16 hists__col_len(struct hists *hists, enum hist_column col) |
22 | { | 22 | { |
23 | return self->col_len[col]; | 23 | return hists->col_len[col]; |
24 | } | 24 | } |
25 | 25 | ||
26 | void hists__set_col_len(struct hists *self, enum hist_column col, u16 len) | 26 | void hists__set_col_len(struct hists *hists, enum hist_column col, u16 len) |
27 | { | 27 | { |
28 | self->col_len[col] = len; | 28 | hists->col_len[col] = len; |
29 | } | 29 | } |
30 | 30 | ||
31 | bool hists__new_col_len(struct hists *self, enum hist_column col, u16 len) | 31 | bool hists__new_col_len(struct hists *hists, enum hist_column col, u16 len) |
32 | { | 32 | { |
33 | if (len > hists__col_len(self, col)) { | 33 | if (len > hists__col_len(hists, col)) { |
34 | hists__set_col_len(self, col, len); | 34 | hists__set_col_len(hists, col, len); |
35 | return true; | 35 | return true; |
36 | } | 36 | } |
37 | return false; | 37 | return false; |
38 | } | 38 | } |
39 | 39 | ||
40 | static void hists__reset_col_len(struct hists *self) | 40 | static void hists__reset_col_len(struct hists *hists) |
41 | { | 41 | { |
42 | enum hist_column col; | 42 | enum hist_column col; |
43 | 43 | ||
44 | for (col = 0; col < HISTC_NR_COLS; ++col) | 44 | for (col = 0; col < HISTC_NR_COLS; ++col) |
45 | hists__set_col_len(self, col, 0); | 45 | hists__set_col_len(hists, col, 0); |
46 | } | 46 | } |
47 | 47 | ||
48 | static void hists__calc_col_len(struct hists *self, struct hist_entry *h) | 48 | static void hists__calc_col_len(struct hists *hists, struct hist_entry *h) |
49 | { | 49 | { |
50 | u16 len; | 50 | u16 len; |
51 | 51 | ||
52 | if (h->ms.sym) | 52 | if (h->ms.sym) |
53 | hists__new_col_len(self, HISTC_SYMBOL, h->ms.sym->namelen); | 53 | hists__new_col_len(hists, HISTC_SYMBOL, h->ms.sym->namelen); |
54 | else { | 54 | else { |
55 | const unsigned int unresolved_col_width = BITS_PER_LONG / 4; | 55 | const unsigned int unresolved_col_width = BITS_PER_LONG / 4; |
56 | 56 | ||
57 | if (hists__col_len(self, HISTC_DSO) < unresolved_col_width && | 57 | if (hists__col_len(hists, HISTC_DSO) < unresolved_col_width && |
58 | !symbol_conf.col_width_list_str && !symbol_conf.field_sep && | 58 | !symbol_conf.col_width_list_str && !symbol_conf.field_sep && |
59 | !symbol_conf.dso_list) | 59 | !symbol_conf.dso_list) |
60 | hists__set_col_len(self, HISTC_DSO, | 60 | hists__set_col_len(hists, HISTC_DSO, |
61 | unresolved_col_width); | 61 | unresolved_col_width); |
62 | } | 62 | } |
63 | 63 | ||
64 | len = thread__comm_len(h->thread); | 64 | len = thread__comm_len(h->thread); |
65 | if (hists__new_col_len(self, HISTC_COMM, len)) | 65 | if (hists__new_col_len(hists, HISTC_COMM, len)) |
66 | hists__set_col_len(self, HISTC_THREAD, len + 6); | 66 | hists__set_col_len(hists, HISTC_THREAD, len + 6); |
67 | 67 | ||
68 | if (h->ms.map) { | 68 | if (h->ms.map) { |
69 | len = dso__name_len(h->ms.map->dso); | 69 | len = dso__name_len(h->ms.map->dso); |
70 | hists__new_col_len(self, HISTC_DSO, len); | 70 | hists__new_col_len(hists, HISTC_DSO, len); |
71 | } | 71 | } |
72 | } | 72 | } |
73 | 73 | ||
@@ -92,6 +92,41 @@ static void hist_entry__add_cpumode_period(struct hist_entry *self, | |||
92 | } | 92 | } |
93 | } | 93 | } |
94 | 94 | ||
95 | static void hist_entry__decay(struct hist_entry *he) | ||
96 | { | ||
97 | he->period = (he->period * 7) / 8; | ||
98 | he->nr_events = (he->nr_events * 7) / 8; | ||
99 | } | ||
100 | |||
101 | static bool hists__decay_entry(struct hists *hists, struct hist_entry *he) | ||
102 | { | ||
103 | hists->stats.total_period -= he->period; | ||
104 | hist_entry__decay(he); | ||
105 | hists->stats.total_period += he->period; | ||
106 | return he->period == 0; | ||
107 | } | ||
108 | |||
109 | void hists__decay_entries(struct hists *hists) | ||
110 | { | ||
111 | struct rb_node *next = rb_first(&hists->entries); | ||
112 | struct hist_entry *n; | ||
113 | |||
114 | while (next) { | ||
115 | n = rb_entry(next, struct hist_entry, rb_node); | ||
116 | next = rb_next(&n->rb_node); | ||
117 | |||
118 | if (hists__decay_entry(hists, n)) { | ||
119 | rb_erase(&n->rb_node, &hists->entries); | ||
120 | |||
121 | if (sort__need_collapse) | ||
122 | rb_erase(&n->rb_node_in, &hists->entries_collapsed); | ||
123 | |||
124 | hist_entry__free(n); | ||
125 | --hists->nr_entries; | ||
126 | } | ||
127 | } | ||
128 | } | ||
129 | |||
95 | /* | 130 | /* |
96 | * histogram, sorted on item, collects periods | 131 | * histogram, sorted on item, collects periods |
97 | */ | 132 | */ |
@@ -113,11 +148,12 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template) | |||
113 | return self; | 148 | return self; |
114 | } | 149 | } |
115 | 150 | ||
116 | static void hists__inc_nr_entries(struct hists *self, struct hist_entry *h) | 151 | static void hists__inc_nr_entries(struct hists *hists, struct hist_entry *h) |
117 | { | 152 | { |
118 | if (!h->filtered) { | 153 | if (!h->filtered) { |
119 | hists__calc_col_len(self, h); | 154 | hists__calc_col_len(hists, h); |
120 | ++self->nr_entries; | 155 | ++hists->nr_entries; |
156 | hists->stats.total_period += h->period; | ||
121 | } | 157 | } |
122 | } | 158 | } |
123 | 159 | ||
@@ -128,11 +164,11 @@ static u8 symbol__parent_filter(const struct symbol *parent) | |||
128 | return 0; | 164 | return 0; |
129 | } | 165 | } |
130 | 166 | ||
131 | struct hist_entry *__hists__add_entry(struct hists *self, | 167 | struct hist_entry *__hists__add_entry(struct hists *hists, |
132 | struct addr_location *al, | 168 | struct addr_location *al, |
133 | struct symbol *sym_parent, u64 period) | 169 | struct symbol *sym_parent, u64 period) |
134 | { | 170 | { |
135 | struct rb_node **p = &self->entries.rb_node; | 171 | struct rb_node **p; |
136 | struct rb_node *parent = NULL; | 172 | struct rb_node *parent = NULL; |
137 | struct hist_entry *he; | 173 | struct hist_entry *he; |
138 | struct hist_entry entry = { | 174 | struct hist_entry entry = { |
@@ -150,9 +186,13 @@ struct hist_entry *__hists__add_entry(struct hists *self, | |||
150 | }; | 186 | }; |
151 | int cmp; | 187 | int cmp; |
152 | 188 | ||
189 | pthread_mutex_lock(&hists->lock); | ||
190 | |||
191 | p = &hists->entries_in->rb_node; | ||
192 | |||
153 | while (*p != NULL) { | 193 | while (*p != NULL) { |
154 | parent = *p; | 194 | parent = *p; |
155 | he = rb_entry(parent, struct hist_entry, rb_node); | 195 | he = rb_entry(parent, struct hist_entry, rb_node_in); |
156 | 196 | ||
157 | cmp = hist_entry__cmp(&entry, he); | 197 | cmp = hist_entry__cmp(&entry, he); |
158 | 198 | ||
@@ -170,12 +210,14 @@ struct hist_entry *__hists__add_entry(struct hists *self, | |||
170 | 210 | ||
171 | he = hist_entry__new(&entry); | 211 | he = hist_entry__new(&entry); |
172 | if (!he) | 212 | if (!he) |
173 | return NULL; | 213 | goto out_unlock; |
174 | rb_link_node(&he->rb_node, parent, p); | 214 | |
175 | rb_insert_color(&he->rb_node, &self->entries); | 215 | rb_link_node(&he->rb_node_in, parent, p); |
176 | hists__inc_nr_entries(self, he); | 216 | rb_insert_color(&he->rb_node_in, hists->entries_in); |
177 | out: | 217 | out: |
178 | hist_entry__add_cpumode_period(he, al->cpumode, period); | 218 | hist_entry__add_cpumode_period(he, al->cpumode, period); |
219 | out_unlock: | ||
220 | pthread_mutex_unlock(&hists->lock); | ||
179 | return he; | 221 | return he; |
180 | } | 222 | } |
181 | 223 | ||
@@ -222,7 +264,7 @@ void hist_entry__free(struct hist_entry *he) | |||
222 | * collapse the histogram | 264 | * collapse the histogram |
223 | */ | 265 | */ |
224 | 266 | ||
225 | static bool hists__collapse_insert_entry(struct hists *self, | 267 | static bool hists__collapse_insert_entry(struct hists *hists, |
226 | struct rb_root *root, | 268 | struct rb_root *root, |
227 | struct hist_entry *he) | 269 | struct hist_entry *he) |
228 | { | 270 | { |
@@ -233,15 +275,16 @@ static bool hists__collapse_insert_entry(struct hists *self, | |||
233 | 275 | ||
234 | while (*p != NULL) { | 276 | while (*p != NULL) { |
235 | parent = *p; | 277 | parent = *p; |
236 | iter = rb_entry(parent, struct hist_entry, rb_node); | 278 | iter = rb_entry(parent, struct hist_entry, rb_node_in); |
237 | 279 | ||
238 | cmp = hist_entry__collapse(iter, he); | 280 | cmp = hist_entry__collapse(iter, he); |
239 | 281 | ||
240 | if (!cmp) { | 282 | if (!cmp) { |
241 | iter->period += he->period; | 283 | iter->period += he->period; |
284 | iter->nr_events += he->nr_events; | ||
242 | if (symbol_conf.use_callchain) { | 285 | if (symbol_conf.use_callchain) { |
243 | callchain_cursor_reset(&self->callchain_cursor); | 286 | callchain_cursor_reset(&hists->callchain_cursor); |
244 | callchain_merge(&self->callchain_cursor, iter->callchain, | 287 | callchain_merge(&hists->callchain_cursor, iter->callchain, |
245 | he->callchain); | 288 | he->callchain); |
246 | } | 289 | } |
247 | hist_entry__free(he); | 290 | hist_entry__free(he); |
@@ -254,35 +297,57 @@ static bool hists__collapse_insert_entry(struct hists *self, | |||
254 | p = &(*p)->rb_right; | 297 | p = &(*p)->rb_right; |
255 | } | 298 | } |
256 | 299 | ||
257 | rb_link_node(&he->rb_node, parent, p); | 300 | rb_link_node(&he->rb_node_in, parent, p); |
258 | rb_insert_color(&he->rb_node, root); | 301 | rb_insert_color(&he->rb_node_in, root); |
259 | return true; | 302 | return true; |
260 | } | 303 | } |
261 | 304 | ||
262 | void hists__collapse_resort(struct hists *self) | 305 | static struct rb_root *hists__get_rotate_entries_in(struct hists *hists) |
306 | { | ||
307 | struct rb_root *root; | ||
308 | |||
309 | pthread_mutex_lock(&hists->lock); | ||
310 | |||
311 | root = hists->entries_in; | ||
312 | if (++hists->entries_in > &hists->entries_in_array[1]) | ||
313 | hists->entries_in = &hists->entries_in_array[0]; | ||
314 | |||
315 | pthread_mutex_unlock(&hists->lock); | ||
316 | |||
317 | return root; | ||
318 | } | ||
319 | |||
320 | static void __hists__collapse_resort(struct hists *hists, bool threaded) | ||
263 | { | 321 | { |
264 | struct rb_root tmp; | 322 | struct rb_root *root; |
265 | struct rb_node *next; | 323 | struct rb_node *next; |
266 | struct hist_entry *n; | 324 | struct hist_entry *n; |
267 | 325 | ||
268 | if (!sort__need_collapse) | 326 | if (!sort__need_collapse && !threaded) |
269 | return; | 327 | return; |
270 | 328 | ||
271 | tmp = RB_ROOT; | 329 | root = hists__get_rotate_entries_in(hists); |
272 | next = rb_first(&self->entries); | 330 | next = rb_first(root); |
273 | self->nr_entries = 0; | 331 | hists->stats.total_period = 0; |
274 | hists__reset_col_len(self); | ||
275 | 332 | ||
276 | while (next) { | 333 | while (next) { |
277 | n = rb_entry(next, struct hist_entry, rb_node); | 334 | n = rb_entry(next, struct hist_entry, rb_node_in); |
278 | next = rb_next(&n->rb_node); | 335 | next = rb_next(&n->rb_node_in); |
279 | 336 | ||
280 | rb_erase(&n->rb_node, &self->entries); | 337 | rb_erase(&n->rb_node_in, root); |
281 | if (hists__collapse_insert_entry(self, &tmp, n)) | 338 | if (hists__collapse_insert_entry(hists, &hists->entries_collapsed, n)) |
282 | hists__inc_nr_entries(self, n); | 339 | hists__inc_nr_entries(hists, n); |
283 | } | 340 | } |
341 | } | ||
284 | 342 | ||
285 | self->entries = tmp; | 343 | void hists__collapse_resort(struct hists *hists) |
344 | { | ||
345 | return __hists__collapse_resort(hists, false); | ||
346 | } | ||
347 | |||
348 | void hists__collapse_resort_threaded(struct hists *hists) | ||
349 | { | ||
350 | return __hists__collapse_resort(hists, true); | ||
286 | } | 351 | } |
287 | 352 | ||
288 | /* | 353 | /* |
@@ -315,31 +380,43 @@ static void __hists__insert_output_entry(struct rb_root *entries, | |||
315 | rb_insert_color(&he->rb_node, entries); | 380 | rb_insert_color(&he->rb_node, entries); |
316 | } | 381 | } |
317 | 382 | ||
318 | void hists__output_resort(struct hists *self) | 383 | static void __hists__output_resort(struct hists *hists, bool threaded) |
319 | { | 384 | { |
320 | struct rb_root tmp; | 385 | struct rb_root *root; |
321 | struct rb_node *next; | 386 | struct rb_node *next; |
322 | struct hist_entry *n; | 387 | struct hist_entry *n; |
323 | u64 min_callchain_hits; | 388 | u64 min_callchain_hits; |
324 | 389 | ||
325 | min_callchain_hits = self->stats.total_period * (callchain_param.min_percent / 100); | 390 | min_callchain_hits = hists->stats.total_period * (callchain_param.min_percent / 100); |
391 | |||
392 | if (sort__need_collapse || threaded) | ||
393 | root = &hists->entries_collapsed; | ||
394 | else | ||
395 | root = hists->entries_in; | ||
326 | 396 | ||
327 | tmp = RB_ROOT; | 397 | next = rb_first(root); |
328 | next = rb_first(&self->entries); | 398 | hists->entries = RB_ROOT; |
329 | 399 | ||
330 | self->nr_entries = 0; | 400 | hists->nr_entries = 0; |
331 | hists__reset_col_len(self); | 401 | hists__reset_col_len(hists); |
332 | 402 | ||
333 | while (next) { | 403 | while (next) { |
334 | n = rb_entry(next, struct hist_entry, rb_node); | 404 | n = rb_entry(next, struct hist_entry, rb_node_in); |
335 | next = rb_next(&n->rb_node); | 405 | next = rb_next(&n->rb_node_in); |
336 | 406 | ||
337 | rb_erase(&n->rb_node, &self->entries); | 407 | __hists__insert_output_entry(&hists->entries, n, min_callchain_hits); |
338 | __hists__insert_output_entry(&tmp, n, min_callchain_hits); | 408 | hists__inc_nr_entries(hists, n); |
339 | hists__inc_nr_entries(self, n); | ||
340 | } | 409 | } |
410 | } | ||
411 | |||
412 | void hists__output_resort(struct hists *hists) | ||
413 | { | ||
414 | return __hists__output_resort(hists, false); | ||
415 | } | ||
341 | 416 | ||
342 | self->entries = tmp; | 417 | void hists__output_resort_threaded(struct hists *hists) |
418 | { | ||
419 | return __hists__output_resort(hists, true); | ||
343 | } | 420 | } |
344 | 421 | ||
345 | static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin) | 422 | static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin) |
@@ -594,6 +671,21 @@ static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, | |||
594 | return ret; | 671 | return ret; |
595 | } | 672 | } |
596 | 673 | ||
674 | void hists__output_recalc_col_len(struct hists *hists, int max_rows) | ||
675 | { | ||
676 | struct rb_node *next = rb_first(&hists->entries); | ||
677 | struct hist_entry *n; | ||
678 | int row = 0; | ||
679 | |||
680 | hists__reset_col_len(hists); | ||
681 | |||
682 | while (next && row++ < max_rows) { | ||
683 | n = rb_entry(next, struct hist_entry, rb_node); | ||
684 | hists__calc_col_len(hists, n); | ||
685 | next = rb_next(&n->rb_node); | ||
686 | } | ||
687 | } | ||
688 | |||
597 | int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, | 689 | int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, |
598 | struct hists *hists, struct hists *pair_hists, | 690 | struct hists *hists, struct hists *pair_hists, |
599 | bool show_displacement, long displacement, | 691 | bool show_displacement, long displacement, |
@@ -664,6 +756,13 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, | |||
664 | ret += snprintf(s + ret, size - ret, "%11" PRIu64, nr_events); | 756 | ret += snprintf(s + ret, size - ret, "%11" PRIu64, nr_events); |
665 | } | 757 | } |
666 | 758 | ||
759 | if (symbol_conf.show_total_period) { | ||
760 | if (sep) | ||
761 | ret += snprintf(s + ret, size - ret, "%c%" PRIu64, *sep, period); | ||
762 | else | ||
763 | ret += snprintf(s + ret, size - ret, " %12" PRIu64, period); | ||
764 | } | ||
765 | |||
667 | if (pair_hists) { | 766 | if (pair_hists) { |
668 | char bf[32]; | 767 | char bf[32]; |
669 | double old_percent = 0, new_percent = 0, diff; | 768 | double old_percent = 0, new_percent = 0, diff; |
@@ -710,12 +809,16 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, | |||
710 | return ret; | 809 | return ret; |
711 | } | 810 | } |
712 | 811 | ||
713 | int hist_entry__fprintf(struct hist_entry *self, struct hists *hists, | 812 | int hist_entry__fprintf(struct hist_entry *he, size_t size, struct hists *hists, |
714 | struct hists *pair_hists, bool show_displacement, | 813 | struct hists *pair_hists, bool show_displacement, |
715 | long displacement, FILE *fp, u64 session_total) | 814 | long displacement, FILE *fp, u64 session_total) |
716 | { | 815 | { |
717 | char bf[512]; | 816 | char bf[512]; |
718 | hist_entry__snprintf(self, bf, sizeof(bf), hists, pair_hists, | 817 | |
818 | if (size == 0 || size > sizeof(bf)) | ||
819 | size = sizeof(bf); | ||
820 | |||
821 | hist_entry__snprintf(he, bf, size, hists, pair_hists, | ||
719 | show_displacement, displacement, | 822 | show_displacement, displacement, |
720 | true, session_total); | 823 | true, session_total); |
721 | return fprintf(fp, "%s\n", bf); | 824 | return fprintf(fp, "%s\n", bf); |
@@ -738,8 +841,9 @@ static size_t hist_entry__fprintf_callchain(struct hist_entry *self, | |||
738 | left_margin); | 841 | left_margin); |
739 | } | 842 | } |
740 | 843 | ||
741 | size_t hists__fprintf(struct hists *self, struct hists *pair, | 844 | size_t hists__fprintf(struct hists *hists, struct hists *pair, |
742 | bool show_displacement, FILE *fp) | 845 | bool show_displacement, bool show_header, int max_rows, |
846 | int max_cols, FILE *fp) | ||
743 | { | 847 | { |
744 | struct sort_entry *se; | 848 | struct sort_entry *se; |
745 | struct rb_node *nd; | 849 | struct rb_node *nd; |
@@ -749,9 +853,13 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, | |||
749 | unsigned int width; | 853 | unsigned int width; |
750 | const char *sep = symbol_conf.field_sep; | 854 | const char *sep = symbol_conf.field_sep; |
751 | const char *col_width = symbol_conf.col_width_list_str; | 855 | const char *col_width = symbol_conf.col_width_list_str; |
856 | int nr_rows = 0; | ||
752 | 857 | ||
753 | init_rem_hits(); | 858 | init_rem_hits(); |
754 | 859 | ||
860 | if (!show_header) | ||
861 | goto print_entries; | ||
862 | |||
755 | fprintf(fp, "# %s", pair ? "Baseline" : "Overhead"); | 863 | fprintf(fp, "# %s", pair ? "Baseline" : "Overhead"); |
756 | 864 | ||
757 | if (symbol_conf.show_nr_samples) { | 865 | if (symbol_conf.show_nr_samples) { |
@@ -761,6 +869,13 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, | |||
761 | fputs(" Samples ", fp); | 869 | fputs(" Samples ", fp); |
762 | } | 870 | } |
763 | 871 | ||
872 | if (symbol_conf.show_total_period) { | ||
873 | if (sep) | ||
874 | ret += fprintf(fp, "%cPeriod", *sep); | ||
875 | else | ||
876 | ret += fprintf(fp, " Period "); | ||
877 | } | ||
878 | |||
764 | if (symbol_conf.show_cpu_utilization) { | 879 | if (symbol_conf.show_cpu_utilization) { |
765 | if (sep) { | 880 | if (sep) { |
766 | ret += fprintf(fp, "%csys", *sep); | 881 | ret += fprintf(fp, "%csys", *sep); |
@@ -803,18 +918,21 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, | |||
803 | width = strlen(se->se_header); | 918 | width = strlen(se->se_header); |
804 | if (symbol_conf.col_width_list_str) { | 919 | if (symbol_conf.col_width_list_str) { |
805 | if (col_width) { | 920 | if (col_width) { |
806 | hists__set_col_len(self, se->se_width_idx, | 921 | hists__set_col_len(hists, se->se_width_idx, |
807 | atoi(col_width)); | 922 | atoi(col_width)); |
808 | col_width = strchr(col_width, ','); | 923 | col_width = strchr(col_width, ','); |
809 | if (col_width) | 924 | if (col_width) |
810 | ++col_width; | 925 | ++col_width; |
811 | } | 926 | } |
812 | } | 927 | } |
813 | if (!hists__new_col_len(self, se->se_width_idx, width)) | 928 | if (!hists__new_col_len(hists, se->se_width_idx, width)) |
814 | width = hists__col_len(self, se->se_width_idx); | 929 | width = hists__col_len(hists, se->se_width_idx); |
815 | fprintf(fp, " %*s", width, se->se_header); | 930 | fprintf(fp, " %*s", width, se->se_header); |
816 | } | 931 | } |
932 | |||
817 | fprintf(fp, "\n"); | 933 | fprintf(fp, "\n"); |
934 | if (max_rows && ++nr_rows >= max_rows) | ||
935 | goto out; | ||
818 | 936 | ||
819 | if (sep) | 937 | if (sep) |
820 | goto print_entries; | 938 | goto print_entries; |
@@ -822,6 +940,8 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, | |||
822 | fprintf(fp, "# ........"); | 940 | fprintf(fp, "# ........"); |
823 | if (symbol_conf.show_nr_samples) | 941 | if (symbol_conf.show_nr_samples) |
824 | fprintf(fp, " .........."); | 942 | fprintf(fp, " .........."); |
943 | if (symbol_conf.show_total_period) | ||
944 | fprintf(fp, " ............"); | ||
825 | if (pair) { | 945 | if (pair) { |
826 | fprintf(fp, " .........."); | 946 | fprintf(fp, " .........."); |
827 | if (show_displacement) | 947 | if (show_displacement) |
@@ -834,17 +954,23 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, | |||
834 | continue; | 954 | continue; |
835 | 955 | ||
836 | fprintf(fp, " "); | 956 | fprintf(fp, " "); |
837 | width = hists__col_len(self, se->se_width_idx); | 957 | width = hists__col_len(hists, se->se_width_idx); |
838 | if (width == 0) | 958 | if (width == 0) |
839 | width = strlen(se->se_header); | 959 | width = strlen(se->se_header); |
840 | for (i = 0; i < width; i++) | 960 | for (i = 0; i < width; i++) |
841 | fprintf(fp, "."); | 961 | fprintf(fp, "."); |
842 | } | 962 | } |
843 | 963 | ||
844 | fprintf(fp, "\n#\n"); | 964 | fprintf(fp, "\n"); |
965 | if (max_rows && ++nr_rows >= max_rows) | ||
966 | goto out; | ||
967 | |||
968 | fprintf(fp, "#\n"); | ||
969 | if (max_rows && ++nr_rows >= max_rows) | ||
970 | goto out; | ||
845 | 971 | ||
846 | print_entries: | 972 | print_entries: |
847 | for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { | 973 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { |
848 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | 974 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); |
849 | 975 | ||
850 | if (h->filtered) | 976 | if (h->filtered) |
@@ -858,19 +984,22 @@ print_entries: | |||
858 | displacement = 0; | 984 | displacement = 0; |
859 | ++position; | 985 | ++position; |
860 | } | 986 | } |
861 | ret += hist_entry__fprintf(h, self, pair, show_displacement, | 987 | ret += hist_entry__fprintf(h, max_cols, hists, pair, show_displacement, |
862 | displacement, fp, self->stats.total_period); | 988 | displacement, fp, hists->stats.total_period); |
863 | 989 | ||
864 | if (symbol_conf.use_callchain) | 990 | if (symbol_conf.use_callchain) |
865 | ret += hist_entry__fprintf_callchain(h, self, fp, | 991 | ret += hist_entry__fprintf_callchain(h, hists, fp, |
866 | self->stats.total_period); | 992 | hists->stats.total_period); |
993 | if (max_rows && ++nr_rows >= max_rows) | ||
994 | goto out; | ||
995 | |||
867 | if (h->ms.map == NULL && verbose > 1) { | 996 | if (h->ms.map == NULL && verbose > 1) { |
868 | __map_groups__fprintf_maps(&h->thread->mg, | 997 | __map_groups__fprintf_maps(&h->thread->mg, |
869 | MAP__FUNCTION, verbose, fp); | 998 | MAP__FUNCTION, verbose, fp); |
870 | fprintf(fp, "%.10s end\n", graph_dotted_line); | 999 | fprintf(fp, "%.10s end\n", graph_dotted_line); |
871 | } | 1000 | } |
872 | } | 1001 | } |
873 | 1002 | out: | |
874 | free(rem_sq_bracket); | 1003 | free(rem_sq_bracket); |
875 | 1004 | ||
876 | return ret; | 1005 | return ret; |
@@ -879,7 +1008,7 @@ print_entries: | |||
879 | /* | 1008 | /* |
880 | * See hists__fprintf to match the column widths | 1009 | * See hists__fprintf to match the column widths |
881 | */ | 1010 | */ |
882 | unsigned int hists__sort_list_width(struct hists *self) | 1011 | unsigned int hists__sort_list_width(struct hists *hists) |
883 | { | 1012 | { |
884 | struct sort_entry *se; | 1013 | struct sort_entry *se; |
885 | int ret = 9; /* total % */ | 1014 | int ret = 9; /* total % */ |
@@ -896,9 +1025,12 @@ unsigned int hists__sort_list_width(struct hists *self) | |||
896 | if (symbol_conf.show_nr_samples) | 1025 | if (symbol_conf.show_nr_samples) |
897 | ret += 11; | 1026 | ret += 11; |
898 | 1027 | ||
1028 | if (symbol_conf.show_total_period) | ||
1029 | ret += 13; | ||
1030 | |||
899 | list_for_each_entry(se, &hist_entry__sort_list, list) | 1031 | list_for_each_entry(se, &hist_entry__sort_list, list) |
900 | if (!se->elide) | 1032 | if (!se->elide) |
901 | ret += 2 + hists__col_len(self, se->se_width_idx); | 1033 | ret += 2 + hists__col_len(hists, se->se_width_idx); |
902 | 1034 | ||
903 | if (verbose) /* Addr + origin */ | 1035 | if (verbose) /* Addr + origin */ |
904 | ret += 3 + BITS_PER_LONG / 4; | 1036 | ret += 3 + BITS_PER_LONG / 4; |
@@ -906,32 +1038,32 @@ unsigned int hists__sort_list_width(struct hists *self) | |||
906 | return ret; | 1038 | return ret; |
907 | } | 1039 | } |
908 | 1040 | ||
909 | static void hists__remove_entry_filter(struct hists *self, struct hist_entry *h, | 1041 | static void hists__remove_entry_filter(struct hists *hists, struct hist_entry *h, |
910 | enum hist_filter filter) | 1042 | enum hist_filter filter) |
911 | { | 1043 | { |
912 | h->filtered &= ~(1 << filter); | 1044 | h->filtered &= ~(1 << filter); |
913 | if (h->filtered) | 1045 | if (h->filtered) |
914 | return; | 1046 | return; |
915 | 1047 | ||
916 | ++self->nr_entries; | 1048 | ++hists->nr_entries; |
917 | if (h->ms.unfolded) | 1049 | if (h->ms.unfolded) |
918 | self->nr_entries += h->nr_rows; | 1050 | hists->nr_entries += h->nr_rows; |
919 | h->row_offset = 0; | 1051 | h->row_offset = 0; |
920 | self->stats.total_period += h->period; | 1052 | hists->stats.total_period += h->period; |
921 | self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events; | 1053 | hists->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events; |
922 | 1054 | ||
923 | hists__calc_col_len(self, h); | 1055 | hists__calc_col_len(hists, h); |
924 | } | 1056 | } |
925 | 1057 | ||
926 | void hists__filter_by_dso(struct hists *self, const struct dso *dso) | 1058 | void hists__filter_by_dso(struct hists *hists, const struct dso *dso) |
927 | { | 1059 | { |
928 | struct rb_node *nd; | 1060 | struct rb_node *nd; |
929 | 1061 | ||
930 | self->nr_entries = self->stats.total_period = 0; | 1062 | hists->nr_entries = hists->stats.total_period = 0; |
931 | self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; | 1063 | hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0; |
932 | hists__reset_col_len(self); | 1064 | hists__reset_col_len(hists); |
933 | 1065 | ||
934 | for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { | 1066 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { |
935 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | 1067 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); |
936 | 1068 | ||
937 | if (symbol_conf.exclude_other && !h->parent) | 1069 | if (symbol_conf.exclude_other && !h->parent) |
@@ -942,19 +1074,19 @@ void hists__filter_by_dso(struct hists *self, const struct dso *dso) | |||
942 | continue; | 1074 | continue; |
943 | } | 1075 | } |
944 | 1076 | ||
945 | hists__remove_entry_filter(self, h, HIST_FILTER__DSO); | 1077 | hists__remove_entry_filter(hists, h, HIST_FILTER__DSO); |
946 | } | 1078 | } |
947 | } | 1079 | } |
948 | 1080 | ||
949 | void hists__filter_by_thread(struct hists *self, const struct thread *thread) | 1081 | void hists__filter_by_thread(struct hists *hists, const struct thread *thread) |
950 | { | 1082 | { |
951 | struct rb_node *nd; | 1083 | struct rb_node *nd; |
952 | 1084 | ||
953 | self->nr_entries = self->stats.total_period = 0; | 1085 | hists->nr_entries = hists->stats.total_period = 0; |
954 | self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; | 1086 | hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0; |
955 | hists__reset_col_len(self); | 1087 | hists__reset_col_len(hists); |
956 | 1088 | ||
957 | for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { | 1089 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { |
958 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | 1090 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); |
959 | 1091 | ||
960 | if (thread != NULL && h->thread != thread) { | 1092 | if (thread != NULL && h->thread != thread) { |
@@ -962,7 +1094,7 @@ void hists__filter_by_thread(struct hists *self, const struct thread *thread) | |||
962 | continue; | 1094 | continue; |
963 | } | 1095 | } |
964 | 1096 | ||
965 | hists__remove_entry_filter(self, h, HIST_FILTER__THREAD); | 1097 | hists__remove_entry_filter(hists, h, HIST_FILTER__THREAD); |
966 | } | 1098 | } |
967 | } | 1099 | } |
968 | 1100 | ||
@@ -976,13 +1108,13 @@ int hist_entry__annotate(struct hist_entry *he, size_t privsize) | |||
976 | return symbol__annotate(he->ms.sym, he->ms.map, privsize); | 1108 | return symbol__annotate(he->ms.sym, he->ms.map, privsize); |
977 | } | 1109 | } |
978 | 1110 | ||
979 | void hists__inc_nr_events(struct hists *self, u32 type) | 1111 | void hists__inc_nr_events(struct hists *hists, u32 type) |
980 | { | 1112 | { |
981 | ++self->stats.nr_events[0]; | 1113 | ++hists->stats.nr_events[0]; |
982 | ++self->stats.nr_events[type]; | 1114 | ++hists->stats.nr_events[type]; |
983 | } | 1115 | } |
984 | 1116 | ||
985 | size_t hists__fprintf_nr_events(struct hists *self, FILE *fp) | 1117 | size_t hists__fprintf_nr_events(struct hists *hists, FILE *fp) |
986 | { | 1118 | { |
987 | int i; | 1119 | int i; |
988 | size_t ret = 0; | 1120 | size_t ret = 0; |
@@ -990,7 +1122,7 @@ size_t hists__fprintf_nr_events(struct hists *self, FILE *fp) | |||
990 | for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) { | 1122 | for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) { |
991 | const char *name; | 1123 | const char *name; |
992 | 1124 | ||
993 | if (self->stats.nr_events[i] == 0) | 1125 | if (hists->stats.nr_events[i] == 0) |
994 | continue; | 1126 | continue; |
995 | 1127 | ||
996 | name = perf_event__name(i); | 1128 | name = perf_event__name(i); |
@@ -998,8 +1130,18 @@ size_t hists__fprintf_nr_events(struct hists *self, FILE *fp) | |||
998 | continue; | 1130 | continue; |
999 | 1131 | ||
1000 | ret += fprintf(fp, "%16s events: %10d\n", name, | 1132 | ret += fprintf(fp, "%16s events: %10d\n", name, |
1001 | self->stats.nr_events[i]); | 1133 | hists->stats.nr_events[i]); |
1002 | } | 1134 | } |
1003 | 1135 | ||
1004 | return ret; | 1136 | return ret; |
1005 | } | 1137 | } |
1138 | |||
1139 | void hists__init(struct hists *hists) | ||
1140 | { | ||
1141 | memset(hists, 0, sizeof(*hists)); | ||
1142 | hists->entries_in_array[0] = hists->entries_in_array[1] = RB_ROOT; | ||
1143 | hists->entries_in = &hists->entries_in_array[0]; | ||
1144 | hists->entries_collapsed = RB_ROOT; | ||
1145 | hists->entries = RB_ROOT; | ||
1146 | pthread_mutex_init(&hists->lock, NULL); | ||
1147 | } | ||
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 3beb97c4d822..7ea1e560e008 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h | |||
@@ -2,6 +2,7 @@ | |||
2 | #define __PERF_HIST_H | 2 | #define __PERF_HIST_H |
3 | 3 | ||
4 | #include <linux/types.h> | 4 | #include <linux/types.h> |
5 | #include <pthread.h> | ||
5 | #include "callchain.h" | 6 | #include "callchain.h" |
6 | 7 | ||
7 | extern struct callchain_param callchain_param; | 8 | extern struct callchain_param callchain_param; |
@@ -43,8 +44,12 @@ enum hist_column { | |||
43 | }; | 44 | }; |
44 | 45 | ||
45 | struct hists { | 46 | struct hists { |
47 | struct rb_root entries_in_array[2]; | ||
48 | struct rb_root *entries_in; | ||
46 | struct rb_root entries; | 49 | struct rb_root entries; |
50 | struct rb_root entries_collapsed; | ||
47 | u64 nr_entries; | 51 | u64 nr_entries; |
52 | pthread_mutex_t lock; | ||
48 | struct events_stats stats; | 53 | struct events_stats stats; |
49 | u64 event_stream; | 54 | u64 event_stream; |
50 | u16 col_len[HISTC_NR_COLS]; | 55 | u16 col_len[HISTC_NR_COLS]; |
@@ -52,14 +57,16 @@ struct hists { | |||
52 | struct callchain_cursor callchain_cursor; | 57 | struct callchain_cursor callchain_cursor; |
53 | }; | 58 | }; |
54 | 59 | ||
60 | void hists__init(struct hists *hists); | ||
61 | |||
55 | struct hist_entry *__hists__add_entry(struct hists *self, | 62 | struct hist_entry *__hists__add_entry(struct hists *self, |
56 | struct addr_location *al, | 63 | struct addr_location *al, |
57 | struct symbol *parent, u64 period); | 64 | struct symbol *parent, u64 period); |
58 | extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *); | 65 | extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *); |
59 | extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *); | 66 | extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *); |
60 | int hist_entry__fprintf(struct hist_entry *self, struct hists *hists, | 67 | int hist_entry__fprintf(struct hist_entry *he, size_t size, struct hists *hists, |
61 | struct hists *pair_hists, bool show_displacement, | 68 | struct hists *pair_hists, bool show_displacement, |
62 | long displacement, FILE *fp, u64 total); | 69 | long displacement, FILE *fp, u64 session_total); |
63 | int hist_entry__snprintf(struct hist_entry *self, char *bf, size_t size, | 70 | int hist_entry__snprintf(struct hist_entry *self, char *bf, size_t size, |
64 | struct hists *hists, struct hists *pair_hists, | 71 | struct hists *hists, struct hists *pair_hists, |
65 | bool show_displacement, long displacement, | 72 | bool show_displacement, long displacement, |
@@ -67,13 +74,19 @@ int hist_entry__snprintf(struct hist_entry *self, char *bf, size_t size, | |||
67 | void hist_entry__free(struct hist_entry *); | 74 | void hist_entry__free(struct hist_entry *); |
68 | 75 | ||
69 | void hists__output_resort(struct hists *self); | 76 | void hists__output_resort(struct hists *self); |
77 | void hists__output_resort_threaded(struct hists *hists); | ||
70 | void hists__collapse_resort(struct hists *self); | 78 | void hists__collapse_resort(struct hists *self); |
79 | void hists__collapse_resort_threaded(struct hists *hists); | ||
80 | |||
81 | void hists__decay_entries(struct hists *hists); | ||
82 | void hists__output_recalc_col_len(struct hists *hists, int max_rows); | ||
71 | 83 | ||
72 | void hists__inc_nr_events(struct hists *self, u32 type); | 84 | void hists__inc_nr_events(struct hists *self, u32 type); |
73 | size_t hists__fprintf_nr_events(struct hists *self, FILE *fp); | 85 | size_t hists__fprintf_nr_events(struct hists *self, FILE *fp); |
74 | 86 | ||
75 | size_t hists__fprintf(struct hists *self, struct hists *pair, | 87 | size_t hists__fprintf(struct hists *self, struct hists *pair, |
76 | bool show_displacement, FILE *fp); | 88 | bool show_displacement, bool show_header, |
89 | int max_rows, int max_cols, FILE *fp); | ||
77 | 90 | ||
78 | int hist_entry__inc_addr_samples(struct hist_entry *self, int evidx, u64 addr); | 91 | int hist_entry__inc_addr_samples(struct hist_entry *self, int evidx, u64 addr); |
79 | int hist_entry__annotate(struct hist_entry *self, size_t privsize); | 92 | int hist_entry__annotate(struct hist_entry *self, size_t privsize); |
@@ -90,13 +103,16 @@ struct perf_evlist; | |||
90 | #ifdef NO_NEWT_SUPPORT | 103 | #ifdef NO_NEWT_SUPPORT |
91 | static inline | 104 | static inline |
92 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __used, | 105 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __used, |
93 | const char *help __used) | 106 | const char *help __used, void(*timer)(void *arg) __used, void *arg, |
107 | int refresh __used) | ||
94 | { | 108 | { |
95 | return 0; | 109 | return 0; |
96 | } | 110 | } |
97 | 111 | ||
98 | static inline int hist_entry__tui_annotate(struct hist_entry *self __used, | 112 | static inline int hist_entry__tui_annotate(struct hist_entry *self __used, |
99 | int evidx __used) | 113 | int evidx __used, int nr_events __used, |
114 | void(*timer)(void *arg) __used, | ||
115 | void *arg __used, int delay_secs __used); | ||
100 | { | 116 | { |
101 | return 0; | 117 | return 0; |
102 | } | 118 | } |
@@ -104,12 +120,15 @@ static inline int hist_entry__tui_annotate(struct hist_entry *self __used, | |||
104 | #define KEY_RIGHT -2 | 120 | #define KEY_RIGHT -2 |
105 | #else | 121 | #else |
106 | #include <newt.h> | 122 | #include <newt.h> |
107 | int hist_entry__tui_annotate(struct hist_entry *self, int evidx); | 123 | int hist_entry__tui_annotate(struct hist_entry *he, int evidx, int nr_events, |
124 | void(*timer)(void *arg), void *arg, int delay_secs); | ||
108 | 125 | ||
109 | #define KEY_LEFT NEWT_KEY_LEFT | 126 | #define KEY_LEFT NEWT_KEY_LEFT |
110 | #define KEY_RIGHT NEWT_KEY_RIGHT | 127 | #define KEY_RIGHT NEWT_KEY_RIGHT |
111 | 128 | ||
112 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help); | 129 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, |
130 | void(*timer)(void *arg), void *arg, | ||
131 | int refresh); | ||
113 | #endif | 132 | #endif |
114 | 133 | ||
115 | unsigned int hists__sort_list_width(struct hists *self); | 134 | unsigned int hists__sort_list_width(struct hists *self); |
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 72458d9da5b1..20e011c99a94 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
@@ -1326,3 +1326,22 @@ int perf_session__cpu_bitmap(struct perf_session *session, | |||
1326 | 1326 | ||
1327 | return 0; | 1327 | return 0; |
1328 | } | 1328 | } |
1329 | |||
1330 | void perf_session__fprintf_info(struct perf_session *session, FILE *fp, | ||
1331 | bool full) | ||
1332 | { | ||
1333 | struct stat st; | ||
1334 | int ret; | ||
1335 | |||
1336 | if (session == NULL || fp == NULL) | ||
1337 | return; | ||
1338 | |||
1339 | ret = fstat(session->fd, &st); | ||
1340 | if (ret == -1) | ||
1341 | return; | ||
1342 | |||
1343 | fprintf(fp, "# ========\n"); | ||
1344 | fprintf(fp, "# captured on: %s", ctime(&st.st_ctime)); | ||
1345 | perf_header__fprintf_info(session, fp, full); | ||
1346 | fprintf(fp, "# ========\n#\n"); | ||
1347 | } | ||
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 974d0cbee5e9..514b06d41f05 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h | |||
@@ -177,4 +177,5 @@ void perf_session__print_ip(union perf_event *event, | |||
177 | int perf_session__cpu_bitmap(struct perf_session *session, | 177 | int perf_session__cpu_bitmap(struct perf_session *session, |
178 | const char *cpu_list, unsigned long *cpu_bitmap); | 178 | const char *cpu_list, unsigned long *cpu_bitmap); |
179 | 179 | ||
180 | void perf_session__fprintf_info(struct perf_session *s, FILE *fp, bool full); | ||
180 | #endif /* __PERF_SESSION_H */ | 181 | #endif /* __PERF_SESSION_H */ |
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 77d0388ad415..03851e301721 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h | |||
@@ -45,6 +45,7 @@ extern enum sort_type sort__first_dimension; | |||
45 | * @nr_rows - rows expanded in callchain, recalculated on folding/unfolding | 45 | * @nr_rows - rows expanded in callchain, recalculated on folding/unfolding |
46 | */ | 46 | */ |
47 | struct hist_entry { | 47 | struct hist_entry { |
48 | struct rb_node rb_node_in; | ||
48 | struct rb_node rb_node; | 49 | struct rb_node rb_node; |
49 | u64 period; | 50 | u64 period; |
50 | u64 period_sys; | 51 | u64 period_sys; |
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 077df15ee705..3f09a23f71b4 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
@@ -46,7 +46,6 @@ struct symbol_conf symbol_conf = { | |||
46 | .exclude_other = true, | 46 | .exclude_other = true, |
47 | .use_modules = true, | 47 | .use_modules = true, |
48 | .try_vmlinux_path = true, | 48 | .try_vmlinux_path = true, |
49 | .annotate_asm_raw = true, | ||
50 | .annotate_src = true, | 49 | .annotate_src = true, |
51 | .symfs = "", | 50 | .symfs = "", |
52 | }; | 51 | }; |
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 7733f0b3cd41..29f8d742e92f 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
@@ -72,6 +72,7 @@ struct symbol_conf { | |||
72 | use_modules, | 72 | use_modules, |
73 | sort_by_name, | 73 | sort_by_name, |
74 | show_nr_samples, | 74 | show_nr_samples, |
75 | show_total_period, | ||
75 | use_callchain, | 76 | use_callchain, |
76 | exclude_other, | 77 | exclude_other, |
77 | show_cpu_utilization, | 78 | show_cpu_utilization, |
diff --git a/tools/perf/util/top.c b/tools/perf/util/top.c index a11f60735a18..500471dffa4f 100644 --- a/tools/perf/util/top.c +++ b/tools/perf/util/top.c | |||
@@ -15,52 +15,6 @@ | |||
15 | #include "top.h" | 15 | #include "top.h" |
16 | #include <inttypes.h> | 16 | #include <inttypes.h> |
17 | 17 | ||
18 | /* | ||
19 | * Ordering weight: count-1 * count-2 * ... / count-n | ||
20 | */ | ||
21 | static double sym_weight(const struct sym_entry *sym, struct perf_top *top) | ||
22 | { | ||
23 | double weight = sym->snap_count; | ||
24 | int counter; | ||
25 | |||
26 | if (!top->display_weighted) | ||
27 | return weight; | ||
28 | |||
29 | for (counter = 1; counter < top->evlist->nr_entries - 1; counter++) | ||
30 | weight *= sym->count[counter]; | ||
31 | |||
32 | weight /= (sym->count[counter] + 1); | ||
33 | |||
34 | return weight; | ||
35 | } | ||
36 | |||
37 | static void perf_top__remove_active_sym(struct perf_top *top, struct sym_entry *syme) | ||
38 | { | ||
39 | pthread_mutex_lock(&top->active_symbols_lock); | ||
40 | list_del_init(&syme->node); | ||
41 | pthread_mutex_unlock(&top->active_symbols_lock); | ||
42 | } | ||
43 | |||
44 | static void rb_insert_active_sym(struct rb_root *tree, struct sym_entry *se) | ||
45 | { | ||
46 | struct rb_node **p = &tree->rb_node; | ||
47 | struct rb_node *parent = NULL; | ||
48 | struct sym_entry *iter; | ||
49 | |||
50 | while (*p != NULL) { | ||
51 | parent = *p; | ||
52 | iter = rb_entry(parent, struct sym_entry, rb_node); | ||
53 | |||
54 | if (se->weight > iter->weight) | ||
55 | p = &(*p)->rb_left; | ||
56 | else | ||
57 | p = &(*p)->rb_right; | ||
58 | } | ||
59 | |||
60 | rb_link_node(&se->rb_node, parent, p); | ||
61 | rb_insert_color(&se->rb_node, tree); | ||
62 | } | ||
63 | |||
64 | #define SNPRINTF(buf, size, fmt, args...) \ | 18 | #define SNPRINTF(buf, size, fmt, args...) \ |
65 | ({ \ | 19 | ({ \ |
66 | size_t r = snprintf(buf, size, fmt, ## args); \ | 20 | size_t r = snprintf(buf, size, fmt, ## args); \ |
@@ -69,7 +23,6 @@ static void rb_insert_active_sym(struct rb_root *tree, struct sym_entry *se) | |||
69 | 23 | ||
70 | size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size) | 24 | size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size) |
71 | { | 25 | { |
72 | struct perf_evsel *counter; | ||
73 | float samples_per_sec = top->samples / top->delay_secs; | 26 | float samples_per_sec = top->samples / top->delay_secs; |
74 | float ksamples_per_sec = top->kernel_samples / top->delay_secs; | 27 | float ksamples_per_sec = top->kernel_samples / top->delay_secs; |
75 | float esamples_percent = (100.0 * top->exact_samples) / top->samples; | 28 | float esamples_percent = (100.0 * top->exact_samples) / top->samples; |
@@ -104,7 +57,7 @@ size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size) | |||
104 | esamples_percent); | 57 | esamples_percent); |
105 | } | 58 | } |
106 | 59 | ||
107 | if (top->evlist->nr_entries == 1 || !top->display_weighted) { | 60 | if (top->evlist->nr_entries == 1) { |
108 | struct perf_evsel *first; | 61 | struct perf_evsel *first; |
109 | first = list_entry(top->evlist->entries.next, struct perf_evsel, node); | 62 | first = list_entry(top->evlist->entries.next, struct perf_evsel, node); |
110 | ret += SNPRINTF(bf + ret, size - ret, "%" PRIu64 "%s ", | 63 | ret += SNPRINTF(bf + ret, size - ret, "%" PRIu64 "%s ", |
@@ -112,27 +65,7 @@ size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size) | |||
112 | top->freq ? "Hz" : ""); | 65 | top->freq ? "Hz" : ""); |
113 | } | 66 | } |
114 | 67 | ||
115 | if (!top->display_weighted) { | 68 | ret += SNPRINTF(bf + ret, size - ret, "%s", event_name(top->sym_evsel)); |
116 | ret += SNPRINTF(bf + ret, size - ret, "%s", | ||
117 | event_name(top->sym_evsel)); | ||
118 | } else { | ||
119 | /* | ||
120 | * Don't let events eat all the space. Leaving 30 bytes | ||
121 | * for the rest should be enough. | ||
122 | */ | ||
123 | size_t last_pos = size - 30; | ||
124 | |||
125 | list_for_each_entry(counter, &top->evlist->entries, node) { | ||
126 | ret += SNPRINTF(bf + ret, size - ret, "%s%s", | ||
127 | counter->idx ? "/" : "", | ||
128 | event_name(counter)); | ||
129 | if (ret > last_pos) { | ||
130 | sprintf(bf + last_pos - 3, ".."); | ||
131 | ret = last_pos - 1; | ||
132 | break; | ||
133 | } | ||
134 | } | ||
135 | } | ||
136 | 69 | ||
137 | ret += SNPRINTF(bf + ret, size - ret, "], "); | 70 | ret += SNPRINTF(bf + ret, size - ret, "], "); |
138 | 71 | ||
@@ -166,73 +99,3 @@ void perf_top__reset_sample_counters(struct perf_top *top) | |||
166 | top->exact_samples = top->guest_kernel_samples = | 99 | top->exact_samples = top->guest_kernel_samples = |
167 | top->guest_us_samples = 0; | 100 | top->guest_us_samples = 0; |
168 | } | 101 | } |
169 | |||
170 | float perf_top__decay_samples(struct perf_top *top, struct rb_root *root) | ||
171 | { | ||
172 | struct sym_entry *syme, *n; | ||
173 | float sum_ksamples = 0.0; | ||
174 | int snap = !top->display_weighted ? top->sym_evsel->idx : 0, j; | ||
175 | |||
176 | /* Sort the active symbols */ | ||
177 | pthread_mutex_lock(&top->active_symbols_lock); | ||
178 | syme = list_entry(top->active_symbols.next, struct sym_entry, node); | ||
179 | pthread_mutex_unlock(&top->active_symbols_lock); | ||
180 | |||
181 | top->rb_entries = 0; | ||
182 | list_for_each_entry_safe_from(syme, n, &top->active_symbols, node) { | ||
183 | syme->snap_count = syme->count[snap]; | ||
184 | if (syme->snap_count != 0) { | ||
185 | |||
186 | if ((top->hide_user_symbols && | ||
187 | syme->map->dso->kernel == DSO_TYPE_USER) || | ||
188 | (top->hide_kernel_symbols && | ||
189 | syme->map->dso->kernel == DSO_TYPE_KERNEL)) { | ||
190 | perf_top__remove_active_sym(top, syme); | ||
191 | continue; | ||
192 | } | ||
193 | syme->weight = sym_weight(syme, top); | ||
194 | |||
195 | if ((int)syme->snap_count >= top->count_filter) { | ||
196 | rb_insert_active_sym(root, syme); | ||
197 | ++top->rb_entries; | ||
198 | } | ||
199 | sum_ksamples += syme->snap_count; | ||
200 | |||
201 | for (j = 0; j < top->evlist->nr_entries; j++) | ||
202 | syme->count[j] = top->zero ? 0 : syme->count[j] * 7 / 8; | ||
203 | } else | ||
204 | perf_top__remove_active_sym(top, syme); | ||
205 | } | ||
206 | |||
207 | return sum_ksamples; | ||
208 | } | ||
209 | |||
210 | /* | ||
211 | * Find the longest symbol name that will be displayed | ||
212 | */ | ||
213 | void perf_top__find_widths(struct perf_top *top, struct rb_root *root, | ||
214 | int *dso_width, int *dso_short_width, int *sym_width) | ||
215 | { | ||
216 | struct rb_node *nd; | ||
217 | int printed = 0; | ||
218 | |||
219 | *sym_width = *dso_width = *dso_short_width = 0; | ||
220 | |||
221 | for (nd = rb_first(root); nd; nd = rb_next(nd)) { | ||
222 | struct sym_entry *syme = rb_entry(nd, struct sym_entry, rb_node); | ||
223 | struct symbol *sym = sym_entry__symbol(syme); | ||
224 | |||
225 | if (++printed > top->print_entries || | ||
226 | (int)syme->snap_count < top->count_filter) | ||
227 | continue; | ||
228 | |||
229 | if (syme->map->dso->long_name_len > *dso_width) | ||
230 | *dso_width = syme->map->dso->long_name_len; | ||
231 | |||
232 | if (syme->map->dso->short_name_len > *dso_short_width) | ||
233 | *dso_short_width = syme->map->dso->short_name_len; | ||
234 | |||
235 | if (sym->namelen > *sym_width) | ||
236 | *sym_width = sym->namelen; | ||
237 | } | ||
238 | } | ||
diff --git a/tools/perf/util/top.h b/tools/perf/util/top.h index b07b0410463c..01d1057f3074 100644 --- a/tools/perf/util/top.h +++ b/tools/perf/util/top.h | |||
@@ -4,64 +4,32 @@ | |||
4 | #include "types.h" | 4 | #include "types.h" |
5 | #include "../perf.h" | 5 | #include "../perf.h" |
6 | #include <stddef.h> | 6 | #include <stddef.h> |
7 | #include <pthread.h> | ||
8 | #include <linux/list.h> | ||
9 | #include <linux/rbtree.h> | ||
10 | 7 | ||
11 | struct perf_evlist; | 8 | struct perf_evlist; |
12 | struct perf_evsel; | 9 | struct perf_evsel; |
13 | struct perf_session; | 10 | struct perf_session; |
14 | 11 | ||
15 | struct sym_entry { | ||
16 | struct rb_node rb_node; | ||
17 | struct list_head node; | ||
18 | unsigned long snap_count; | ||
19 | double weight; | ||
20 | struct map *map; | ||
21 | unsigned long count[0]; | ||
22 | }; | ||
23 | |||
24 | static inline struct symbol *sym_entry__symbol(struct sym_entry *self) | ||
25 | { | ||
26 | return ((void *)self) + symbol_conf.priv_size; | ||
27 | } | ||
28 | |||
29 | struct perf_top { | 12 | struct perf_top { |
30 | struct perf_evlist *evlist; | 13 | struct perf_evlist *evlist; |
31 | /* | 14 | /* |
32 | * Symbols will be added here in perf_event__process_sample and will | 15 | * Symbols will be added here in perf_event__process_sample and will |
33 | * get out after decayed. | 16 | * get out after decayed. |
34 | */ | 17 | */ |
35 | struct list_head active_symbols; | ||
36 | pthread_mutex_t active_symbols_lock; | ||
37 | pthread_cond_t active_symbols_cond; | ||
38 | u64 samples; | 18 | u64 samples; |
39 | u64 kernel_samples, us_samples; | 19 | u64 kernel_samples, us_samples; |
40 | u64 exact_samples; | 20 | u64 exact_samples; |
41 | u64 guest_us_samples, guest_kernel_samples; | 21 | u64 guest_us_samples, guest_kernel_samples; |
42 | u64 total_lost_warned; | 22 | u64 total_lost_warned; |
43 | int print_entries, count_filter, delay_secs; | 23 | int print_entries, count_filter, delay_secs; |
44 | int display_weighted, freq, rb_entries; | 24 | int freq; |
45 | pid_t target_pid, target_tid; | 25 | pid_t target_pid, target_tid; |
46 | bool hide_kernel_symbols, hide_user_symbols, zero; | 26 | bool hide_kernel_symbols, hide_user_symbols, zero; |
47 | const char *cpu_list; | 27 | const char *cpu_list; |
48 | struct sym_entry *sym_filter_entry; | 28 | struct hist_entry *sym_filter_entry; |
49 | struct perf_evsel *sym_evsel; | 29 | struct perf_evsel *sym_evsel; |
50 | struct perf_session *session; | 30 | struct perf_session *session; |
51 | }; | 31 | }; |
52 | 32 | ||
53 | size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size); | 33 | size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size); |
54 | void perf_top__reset_sample_counters(struct perf_top *top); | 34 | void perf_top__reset_sample_counters(struct perf_top *top); |
55 | float perf_top__decay_samples(struct perf_top *top, struct rb_root *root); | ||
56 | void perf_top__find_widths(struct perf_top *top, struct rb_root *root, | ||
57 | int *dso_width, int *dso_short_width, int *sym_width); | ||
58 | |||
59 | #ifdef NO_NEWT_SUPPORT | ||
60 | static inline int perf_top__tui_browser(struct perf_top *top __used) | ||
61 | { | ||
62 | return 0; | ||
63 | } | ||
64 | #else | ||
65 | int perf_top__tui_browser(struct perf_top *top); | ||
66 | #endif | ||
67 | #endif /* __PERF_TOP_H */ | 35 | #endif /* __PERF_TOP_H */ |
diff --git a/tools/perf/util/ui/browsers/annotate.c b/tools/perf/util/ui/browsers/annotate.c index 0229723aceb3..674b55e686fd 100644 --- a/tools/perf/util/ui/browsers/annotate.c +++ b/tools/perf/util/ui/browsers/annotate.c | |||
@@ -20,6 +20,7 @@ struct annotate_browser { | |||
20 | struct ui_browser b; | 20 | struct ui_browser b; |
21 | struct rb_root entries; | 21 | struct rb_root entries; |
22 | struct rb_node *curr_hot; | 22 | struct rb_node *curr_hot; |
23 | struct objdump_line *selection; | ||
23 | }; | 24 | }; |
24 | 25 | ||
25 | struct objdump_line_rb_node { | 26 | struct objdump_line_rb_node { |
@@ -36,6 +37,7 @@ struct objdump_line_rb_node *objdump_line__rb(struct objdump_line *self) | |||
36 | 37 | ||
37 | static void annotate_browser__write(struct ui_browser *self, void *entry, int row) | 38 | static void annotate_browser__write(struct ui_browser *self, void *entry, int row) |
38 | { | 39 | { |
40 | struct annotate_browser *ab = container_of(self, struct annotate_browser, b); | ||
39 | struct objdump_line *ol = rb_entry(entry, struct objdump_line, node); | 41 | struct objdump_line *ol = rb_entry(entry, struct objdump_line, node); |
40 | bool current_entry = ui_browser__is_current_entry(self, row); | 42 | bool current_entry = ui_browser__is_current_entry(self, row); |
41 | int width = self->width; | 43 | int width = self->width; |
@@ -58,6 +60,8 @@ static void annotate_browser__write(struct ui_browser *self, void *entry, int ro | |||
58 | 60 | ||
59 | if (!current_entry) | 61 | if (!current_entry) |
60 | ui_browser__set_color(self, HE_COLORSET_CODE); | 62 | ui_browser__set_color(self, HE_COLORSET_CODE); |
63 | else | ||
64 | ab->selection = ol; | ||
61 | } | 65 | } |
62 | 66 | ||
63 | static double objdump_line__calc_percent(struct objdump_line *self, | 67 | static double objdump_line__calc_percent(struct objdump_line *self, |
@@ -141,7 +145,8 @@ static void annotate_browser__set_top(struct annotate_browser *self, | |||
141 | static void annotate_browser__calc_percent(struct annotate_browser *browser, | 145 | static void annotate_browser__calc_percent(struct annotate_browser *browser, |
142 | int evidx) | 146 | int evidx) |
143 | { | 147 | { |
144 | struct symbol *sym = browser->b.priv; | 148 | struct map_symbol *ms = browser->b.priv; |
149 | struct symbol *sym = ms->sym; | ||
145 | struct annotation *notes = symbol__annotation(sym); | 150 | struct annotation *notes = symbol__annotation(sym); |
146 | struct objdump_line *pos; | 151 | struct objdump_line *pos; |
147 | 152 | ||
@@ -164,21 +169,23 @@ static void annotate_browser__calc_percent(struct annotate_browser *browser, | |||
164 | } | 169 | } |
165 | 170 | ||
166 | static int annotate_browser__run(struct annotate_browser *self, int evidx, | 171 | static int annotate_browser__run(struct annotate_browser *self, int evidx, |
167 | int refresh) | 172 | int nr_events, void(*timer)(void *arg), |
173 | void *arg, int delay_secs) | ||
168 | { | 174 | { |
169 | struct rb_node *nd = NULL; | 175 | struct rb_node *nd = NULL; |
170 | struct symbol *sym = self->b.priv; | 176 | struct map_symbol *ms = self->b.priv; |
177 | struct symbol *sym = ms->sym; | ||
171 | /* | 178 | /* |
172 | * RIGHT To allow builtin-annotate to cycle thru multiple symbols by | 179 | * RIGHT To allow builtin-annotate to cycle thru multiple symbols by |
173 | * examining the exit key for this function. | 180 | * examining the exit key for this function. |
174 | */ | 181 | */ |
175 | int exit_keys[] = { 'H', NEWT_KEY_TAB, NEWT_KEY_UNTAB, | 182 | int exit_keys[] = { 'H', NEWT_KEY_TAB, NEWT_KEY_UNTAB, |
176 | NEWT_KEY_RIGHT, 0 }; | 183 | NEWT_KEY_RIGHT, NEWT_KEY_ENTER, 0 }; |
177 | int key; | 184 | int key; |
178 | 185 | ||
179 | if (ui_browser__show(&self->b, sym->name, | 186 | if (ui_browser__show(&self->b, sym->name, |
180 | "<-, -> or ESC: exit, TAB/shift+TAB: " | 187 | "<- or ESC: exit, TAB/shift+TAB: " |
181 | "cycle hottest lines, H: Hottest") < 0) | 188 | "cycle hottest lines, H: Hottest, -> Line action") < 0) |
182 | return -1; | 189 | return -1; |
183 | 190 | ||
184 | ui_browser__add_exit_keys(&self->b, exit_keys); | 191 | ui_browser__add_exit_keys(&self->b, exit_keys); |
@@ -189,13 +196,13 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx, | |||
189 | 196 | ||
190 | nd = self->curr_hot; | 197 | nd = self->curr_hot; |
191 | 198 | ||
192 | if (refresh != 0) | 199 | if (delay_secs != 0) |
193 | newtFormSetTimer(self->b.form, refresh); | 200 | newtFormSetTimer(self->b.form, delay_secs * 1000); |
194 | 201 | ||
195 | while (1) { | 202 | while (1) { |
196 | key = ui_browser__run(&self->b); | 203 | key = ui_browser__run(&self->b); |
197 | 204 | ||
198 | if (refresh != 0) { | 205 | if (delay_secs != 0) { |
199 | annotate_browser__calc_percent(self, evidx); | 206 | annotate_browser__calc_percent(self, evidx); |
200 | /* | 207 | /* |
201 | * Current line focus got out of the list of most active | 208 | * Current line focus got out of the list of most active |
@@ -212,7 +219,10 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx, | |||
212 | * FIXME we need to check if it was | 219 | * FIXME we need to check if it was |
213 | * es.reason == NEWT_EXIT_TIMER | 220 | * es.reason == NEWT_EXIT_TIMER |
214 | */ | 221 | */ |
215 | if (refresh != 0) | 222 | if (timer != NULL) |
223 | timer(arg); | ||
224 | |||
225 | if (delay_secs != 0) | ||
216 | symbol__annotate_decay_histogram(sym, evidx); | 226 | symbol__annotate_decay_histogram(sym, evidx); |
217 | continue; | 227 | continue; |
218 | case NEWT_KEY_TAB: | 228 | case NEWT_KEY_TAB: |
@@ -234,6 +244,57 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx, | |||
234 | case 'H': | 244 | case 'H': |
235 | nd = self->curr_hot; | 245 | nd = self->curr_hot; |
236 | break; | 246 | break; |
247 | case NEWT_KEY_ENTER: | ||
248 | case NEWT_KEY_RIGHT: | ||
249 | if (self->selection == NULL) { | ||
250 | ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org"); | ||
251 | continue; | ||
252 | } | ||
253 | |||
254 | if (self->selection->offset == -1) { | ||
255 | ui_helpline__puts("Actions are only available for assembly lines."); | ||
256 | continue; | ||
257 | } else { | ||
258 | char *s = strstr(self->selection->line, "callq "); | ||
259 | struct annotation *notes; | ||
260 | struct symbol *target; | ||
261 | u64 ip; | ||
262 | |||
263 | if (s == NULL) { | ||
264 | ui_helpline__puts("Actions are only available for the 'callq' instruction."); | ||
265 | continue; | ||
266 | } | ||
267 | |||
268 | s = strchr(s, ' '); | ||
269 | if (s++ == NULL) { | ||
270 | ui_helpline__puts("Invallid callq instruction."); | ||
271 | continue; | ||
272 | } | ||
273 | |||
274 | ip = strtoull(s, NULL, 16); | ||
275 | ip = ms->map->map_ip(ms->map, ip); | ||
276 | target = map__find_symbol(ms->map, ip, NULL); | ||
277 | if (target == NULL) { | ||
278 | ui_helpline__puts("The called function was not found."); | ||
279 | continue; | ||
280 | } | ||
281 | |||
282 | notes = symbol__annotation(target); | ||
283 | pthread_mutex_lock(¬es->lock); | ||
284 | |||
285 | if (notes->src == NULL && | ||
286 | symbol__alloc_hist(target, nr_events) < 0) { | ||
287 | pthread_mutex_unlock(¬es->lock); | ||
288 | ui__warning("Not enough memory for annotating '%s' symbol!\n", | ||
289 | target->name); | ||
290 | continue; | ||
291 | } | ||
292 | |||
293 | pthread_mutex_unlock(¬es->lock); | ||
294 | symbol__tui_annotate(target, ms->map, evidx, nr_events, | ||
295 | timer, arg, delay_secs); | ||
296 | } | ||
297 | break; | ||
237 | default: | 298 | default: |
238 | goto out; | 299 | goto out; |
239 | } | 300 | } |
@@ -246,22 +307,29 @@ out: | |||
246 | return key; | 307 | return key; |
247 | } | 308 | } |
248 | 309 | ||
249 | int hist_entry__tui_annotate(struct hist_entry *he, int evidx) | 310 | int hist_entry__tui_annotate(struct hist_entry *he, int evidx, int nr_events, |
311 | void(*timer)(void *arg), void *arg, int delay_secs) | ||
250 | { | 312 | { |
251 | return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx, 0); | 313 | return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx, nr_events, |
314 | timer, arg, delay_secs); | ||
252 | } | 315 | } |
253 | 316 | ||
254 | int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, | 317 | int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, |
255 | int refresh) | 318 | int nr_events, void(*timer)(void *arg), void *arg, |
319 | int delay_secs) | ||
256 | { | 320 | { |
257 | struct objdump_line *pos, *n; | 321 | struct objdump_line *pos, *n; |
258 | struct annotation *notes; | 322 | struct annotation *notes; |
323 | struct map_symbol ms = { | ||
324 | .map = map, | ||
325 | .sym = sym, | ||
326 | }; | ||
259 | struct annotate_browser browser = { | 327 | struct annotate_browser browser = { |
260 | .b = { | 328 | .b = { |
261 | .refresh = ui_browser__list_head_refresh, | 329 | .refresh = ui_browser__list_head_refresh, |
262 | .seek = ui_browser__list_head_seek, | 330 | .seek = ui_browser__list_head_seek, |
263 | .write = annotate_browser__write, | 331 | .write = annotate_browser__write, |
264 | .priv = sym, | 332 | .priv = &ms, |
265 | }, | 333 | }, |
266 | }; | 334 | }; |
267 | int ret; | 335 | int ret; |
@@ -293,7 +361,8 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, | |||
293 | 361 | ||
294 | browser.b.entries = ¬es->src->source, | 362 | browser.b.entries = ¬es->src->source, |
295 | browser.b.width += 18; /* Percentage */ | 363 | browser.b.width += 18; /* Percentage */ |
296 | ret = annotate_browser__run(&browser, evidx, refresh); | 364 | ret = annotate_browser__run(&browser, evidx, nr_events, |
365 | timer, arg, delay_secs); | ||
297 | list_for_each_entry_safe(pos, n, ¬es->src->source, node) { | 366 | list_for_each_entry_safe(pos, n, ¬es->src->source, node) { |
298 | list_del(&pos->node); | 367 | list_del(&pos->node); |
299 | objdump_line__free(pos); | 368 | objdump_line__free(pos); |
diff --git a/tools/perf/util/ui/browsers/hists.c b/tools/perf/util/ui/browsers/hists.c index 5d767c622dfc..e64d9527f14e 100644 --- a/tools/perf/util/ui/browsers/hists.c +++ b/tools/perf/util/ui/browsers/hists.c | |||
@@ -24,8 +24,15 @@ struct hist_browser { | |||
24 | struct hists *hists; | 24 | struct hists *hists; |
25 | struct hist_entry *he_selection; | 25 | struct hist_entry *he_selection; |
26 | struct map_symbol *selection; | 26 | struct map_symbol *selection; |
27 | const struct thread *thread_filter; | ||
28 | const struct dso *dso_filter; | ||
29 | bool has_symbols; | ||
27 | }; | 30 | }; |
28 | 31 | ||
32 | static int hists__browser_title(struct hists *self, char *bf, size_t size, | ||
33 | const char *ev_name, const struct dso *dso, | ||
34 | const struct thread *thread); | ||
35 | |||
29 | static void hist_browser__refresh_dimensions(struct hist_browser *self) | 36 | static void hist_browser__refresh_dimensions(struct hist_browser *self) |
30 | { | 37 | { |
31 | /* 3 == +/- toggle symbol before actual hist_entry rendering */ | 38 | /* 3 == +/- toggle symbol before actual hist_entry rendering */ |
@@ -290,28 +297,53 @@ static void hist_browser__set_folding(struct hist_browser *self, bool unfold) | |||
290 | ui_browser__reset_index(&self->b); | 297 | ui_browser__reset_index(&self->b); |
291 | } | 298 | } |
292 | 299 | ||
293 | static int hist_browser__run(struct hist_browser *self, const char *title) | 300 | static int hist_browser__run(struct hist_browser *self, const char *ev_name, |
301 | void(*timer)(void *arg), void *arg, int delay_secs) | ||
294 | { | 302 | { |
295 | int key; | 303 | int key; |
296 | int exit_keys[] = { 'a', '?', 'h', 'C', 'd', 'D', 'E', 't', | 304 | int delay_msecs = delay_secs * 1000; |
297 | NEWT_KEY_ENTER, NEWT_KEY_RIGHT, NEWT_KEY_LEFT, | 305 | char title[160]; |
298 | NEWT_KEY_TAB, NEWT_KEY_UNTAB, 0, }; | 306 | int sym_exit_keys[] = { 'a', 'h', 'C', 'd', 'E', 't', 0, }; |
307 | int exit_keys[] = { '?', 'h', 'D', NEWT_KEY_LEFT, NEWT_KEY_RIGHT, | ||
308 | NEWT_KEY_TAB, NEWT_KEY_UNTAB, NEWT_KEY_ENTER, 0, }; | ||
299 | 309 | ||
300 | self->b.entries = &self->hists->entries; | 310 | self->b.entries = &self->hists->entries; |
301 | self->b.nr_entries = self->hists->nr_entries; | 311 | self->b.nr_entries = self->hists->nr_entries; |
302 | 312 | ||
303 | hist_browser__refresh_dimensions(self); | 313 | hist_browser__refresh_dimensions(self); |
314 | hists__browser_title(self->hists, title, sizeof(title), ev_name, | ||
315 | self->dso_filter, self->thread_filter); | ||
304 | 316 | ||
305 | if (ui_browser__show(&self->b, title, | 317 | if (ui_browser__show(&self->b, title, |
306 | "Press '?' for help on key bindings") < 0) | 318 | "Press '?' for help on key bindings") < 0) |
307 | return -1; | 319 | return -1; |
308 | 320 | ||
321 | if (timer != NULL) | ||
322 | newtFormSetTimer(self->b.form, delay_msecs); | ||
323 | |||
309 | ui_browser__add_exit_keys(&self->b, exit_keys); | 324 | ui_browser__add_exit_keys(&self->b, exit_keys); |
325 | if (self->has_symbols) | ||
326 | ui_browser__add_exit_keys(&self->b, sym_exit_keys); | ||
310 | 327 | ||
311 | while (1) { | 328 | while (1) { |
312 | key = ui_browser__run(&self->b); | 329 | key = ui_browser__run(&self->b); |
313 | 330 | ||
314 | switch (key) { | 331 | switch (key) { |
332 | case -1: | ||
333 | /* FIXME we need to check if it was es.reason == NEWT_EXIT_TIMER */ | ||
334 | timer(arg); | ||
335 | /* | ||
336 | * The timer may have changed the number of entries. | ||
337 | * XXX: Find better way to keep this in synch, probably | ||
338 | * removing this timer function altogether and just sync | ||
339 | * using the hists->lock... | ||
340 | */ | ||
341 | self->b.nr_entries = self->hists->nr_entries; | ||
342 | hists__browser_title(self->hists, title, sizeof(title), | ||
343 | ev_name, self->dso_filter, | ||
344 | self->thread_filter); | ||
345 | ui_browser__show_title(&self->b, title); | ||
346 | continue; | ||
315 | case 'D': { /* Debug */ | 347 | case 'D': { /* Debug */ |
316 | static int seq; | 348 | static int seq; |
317 | struct hist_entry *h = rb_entry(self->b.top, | 349 | struct hist_entry *h = rb_entry(self->b.top, |
@@ -761,6 +793,7 @@ static struct hist_browser *hist_browser__new(struct hists *hists) | |||
761 | self->hists = hists; | 793 | self->hists = hists; |
762 | self->b.refresh = hist_browser__refresh; | 794 | self->b.refresh = hist_browser__refresh; |
763 | self->b.seek = ui_browser__hists_seek; | 795 | self->b.seek = ui_browser__hists_seek; |
796 | self->has_symbols = sort_sym.list.next != NULL; | ||
764 | } | 797 | } |
765 | 798 | ||
766 | return self; | 799 | return self; |
@@ -803,16 +836,15 @@ static int hists__browser_title(struct hists *self, char *bf, size_t size, | |||
803 | return printed; | 836 | return printed; |
804 | } | 837 | } |
805 | 838 | ||
806 | static int perf_evsel__hists_browse(struct perf_evsel *evsel, | 839 | static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, |
807 | const char *helpline, const char *ev_name, | 840 | const char *helpline, const char *ev_name, |
808 | bool left_exits) | 841 | bool left_exits, |
842 | void(*timer)(void *arg), void *arg, | ||
843 | int delay_secs) | ||
809 | { | 844 | { |
810 | struct hists *self = &evsel->hists; | 845 | struct hists *self = &evsel->hists; |
811 | struct hist_browser *browser = hist_browser__new(self); | 846 | struct hist_browser *browser = hist_browser__new(self); |
812 | struct pstack *fstack; | 847 | struct pstack *fstack; |
813 | const struct thread *thread_filter = NULL; | ||
814 | const struct dso *dso_filter = NULL; | ||
815 | char msg[160]; | ||
816 | int key = -1; | 848 | int key = -1; |
817 | 849 | ||
818 | if (browser == NULL) | 850 | if (browser == NULL) |
@@ -824,8 +856,6 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, | |||
824 | 856 | ||
825 | ui_helpline__push(helpline); | 857 | ui_helpline__push(helpline); |
826 | 858 | ||
827 | hists__browser_title(self, msg, sizeof(msg), ev_name, | ||
828 | dso_filter, thread_filter); | ||
829 | while (1) { | 859 | while (1) { |
830 | const struct thread *thread = NULL; | 860 | const struct thread *thread = NULL; |
831 | const struct dso *dso = NULL; | 861 | const struct dso *dso = NULL; |
@@ -834,7 +864,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, | |||
834 | annotate = -2, zoom_dso = -2, zoom_thread = -2, | 864 | annotate = -2, zoom_dso = -2, zoom_thread = -2, |
835 | browse_map = -2; | 865 | browse_map = -2; |
836 | 866 | ||
837 | key = hist_browser__run(browser, msg); | 867 | key = hist_browser__run(browser, ev_name, timer, arg, delay_secs); |
838 | 868 | ||
839 | if (browser->he_selection != NULL) { | 869 | if (browser->he_selection != NULL) { |
840 | thread = hist_browser__selected_thread(browser); | 870 | thread = hist_browser__selected_thread(browser); |
@@ -862,16 +892,17 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, | |||
862 | case NEWT_KEY_F1: | 892 | case NEWT_KEY_F1: |
863 | case 'h': | 893 | case 'h': |
864 | case '?': | 894 | case '?': |
865 | ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n" | 895 | ui__help_window("h/?/F1 Show this window\n" |
896 | "TAB/UNTAB Switch events\n" | ||
897 | "q/CTRL+C Exit browser\n\n" | ||
898 | "For symbolic views (--sort has sym):\n\n" | ||
899 | "-> Zoom into DSO/Threads & Annotate current symbol\n" | ||
866 | "<- Zoom out\n" | 900 | "<- Zoom out\n" |
867 | "a Annotate current symbol\n" | 901 | "a Annotate current symbol\n" |
868 | "h/?/F1 Show this window\n" | ||
869 | "C Collapse all callchains\n" | 902 | "C Collapse all callchains\n" |
870 | "E Expand all callchains\n" | 903 | "E Expand all callchains\n" |
871 | "d Zoom into current DSO\n" | 904 | "d Zoom into current DSO\n" |
872 | "t Zoom into current Thread\n" | 905 | "t Zoom into current Thread\n"); |
873 | "TAB/UNTAB Switch events\n" | ||
874 | "q/CTRL+C Exit browser"); | ||
875 | continue; | 906 | continue; |
876 | case NEWT_KEY_ENTER: | 907 | case NEWT_KEY_ENTER: |
877 | case NEWT_KEY_RIGHT: | 908 | case NEWT_KEY_RIGHT: |
@@ -889,9 +920,9 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, | |||
889 | continue; | 920 | continue; |
890 | } | 921 | } |
891 | top = pstack__pop(fstack); | 922 | top = pstack__pop(fstack); |
892 | if (top == &dso_filter) | 923 | if (top == &browser->dso_filter) |
893 | goto zoom_out_dso; | 924 | goto zoom_out_dso; |
894 | if (top == &thread_filter) | 925 | if (top == &browser->thread_filter) |
895 | goto zoom_out_thread; | 926 | goto zoom_out_thread; |
896 | continue; | 927 | continue; |
897 | } | 928 | } |
@@ -904,6 +935,9 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, | |||
904 | goto out_free_stack; | 935 | goto out_free_stack; |
905 | } | 936 | } |
906 | 937 | ||
938 | if (!browser->has_symbols) | ||
939 | goto add_exit_option; | ||
940 | |||
907 | if (browser->selection != NULL && | 941 | if (browser->selection != NULL && |
908 | browser->selection->sym != NULL && | 942 | browser->selection->sym != NULL && |
909 | !browser->selection->map->dso->annotate_warned && | 943 | !browser->selection->map->dso->annotate_warned && |
@@ -913,14 +947,14 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, | |||
913 | 947 | ||
914 | if (thread != NULL && | 948 | if (thread != NULL && |
915 | asprintf(&options[nr_options], "Zoom %s %s(%d) thread", | 949 | asprintf(&options[nr_options], "Zoom %s %s(%d) thread", |
916 | (thread_filter ? "out of" : "into"), | 950 | (browser->thread_filter ? "out of" : "into"), |
917 | (thread->comm_set ? thread->comm : ""), | 951 | (thread->comm_set ? thread->comm : ""), |
918 | thread->pid) > 0) | 952 | thread->pid) > 0) |
919 | zoom_thread = nr_options++; | 953 | zoom_thread = nr_options++; |
920 | 954 | ||
921 | if (dso != NULL && | 955 | if (dso != NULL && |
922 | asprintf(&options[nr_options], "Zoom %s %s DSO", | 956 | asprintf(&options[nr_options], "Zoom %s %s DSO", |
923 | (dso_filter ? "out of" : "into"), | 957 | (browser->dso_filter ? "out of" : "into"), |
924 | (dso->kernel ? "the Kernel" : dso->short_name)) > 0) | 958 | (dso->kernel ? "the Kernel" : dso->short_name)) > 0) |
925 | zoom_dso = nr_options++; | 959 | zoom_dso = nr_options++; |
926 | 960 | ||
@@ -928,7 +962,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, | |||
928 | browser->selection->map != NULL && | 962 | browser->selection->map != NULL && |
929 | asprintf(&options[nr_options], "Browse map details") > 0) | 963 | asprintf(&options[nr_options], "Browse map details") > 0) |
930 | browse_map = nr_options++; | 964 | browse_map = nr_options++; |
931 | 965 | add_exit_option: | |
932 | options[nr_options++] = (char *)"Exit"; | 966 | options[nr_options++] = (char *)"Exit"; |
933 | 967 | ||
934 | choice = ui__popup_menu(nr_options, options); | 968 | choice = ui__popup_menu(nr_options, options); |
@@ -949,45 +983,42 @@ do_annotate: | |||
949 | if (he == NULL) | 983 | if (he == NULL) |
950 | continue; | 984 | continue; |
951 | 985 | ||
952 | hist_entry__tui_annotate(he, evsel->idx); | 986 | hist_entry__tui_annotate(he, evsel->idx, nr_events, |
987 | timer, arg, delay_secs); | ||
953 | } else if (choice == browse_map) | 988 | } else if (choice == browse_map) |
954 | map__browse(browser->selection->map); | 989 | map__browse(browser->selection->map); |
955 | else if (choice == zoom_dso) { | 990 | else if (choice == zoom_dso) { |
956 | zoom_dso: | 991 | zoom_dso: |
957 | if (dso_filter) { | 992 | if (browser->dso_filter) { |
958 | pstack__remove(fstack, &dso_filter); | 993 | pstack__remove(fstack, &browser->dso_filter); |
959 | zoom_out_dso: | 994 | zoom_out_dso: |
960 | ui_helpline__pop(); | 995 | ui_helpline__pop(); |
961 | dso_filter = NULL; | 996 | browser->dso_filter = NULL; |
962 | } else { | 997 | } else { |
963 | if (dso == NULL) | 998 | if (dso == NULL) |
964 | continue; | 999 | continue; |
965 | ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"", | 1000 | ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"", |
966 | dso->kernel ? "the Kernel" : dso->short_name); | 1001 | dso->kernel ? "the Kernel" : dso->short_name); |
967 | dso_filter = dso; | 1002 | browser->dso_filter = dso; |
968 | pstack__push(fstack, &dso_filter); | 1003 | pstack__push(fstack, &browser->dso_filter); |
969 | } | 1004 | } |
970 | hists__filter_by_dso(self, dso_filter); | 1005 | hists__filter_by_dso(self, browser->dso_filter); |
971 | hists__browser_title(self, msg, sizeof(msg), ev_name, | ||
972 | dso_filter, thread_filter); | ||
973 | hist_browser__reset(browser); | 1006 | hist_browser__reset(browser); |
974 | } else if (choice == zoom_thread) { | 1007 | } else if (choice == zoom_thread) { |
975 | zoom_thread: | 1008 | zoom_thread: |
976 | if (thread_filter) { | 1009 | if (browser->thread_filter) { |
977 | pstack__remove(fstack, &thread_filter); | 1010 | pstack__remove(fstack, &browser->thread_filter); |
978 | zoom_out_thread: | 1011 | zoom_out_thread: |
979 | ui_helpline__pop(); | 1012 | ui_helpline__pop(); |
980 | thread_filter = NULL; | 1013 | browser->thread_filter = NULL; |
981 | } else { | 1014 | } else { |
982 | ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"", | 1015 | ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"", |
983 | thread->comm_set ? thread->comm : "", | 1016 | thread->comm_set ? thread->comm : "", |
984 | thread->pid); | 1017 | thread->pid); |
985 | thread_filter = thread; | 1018 | browser->thread_filter = thread; |
986 | pstack__push(fstack, &thread_filter); | 1019 | pstack__push(fstack, &browser->thread_filter); |
987 | } | 1020 | } |
988 | hists__filter_by_thread(self, thread_filter); | 1021 | hists__filter_by_thread(self, browser->thread_filter); |
989 | hists__browser_title(self, msg, sizeof(msg), ev_name, | ||
990 | dso_filter, thread_filter); | ||
991 | hist_browser__reset(browser); | 1022 | hist_browser__reset(browser); |
992 | } | 1023 | } |
993 | } | 1024 | } |
@@ -1026,9 +1057,12 @@ static void perf_evsel_menu__write(struct ui_browser *browser, | |||
1026 | menu->selection = evsel; | 1057 | menu->selection = evsel; |
1027 | } | 1058 | } |
1028 | 1059 | ||
1029 | static int perf_evsel_menu__run(struct perf_evsel_menu *menu, const char *help) | 1060 | static int perf_evsel_menu__run(struct perf_evsel_menu *menu, |
1061 | int nr_events, const char *help, | ||
1062 | void(*timer)(void *arg), void *arg, int delay_secs) | ||
1030 | { | 1063 | { |
1031 | int exit_keys[] = { NEWT_KEY_ENTER, NEWT_KEY_RIGHT, 0, }; | 1064 | int exit_keys[] = { NEWT_KEY_ENTER, NEWT_KEY_RIGHT, 0, }; |
1065 | int delay_msecs = delay_secs * 1000; | ||
1032 | struct perf_evlist *evlist = menu->b.priv; | 1066 | struct perf_evlist *evlist = menu->b.priv; |
1033 | struct perf_evsel *pos; | 1067 | struct perf_evsel *pos; |
1034 | const char *ev_name, *title = "Available samples"; | 1068 | const char *ev_name, *title = "Available samples"; |
@@ -1038,20 +1072,30 @@ static int perf_evsel_menu__run(struct perf_evsel_menu *menu, const char *help) | |||
1038 | "ESC: exit, ENTER|->: Browse histograms") < 0) | 1072 | "ESC: exit, ENTER|->: Browse histograms") < 0) |
1039 | return -1; | 1073 | return -1; |
1040 | 1074 | ||
1075 | if (timer != NULL) | ||
1076 | newtFormSetTimer(menu->b.form, delay_msecs); | ||
1077 | |||
1041 | ui_browser__add_exit_keys(&menu->b, exit_keys); | 1078 | ui_browser__add_exit_keys(&menu->b, exit_keys); |
1042 | 1079 | ||
1043 | while (1) { | 1080 | while (1) { |
1044 | key = ui_browser__run(&menu->b); | 1081 | key = ui_browser__run(&menu->b); |
1045 | 1082 | ||
1046 | switch (key) { | 1083 | switch (key) { |
1084 | case -1: | ||
1085 | /* FIXME we need to check if it was es.reason == NEWT_EXIT_TIMER */ | ||
1086 | timer(arg); | ||
1087 | continue; | ||
1047 | case NEWT_KEY_RIGHT: | 1088 | case NEWT_KEY_RIGHT: |
1048 | case NEWT_KEY_ENTER: | 1089 | case NEWT_KEY_ENTER: |
1049 | if (!menu->selection) | 1090 | if (!menu->selection) |
1050 | continue; | 1091 | continue; |
1051 | pos = menu->selection; | 1092 | pos = menu->selection; |
1093 | perf_evlist__set_selected(evlist, pos); | ||
1052 | browse_hists: | 1094 | browse_hists: |
1053 | ev_name = event_name(pos); | 1095 | ev_name = event_name(pos); |
1054 | key = perf_evsel__hists_browse(pos, help, ev_name, true); | 1096 | key = perf_evsel__hists_browse(pos, nr_events, help, |
1097 | ev_name, true, timer, | ||
1098 | arg, delay_secs); | ||
1055 | ui_browser__show_title(&menu->b, title); | 1099 | ui_browser__show_title(&menu->b, title); |
1056 | break; | 1100 | break; |
1057 | case NEWT_KEY_LEFT: | 1101 | case NEWT_KEY_LEFT: |
@@ -1070,12 +1114,14 @@ browse_hists: | |||
1070 | pos = list_entry(evlist->entries.next, struct perf_evsel, node); | 1114 | pos = list_entry(evlist->entries.next, struct perf_evsel, node); |
1071 | else | 1115 | else |
1072 | pos = list_entry(pos->node.next, struct perf_evsel, node); | 1116 | pos = list_entry(pos->node.next, struct perf_evsel, node); |
1117 | perf_evlist__set_selected(evlist, pos); | ||
1073 | goto browse_hists; | 1118 | goto browse_hists; |
1074 | case NEWT_KEY_UNTAB: | 1119 | case NEWT_KEY_UNTAB: |
1075 | if (pos->node.prev == &evlist->entries) | 1120 | if (pos->node.prev == &evlist->entries) |
1076 | pos = list_entry(evlist->entries.prev, struct perf_evsel, node); | 1121 | pos = list_entry(evlist->entries.prev, struct perf_evsel, node); |
1077 | else | 1122 | else |
1078 | pos = list_entry(pos->node.prev, struct perf_evsel, node); | 1123 | pos = list_entry(pos->node.prev, struct perf_evsel, node); |
1124 | perf_evlist__set_selected(evlist, pos); | ||
1079 | goto browse_hists; | 1125 | goto browse_hists; |
1080 | case 'q': | 1126 | case 'q': |
1081 | case CTRL('c'): | 1127 | case CTRL('c'): |
@@ -1091,7 +1137,9 @@ out: | |||
1091 | } | 1137 | } |
1092 | 1138 | ||
1093 | static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, | 1139 | static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, |
1094 | const char *help) | 1140 | const char *help, |
1141 | void(*timer)(void *arg), void *arg, | ||
1142 | int delay_secs) | ||
1095 | { | 1143 | { |
1096 | struct perf_evsel *pos; | 1144 | struct perf_evsel *pos; |
1097 | struct perf_evsel_menu menu = { | 1145 | struct perf_evsel_menu menu = { |
@@ -1121,18 +1169,24 @@ static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, | |||
1121 | pos->name = strdup(ev_name); | 1169 | pos->name = strdup(ev_name); |
1122 | } | 1170 | } |
1123 | 1171 | ||
1124 | return perf_evsel_menu__run(&menu, help); | 1172 | return perf_evsel_menu__run(&menu, evlist->nr_entries, help, timer, |
1173 | arg, delay_secs); | ||
1125 | } | 1174 | } |
1126 | 1175 | ||
1127 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help) | 1176 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, |
1177 | void(*timer)(void *arg), void *arg, | ||
1178 | int delay_secs) | ||
1128 | { | 1179 | { |
1129 | 1180 | ||
1130 | if (evlist->nr_entries == 1) { | 1181 | if (evlist->nr_entries == 1) { |
1131 | struct perf_evsel *first = list_entry(evlist->entries.next, | 1182 | struct perf_evsel *first = list_entry(evlist->entries.next, |
1132 | struct perf_evsel, node); | 1183 | struct perf_evsel, node); |
1133 | const char *ev_name = event_name(first); | 1184 | const char *ev_name = event_name(first); |
1134 | return perf_evsel__hists_browse(first, help, ev_name, false); | 1185 | return perf_evsel__hists_browse(first, evlist->nr_entries, help, |
1186 | ev_name, false, timer, arg, | ||
1187 | delay_secs); | ||
1135 | } | 1188 | } |
1136 | 1189 | ||
1137 | return __perf_evlist__tui_browse_hists(evlist, help); | 1190 | return __perf_evlist__tui_browse_hists(evlist, help, |
1191 | timer, arg, delay_secs); | ||
1138 | } | 1192 | } |
diff --git a/tools/perf/util/ui/browsers/top.c b/tools/perf/util/ui/browsers/top.c deleted file mode 100644 index 9b6b43b32ac8..000000000000 --- a/tools/perf/util/ui/browsers/top.c +++ /dev/null | |||
@@ -1,236 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> | ||
3 | * | ||
4 | * Parts came from builtin-{top,stat,record}.c, see those files for further | ||
5 | * copyright notes. | ||
6 | * | ||
7 | * Released under the GPL v2. (and only v2, not any later version) | ||
8 | */ | ||
9 | #include "../browser.h" | ||
10 | #include "../../annotate.h" | ||
11 | #include "../helpline.h" | ||
12 | #include "../libslang.h" | ||
13 | #include "../util.h" | ||
14 | #include "../ui.h" | ||
15 | #include "../../evlist.h" | ||
16 | #include "../../hist.h" | ||
17 | #include "../../sort.h" | ||
18 | #include "../../symbol.h" | ||
19 | #include "../../session.h" | ||
20 | #include "../../top.h" | ||
21 | |||
22 | struct perf_top_browser { | ||
23 | struct ui_browser b; | ||
24 | struct rb_root root; | ||
25 | struct sym_entry *selection; | ||
26 | float sum_ksamples; | ||
27 | int dso_width; | ||
28 | int dso_short_width; | ||
29 | int sym_width; | ||
30 | }; | ||
31 | |||
32 | static void perf_top_browser__write(struct ui_browser *browser, void *entry, int row) | ||
33 | { | ||
34 | struct perf_top_browser *top_browser = container_of(browser, struct perf_top_browser, b); | ||
35 | struct sym_entry *syme = rb_entry(entry, struct sym_entry, rb_node); | ||
36 | bool current_entry = ui_browser__is_current_entry(browser, row); | ||
37 | struct symbol *symbol = sym_entry__symbol(syme); | ||
38 | struct perf_top *top = browser->priv; | ||
39 | int width = browser->width; | ||
40 | double pcnt; | ||
41 | |||
42 | pcnt = 100.0 - (100.0 * ((top_browser->sum_ksamples - syme->snap_count) / | ||
43 | top_browser->sum_ksamples)); | ||
44 | ui_browser__set_percent_color(browser, pcnt, current_entry); | ||
45 | |||
46 | if (top->evlist->nr_entries == 1 || !top->display_weighted) { | ||
47 | slsmg_printf("%20.2f ", syme->weight); | ||
48 | width -= 21; | ||
49 | } else { | ||
50 | slsmg_printf("%9.1f %10ld ", syme->weight, syme->snap_count); | ||
51 | width -= 20; | ||
52 | } | ||
53 | |||
54 | slsmg_printf("%4.1f%%", pcnt); | ||
55 | width -= 7; | ||
56 | |||
57 | if (verbose) { | ||
58 | slsmg_printf(" %016" PRIx64, symbol->start); | ||
59 | width -= 17; | ||
60 | } | ||
61 | |||
62 | slsmg_printf(" %-*.*s ", top_browser->sym_width, top_browser->sym_width, | ||
63 | symbol->name); | ||
64 | width -= top_browser->sym_width; | ||
65 | slsmg_write_nstring(width >= syme->map->dso->long_name_len ? | ||
66 | syme->map->dso->long_name : | ||
67 | syme->map->dso->short_name, width); | ||
68 | |||
69 | if (current_entry) | ||
70 | top_browser->selection = syme; | ||
71 | } | ||
72 | |||
73 | static void perf_top_browser__update_rb_tree(struct perf_top_browser *browser) | ||
74 | { | ||
75 | struct perf_top *top = browser->b.priv; | ||
76 | u64 top_idx = browser->b.top_idx; | ||
77 | |||
78 | browser->root = RB_ROOT; | ||
79 | browser->b.top = NULL; | ||
80 | browser->sum_ksamples = perf_top__decay_samples(top, &browser->root); | ||
81 | /* | ||
82 | * No active symbols | ||
83 | */ | ||
84 | if (top->rb_entries == 0) | ||
85 | return; | ||
86 | |||
87 | perf_top__find_widths(top, &browser->root, &browser->dso_width, | ||
88 | &browser->dso_short_width, | ||
89 | &browser->sym_width); | ||
90 | if (browser->sym_width + browser->dso_width > browser->b.width - 29) { | ||
91 | browser->dso_width = browser->dso_short_width; | ||
92 | if (browser->sym_width + browser->dso_width > browser->b.width - 29) | ||
93 | browser->sym_width = browser->b.width - browser->dso_width - 29; | ||
94 | } | ||
95 | |||
96 | /* | ||
97 | * Adjust the ui_browser indexes since the entries in the browser->root | ||
98 | * rb_tree may have changed, then seek it from start, so that we get a | ||
99 | * possible new top of the screen. | ||
100 | */ | ||
101 | browser->b.nr_entries = top->rb_entries; | ||
102 | |||
103 | if (top_idx >= browser->b.nr_entries) { | ||
104 | if (browser->b.height >= browser->b.nr_entries) | ||
105 | top_idx = browser->b.nr_entries - browser->b.height; | ||
106 | else | ||
107 | top_idx = 0; | ||
108 | } | ||
109 | |||
110 | if (browser->b.index >= top_idx + browser->b.height) | ||
111 | browser->b.index = top_idx + browser->b.index - browser->b.top_idx; | ||
112 | |||
113 | if (browser->b.index >= browser->b.nr_entries) | ||
114 | browser->b.index = browser->b.nr_entries - 1; | ||
115 | |||
116 | browser->b.top_idx = top_idx; | ||
117 | browser->b.seek(&browser->b, top_idx, SEEK_SET); | ||
118 | } | ||
119 | |||
120 | static void perf_top_browser__annotate(struct perf_top_browser *browser) | ||
121 | { | ||
122 | struct sym_entry *syme = browser->selection; | ||
123 | struct symbol *sym = sym_entry__symbol(syme); | ||
124 | struct annotation *notes = symbol__annotation(sym); | ||
125 | struct perf_top *top = browser->b.priv; | ||
126 | |||
127 | if (notes->src != NULL) | ||
128 | goto do_annotation; | ||
129 | |||
130 | pthread_mutex_lock(¬es->lock); | ||
131 | |||
132 | top->sym_filter_entry = NULL; | ||
133 | |||
134 | if (symbol__alloc_hist(sym, top->evlist->nr_entries) < 0) { | ||
135 | pr_err("Not enough memory for annotating '%s' symbol!\n", | ||
136 | sym->name); | ||
137 | pthread_mutex_unlock(¬es->lock); | ||
138 | return; | ||
139 | } | ||
140 | |||
141 | top->sym_filter_entry = syme; | ||
142 | |||
143 | pthread_mutex_unlock(¬es->lock); | ||
144 | do_annotation: | ||
145 | symbol__tui_annotate(sym, syme->map, 0, top->delay_secs * 1000); | ||
146 | } | ||
147 | |||
148 | static void perf_top_browser__warn_lost(struct perf_top_browser *browser) | ||
149 | { | ||
150 | struct perf_top *top = browser->b.priv; | ||
151 | char msg[128]; | ||
152 | int len; | ||
153 | |||
154 | top->total_lost_warned = top->session->hists.stats.total_lost; | ||
155 | pthread_mutex_lock(&ui__lock); | ||
156 | ui_browser__set_color(&browser->b, HE_COLORSET_TOP); | ||
157 | len = snprintf(msg, sizeof(msg), | ||
158 | " WARNING: LOST %" PRIu64 " events, Check IO/CPU overload", | ||
159 | top->total_lost_warned); | ||
160 | if (len > browser->b.width) | ||
161 | len = browser->b.width; | ||
162 | SLsmg_gotorc(0, browser->b.width - len); | ||
163 | slsmg_write_nstring(msg, len); | ||
164 | pthread_mutex_unlock(&ui__lock); | ||
165 | } | ||
166 | |||
167 | static int perf_top_browser__run(struct perf_top_browser *browser) | ||
168 | { | ||
169 | int key; | ||
170 | char title[160]; | ||
171 | struct perf_top *top = browser->b.priv; | ||
172 | int delay_msecs = top->delay_secs * 1000; | ||
173 | int exit_keys[] = { 'a', NEWT_KEY_ENTER, NEWT_KEY_RIGHT, 0, }; | ||
174 | |||
175 | perf_top_browser__update_rb_tree(browser); | ||
176 | perf_top__header_snprintf(top, title, sizeof(title)); | ||
177 | perf_top__reset_sample_counters(top); | ||
178 | |||
179 | if (ui_browser__show(&browser->b, title, | ||
180 | "ESC: exit, ENTER|->|a: Live Annotate") < 0) | ||
181 | return -1; | ||
182 | |||
183 | newtFormSetTimer(browser->b.form, delay_msecs); | ||
184 | ui_browser__add_exit_keys(&browser->b, exit_keys); | ||
185 | |||
186 | while (1) { | ||
187 | key = ui_browser__run(&browser->b); | ||
188 | |||
189 | switch (key) { | ||
190 | case -1: | ||
191 | /* FIXME we need to check if it was es.reason == NEWT_EXIT_TIMER */ | ||
192 | perf_top_browser__update_rb_tree(browser); | ||
193 | perf_top__header_snprintf(top, title, sizeof(title)); | ||
194 | perf_top__reset_sample_counters(top); | ||
195 | ui_browser__set_color(&browser->b, NEWT_COLORSET_ROOT); | ||
196 | SLsmg_gotorc(0, 0); | ||
197 | slsmg_write_nstring(title, browser->b.width); | ||
198 | |||
199 | if (top->total_lost_warned != top->session->hists.stats.total_lost) | ||
200 | perf_top_browser__warn_lost(browser); | ||
201 | break; | ||
202 | case 'a': | ||
203 | case NEWT_KEY_RIGHT: | ||
204 | case NEWT_KEY_ENTER: | ||
205 | if (browser->selection) | ||
206 | perf_top_browser__annotate(browser); | ||
207 | break; | ||
208 | case NEWT_KEY_LEFT: | ||
209 | continue; | ||
210 | case NEWT_KEY_ESCAPE: | ||
211 | if (!ui__dialog_yesno("Do you really want to exit?")) | ||
212 | continue; | ||
213 | /* Fall thru */ | ||
214 | default: | ||
215 | goto out; | ||
216 | } | ||
217 | } | ||
218 | out: | ||
219 | ui_browser__hide(&browser->b); | ||
220 | return key; | ||
221 | } | ||
222 | |||
223 | int perf_top__tui_browser(struct perf_top *top) | ||
224 | { | ||
225 | struct perf_top_browser browser = { | ||
226 | .b = { | ||
227 | .entries = &browser.root, | ||
228 | .refresh = ui_browser__rb_tree_refresh, | ||
229 | .seek = ui_browser__rb_tree_seek, | ||
230 | .write = perf_top_browser__write, | ||
231 | .priv = top, | ||
232 | }, | ||
233 | }; | ||
234 | |||
235 | return perf_top_browser__run(&browser); | ||
236 | } | ||