diff options
Diffstat (limited to 'tools/perf')
72 files changed, 3998 insertions, 1424 deletions
diff --git a/tools/perf/Documentation/perf-annotate.txt b/tools/perf/Documentation/perf-annotate.txt index 85c5f026930d..fe6762ed56bd 100644 --- a/tools/perf/Documentation/perf-annotate.txt +++ b/tools/perf/Documentation/perf-annotate.txt | |||
| @@ -72,6 +72,19 @@ OPTIONS | |||
| 72 | CPUs are specified with -: 0-2. Default is to report samples on all | 72 | CPUs are specified with -: 0-2. Default is to report samples on all |
| 73 | CPUs. | 73 | CPUs. |
| 74 | 74 | ||
| 75 | --asm-raw:: | ||
| 76 | Show raw instruction encoding of assembly instructions. | ||
| 77 | |||
| 78 | --source:: | ||
| 79 | Interleave source code with assembly code. Enabled by default, | ||
| 80 | disable with --no-source. | ||
| 81 | |||
| 82 | --symfs=<directory>:: | ||
| 83 | Look for files with symbols relative to this directory. | ||
| 84 | |||
| 85 | -M:: | ||
| 86 | --disassembler-style=:: Set disassembler style for objdump. | ||
| 87 | |||
| 75 | SEE ALSO | 88 | SEE ALSO |
| 76 | -------- | 89 | -------- |
| 77 | linkperf:perf-record[1], linkperf:perf-report[1] | 90 | linkperf:perf-record[1], linkperf:perf-report[1] |
diff --git a/tools/perf/Documentation/perf-buildid-list.txt b/tools/perf/Documentation/perf-buildid-list.txt index 5eaac6f26d51..cc22325ffd1b 100644 --- a/tools/perf/Documentation/perf-buildid-list.txt +++ b/tools/perf/Documentation/perf-buildid-list.txt | |||
| @@ -16,6 +16,9 @@ This command displays the buildids found in a perf.data file, so that other | |||
| 16 | tools can be used to fetch packages with matching symbol tables for use by | 16 | tools can be used to fetch packages with matching symbol tables for use by |
| 17 | perf report. | 17 | perf report. |
| 18 | 18 | ||
| 19 | It can also be used to show the build id of the running kernel or in an ELF | ||
| 20 | file using -i/--input. | ||
| 21 | |||
| 19 | OPTIONS | 22 | OPTIONS |
| 20 | ------- | 23 | ------- |
| 21 | -H:: | 24 | -H:: |
| @@ -27,6 +30,9 @@ OPTIONS | |||
| 27 | -f:: | 30 | -f:: |
| 28 | --force:: | 31 | --force:: |
| 29 | Don't do ownership validation. | 32 | Don't do ownership validation. |
| 33 | -k:: | ||
| 34 | --kernel:: | ||
| 35 | Show running kernel build id. | ||
| 30 | -v:: | 36 | -v:: |
| 31 | --verbose:: | 37 | --verbose:: |
| 32 | Be more verbose. | 38 | Be more verbose. |
diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index 04253c07d19a..212f24d672e1 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt | |||
| @@ -134,6 +134,24 @@ OPTIONS | |||
| 134 | CPUs are specified with -: 0-2. Default is to report samples on all | 134 | CPUs are specified with -: 0-2. Default is to report samples on all |
| 135 | CPUs. | 135 | CPUs. |
| 136 | 136 | ||
| 137 | -M:: | ||
| 138 | --disassembler-style=:: Set disassembler style for objdump. | ||
| 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 | |||
| 137 | SEE ALSO | 155 | SEE ALSO |
| 138 | -------- | 156 | -------- |
| 139 | linkperf:perf-stat[1] | 157 | linkperf:perf-stat[1], linkperf:perf-annotate[1] |
diff --git a/tools/perf/Documentation/perf-sched.txt b/tools/perf/Documentation/perf-sched.txt index 46822d5fde1c..5b212b57f70b 100644 --- a/tools/perf/Documentation/perf-sched.txt +++ b/tools/perf/Documentation/perf-sched.txt | |||
| @@ -8,7 +8,7 @@ perf-sched - Tool to trace/measure scheduler properties (latencies) | |||
| 8 | SYNOPSIS | 8 | SYNOPSIS |
| 9 | -------- | 9 | -------- |
| 10 | [verse] | 10 | [verse] |
| 11 | 'perf sched' {record|latency|map|replay|trace} | 11 | 'perf sched' {record|latency|map|replay|script} |
| 12 | 12 | ||
| 13 | DESCRIPTION | 13 | DESCRIPTION |
| 14 | ----------- | 14 | ----------- |
| @@ -20,8 +20,8 @@ There are five variants of perf sched: | |||
| 20 | 'perf sched latency' to report the per task scheduling latencies | 20 | 'perf sched latency' to report the per task scheduling latencies |
| 21 | and other scheduling properties of the workload. | 21 | and other scheduling properties of the workload. |
| 22 | 22 | ||
| 23 | 'perf sched trace' to see a detailed trace of the workload that | 23 | 'perf sched script' to see a detailed trace of the workload that |
| 24 | was recorded. | 24 | was recorded (aliased to 'perf script' for now). |
| 25 | 25 | ||
| 26 | 'perf sched replay' to simulate the workload that was recorded | 26 | 'perf sched replay' to simulate the workload that was recorded |
| 27 | via perf sched record. (this is done by starting up mockup threads | 27 | via perf sched record. (this is done by starting up mockup threads |
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-stat.txt b/tools/perf/Documentation/perf-stat.txt index 918cc38ee6d1..8966b9ab2014 100644 --- a/tools/perf/Documentation/perf-stat.txt +++ b/tools/perf/Documentation/perf-stat.txt | |||
| @@ -94,6 +94,22 @@ an empty cgroup (monitor all the time) using, e.g., -G foo,,bar. Cgroups must ha | |||
| 94 | corresponding events, i.e., they always refer to events defined earlier on the command | 94 | corresponding events, i.e., they always refer to events defined earlier on the command |
| 95 | line. | 95 | line. |
| 96 | 96 | ||
| 97 | -o file:: | ||
| 98 | --output file:: | ||
| 99 | Print the output into the designated file. | ||
| 100 | |||
| 101 | --append:: | ||
| 102 | Append to the output file designated with the -o option. Ignored if -o is not specified. | ||
| 103 | |||
| 104 | --log-fd:: | ||
| 105 | |||
| 106 | Log output to fd, instead of stderr. Complementary to --output, and mutually exclusive | ||
| 107 | with it. --append may be used here. Examples: | ||
| 108 | 3>results perf stat --log-fd 3 -- $cmd | ||
| 109 | 3>>results perf stat --log-fd 3 --append -- $cmd | ||
| 110 | |||
| 111 | |||
| 112 | |||
| 97 | EXAMPLES | 113 | EXAMPLES |
| 98 | -------- | 114 | -------- |
| 99 | 115 | ||
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/Documentation/perfconfig.example b/tools/perf/Documentation/perfconfig.example new file mode 100644 index 000000000000..d1448668f4d4 --- /dev/null +++ b/tools/perf/Documentation/perfconfig.example | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | [colors] | ||
| 2 | |||
| 3 | # These were the old defaults | ||
| 4 | top = red, lightgray | ||
| 5 | medium = green, lightgray | ||
| 6 | normal = black, lightgray | ||
| 7 | selected = lightgray, magenta | ||
| 8 | code = blue, lightgray | ||
| 9 | |||
| 10 | [tui] | ||
| 11 | |||
| 12 | # Defaults if linked with libslang | ||
| 13 | report = on | ||
| 14 | annotate = on | ||
| 15 | top = on | ||
| 16 | |||
| 17 | [buildid] | ||
| 18 | |||
| 19 | # Default, disable using /dev/null | ||
| 20 | dir = /root/.debug | ||
diff --git a/tools/perf/Makefile b/tools/perf/Makefile index e9d5c271db69..b98e3075646b 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile | |||
| @@ -466,13 +466,13 @@ 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 |
| 473 | LIB_H += util/ui/browser.h | 472 | LIB_H += util/ui/browser.h |
| 474 | LIB_H += util/ui/browsers/map.h | 473 | LIB_H += util/ui/browsers/map.h |
| 475 | LIB_H += util/ui/helpline.h | 474 | LIB_H += util/ui/helpline.h |
| 475 | LIB_H += util/ui/keysyms.h | ||
| 476 | LIB_H += util/ui/libslang.h | 476 | LIB_H += util/ui/libslang.h |
| 477 | LIB_H += util/ui/progress.h | 477 | LIB_H += util/ui/progress.h |
| 478 | LIB_H += util/ui/util.h | 478 | LIB_H += util/ui/util.h |
| @@ -729,9 +729,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 | 729 | $(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 $< | 730 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< |
| 731 | 731 | ||
| 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 | 732 | $(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 $< | 733 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< |
| 737 | 734 | ||
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 555aefd7fe01..46b4c24f338e 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c | |||
| @@ -114,10 +114,11 @@ 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 = K_RIGHT; |
| 121 | 122 | ||
| 122 | while (nd) { | 123 | while (nd) { |
| 123 | struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node); | 124 | struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node); |
| @@ -129,7 +130,7 @@ static void hists__find_annotations(struct hists *self, int evidx) | |||
| 129 | notes = symbol__annotation(he->ms.sym); | 130 | notes = symbol__annotation(he->ms.sym); |
| 130 | if (notes->src == NULL) { | 131 | if (notes->src == NULL) { |
| 131 | find_next: | 132 | find_next: |
| 132 | if (key == KEY_LEFT) | 133 | if (key == K_LEFT) |
| 133 | nd = rb_prev(nd); | 134 | nd = rb_prev(nd); |
| 134 | else | 135 | else |
| 135 | nd = rb_next(nd); | 136 | nd = rb_next(nd); |
| @@ -137,12 +138,13 @@ 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 K_RIGHT: |
| 143 | next = rb_next(nd); | 145 | next = rb_next(nd); |
| 144 | break; | 146 | break; |
| 145 | case KEY_LEFT: | 147 | case K_LEFT: |
| 146 | next = rb_prev(nd); | 148 | next = rb_prev(nd); |
| 147 | break; | 149 | break; |
| 148 | default: | 150 | default: |
| @@ -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 | ||
| @@ -267,6 +270,14 @@ static const struct option options[] = { | |||
| 267 | OPT_BOOLEAN('P', "full-paths", &full_paths, | 270 | OPT_BOOLEAN('P', "full-paths", &full_paths, |
| 268 | "Don't shorten the displayed pathnames"), | 271 | "Don't shorten the displayed pathnames"), |
| 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"), |
| 273 | OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", | ||
| 274 | "Look for files with symbols relative to this directory"), | ||
| 275 | OPT_BOOLEAN(0, "source", &symbol_conf.annotate_src, | ||
| 276 | "Interleave source code with assembly code (default)"), | ||
| 277 | OPT_BOOLEAN(0, "asm-raw", &symbol_conf.annotate_asm_raw, | ||
| 278 | "Display raw encoding of assembly instructions (default)"), | ||
| 279 | OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style", | ||
| 280 | "Specify disassembler style (e.g. -M intel for intel syntax)"), | ||
| 270 | OPT_END() | 281 | OPT_END() |
| 271 | }; | 282 | }; |
| 272 | 283 | ||
diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c index 5af32ae9031e..cb690a65bf02 100644 --- a/tools/perf/builtin-buildid-list.c +++ b/tools/perf/builtin-buildid-list.c | |||
| @@ -1,7 +1,8 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * builtin-buildid-list.c | 2 | * builtin-buildid-list.c |
| 3 | * | 3 | * |
| 4 | * Builtin buildid-list command: list buildids in perf.data | 4 | * Builtin buildid-list command: list buildids in perf.data, in the running |
| 5 | * kernel and in ELF files. | ||
| 5 | * | 6 | * |
| 6 | * Copyright (C) 2009, Red Hat Inc. | 7 | * Copyright (C) 2009, Red Hat Inc. |
| 7 | * Copyright (C) 2009, Arnaldo Carvalho de Melo <acme@redhat.com> | 8 | * Copyright (C) 2009, Arnaldo Carvalho de Melo <acme@redhat.com> |
| @@ -15,8 +16,11 @@ | |||
| 15 | #include "util/session.h" | 16 | #include "util/session.h" |
| 16 | #include "util/symbol.h" | 17 | #include "util/symbol.h" |
| 17 | 18 | ||
| 19 | #include <libelf.h> | ||
| 20 | |||
| 18 | static char const *input_name = "perf.data"; | 21 | static char const *input_name = "perf.data"; |
| 19 | static bool force; | 22 | static bool force; |
| 23 | static bool show_kernel; | ||
| 20 | static bool with_hits; | 24 | static bool with_hits; |
| 21 | 25 | ||
| 22 | static const char * const buildid_list_usage[] = { | 26 | static const char * const buildid_list_usage[] = { |
| @@ -29,12 +33,13 @@ static const struct option options[] = { | |||
| 29 | OPT_STRING('i', "input", &input_name, "file", | 33 | OPT_STRING('i', "input", &input_name, "file", |
| 30 | "input file name"), | 34 | "input file name"), |
| 31 | OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), | 35 | OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), |
| 36 | OPT_BOOLEAN('k', "kernel", &show_kernel, "Show current kernel build id"), | ||
| 32 | OPT_INCR('v', "verbose", &verbose, | 37 | OPT_INCR('v', "verbose", &verbose, |
| 33 | "be more verbose"), | 38 | "be more verbose"), |
| 34 | OPT_END() | 39 | OPT_END() |
| 35 | }; | 40 | }; |
| 36 | 41 | ||
| 37 | static int __cmd_buildid_list(void) | 42 | static int perf_session__list_build_ids(void) |
| 38 | { | 43 | { |
| 39 | struct perf_session *session; | 44 | struct perf_session *session; |
| 40 | 45 | ||
| @@ -52,6 +57,49 @@ static int __cmd_buildid_list(void) | |||
| 52 | return 0; | 57 | return 0; |
| 53 | } | 58 | } |
| 54 | 59 | ||
| 60 | static int sysfs__fprintf_build_id(FILE *fp) | ||
| 61 | { | ||
| 62 | u8 kallsyms_build_id[BUILD_ID_SIZE]; | ||
| 63 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | ||
| 64 | |||
| 65 | if (sysfs__read_build_id("/sys/kernel/notes", kallsyms_build_id, | ||
| 66 | sizeof(kallsyms_build_id)) != 0) | ||
| 67 | return -1; | ||
| 68 | |||
| 69 | build_id__sprintf(kallsyms_build_id, sizeof(kallsyms_build_id), | ||
| 70 | sbuild_id); | ||
| 71 | fprintf(fp, "%s\n", sbuild_id); | ||
| 72 | return 0; | ||
| 73 | } | ||
| 74 | |||
| 75 | static int filename__fprintf_build_id(const char *name, FILE *fp) | ||
| 76 | { | ||
| 77 | u8 build_id[BUILD_ID_SIZE]; | ||
| 78 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | ||
| 79 | |||
| 80 | if (filename__read_build_id(name, build_id, | ||
| 81 | sizeof(build_id)) != sizeof(build_id)) | ||
| 82 | return 0; | ||
| 83 | |||
| 84 | build_id__sprintf(build_id, sizeof(build_id), sbuild_id); | ||
| 85 | return fprintf(fp, "%s\n", sbuild_id); | ||
| 86 | } | ||
| 87 | |||
| 88 | static int __cmd_buildid_list(void) | ||
| 89 | { | ||
| 90 | if (show_kernel) | ||
| 91 | return sysfs__fprintf_build_id(stdout); | ||
| 92 | |||
| 93 | elf_version(EV_CURRENT); | ||
| 94 | /* | ||
| 95 | * See if this is an ELF file first: | ||
| 96 | */ | ||
| 97 | if (filename__fprintf_build_id(input_name, stdout)) | ||
| 98 | return 0; | ||
| 99 | |||
| 100 | return perf_session__list_build_ids(); | ||
| 101 | } | ||
| 102 | |||
| 55 | int cmd_buildid_list(int argc, const char **argv, const char *prefix __used) | 103 | int cmd_buildid_list(int argc, const char **argv, const char *prefix __used) |
| 56 | { | 104 | { |
| 57 | argc = parse_options(argc, argv, options, buildid_list_usage, 0); | 105 | argc = parse_options(argc, argv, options, buildid_list_usage, 0); |
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 f4c3fbee4bad..6ab58cc99d53 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
| @@ -73,6 +73,7 @@ static off_t post_processing_offset; | |||
| 73 | 73 | ||
| 74 | static struct perf_session *session; | 74 | static struct perf_session *session; |
| 75 | static const char *cpu_list; | 75 | static const char *cpu_list; |
| 76 | static const char *progname; | ||
| 76 | 77 | ||
| 77 | static void advance_output(size_t size) | 78 | static void advance_output(size_t size) |
| 78 | { | 79 | { |
| @@ -137,17 +138,29 @@ static void mmap_read(struct perf_mmap *md) | |||
| 137 | 138 | ||
| 138 | static volatile int done = 0; | 139 | static volatile int done = 0; |
| 139 | static volatile int signr = -1; | 140 | static volatile int signr = -1; |
| 141 | static volatile int child_finished = 0; | ||
| 140 | 142 | ||
| 141 | static void sig_handler(int sig) | 143 | static void sig_handler(int sig) |
| 142 | { | 144 | { |
| 145 | if (sig == SIGCHLD) | ||
| 146 | child_finished = 1; | ||
| 147 | |||
| 143 | done = 1; | 148 | done = 1; |
| 144 | signr = sig; | 149 | signr = sig; |
| 145 | } | 150 | } |
| 146 | 151 | ||
| 147 | static void sig_atexit(void) | 152 | static void sig_atexit(void) |
| 148 | { | 153 | { |
| 149 | if (child_pid > 0) | 154 | int status; |
| 150 | kill(child_pid, SIGTERM); | 155 | |
| 156 | if (child_pid > 0) { | ||
| 157 | if (!child_finished) | ||
| 158 | kill(child_pid, SIGTERM); | ||
| 159 | |||
| 160 | wait(&status); | ||
| 161 | if (WIFSIGNALED(status)) | ||
| 162 | psignal(WTERMSIG(status), progname); | ||
| 163 | } | ||
| 151 | 164 | ||
| 152 | if (signr == -1 || signr == SIGUSR1) | 165 | if (signr == -1 || signr == SIGUSR1) |
| 153 | return; | 166 | return; |
| @@ -249,13 +262,16 @@ static bool perf_evlist__equal(struct perf_evlist *evlist, | |||
| 249 | 262 | ||
| 250 | static void open_counters(struct perf_evlist *evlist) | 263 | static void open_counters(struct perf_evlist *evlist) |
| 251 | { | 264 | { |
| 252 | struct perf_evsel *pos; | 265 | struct perf_evsel *pos, *first; |
| 253 | 266 | ||
| 254 | if (evlist->cpus->map[0] < 0) | 267 | if (evlist->cpus->map[0] < 0) |
| 255 | no_inherit = true; | 268 | no_inherit = true; |
| 256 | 269 | ||
| 270 | first = list_entry(evlist->entries.next, struct perf_evsel, node); | ||
| 271 | |||
| 257 | list_for_each_entry(pos, &evlist->entries, node) { | 272 | list_for_each_entry(pos, &evlist->entries, node) { |
| 258 | struct perf_event_attr *attr = &pos->attr; | 273 | struct perf_event_attr *attr = &pos->attr; |
| 274 | struct xyarray *group_fd = NULL; | ||
| 259 | /* | 275 | /* |
| 260 | * Check if parse_single_tracepoint_event has already asked for | 276 | * Check if parse_single_tracepoint_event has already asked for |
| 261 | * PERF_SAMPLE_TIME. | 277 | * PERF_SAMPLE_TIME. |
| @@ -270,15 +286,19 @@ static void open_counters(struct perf_evlist *evlist) | |||
| 270 | */ | 286 | */ |
| 271 | bool time_needed = attr->sample_type & PERF_SAMPLE_TIME; | 287 | bool time_needed = attr->sample_type & PERF_SAMPLE_TIME; |
| 272 | 288 | ||
| 289 | if (group && pos != first) | ||
| 290 | group_fd = first->fd; | ||
| 291 | |||
| 273 | config_attr(pos, evlist); | 292 | config_attr(pos, evlist); |
| 274 | retry_sample_id: | 293 | retry_sample_id: |
| 275 | attr->sample_id_all = sample_id_all_avail ? 1 : 0; | 294 | attr->sample_id_all = sample_id_all_avail ? 1 : 0; |
| 276 | try_again: | 295 | try_again: |
| 277 | if (perf_evsel__open(pos, evlist->cpus, evlist->threads, group) < 0) { | 296 | if (perf_evsel__open(pos, evlist->cpus, evlist->threads, group, |
| 297 | group_fd) < 0) { | ||
| 278 | int err = errno; | 298 | int err = errno; |
| 279 | 299 | ||
| 280 | if (err == EPERM || err == EACCES) { | 300 | if (err == EPERM || err == EACCES) { |
| 281 | ui__warning_paranoid(); | 301 | ui__error_paranoid(); |
| 282 | exit(EXIT_FAILURE); | 302 | exit(EXIT_FAILURE); |
| 283 | } else if (err == ENODEV && cpu_list) { | 303 | } else if (err == ENODEV && cpu_list) { |
| 284 | die("No such device - did you specify" | 304 | die("No such device - did you specify" |
| @@ -446,6 +466,8 @@ static int __cmd_record(int argc, const char **argv) | |||
| 446 | char buf; | 466 | char buf; |
| 447 | struct machine *machine; | 467 | struct machine *machine; |
| 448 | 468 | ||
| 469 | progname = argv[0]; | ||
| 470 | |||
| 449 | page_size = sysconf(_SC_PAGE_SIZE); | 471 | page_size = sysconf(_SC_PAGE_SIZE); |
| 450 | 472 | ||
| 451 | atexit(sig_atexit); | 473 | atexit(sig_atexit); |
| @@ -514,6 +536,19 @@ static int __cmd_record(int argc, const char **argv) | |||
| 514 | if (have_tracepoints(&evsel_list->entries)) | 536 | if (have_tracepoints(&evsel_list->entries)) |
| 515 | perf_header__set_feat(&session->header, HEADER_TRACE_INFO); | 537 | perf_header__set_feat(&session->header, HEADER_TRACE_INFO); |
| 516 | 538 | ||
| 539 | perf_header__set_feat(&session->header, HEADER_HOSTNAME); | ||
| 540 | perf_header__set_feat(&session->header, HEADER_OSRELEASE); | ||
| 541 | perf_header__set_feat(&session->header, HEADER_ARCH); | ||
| 542 | perf_header__set_feat(&session->header, HEADER_CPUDESC); | ||
| 543 | perf_header__set_feat(&session->header, HEADER_NRCPUS); | ||
| 544 | perf_header__set_feat(&session->header, HEADER_EVENT_DESC); | ||
| 545 | perf_header__set_feat(&session->header, HEADER_CMDLINE); | ||
| 546 | perf_header__set_feat(&session->header, HEADER_VERSION); | ||
| 547 | perf_header__set_feat(&session->header, HEADER_CPU_TOPOLOGY); | ||
| 548 | perf_header__set_feat(&session->header, HEADER_TOTAL_MEM); | ||
| 549 | perf_header__set_feat(&session->header, HEADER_NUMA_TOPOLOGY); | ||
| 550 | perf_header__set_feat(&session->header, HEADER_CPUID); | ||
| 551 | |||
| 517 | /* 512 kiB: default amount of unprivileged mlocked memory */ | 552 | /* 512 kiB: default amount of unprivileged mlocked memory */ |
| 518 | if (mmap_pages == UINT_MAX) | 553 | if (mmap_pages == UINT_MAX) |
| 519 | mmap_pages = (512 * 1024) / page_size; | 554 | mmap_pages = (512 * 1024) / page_size; |
| @@ -785,6 +820,8 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) | |||
| 785 | int err = -ENOMEM; | 820 | int err = -ENOMEM; |
| 786 | struct perf_evsel *pos; | 821 | struct perf_evsel *pos; |
| 787 | 822 | ||
| 823 | perf_header__set_cmdline(argc, argv); | ||
| 824 | |||
| 788 | evsel_list = perf_evlist__new(NULL, NULL); | 825 | evsel_list = perf_evlist__new(NULL, NULL); |
| 789 | if (evsel_list == NULL) | 826 | if (evsel_list == NULL) |
| 790 | return -ENOMEM; | 827 | return -ENOMEM; |
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index d7ff277bdb78..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; |
| @@ -229,13 +230,10 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist, | |||
| 229 | 230 | ||
| 230 | list_for_each_entry(pos, &evlist->entries, node) { | 231 | list_for_each_entry(pos, &evlist->entries, node) { |
| 231 | struct hists *hists = &pos->hists; | 232 | struct hists *hists = &pos->hists; |
| 232 | const char *evname = NULL; | 233 | const char *evname = event_name(pos); |
| 233 | |||
| 234 | if (rb_first(&hists->entries) != rb_last(&hists->entries)) | ||
| 235 | evname = event_name(pos); | ||
| 236 | 234 | ||
| 237 | hists__fprintf_nr_sample_events(hists, evname, stdout); | 235 | hists__fprintf_nr_sample_events(hists, evname, stdout); |
| 238 | hists__fprintf(hists, NULL, false, stdout); | 236 | hists__fprintf(hists, NULL, false, true, 0, 0, stdout); |
| 239 | fprintf(stdout, "\n\n"); | 237 | fprintf(stdout, "\n\n"); |
| 240 | } | 238 | } |
| 241 | 239 | ||
| @@ -276,6 +274,9 @@ static int __cmd_report(void) | |||
| 276 | goto out_delete; | 274 | goto out_delete; |
| 277 | } | 275 | } |
| 278 | 276 | ||
| 277 | if (use_browser <= 0) | ||
| 278 | perf_session__fprintf_info(session, stdout, show_full_info); | ||
| 279 | |||
| 279 | if (show_threads) | 280 | if (show_threads) |
| 280 | perf_read_values_init(&show_threads_values); | 281 | perf_read_values_init(&show_threads_values); |
| 281 | 282 | ||
| @@ -330,9 +331,10 @@ static int __cmd_report(void) | |||
| 330 | goto out_delete; | 331 | goto out_delete; |
| 331 | } | 332 | } |
| 332 | 333 | ||
| 333 | if (use_browser > 0) | 334 | if (use_browser > 0) { |
| 334 | perf_evlist__tui_browse_hists(session->evlist, help); | 335 | perf_evlist__tui_browse_hists(session->evlist, help, |
| 335 | else | 336 | NULL, NULL, 0); |
| 337 | } else | ||
| 336 | perf_evlist__tty_browse_hists(session->evlist, help); | 338 | perf_evlist__tty_browse_hists(session->evlist, help); |
| 337 | 339 | ||
| 338 | out_delete: | 340 | out_delete: |
| @@ -487,6 +489,16 @@ static const struct option options[] = { | |||
| 487 | OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", | 489 | OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", |
| 488 | "Look for files with symbols relative to this directory"), | 490 | "Look for files with symbols relative to this directory"), |
| 489 | 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)"), | ||
| 498 | OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style", | ||
| 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"), | ||
| 490 | OPT_END() | 502 | OPT_END() |
| 491 | }; | 503 | }; |
| 492 | 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-stat.c b/tools/perf/builtin-stat.c index 5deb17d9e795..7d98676808d8 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c | |||
| @@ -194,6 +194,9 @@ static const char *cpu_list; | |||
| 194 | static const char *csv_sep = NULL; | 194 | static const char *csv_sep = NULL; |
| 195 | static bool csv_output = false; | 195 | static bool csv_output = false; |
| 196 | static bool group = false; | 196 | static bool group = false; |
| 197 | static const char *output_name = NULL; | ||
| 198 | static FILE *output = NULL; | ||
| 199 | static int output_fd; | ||
| 197 | 200 | ||
| 198 | static volatile int done = 0; | 201 | static volatile int done = 0; |
| 199 | 202 | ||
| @@ -251,8 +254,13 @@ static double avg_stats(struct stats *stats) | |||
| 251 | */ | 254 | */ |
| 252 | static double stddev_stats(struct stats *stats) | 255 | static double stddev_stats(struct stats *stats) |
| 253 | { | 256 | { |
| 254 | double variance = stats->M2 / (stats->n - 1); | 257 | double variance, variance_mean; |
| 255 | double variance_mean = variance / stats->n; | 258 | |
| 259 | if (!stats->n) | ||
| 260 | return 0.0; | ||
| 261 | |||
| 262 | variance = stats->M2 / (stats->n - 1); | ||
| 263 | variance_mean = variance / stats->n; | ||
| 256 | 264 | ||
| 257 | return sqrt(variance_mean); | 265 | return sqrt(variance_mean); |
| 258 | } | 266 | } |
| @@ -270,9 +278,14 @@ struct stats runtime_itlb_cache_stats[MAX_NR_CPUS]; | |||
| 270 | struct stats runtime_dtlb_cache_stats[MAX_NR_CPUS]; | 278 | struct stats runtime_dtlb_cache_stats[MAX_NR_CPUS]; |
| 271 | struct stats walltime_nsecs_stats; | 279 | struct stats walltime_nsecs_stats; |
| 272 | 280 | ||
| 273 | static int create_perf_stat_counter(struct perf_evsel *evsel) | 281 | static int create_perf_stat_counter(struct perf_evsel *evsel, |
| 282 | struct perf_evsel *first) | ||
| 274 | { | 283 | { |
| 275 | struct perf_event_attr *attr = &evsel->attr; | 284 | struct perf_event_attr *attr = &evsel->attr; |
| 285 | struct xyarray *group_fd = NULL; | ||
| 286 | |||
| 287 | if (group && evsel != first) | ||
| 288 | group_fd = first->fd; | ||
| 276 | 289 | ||
| 277 | if (scale) | 290 | if (scale) |
| 278 | attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | | 291 | attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | |
| @@ -281,14 +294,15 @@ static int create_perf_stat_counter(struct perf_evsel *evsel) | |||
| 281 | attr->inherit = !no_inherit; | 294 | attr->inherit = !no_inherit; |
| 282 | 295 | ||
| 283 | if (system_wide) | 296 | if (system_wide) |
| 284 | return perf_evsel__open_per_cpu(evsel, evsel_list->cpus, group); | 297 | return perf_evsel__open_per_cpu(evsel, evsel_list->cpus, |
| 285 | 298 | group, group_fd); | |
| 286 | if (target_pid == -1 && target_tid == -1) { | 299 | if (target_pid == -1 && target_tid == -1) { |
| 287 | attr->disabled = 1; | 300 | attr->disabled = 1; |
| 288 | attr->enable_on_exec = 1; | 301 | attr->enable_on_exec = 1; |
| 289 | } | 302 | } |
| 290 | 303 | ||
| 291 | return perf_evsel__open_per_thread(evsel, evsel_list->threads, group); | 304 | return perf_evsel__open_per_thread(evsel, evsel_list->threads, |
| 305 | group, group_fd); | ||
| 292 | } | 306 | } |
| 293 | 307 | ||
| 294 | /* | 308 | /* |
| @@ -352,7 +366,7 @@ static int read_counter_aggr(struct perf_evsel *counter) | |||
| 352 | update_stats(&ps->res_stats[i], count[i]); | 366 | update_stats(&ps->res_stats[i], count[i]); |
| 353 | 367 | ||
| 354 | if (verbose) { | 368 | if (verbose) { |
| 355 | fprintf(stderr, "%s: %" PRIu64 " %" PRIu64 " %" PRIu64 "\n", | 369 | fprintf(output, "%s: %" PRIu64 " %" PRIu64 " %" PRIu64 "\n", |
| 356 | event_name(counter), count[0], count[1], count[2]); | 370 | event_name(counter), count[0], count[1], count[2]); |
| 357 | } | 371 | } |
| 358 | 372 | ||
| @@ -388,7 +402,7 @@ static int read_counter(struct perf_evsel *counter) | |||
| 388 | static int run_perf_stat(int argc __used, const char **argv) | 402 | static int run_perf_stat(int argc __used, const char **argv) |
| 389 | { | 403 | { |
| 390 | unsigned long long t0, t1; | 404 | unsigned long long t0, t1; |
| 391 | struct perf_evsel *counter; | 405 | struct perf_evsel *counter, *first; |
| 392 | int status = 0; | 406 | int status = 0; |
| 393 | int child_ready_pipe[2], go_pipe[2]; | 407 | int child_ready_pipe[2], go_pipe[2]; |
| 394 | const bool forks = (argc > 0); | 408 | const bool forks = (argc > 0); |
| @@ -445,8 +459,10 @@ static int run_perf_stat(int argc __used, const char **argv) | |||
| 445 | close(child_ready_pipe[0]); | 459 | close(child_ready_pipe[0]); |
| 446 | } | 460 | } |
| 447 | 461 | ||
| 462 | first = list_entry(evsel_list->entries.next, struct perf_evsel, node); | ||
| 463 | |||
| 448 | list_for_each_entry(counter, &evsel_list->entries, node) { | 464 | list_for_each_entry(counter, &evsel_list->entries, node) { |
| 449 | if (create_perf_stat_counter(counter) < 0) { | 465 | if (create_perf_stat_counter(counter, first) < 0) { |
| 450 | if (errno == EINVAL || errno == ENOSYS || errno == ENOENT) { | 466 | if (errno == EINVAL || errno == ENOSYS || errno == ENOENT) { |
| 451 | if (verbose) | 467 | if (verbose) |
| 452 | ui__warning("%s event is not supported by the kernel.\n", | 468 | ui__warning("%s event is not supported by the kernel.\n", |
| @@ -487,6 +503,8 @@ static int run_perf_stat(int argc __used, const char **argv) | |||
| 487 | if (forks) { | 503 | if (forks) { |
| 488 | close(go_pipe[1]); | 504 | close(go_pipe[1]); |
| 489 | wait(&status); | 505 | wait(&status); |
| 506 | if (WIFSIGNALED(status)) | ||
| 507 | psignal(WTERMSIG(status), argv[0]); | ||
| 490 | } else { | 508 | } else { |
| 491 | while(!done) sleep(1); | 509 | while(!done) sleep(1); |
| 492 | } | 510 | } |
| @@ -519,9 +537,9 @@ static void print_noise_pct(double total, double avg) | |||
| 519 | pct = 100.0*total/avg; | 537 | pct = 100.0*total/avg; |
| 520 | 538 | ||
| 521 | if (csv_output) | 539 | if (csv_output) |
| 522 | fprintf(stderr, "%s%.2f%%", csv_sep, pct); | 540 | fprintf(output, "%s%.2f%%", csv_sep, pct); |
| 523 | else | 541 | else if (pct) |
| 524 | fprintf(stderr, " ( +-%6.2f%% )", pct); | 542 | fprintf(output, " ( +-%6.2f%% )", pct); |
| 525 | } | 543 | } |
| 526 | 544 | ||
| 527 | static void print_noise(struct perf_evsel *evsel, double avg) | 545 | static void print_noise(struct perf_evsel *evsel, double avg) |
| @@ -546,16 +564,17 @@ static void nsec_printout(int cpu, struct perf_evsel *evsel, double avg) | |||
| 546 | csv_output ? 0 : -4, | 564 | csv_output ? 0 : -4, |
| 547 | evsel_list->cpus->map[cpu], csv_sep); | 565 | evsel_list->cpus->map[cpu], csv_sep); |
| 548 | 566 | ||
| 549 | fprintf(stderr, fmt, cpustr, msecs, csv_sep, event_name(evsel)); | 567 | fprintf(output, fmt, cpustr, msecs, csv_sep, event_name(evsel)); |
| 550 | 568 | ||
| 551 | if (evsel->cgrp) | 569 | if (evsel->cgrp) |
| 552 | fprintf(stderr, "%s%s", csv_sep, evsel->cgrp->name); | 570 | fprintf(output, "%s%s", csv_sep, evsel->cgrp->name); |
| 553 | 571 | ||
| 554 | if (csv_output) | 572 | if (csv_output) |
| 555 | return; | 573 | return; |
| 556 | 574 | ||
| 557 | if (perf_evsel__match(evsel, SOFTWARE, SW_TASK_CLOCK)) | 575 | if (perf_evsel__match(evsel, SOFTWARE, SW_TASK_CLOCK)) |
| 558 | fprintf(stderr, " # %8.3f CPUs utilized ", avg / avg_stats(&walltime_nsecs_stats)); | 576 | fprintf(output, " # %8.3f CPUs utilized ", |
| 577 | avg / avg_stats(&walltime_nsecs_stats)); | ||
| 559 | } | 578 | } |
| 560 | 579 | ||
| 561 | static void print_stalled_cycles_frontend(int cpu, struct perf_evsel *evsel __used, double avg) | 580 | static void print_stalled_cycles_frontend(int cpu, struct perf_evsel *evsel __used, double avg) |
| @@ -576,9 +595,9 @@ static void print_stalled_cycles_frontend(int cpu, struct perf_evsel *evsel __us | |||
| 576 | else if (ratio > 10.0) | 595 | else if (ratio > 10.0) |
| 577 | color = PERF_COLOR_YELLOW; | 596 | color = PERF_COLOR_YELLOW; |
| 578 | 597 | ||
| 579 | fprintf(stderr, " # "); | 598 | fprintf(output, " # "); |
| 580 | color_fprintf(stderr, color, "%6.2f%%", ratio); | 599 | color_fprintf(output, color, "%6.2f%%", ratio); |
| 581 | fprintf(stderr, " frontend cycles idle "); | 600 | fprintf(output, " frontend cycles idle "); |
| 582 | } | 601 | } |
| 583 | 602 | ||
| 584 | static void print_stalled_cycles_backend(int cpu, struct perf_evsel *evsel __used, double avg) | 603 | static void print_stalled_cycles_backend(int cpu, struct perf_evsel *evsel __used, double avg) |
| @@ -599,9 +618,9 @@ static void print_stalled_cycles_backend(int cpu, struct perf_evsel *evsel __use | |||
| 599 | else if (ratio > 20.0) | 618 | else if (ratio > 20.0) |
| 600 | color = PERF_COLOR_YELLOW; | 619 | color = PERF_COLOR_YELLOW; |
| 601 | 620 | ||
| 602 | fprintf(stderr, " # "); | 621 | fprintf(output, " # "); |
| 603 | color_fprintf(stderr, color, "%6.2f%%", ratio); | 622 | color_fprintf(output, color, "%6.2f%%", ratio); |
| 604 | fprintf(stderr, " backend cycles idle "); | 623 | fprintf(output, " backend cycles idle "); |
| 605 | } | 624 | } |
| 606 | 625 | ||
| 607 | static void print_branch_misses(int cpu, struct perf_evsel *evsel __used, double avg) | 626 | static void print_branch_misses(int cpu, struct perf_evsel *evsel __used, double avg) |
| @@ -622,9 +641,9 @@ static void print_branch_misses(int cpu, struct perf_evsel *evsel __used, double | |||
| 622 | else if (ratio > 5.0) | 641 | else if (ratio > 5.0) |
| 623 | color = PERF_COLOR_YELLOW; | 642 | color = PERF_COLOR_YELLOW; |
| 624 | 643 | ||
| 625 | fprintf(stderr, " # "); | 644 | fprintf(output, " # "); |
| 626 | color_fprintf(stderr, color, "%6.2f%%", ratio); | 645 | color_fprintf(output, color, "%6.2f%%", ratio); |
| 627 | fprintf(stderr, " of all branches "); | 646 | fprintf(output, " of all branches "); |
| 628 | } | 647 | } |
| 629 | 648 | ||
| 630 | static void print_l1_dcache_misses(int cpu, struct perf_evsel *evsel __used, double avg) | 649 | static void print_l1_dcache_misses(int cpu, struct perf_evsel *evsel __used, double avg) |
| @@ -645,9 +664,9 @@ static void print_l1_dcache_misses(int cpu, struct perf_evsel *evsel __used, dou | |||
| 645 | else if (ratio > 5.0) | 664 | else if (ratio > 5.0) |
| 646 | color = PERF_COLOR_YELLOW; | 665 | color = PERF_COLOR_YELLOW; |
| 647 | 666 | ||
| 648 | fprintf(stderr, " # "); | 667 | fprintf(output, " # "); |
| 649 | color_fprintf(stderr, color, "%6.2f%%", ratio); | 668 | color_fprintf(output, color, "%6.2f%%", ratio); |
| 650 | fprintf(stderr, " of all L1-dcache hits "); | 669 | fprintf(output, " of all L1-dcache hits "); |
| 651 | } | 670 | } |
| 652 | 671 | ||
| 653 | static void print_l1_icache_misses(int cpu, struct perf_evsel *evsel __used, double avg) | 672 | static void print_l1_icache_misses(int cpu, struct perf_evsel *evsel __used, double avg) |
| @@ -668,9 +687,9 @@ static void print_l1_icache_misses(int cpu, struct perf_evsel *evsel __used, dou | |||
| 668 | else if (ratio > 5.0) | 687 | else if (ratio > 5.0) |
| 669 | color = PERF_COLOR_YELLOW; | 688 | color = PERF_COLOR_YELLOW; |
| 670 | 689 | ||
| 671 | fprintf(stderr, " # "); | 690 | fprintf(output, " # "); |
| 672 | color_fprintf(stderr, color, "%6.2f%%", ratio); | 691 | color_fprintf(output, color, "%6.2f%%", ratio); |
| 673 | fprintf(stderr, " of all L1-icache hits "); | 692 | fprintf(output, " of all L1-icache hits "); |
| 674 | } | 693 | } |
| 675 | 694 | ||
| 676 | static void print_dtlb_cache_misses(int cpu, struct perf_evsel *evsel __used, double avg) | 695 | static void print_dtlb_cache_misses(int cpu, struct perf_evsel *evsel __used, double avg) |
| @@ -691,9 +710,9 @@ static void print_dtlb_cache_misses(int cpu, struct perf_evsel *evsel __used, do | |||
| 691 | else if (ratio > 5.0) | 710 | else if (ratio > 5.0) |
| 692 | color = PERF_COLOR_YELLOW; | 711 | color = PERF_COLOR_YELLOW; |
| 693 | 712 | ||
| 694 | fprintf(stderr, " # "); | 713 | fprintf(output, " # "); |
| 695 | color_fprintf(stderr, color, "%6.2f%%", ratio); | 714 | color_fprintf(output, color, "%6.2f%%", ratio); |
| 696 | fprintf(stderr, " of all dTLB cache hits "); | 715 | fprintf(output, " of all dTLB cache hits "); |
| 697 | } | 716 | } |
| 698 | 717 | ||
| 699 | static void print_itlb_cache_misses(int cpu, struct perf_evsel *evsel __used, double avg) | 718 | static void print_itlb_cache_misses(int cpu, struct perf_evsel *evsel __used, double avg) |
| @@ -714,9 +733,9 @@ static void print_itlb_cache_misses(int cpu, struct perf_evsel *evsel __used, do | |||
| 714 | else if (ratio > 5.0) | 733 | else if (ratio > 5.0) |
| 715 | color = PERF_COLOR_YELLOW; | 734 | color = PERF_COLOR_YELLOW; |
| 716 | 735 | ||
| 717 | fprintf(stderr, " # "); | 736 | fprintf(output, " # "); |
| 718 | color_fprintf(stderr, color, "%6.2f%%", ratio); | 737 | color_fprintf(output, color, "%6.2f%%", ratio); |
| 719 | fprintf(stderr, " of all iTLB cache hits "); | 738 | fprintf(output, " of all iTLB cache hits "); |
| 720 | } | 739 | } |
| 721 | 740 | ||
| 722 | static void print_ll_cache_misses(int cpu, struct perf_evsel *evsel __used, double avg) | 741 | static void print_ll_cache_misses(int cpu, struct perf_evsel *evsel __used, double avg) |
| @@ -737,9 +756,9 @@ static void print_ll_cache_misses(int cpu, struct perf_evsel *evsel __used, doub | |||
| 737 | else if (ratio > 5.0) | 756 | else if (ratio > 5.0) |
| 738 | color = PERF_COLOR_YELLOW; | 757 | color = PERF_COLOR_YELLOW; |
| 739 | 758 | ||
| 740 | fprintf(stderr, " # "); | 759 | fprintf(output, " # "); |
| 741 | color_fprintf(stderr, color, "%6.2f%%", ratio); | 760 | color_fprintf(output, color, "%6.2f%%", ratio); |
| 742 | fprintf(stderr, " of all LL-cache hits "); | 761 | fprintf(output, " of all LL-cache hits "); |
| 743 | } | 762 | } |
| 744 | 763 | ||
| 745 | static void abs_printout(int cpu, struct perf_evsel *evsel, double avg) | 764 | static void abs_printout(int cpu, struct perf_evsel *evsel, double avg) |
| @@ -762,10 +781,10 @@ static void abs_printout(int cpu, struct perf_evsel *evsel, double avg) | |||
| 762 | else | 781 | else |
| 763 | cpu = 0; | 782 | cpu = 0; |
| 764 | 783 | ||
| 765 | fprintf(stderr, fmt, cpustr, avg, csv_sep, event_name(evsel)); | 784 | fprintf(output, fmt, cpustr, avg, csv_sep, event_name(evsel)); |
| 766 | 785 | ||
| 767 | if (evsel->cgrp) | 786 | if (evsel->cgrp) |
| 768 | fprintf(stderr, "%s%s", csv_sep, evsel->cgrp->name); | 787 | fprintf(output, "%s%s", csv_sep, evsel->cgrp->name); |
| 769 | 788 | ||
| 770 | if (csv_output) | 789 | if (csv_output) |
| 771 | return; | 790 | return; |
| @@ -776,14 +795,14 @@ static void abs_printout(int cpu, struct perf_evsel *evsel, double avg) | |||
| 776 | if (total) | 795 | if (total) |
| 777 | ratio = avg / total; | 796 | ratio = avg / total; |
| 778 | 797 | ||
| 779 | fprintf(stderr, " # %5.2f insns per cycle ", ratio); | 798 | fprintf(output, " # %5.2f insns per cycle ", ratio); |
| 780 | 799 | ||
| 781 | total = avg_stats(&runtime_stalled_cycles_front_stats[cpu]); | 800 | total = avg_stats(&runtime_stalled_cycles_front_stats[cpu]); |
| 782 | total = max(total, avg_stats(&runtime_stalled_cycles_back_stats[cpu])); | 801 | total = max(total, avg_stats(&runtime_stalled_cycles_back_stats[cpu])); |
| 783 | 802 | ||
| 784 | if (total && avg) { | 803 | if (total && avg) { |
| 785 | ratio = total / avg; | 804 | ratio = total / avg; |
| 786 | fprintf(stderr, "\n # %5.2f stalled cycles per insn", ratio); | 805 | fprintf(output, "\n # %5.2f stalled cycles per insn", ratio); |
| 787 | } | 806 | } |
| 788 | 807 | ||
| 789 | } else if (perf_evsel__match(evsel, HARDWARE, HW_BRANCH_MISSES) && | 808 | } else if (perf_evsel__match(evsel, HARDWARE, HW_BRANCH_MISSES) && |
| @@ -831,7 +850,7 @@ static void abs_printout(int cpu, struct perf_evsel *evsel, double avg) | |||
| 831 | if (total) | 850 | if (total) |
| 832 | ratio = avg * 100 / total; | 851 | ratio = avg * 100 / total; |
| 833 | 852 | ||
| 834 | fprintf(stderr, " # %8.3f %% of all cache refs ", ratio); | 853 | fprintf(output, " # %8.3f %% of all cache refs ", ratio); |
| 835 | 854 | ||
| 836 | } else if (perf_evsel__match(evsel, HARDWARE, HW_STALLED_CYCLES_FRONTEND)) { | 855 | } else if (perf_evsel__match(evsel, HARDWARE, HW_STALLED_CYCLES_FRONTEND)) { |
| 837 | print_stalled_cycles_frontend(cpu, evsel, avg); | 856 | print_stalled_cycles_frontend(cpu, evsel, avg); |
| @@ -843,16 +862,16 @@ static void abs_printout(int cpu, struct perf_evsel *evsel, double avg) | |||
| 843 | if (total) | 862 | if (total) |
| 844 | ratio = 1.0 * avg / total; | 863 | ratio = 1.0 * avg / total; |
| 845 | 864 | ||
| 846 | fprintf(stderr, " # %8.3f GHz ", ratio); | 865 | fprintf(output, " # %8.3f GHz ", ratio); |
| 847 | } else if (runtime_nsecs_stats[cpu].n != 0) { | 866 | } else if (runtime_nsecs_stats[cpu].n != 0) { |
| 848 | total = avg_stats(&runtime_nsecs_stats[cpu]); | 867 | total = avg_stats(&runtime_nsecs_stats[cpu]); |
| 849 | 868 | ||
| 850 | if (total) | 869 | if (total) |
| 851 | ratio = 1000.0 * avg / total; | 870 | ratio = 1000.0 * avg / total; |
| 852 | 871 | ||
| 853 | fprintf(stderr, " # %8.3f M/sec ", ratio); | 872 | fprintf(output, " # %8.3f M/sec ", ratio); |
| 854 | } else { | 873 | } else { |
| 855 | fprintf(stderr, " "); | 874 | fprintf(output, " "); |
| 856 | } | 875 | } |
| 857 | } | 876 | } |
| 858 | 877 | ||
| @@ -867,7 +886,7 @@ static void print_counter_aggr(struct perf_evsel *counter) | |||
| 867 | int scaled = counter->counts->scaled; | 886 | int scaled = counter->counts->scaled; |
| 868 | 887 | ||
| 869 | if (scaled == -1) { | 888 | if (scaled == -1) { |
| 870 | fprintf(stderr, "%*s%s%*s", | 889 | fprintf(output, "%*s%s%*s", |
| 871 | csv_output ? 0 : 18, | 890 | csv_output ? 0 : 18, |
| 872 | counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED, | 891 | counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED, |
| 873 | csv_sep, | 892 | csv_sep, |
| @@ -875,9 +894,9 @@ static void print_counter_aggr(struct perf_evsel *counter) | |||
| 875 | event_name(counter)); | 894 | event_name(counter)); |
| 876 | 895 | ||
| 877 | if (counter->cgrp) | 896 | if (counter->cgrp) |
| 878 | fprintf(stderr, "%s%s", csv_sep, counter->cgrp->name); | 897 | fprintf(output, "%s%s", csv_sep, counter->cgrp->name); |
| 879 | 898 | ||
| 880 | fputc('\n', stderr); | 899 | fputc('\n', output); |
| 881 | return; | 900 | return; |
| 882 | } | 901 | } |
| 883 | 902 | ||
| @@ -889,7 +908,7 @@ static void print_counter_aggr(struct perf_evsel *counter) | |||
| 889 | print_noise(counter, avg); | 908 | print_noise(counter, avg); |
| 890 | 909 | ||
| 891 | if (csv_output) { | 910 | if (csv_output) { |
| 892 | fputc('\n', stderr); | 911 | fputc('\n', output); |
| 893 | return; | 912 | return; |
| 894 | } | 913 | } |
| 895 | 914 | ||
| @@ -899,9 +918,9 @@ static void print_counter_aggr(struct perf_evsel *counter) | |||
| 899 | avg_enabled = avg_stats(&ps->res_stats[1]); | 918 | avg_enabled = avg_stats(&ps->res_stats[1]); |
| 900 | avg_running = avg_stats(&ps->res_stats[2]); | 919 | avg_running = avg_stats(&ps->res_stats[2]); |
| 901 | 920 | ||
| 902 | fprintf(stderr, " [%5.2f%%]", 100 * avg_running / avg_enabled); | 921 | fprintf(output, " [%5.2f%%]", 100 * avg_running / avg_enabled); |
| 903 | } | 922 | } |
| 904 | fprintf(stderr, "\n"); | 923 | fprintf(output, "\n"); |
| 905 | } | 924 | } |
| 906 | 925 | ||
| 907 | /* | 926 | /* |
| @@ -918,7 +937,7 @@ static void print_counter(struct perf_evsel *counter) | |||
| 918 | ena = counter->counts->cpu[cpu].ena; | 937 | ena = counter->counts->cpu[cpu].ena; |
| 919 | run = counter->counts->cpu[cpu].run; | 938 | run = counter->counts->cpu[cpu].run; |
| 920 | if (run == 0 || ena == 0) { | 939 | if (run == 0 || ena == 0) { |
| 921 | fprintf(stderr, "CPU%*d%s%*s%s%*s", | 940 | fprintf(output, "CPU%*d%s%*s%s%*s", |
| 922 | csv_output ? 0 : -4, | 941 | csv_output ? 0 : -4, |
| 923 | evsel_list->cpus->map[cpu], csv_sep, | 942 | evsel_list->cpus->map[cpu], csv_sep, |
| 924 | csv_output ? 0 : 18, | 943 | csv_output ? 0 : 18, |
| @@ -928,9 +947,10 @@ static void print_counter(struct perf_evsel *counter) | |||
| 928 | event_name(counter)); | 947 | event_name(counter)); |
| 929 | 948 | ||
| 930 | if (counter->cgrp) | 949 | if (counter->cgrp) |
| 931 | fprintf(stderr, "%s%s", csv_sep, counter->cgrp->name); | 950 | fprintf(output, "%s%s", |
| 951 | csv_sep, counter->cgrp->name); | ||
| 932 | 952 | ||
| 933 | fputc('\n', stderr); | 953 | fputc('\n', output); |
| 934 | continue; | 954 | continue; |
| 935 | } | 955 | } |
| 936 | 956 | ||
| @@ -943,9 +963,10 @@ static void print_counter(struct perf_evsel *counter) | |||
| 943 | print_noise(counter, 1.0); | 963 | print_noise(counter, 1.0); |
| 944 | 964 | ||
| 945 | if (run != ena) | 965 | if (run != ena) |
| 946 | fprintf(stderr, " (%.2f%%)", 100.0 * run / ena); | 966 | fprintf(output, " (%.2f%%)", |
| 967 | 100.0 * run / ena); | ||
| 947 | } | 968 | } |
| 948 | fputc('\n', stderr); | 969 | fputc('\n', output); |
| 949 | } | 970 | } |
| 950 | } | 971 | } |
| 951 | 972 | ||
| @@ -957,21 +978,21 @@ static void print_stat(int argc, const char **argv) | |||
| 957 | fflush(stdout); | 978 | fflush(stdout); |
| 958 | 979 | ||
| 959 | if (!csv_output) { | 980 | if (!csv_output) { |
| 960 | fprintf(stderr, "\n"); | 981 | fprintf(output, "\n"); |
| 961 | fprintf(stderr, " Performance counter stats for "); | 982 | fprintf(output, " Performance counter stats for "); |
| 962 | if(target_pid == -1 && target_tid == -1) { | 983 | if(target_pid == -1 && target_tid == -1) { |
| 963 | fprintf(stderr, "\'%s", argv[0]); | 984 | fprintf(output, "\'%s", argv[0]); |
| 964 | for (i = 1; i < argc; i++) | 985 | for (i = 1; i < argc; i++) |
| 965 | fprintf(stderr, " %s", argv[i]); | 986 | fprintf(output, " %s", argv[i]); |
| 966 | } else if (target_pid != -1) | 987 | } else if (target_pid != -1) |
| 967 | fprintf(stderr, "process id \'%d", target_pid); | 988 | fprintf(output, "process id \'%d", target_pid); |
| 968 | else | 989 | else |
| 969 | fprintf(stderr, "thread id \'%d", target_tid); | 990 | fprintf(output, "thread id \'%d", target_tid); |
| 970 | 991 | ||
| 971 | fprintf(stderr, "\'"); | 992 | fprintf(output, "\'"); |
| 972 | if (run_count > 1) | 993 | if (run_count > 1) |
| 973 | fprintf(stderr, " (%d runs)", run_count); | 994 | fprintf(output, " (%d runs)", run_count); |
| 974 | fprintf(stderr, ":\n\n"); | 995 | fprintf(output, ":\n\n"); |
| 975 | } | 996 | } |
| 976 | 997 | ||
| 977 | if (no_aggr) { | 998 | if (no_aggr) { |
| @@ -984,15 +1005,15 @@ static void print_stat(int argc, const char **argv) | |||
| 984 | 1005 | ||
| 985 | if (!csv_output) { | 1006 | if (!csv_output) { |
| 986 | if (!null_run) | 1007 | if (!null_run) |
| 987 | fprintf(stderr, "\n"); | 1008 | fprintf(output, "\n"); |
| 988 | fprintf(stderr, " %17.9f seconds time elapsed", | 1009 | fprintf(output, " %17.9f seconds time elapsed", |
| 989 | avg_stats(&walltime_nsecs_stats)/1e9); | 1010 | avg_stats(&walltime_nsecs_stats)/1e9); |
| 990 | if (run_count > 1) { | 1011 | if (run_count > 1) { |
| 991 | fprintf(stderr, " "); | 1012 | fprintf(output, " "); |
| 992 | print_noise_pct(stddev_stats(&walltime_nsecs_stats), | 1013 | print_noise_pct(stddev_stats(&walltime_nsecs_stats), |
| 993 | avg_stats(&walltime_nsecs_stats)); | 1014 | avg_stats(&walltime_nsecs_stats)); |
| 994 | } | 1015 | } |
| 995 | fprintf(stderr, "\n\n"); | 1016 | fprintf(output, "\n\n"); |
| 996 | } | 1017 | } |
| 997 | } | 1018 | } |
| 998 | 1019 | ||
| @@ -1030,6 +1051,8 @@ static int stat__set_big_num(const struct option *opt __used, | |||
| 1030 | return 0; | 1051 | return 0; |
| 1031 | } | 1052 | } |
| 1032 | 1053 | ||
| 1054 | static bool append_file; | ||
| 1055 | |||
| 1033 | static const struct option options[] = { | 1056 | static const struct option options[] = { |
| 1034 | OPT_CALLBACK('e', "event", &evsel_list, "event", | 1057 | OPT_CALLBACK('e', "event", &evsel_list, "event", |
| 1035 | "event selector. use 'perf list' to list available events", | 1058 | "event selector. use 'perf list' to list available events", |
| @@ -1070,6 +1093,11 @@ static const struct option options[] = { | |||
| 1070 | OPT_CALLBACK('G', "cgroup", &evsel_list, "name", | 1093 | OPT_CALLBACK('G', "cgroup", &evsel_list, "name", |
| 1071 | "monitor event in cgroup name only", | 1094 | "monitor event in cgroup name only", |
| 1072 | parse_cgroups), | 1095 | parse_cgroups), |
| 1096 | OPT_STRING('o', "output", &output_name, "file", | ||
| 1097 | "output file name"), | ||
| 1098 | OPT_BOOLEAN(0, "append", &append_file, "append to the output file"), | ||
| 1099 | OPT_INTEGER(0, "log-fd", &output_fd, | ||
| 1100 | "log output to fd, instead of stderr"), | ||
| 1073 | OPT_END() | 1101 | OPT_END() |
| 1074 | }; | 1102 | }; |
| 1075 | 1103 | ||
| @@ -1141,6 +1169,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used) | |||
| 1141 | { | 1169 | { |
| 1142 | struct perf_evsel *pos; | 1170 | struct perf_evsel *pos; |
| 1143 | int status = -ENOMEM; | 1171 | int status = -ENOMEM; |
| 1172 | const char *mode; | ||
| 1144 | 1173 | ||
| 1145 | setlocale(LC_ALL, ""); | 1174 | setlocale(LC_ALL, ""); |
| 1146 | 1175 | ||
| @@ -1151,16 +1180,46 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used) | |||
| 1151 | argc = parse_options(argc, argv, options, stat_usage, | 1180 | argc = parse_options(argc, argv, options, stat_usage, |
| 1152 | PARSE_OPT_STOP_AT_NON_OPTION); | 1181 | PARSE_OPT_STOP_AT_NON_OPTION); |
| 1153 | 1182 | ||
| 1154 | if (csv_sep) | 1183 | output = stderr; |
| 1184 | if (output_name && strcmp(output_name, "-")) | ||
| 1185 | output = NULL; | ||
| 1186 | |||
| 1187 | if (output_name && output_fd) { | ||
| 1188 | fprintf(stderr, "cannot use both --output and --log-fd\n"); | ||
| 1189 | usage_with_options(stat_usage, options); | ||
| 1190 | } | ||
| 1191 | if (!output) { | ||
| 1192 | struct timespec tm; | ||
| 1193 | mode = append_file ? "a" : "w"; | ||
| 1194 | |||
| 1195 | output = fopen(output_name, mode); | ||
| 1196 | if (!output) { | ||
| 1197 | perror("failed to create output file"); | ||
| 1198 | exit(-1); | ||
| 1199 | } | ||
| 1200 | clock_gettime(CLOCK_REALTIME, &tm); | ||
| 1201 | fprintf(output, "# started on %s\n", ctime(&tm.tv_sec)); | ||
| 1202 | } else if (output_fd != 2) { | ||
| 1203 | mode = append_file ? "a" : "w"; | ||
| 1204 | output = fdopen(output_fd, mode); | ||
| 1205 | if (!output) { | ||
| 1206 | perror("Failed opening logfd"); | ||
| 1207 | return -errno; | ||
| 1208 | } | ||
| 1209 | } | ||
| 1210 | |||
| 1211 | if (csv_sep) { | ||
| 1155 | csv_output = true; | 1212 | csv_output = true; |
| 1156 | else | 1213 | if (!strcmp(csv_sep, "\\t")) |
| 1214 | csv_sep = "\t"; | ||
| 1215 | } else | ||
| 1157 | csv_sep = DEFAULT_SEPARATOR; | 1216 | csv_sep = DEFAULT_SEPARATOR; |
| 1158 | 1217 | ||
| 1159 | /* | 1218 | /* |
| 1160 | * let the spreadsheet do the pretty-printing | 1219 | * let the spreadsheet do the pretty-printing |
| 1161 | */ | 1220 | */ |
| 1162 | if (csv_output) { | 1221 | if (csv_output) { |
| 1163 | /* User explicitely passed -B? */ | 1222 | /* User explicitly passed -B? */ |
| 1164 | if (big_num_opt == 1) { | 1223 | if (big_num_opt == 1) { |
| 1165 | fprintf(stderr, "-B option not supported with -x\n"); | 1224 | fprintf(stderr, "-B option not supported with -x\n"); |
| 1166 | usage_with_options(stat_usage, options); | 1225 | usage_with_options(stat_usage, options); |
| @@ -1226,7 +1285,8 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used) | |||
| 1226 | status = 0; | 1285 | status = 0; |
| 1227 | for (run_idx = 0; run_idx < run_count; run_idx++) { | 1286 | for (run_idx = 0; run_idx < run_count; run_idx++) { |
| 1228 | if (run_count != 1 && verbose) | 1287 | if (run_count != 1 && verbose) |
| 1229 | fprintf(stderr, "[ perf stat: executing run #%d ... ]\n", run_idx + 1); | 1288 | fprintf(output, "[ perf stat: executing run #%d ... ]\n", |
| 1289 | run_idx + 1); | ||
| 1230 | 1290 | ||
| 1231 | if (sync_run) | 1291 | if (sync_run) |
| 1232 | sync(); | 1292 | sync(); |
diff --git a/tools/perf/builtin-test.c b/tools/perf/builtin-test.c index efe696f936e2..831d1baeac37 100644 --- a/tools/perf/builtin-test.c +++ b/tools/perf/builtin-test.c | |||
| @@ -291,7 +291,7 @@ static int test__open_syscall_event(void) | |||
| 291 | goto out_thread_map_delete; | 291 | goto out_thread_map_delete; |
| 292 | } | 292 | } |
| 293 | 293 | ||
| 294 | if (perf_evsel__open_per_thread(evsel, threads, false) < 0) { | 294 | if (perf_evsel__open_per_thread(evsel, threads, false, NULL) < 0) { |
| 295 | pr_debug("failed to open counter: %s, " | 295 | pr_debug("failed to open counter: %s, " |
| 296 | "tweak /proc/sys/kernel/perf_event_paranoid?\n", | 296 | "tweak /proc/sys/kernel/perf_event_paranoid?\n", |
| 297 | strerror(errno)); | 297 | strerror(errno)); |
| @@ -366,7 +366,7 @@ static int test__open_syscall_event_on_all_cpus(void) | |||
| 366 | goto out_thread_map_delete; | 366 | goto out_thread_map_delete; |
| 367 | } | 367 | } |
| 368 | 368 | ||
| 369 | if (perf_evsel__open(evsel, cpus, threads, false) < 0) { | 369 | if (perf_evsel__open(evsel, cpus, threads, false, NULL) < 0) { |
| 370 | pr_debug("failed to open counter: %s, " | 370 | pr_debug("failed to open counter: %s, " |
| 371 | "tweak /proc/sys/kernel/perf_event_paranoid?\n", | 371 | "tweak /proc/sys/kernel/perf_event_paranoid?\n", |
| 372 | strerror(errno)); | 372 | strerror(errno)); |
| @@ -531,7 +531,7 @@ static int test__basic_mmap(void) | |||
| 531 | 531 | ||
| 532 | perf_evlist__add(evlist, evsels[i]); | 532 | perf_evlist__add(evlist, evsels[i]); |
| 533 | 533 | ||
| 534 | if (perf_evsel__open(evsels[i], cpus, threads, false) < 0) { | 534 | if (perf_evsel__open(evsels[i], cpus, threads, false, NULL) < 0) { |
| 535 | pr_debug("failed to open counter: %s, " | 535 | pr_debug("failed to open counter: %s, " |
| 536 | "tweak /proc/sys/kernel/perf_event_paranoid?\n", | 536 | "tweak /proc/sys/kernel/perf_event_paranoid?\n", |
| 537 | strerror(errno)); | 537 | strerror(errno)); |
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index d28013b7d61c..c9cdedb58134 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,7 @@ 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; | 92 | static bool sample_id_all_avail = true; |
| 89 | static unsigned int mmap_pages = 128; | 93 | static unsigned int mmap_pages = 128; |
| 90 | 94 | ||
| 91 | static bool dump_symtab = false; | 95 | static bool dump_symtab = false; |
| @@ -93,7 +97,6 @@ static bool dump_symtab = false; | |||
| 93 | static struct winsize winsize; | 97 | static struct winsize winsize; |
| 94 | 98 | ||
| 95 | static const char *sym_filter = NULL; | 99 | static const char *sym_filter = NULL; |
| 96 | struct sym_entry *sym_filter_entry_sched = NULL; | ||
| 97 | static int sym_pcnt_filter = 5; | 100 | static int sym_pcnt_filter = 5; |
| 98 | 101 | ||
| 99 | /* | 102 | /* |
| @@ -136,18 +139,18 @@ static void sig_winch_handler(int sig __used) | |||
| 136 | update_print_entries(&winsize); | 139 | update_print_entries(&winsize); |
| 137 | } | 140 | } |
| 138 | 141 | ||
| 139 | static int parse_source(struct sym_entry *syme) | 142 | static int parse_source(struct hist_entry *he) |
| 140 | { | 143 | { |
| 141 | struct symbol *sym; | 144 | struct symbol *sym; |
| 142 | struct annotation *notes; | 145 | struct annotation *notes; |
| 143 | struct map *map; | 146 | struct map *map; |
| 144 | int err = -1; | 147 | int err = -1; |
| 145 | 148 | ||
| 146 | if (!syme) | 149 | if (!he || !he->ms.sym) |
| 147 | return -1; | 150 | return -1; |
| 148 | 151 | ||
| 149 | sym = sym_entry__symbol(syme); | 152 | sym = he->ms.sym; |
| 150 | map = syme->map; | 153 | map = he->ms.map; |
| 151 | 154 | ||
| 152 | /* | 155 | /* |
| 153 | * We can't annotate with just /proc/kallsyms | 156 | * We can't annotate with just /proc/kallsyms |
| @@ -175,53 +178,63 @@ static int parse_source(struct sym_entry *syme) | |||
| 175 | return err; | 178 | return err; |
| 176 | } | 179 | } |
| 177 | 180 | ||
| 178 | err = symbol__annotate(sym, syme->map, 0); | 181 | err = symbol__annotate(sym, map, 0); |
| 179 | if (err == 0) { | 182 | if (err == 0) { |
| 180 | out_assign: | 183 | out_assign: |
| 181 | top.sym_filter_entry = syme; | 184 | top.sym_filter_entry = he; |
| 182 | } | 185 | } |
| 183 | 186 | ||
| 184 | pthread_mutex_unlock(¬es->lock); | 187 | pthread_mutex_unlock(¬es->lock); |
| 185 | return err; | 188 | return err; |
| 186 | } | 189 | } |
| 187 | 190 | ||
| 188 | static void __zero_source_counters(struct sym_entry *syme) | 191 | static void __zero_source_counters(struct hist_entry *he) |
| 189 | { | 192 | { |
| 190 | struct symbol *sym = sym_entry__symbol(syme); | 193 | struct symbol *sym = he->ms.sym; |
| 191 | symbol__annotate_zero_histograms(sym); | 194 | symbol__annotate_zero_histograms(sym); |
| 192 | } | 195 | } |
| 193 | 196 | ||
| 194 | static void record_precise_ip(struct sym_entry *syme, struct map *map, | 197 | static void record_precise_ip(struct hist_entry *he, int counter, u64 ip) |
| 195 | int counter, u64 ip) | ||
| 196 | { | 198 | { |
| 197 | struct annotation *notes; | 199 | struct annotation *notes; |
| 198 | struct symbol *sym; | 200 | struct symbol *sym; |
| 199 | 201 | ||
| 200 | if (syme != top.sym_filter_entry) | 202 | if (he == NULL || he->ms.sym == NULL || |
| 203 | ((top.sym_filter_entry == NULL || | ||
| 204 | top.sym_filter_entry->ms.sym != he->ms.sym) && use_browser != 1)) | ||
| 201 | return; | 205 | return; |
| 202 | 206 | ||
| 203 | sym = sym_entry__symbol(syme); | 207 | sym = he->ms.sym; |
| 204 | notes = symbol__annotation(sym); | 208 | notes = symbol__annotation(sym); |
| 205 | 209 | ||
| 206 | if (pthread_mutex_trylock(¬es->lock)) | 210 | if (pthread_mutex_trylock(¬es->lock)) |
| 207 | return; | 211 | return; |
| 208 | 212 | ||
| 209 | ip = map->map_ip(map, ip); | 213 | if (notes->src == NULL && |
| 210 | symbol__inc_addr_samples(sym, map, counter, ip); | 214 | symbol__alloc_hist(sym, top.evlist->nr_entries) < 0) { |
| 215 | pthread_mutex_unlock(¬es->lock); | ||
| 216 | pr_err("Not enough memory for annotating '%s' symbol!\n", | ||
| 217 | sym->name); | ||
| 218 | sleep(1); | ||
| 219 | return; | ||
| 220 | } | ||
| 221 | |||
| 222 | ip = he->ms.map->map_ip(he->ms.map, ip); | ||
| 223 | symbol__inc_addr_samples(sym, he->ms.map, counter, ip); | ||
| 211 | 224 | ||
| 212 | pthread_mutex_unlock(¬es->lock); | 225 | pthread_mutex_unlock(¬es->lock); |
| 213 | } | 226 | } |
| 214 | 227 | ||
| 215 | static void show_details(struct sym_entry *syme) | 228 | static void show_details(struct hist_entry *he) |
| 216 | { | 229 | { |
| 217 | struct annotation *notes; | 230 | struct annotation *notes; |
| 218 | struct symbol *symbol; | 231 | struct symbol *symbol; |
| 219 | int more; | 232 | int more; |
| 220 | 233 | ||
| 221 | if (!syme) | 234 | if (!he) |
| 222 | return; | 235 | return; |
| 223 | 236 | ||
| 224 | symbol = sym_entry__symbol(syme); | 237 | symbol = he->ms.sym; |
| 225 | notes = symbol__annotation(symbol); | 238 | notes = symbol__annotation(symbol); |
| 226 | 239 | ||
| 227 | pthread_mutex_lock(¬es->lock); | 240 | pthread_mutex_lock(¬es->lock); |
| @@ -232,7 +245,7 @@ static void show_details(struct sym_entry *syme) | |||
| 232 | printf("Showing %s for %s\n", event_name(top.sym_evsel), symbol->name); | 245 | printf("Showing %s for %s\n", event_name(top.sym_evsel), symbol->name); |
| 233 | printf(" Events Pcnt (>=%d%%)\n", sym_pcnt_filter); | 246 | printf(" Events Pcnt (>=%d%%)\n", sym_pcnt_filter); |
| 234 | 247 | ||
| 235 | more = symbol__annotate_printf(symbol, syme->map, top.sym_evsel->idx, | 248 | more = symbol__annotate_printf(symbol, he->ms.map, top.sym_evsel->idx, |
| 236 | 0, sym_pcnt_filter, top.print_entries, 4); | 249 | 0, sym_pcnt_filter, top.print_entries, 4); |
| 237 | if (top.zero) | 250 | if (top.zero) |
| 238 | symbol__annotate_zero_histogram(symbol, top.sym_evsel->idx); | 251 | symbol__annotate_zero_histogram(symbol, top.sym_evsel->idx); |
| @@ -246,21 +259,28 @@ out_unlock: | |||
| 246 | 259 | ||
| 247 | static const char CONSOLE_CLEAR[] = "[H[2J"; | 260 | static const char CONSOLE_CLEAR[] = "[H[2J"; |
| 248 | 261 | ||
| 249 | static void __list_insert_active_sym(struct sym_entry *syme) | 262 | static struct hist_entry * |
| 263 | perf_session__add_hist_entry(struct perf_session *session, | ||
| 264 | struct addr_location *al, | ||
| 265 | struct perf_sample *sample, | ||
| 266 | struct perf_evsel *evsel) | ||
| 250 | { | 267 | { |
| 251 | list_add(&syme->node, &top.active_symbols); | 268 | struct hist_entry *he; |
| 269 | |||
| 270 | he = __hists__add_entry(&evsel->hists, al, NULL, sample->period); | ||
| 271 | if (he == NULL) | ||
| 272 | return NULL; | ||
| 273 | |||
| 274 | session->hists.stats.total_period += sample->period; | ||
| 275 | hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); | ||
| 276 | return he; | ||
| 252 | } | 277 | } |
| 253 | 278 | ||
| 254 | static void print_sym_table(struct perf_session *session) | 279 | static void print_sym_table(void) |
| 255 | { | 280 | { |
| 256 | char bf[160]; | 281 | char bf[160]; |
| 257 | int printed = 0; | 282 | 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; | 283 | 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 | 284 | ||
| 265 | puts(CONSOLE_CLEAR); | 285 | puts(CONSOLE_CLEAR); |
| 266 | 286 | ||
| @@ -271,10 +291,14 @@ static void print_sym_table(struct perf_session *session) | |||
| 271 | 291 | ||
| 272 | printf("%-*.*s\n", win_width, win_width, graph_dotted_line); | 292 | printf("%-*.*s\n", win_width, win_width, graph_dotted_line); |
| 273 | 293 | ||
| 274 | if (session->hists.stats.total_lost != 0) { | 294 | if (top.sym_evsel->hists.stats.nr_lost_warned != |
| 275 | color_fprintf(stdout, PERF_COLOR_RED, "WARNING:"); | 295 | top.sym_evsel->hists.stats.nr_events[PERF_RECORD_LOST]) { |
| 276 | printf(" LOST %" PRIu64 " events, Check IO/CPU overload\n", | 296 | top.sym_evsel->hists.stats.nr_lost_warned = |
| 277 | session->hists.stats.total_lost); | 297 | top.sym_evsel->hists.stats.nr_events[PERF_RECORD_LOST]; |
| 298 | color_fprintf(stdout, PERF_COLOR_RED, | ||
| 299 | "WARNING: LOST %d chunks, Check IO/CPU overload", | ||
| 300 | top.sym_evsel->hists.stats.nr_lost_warned); | ||
| 301 | ++printed; | ||
| 278 | } | 302 | } |
| 279 | 303 | ||
| 280 | if (top.sym_filter_entry) { | 304 | if (top.sym_filter_entry) { |
| @@ -282,58 +306,15 @@ static void print_sym_table(struct perf_session *session) | |||
| 282 | return; | 306 | return; |
| 283 | } | 307 | } |
| 284 | 308 | ||
| 285 | perf_top__find_widths(&top, &tmp, &dso_width, &dso_short_width, | 309 | hists__collapse_resort_threaded(&top.sym_evsel->hists); |
| 286 | &sym_width); | 310 | hists__output_resort_threaded(&top.sym_evsel->hists); |
| 287 | 311 | hists__decay_entries_threaded(&top.sym_evsel->hists, | |
| 288 | if (sym_width + dso_width > winsize.ws_col - 29) { | 312 | top.hide_user_symbols, |
| 289 | dso_width = dso_short_width; | 313 | top.hide_kernel_symbols); |
| 290 | if (sym_width + dso_width > winsize.ws_col - 29) | 314 | hists__output_recalc_col_len(&top.sym_evsel->hists, winsize.ws_row - 3); |
| 291 | sym_width = winsize.ws_col - dso_width - 29; | ||
| 292 | } | ||
| 293 | putchar('\n'); | 315 | putchar('\n'); |
| 294 | if (top.evlist->nr_entries == 1) | 316 | hists__fprintf(&top.sym_evsel->hists, NULL, false, false, |
| 295 | printf(" samples pcnt"); | 317 | winsize.ws_row - 4 - printed, win_width, stdout); |
| 296 | else | ||
| 297 | printf(" weight samples pcnt"); | ||
| 298 | |||
| 299 | if (verbose) | ||
| 300 | printf(" RIP "); | ||
| 301 | printf(" %-*.*s DSO\n", sym_width, sym_width, "function"); | ||
| 302 | printf(" %s _______ _____", | ||
| 303 | top.evlist->nr_entries == 1 ? " " : "______"); | ||
| 304 | if (verbose) | ||
| 305 | printf(" ________________"); | ||
| 306 | printf(" %-*.*s", sym_width, sym_width, graph_line); | ||
| 307 | printf(" %-*.*s", dso_width, dso_width, graph_line); | ||
| 308 | puts("\n"); | ||
| 309 | |||
| 310 | for (nd = rb_first(&tmp); nd; nd = rb_next(nd)) { | ||
| 311 | struct symbol *sym; | ||
| 312 | double pcnt; | ||
| 313 | |||
| 314 | syme = rb_entry(nd, struct sym_entry, rb_node); | ||
| 315 | sym = sym_entry__symbol(syme); | ||
| 316 | if (++printed > top.print_entries || | ||
| 317 | (int)syme->snap_count < top.count_filter) | ||
| 318 | continue; | ||
| 319 | |||
| 320 | pcnt = 100.0 - (100.0 * ((sum_ksamples - syme->snap_count) / | ||
| 321 | sum_ksamples)); | ||
| 322 | |||
| 323 | if (top.evlist->nr_entries == 1 || !top.display_weighted) | ||
| 324 | printf("%20.2f ", syme->weight); | ||
| 325 | else | ||
| 326 | printf("%9.1f %10ld ", syme->weight, syme->snap_count); | ||
| 327 | |||
| 328 | percent_color_fprintf(stdout, "%4.1f%%", pcnt); | ||
| 329 | if (verbose) | ||
| 330 | printf(" %016" PRIx64, sym->start); | ||
| 331 | printf(" %-*.*s", sym_width, sym_width, sym->name); | ||
| 332 | printf(" %-*.*s\n", dso_width, dso_width, | ||
| 333 | dso_width >= syme->map->dso->long_name_len ? | ||
| 334 | syme->map->dso->long_name : | ||
| 335 | syme->map->dso->short_name); | ||
| 336 | } | ||
| 337 | } | 318 | } |
| 338 | 319 | ||
| 339 | static void prompt_integer(int *target, const char *msg) | 320 | static void prompt_integer(int *target, const char *msg) |
| @@ -371,10 +352,11 @@ static void prompt_percent(int *target, const char *msg) | |||
| 371 | *target = tmp; | 352 | *target = tmp; |
| 372 | } | 353 | } |
| 373 | 354 | ||
| 374 | static void prompt_symbol(struct sym_entry **target, const char *msg) | 355 | static void prompt_symbol(struct hist_entry **target, const char *msg) |
| 375 | { | 356 | { |
| 376 | char *buf = malloc(0), *p; | 357 | char *buf = malloc(0), *p; |
| 377 | struct sym_entry *syme = *target, *n, *found = NULL; | 358 | struct hist_entry *syme = *target, *n, *found = NULL; |
| 359 | struct rb_node *next; | ||
| 378 | size_t dummy = 0; | 360 | size_t dummy = 0; |
| 379 | 361 | ||
| 380 | /* zero counters of active symbol */ | 362 | /* zero counters of active symbol */ |
| @@ -391,17 +373,14 @@ static void prompt_symbol(struct sym_entry **target, const char *msg) | |||
| 391 | if (p) | 373 | if (p) |
| 392 | *p = 0; | 374 | *p = 0; |
| 393 | 375 | ||
| 394 | pthread_mutex_lock(&top.active_symbols_lock); | 376 | next = rb_first(&top.sym_evsel->hists.entries); |
| 395 | syme = list_entry(top.active_symbols.next, struct sym_entry, node); | 377 | while (next) { |
| 396 | pthread_mutex_unlock(&top.active_symbols_lock); | 378 | n = rb_entry(next, struct hist_entry, rb_node); |
| 397 | 379 | if (n->ms.sym && !strcmp(buf, n->ms.sym->name)) { | |
| 398 | list_for_each_entry_safe_from(syme, n, &top.active_symbols, node) { | 380 | found = n; |
| 399 | struct symbol *sym = sym_entry__symbol(syme); | ||
| 400 | |||
| 401 | if (!strcmp(buf, sym->name)) { | ||
| 402 | found = syme; | ||
| 403 | break; | 381 | break; |
| 404 | } | 382 | } |
| 383 | next = rb_next(&n->rb_node); | ||
| 405 | } | 384 | } |
| 406 | 385 | ||
| 407 | if (!found) { | 386 | if (!found) { |
| @@ -420,7 +399,7 @@ static void print_mapped_keys(void) | |||
| 420 | char *name = NULL; | 399 | char *name = NULL; |
| 421 | 400 | ||
| 422 | if (top.sym_filter_entry) { | 401 | if (top.sym_filter_entry) { |
| 423 | struct symbol *sym = sym_entry__symbol(top.sym_filter_entry); | 402 | struct symbol *sym = top.sym_filter_entry->ms.sym; |
| 424 | name = sym->name; | 403 | name = sym->name; |
| 425 | } | 404 | } |
| 426 | 405 | ||
| @@ -437,9 +416,6 @@ static void print_mapped_keys(void) | |||
| 437 | fprintf(stdout, "\t[s] annotate symbol. \t(%s)\n", name?: "NULL"); | 416 | fprintf(stdout, "\t[s] annotate symbol. \t(%s)\n", name?: "NULL"); |
| 438 | fprintf(stdout, "\t[S] stop annotation.\n"); | 417 | fprintf(stdout, "\t[S] stop annotation.\n"); |
| 439 | 418 | ||
| 440 | if (top.evlist->nr_entries > 1) | ||
| 441 | fprintf(stdout, "\t[w] toggle display weighted/count[E]r. \t(%d)\n", top.display_weighted ? 1 : 0); | ||
| 442 | |||
| 443 | fprintf(stdout, | 419 | fprintf(stdout, |
| 444 | "\t[K] hide kernel_symbols symbols. \t(%s)\n", | 420 | "\t[K] hide kernel_symbols symbols. \t(%s)\n", |
| 445 | top.hide_kernel_symbols ? "yes" : "no"); | 421 | top.hide_kernel_symbols ? "yes" : "no"); |
| @@ -466,7 +442,6 @@ static int key_mapped(int c) | |||
| 466 | case 'S': | 442 | case 'S': |
| 467 | return 1; | 443 | return 1; |
| 468 | case 'E': | 444 | case 'E': |
| 469 | case 'w': | ||
| 470 | return top.evlist->nr_entries > 1 ? 1 : 0; | 445 | return top.evlist->nr_entries > 1 ? 1 : 0; |
| 471 | default: | 446 | default: |
| 472 | break; | 447 | break; |
| @@ -475,7 +450,7 @@ static int key_mapped(int c) | |||
| 475 | return 0; | 450 | return 0; |
| 476 | } | 451 | } |
| 477 | 452 | ||
| 478 | static void handle_keypress(struct perf_session *session, int c) | 453 | static void handle_keypress(int c) |
| 479 | { | 454 | { |
| 480 | if (!key_mapped(c)) { | 455 | if (!key_mapped(c)) { |
| 481 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; | 456 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; |
| @@ -551,7 +526,7 @@ static void handle_keypress(struct perf_session *session, int c) | |||
| 551 | case 'Q': | 526 | case 'Q': |
| 552 | printf("exiting.\n"); | 527 | printf("exiting.\n"); |
| 553 | if (dump_symtab) | 528 | if (dump_symtab) |
| 554 | perf_session__fprintf_dsos(session, stderr); | 529 | perf_session__fprintf_dsos(top.session, stderr); |
| 555 | exit(0); | 530 | exit(0); |
| 556 | case 's': | 531 | case 's': |
| 557 | prompt_symbol(&top.sym_filter_entry, "Enter details symbol"); | 532 | prompt_symbol(&top.sym_filter_entry, "Enter details symbol"); |
| @@ -560,7 +535,7 @@ static void handle_keypress(struct perf_session *session, int c) | |||
| 560 | if (!top.sym_filter_entry) | 535 | if (!top.sym_filter_entry) |
| 561 | break; | 536 | break; |
| 562 | else { | 537 | else { |
| 563 | struct sym_entry *syme = top.sym_filter_entry; | 538 | struct hist_entry *syme = top.sym_filter_entry; |
| 564 | 539 | ||
| 565 | top.sym_filter_entry = NULL; | 540 | top.sym_filter_entry = NULL; |
| 566 | __zero_source_counters(syme); | 541 | __zero_source_counters(syme); |
| @@ -569,9 +544,6 @@ static void handle_keypress(struct perf_session *session, int c) | |||
| 569 | case 'U': | 544 | case 'U': |
| 570 | top.hide_user_symbols = !top.hide_user_symbols; | 545 | top.hide_user_symbols = !top.hide_user_symbols; |
| 571 | break; | 546 | break; |
| 572 | case 'w': | ||
| 573 | top.display_weighted = ~top.display_weighted; | ||
| 574 | break; | ||
| 575 | case 'z': | 547 | case 'z': |
| 576 | top.zero = !top.zero; | 548 | top.zero = !top.zero; |
| 577 | break; | 549 | break; |
| @@ -580,19 +552,30 @@ static void handle_keypress(struct perf_session *session, int c) | |||
| 580 | } | 552 | } |
| 581 | } | 553 | } |
| 582 | 554 | ||
| 555 | static void perf_top__sort_new_samples(void *arg) | ||
| 556 | { | ||
| 557 | struct perf_top *t = arg; | ||
| 558 | perf_top__reset_sample_counters(t); | ||
| 559 | |||
| 560 | if (t->evlist->selected != NULL) | ||
| 561 | t->sym_evsel = t->evlist->selected; | ||
| 562 | |||
| 563 | hists__collapse_resort_threaded(&t->sym_evsel->hists); | ||
| 564 | hists__output_resort_threaded(&t->sym_evsel->hists); | ||
| 565 | hists__decay_entries_threaded(&t->sym_evsel->hists, | ||
| 566 | top.hide_user_symbols, | ||
| 567 | top.hide_kernel_symbols); | ||
| 568 | } | ||
| 569 | |||
| 583 | static void *display_thread_tui(void *arg __used) | 570 | static void *display_thread_tui(void *arg __used) |
| 584 | { | 571 | { |
| 585 | int err = 0; | 572 | const char *help = "For a higher level overview, try: perf top --sort comm,dso"; |
| 586 | pthread_mutex_lock(&top.active_symbols_lock); | 573 | |
| 587 | while (list_empty(&top.active_symbols)) { | 574 | perf_top__sort_new_samples(&top); |
| 588 | err = pthread_cond_wait(&top.active_symbols_cond, | 575 | perf_evlist__tui_browse_hists(top.evlist, help, |
| 589 | &top.active_symbols_lock); | 576 | perf_top__sort_new_samples, |
| 590 | if (err) | 577 | &top, top.delay_secs); |
| 591 | break; | 578 | |
| 592 | } | ||
| 593 | pthread_mutex_unlock(&top.active_symbols_lock); | ||
| 594 | if (!err) | ||
| 595 | perf_top__tui_browser(&top); | ||
| 596 | exit_browser(0); | 579 | exit_browser(0); |
| 597 | exit(0); | 580 | exit(0); |
| 598 | return NULL; | 581 | return NULL; |
| @@ -603,7 +586,6 @@ static void *display_thread(void *arg __used) | |||
| 603 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; | 586 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; |
| 604 | struct termios tc, save; | 587 | struct termios tc, save; |
| 605 | int delay_msecs, c; | 588 | int delay_msecs, c; |
| 606 | struct perf_session *session = (struct perf_session *) arg; | ||
| 607 | 589 | ||
| 608 | tcgetattr(0, &save); | 590 | tcgetattr(0, &save); |
| 609 | tc = save; | 591 | tc = save; |
| @@ -611,20 +593,35 @@ static void *display_thread(void *arg __used) | |||
| 611 | tc.c_cc[VMIN] = 0; | 593 | tc.c_cc[VMIN] = 0; |
| 612 | tc.c_cc[VTIME] = 0; | 594 | tc.c_cc[VTIME] = 0; |
| 613 | 595 | ||
| 596 | pthread__unblock_sigwinch(); | ||
| 614 | repeat: | 597 | repeat: |
| 615 | delay_msecs = top.delay_secs * 1000; | 598 | delay_msecs = top.delay_secs * 1000; |
| 616 | tcsetattr(0, TCSANOW, &tc); | 599 | tcsetattr(0, TCSANOW, &tc); |
| 617 | /* trash return*/ | 600 | /* trash return*/ |
| 618 | getc(stdin); | 601 | getc(stdin); |
| 619 | 602 | ||
| 620 | do { | 603 | while (1) { |
| 621 | print_sym_table(session); | 604 | print_sym_table(); |
| 622 | } while (!poll(&stdin_poll, 1, delay_msecs) == 1); | 605 | /* |
| 623 | 606 | * Either timeout expired or we got an EINTR due to SIGWINCH, | |
| 607 | * refresh screen in both cases. | ||
| 608 | */ | ||
| 609 | switch (poll(&stdin_poll, 1, delay_msecs)) { | ||
| 610 | case 0: | ||
| 611 | continue; | ||
| 612 | case -1: | ||
| 613 | if (errno == EINTR) | ||
| 614 | continue; | ||
| 615 | /* Fall trhu */ | ||
| 616 | default: | ||
| 617 | goto process_hotkey; | ||
| 618 | } | ||
| 619 | } | ||
| 620 | process_hotkey: | ||
| 624 | c = getc(stdin); | 621 | c = getc(stdin); |
| 625 | tcsetattr(0, TCSAFLUSH, &save); | 622 | tcsetattr(0, TCSAFLUSH, &save); |
| 626 | 623 | ||
| 627 | handle_keypress(session, c); | 624 | handle_keypress(c); |
| 628 | goto repeat; | 625 | goto repeat; |
| 629 | 626 | ||
| 630 | return NULL; | 627 | return NULL; |
| @@ -645,9 +642,8 @@ static const char *skip_symbols[] = { | |||
| 645 | NULL | 642 | NULL |
| 646 | }; | 643 | }; |
| 647 | 644 | ||
| 648 | static int symbol_filter(struct map *map, struct symbol *sym) | 645 | static int symbol_filter(struct map *map __used, struct symbol *sym) |
| 649 | { | 646 | { |
| 650 | struct sym_entry *syme; | ||
| 651 | const char *name = sym->name; | 647 | const char *name = sym->name; |
| 652 | int i; | 648 | int i; |
| 653 | 649 | ||
| @@ -667,16 +663,6 @@ static int symbol_filter(struct map *map, struct symbol *sym) | |||
| 667 | strstr(name, "_text_end")) | 663 | strstr(name, "_text_end")) |
| 668 | return 1; | 664 | return 1; |
| 669 | 665 | ||
| 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++) { | 666 | for (i = 0; skip_symbols[i]; i++) { |
| 681 | if (!strcmp(skip_symbols[i], name)) { | 667 | if (!strcmp(skip_symbols[i], name)) { |
| 682 | sym->ignore = true; | 668 | sym->ignore = true; |
| @@ -688,13 +674,15 @@ static int symbol_filter(struct map *map, struct symbol *sym) | |||
| 688 | } | 674 | } |
| 689 | 675 | ||
| 690 | static void perf_event__process_sample(const union perf_event *event, | 676 | static void perf_event__process_sample(const union perf_event *event, |
| 677 | struct perf_evsel *evsel, | ||
| 691 | struct perf_sample *sample, | 678 | struct perf_sample *sample, |
| 692 | struct perf_session *session) | 679 | struct perf_session *session) |
| 693 | { | 680 | { |
| 681 | struct symbol *parent = NULL; | ||
| 694 | u64 ip = event->ip.ip; | 682 | u64 ip = event->ip.ip; |
| 695 | struct sym_entry *syme; | ||
| 696 | struct addr_location al; | 683 | struct addr_location al; |
| 697 | struct machine *machine; | 684 | struct machine *machine; |
| 685 | int err; | ||
| 698 | u8 origin = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | 686 | u8 origin = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
| 699 | 687 | ||
| 700 | ++top.samples; | 688 | ++top.samples; |
| @@ -783,51 +771,43 @@ static void perf_event__process_sample(const union perf_event *event, | |||
| 783 | sleep(5); | 771 | sleep(5); |
| 784 | vmlinux_warned = true; | 772 | vmlinux_warned = true; |
| 785 | } | 773 | } |
| 786 | |||
| 787 | return; | ||
| 788 | } | 774 | } |
| 789 | 775 | ||
| 790 | /* let's see, whether we need to install initial sym_filter_entry */ | 776 | if (al.sym == NULL || !al.sym->ignore) { |
| 791 | if (sym_filter_entry_sched) { | 777 | struct hist_entry *he; |
| 792 | top.sym_filter_entry = sym_filter_entry_sched; | 778 | |
| 793 | sym_filter_entry_sched = NULL; | 779 | if ((sort__has_parent || symbol_conf.use_callchain) && |
| 794 | if (parse_source(top.sym_filter_entry) < 0) { | 780 | sample->callchain) { |
| 795 | struct symbol *sym = sym_entry__symbol(top.sym_filter_entry); | 781 | err = perf_session__resolve_callchain(session, al.thread, |
| 796 | 782 | sample->callchain, &parent); | |
| 797 | pr_err("Can't annotate %s", sym->name); | 783 | if (err) |
| 798 | if (top.sym_filter_entry->map->dso->symtab_type == SYMTAB__KALLSYMS) { | 784 | return; |
| 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 | } | 785 | } |
| 805 | } | ||
| 806 | 786 | ||
| 807 | syme = symbol__priv(al.sym); | 787 | he = perf_session__add_hist_entry(session, &al, sample, evsel); |
| 808 | if (!al.sym->ignore) { | 788 | if (he == NULL) { |
| 809 | struct perf_evsel *evsel; | 789 | pr_err("Problem incrementing symbol period, skipping event\n"); |
| 790 | return; | ||
| 791 | } | ||
| 810 | 792 | ||
| 811 | evsel = perf_evlist__id2evsel(top.evlist, sample->id); | 793 | if (symbol_conf.use_callchain) { |
| 812 | assert(evsel != NULL); | 794 | err = callchain_append(he->callchain, &session->callchain_cursor, |
| 813 | syme->count[evsel->idx]++; | 795 | sample->period); |
| 814 | record_precise_ip(syme, al.map, evsel->idx, ip); | 796 | if (err) |
| 815 | pthread_mutex_lock(&top.active_symbols_lock); | 797 | return; |
| 816 | if (list_empty(&syme->node) || !syme->node.next) { | ||
| 817 | static bool first = true; | ||
| 818 | __list_insert_active_sym(syme); | ||
| 819 | if (first) { | ||
| 820 | pthread_cond_broadcast(&top.active_symbols_cond); | ||
| 821 | first = false; | ||
| 822 | } | ||
| 823 | } | 798 | } |
| 824 | pthread_mutex_unlock(&top.active_symbols_lock); | 799 | |
| 800 | if (sort_has_symbols) | ||
| 801 | record_precise_ip(he, evsel->idx, ip); | ||
| 825 | } | 802 | } |
| 803 | |||
| 804 | return; | ||
| 826 | } | 805 | } |
| 827 | 806 | ||
| 828 | static void perf_session__mmap_read_idx(struct perf_session *self, int idx) | 807 | static void perf_session__mmap_read_idx(struct perf_session *self, int idx) |
| 829 | { | 808 | { |
| 830 | struct perf_sample sample; | 809 | struct perf_sample sample; |
| 810 | struct perf_evsel *evsel; | ||
| 831 | union perf_event *event; | 811 | union perf_event *event; |
| 832 | int ret; | 812 | int ret; |
| 833 | 813 | ||
| @@ -838,10 +818,16 @@ static void perf_session__mmap_read_idx(struct perf_session *self, int idx) | |||
| 838 | continue; | 818 | continue; |
| 839 | } | 819 | } |
| 840 | 820 | ||
| 821 | evsel = perf_evlist__id2evsel(self->evlist, sample.id); | ||
| 822 | assert(evsel != NULL); | ||
| 823 | |||
| 841 | if (event->header.type == PERF_RECORD_SAMPLE) | 824 | if (event->header.type == PERF_RECORD_SAMPLE) |
| 842 | perf_event__process_sample(event, &sample, self); | 825 | perf_event__process_sample(event, evsel, &sample, self); |
| 843 | else | 826 | else if (event->header.type < PERF_RECORD_MAX) { |
| 827 | hists__inc_nr_events(&evsel->hists, event->header.type); | ||
| 844 | perf_event__process(event, &sample, self); | 828 | perf_event__process(event, &sample, self); |
| 829 | } else | ||
| 830 | ++self->hists.stats.nr_unknown_events; | ||
| 845 | } | 831 | } |
| 846 | } | 832 | } |
| 847 | 833 | ||
| @@ -855,10 +841,16 @@ static void perf_session__mmap_read(struct perf_session *self) | |||
| 855 | 841 | ||
| 856 | static void start_counters(struct perf_evlist *evlist) | 842 | static void start_counters(struct perf_evlist *evlist) |
| 857 | { | 843 | { |
| 858 | struct perf_evsel *counter; | 844 | struct perf_evsel *counter, *first; |
| 845 | |||
| 846 | first = list_entry(evlist->entries.next, struct perf_evsel, node); | ||
| 859 | 847 | ||
| 860 | list_for_each_entry(counter, &evlist->entries, node) { | 848 | list_for_each_entry(counter, &evlist->entries, node) { |
| 861 | struct perf_event_attr *attr = &counter->attr; | 849 | struct perf_event_attr *attr = &counter->attr; |
| 850 | struct xyarray *group_fd = NULL; | ||
| 851 | |||
| 852 | if (group && counter != first) | ||
| 853 | group_fd = first->fd; | ||
| 862 | 854 | ||
| 863 | attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID; | 855 | attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID; |
| 864 | 856 | ||
| @@ -873,16 +865,29 @@ static void start_counters(struct perf_evlist *evlist) | |||
| 873 | attr->read_format |= PERF_FORMAT_ID; | 865 | attr->read_format |= PERF_FORMAT_ID; |
| 874 | } | 866 | } |
| 875 | 867 | ||
| 868 | if (symbol_conf.use_callchain) | ||
| 869 | attr->sample_type |= PERF_SAMPLE_CALLCHAIN; | ||
| 870 | |||
| 876 | attr->mmap = 1; | 871 | attr->mmap = 1; |
| 872 | attr->comm = 1; | ||
| 877 | attr->inherit = inherit; | 873 | attr->inherit = inherit; |
| 874 | retry_sample_id: | ||
| 875 | attr->sample_id_all = sample_id_all_avail ? 1 : 0; | ||
| 878 | try_again: | 876 | try_again: |
| 879 | if (perf_evsel__open(counter, top.evlist->cpus, | 877 | if (perf_evsel__open(counter, top.evlist->cpus, |
| 880 | top.evlist->threads, group) < 0) { | 878 | top.evlist->threads, group, |
| 879 | group_fd) < 0) { | ||
| 881 | int err = errno; | 880 | int err = errno; |
| 882 | 881 | ||
| 883 | if (err == EPERM || err == EACCES) { | 882 | if (err == EPERM || err == EACCES) { |
| 884 | ui__warning_paranoid(); | 883 | ui__error_paranoid(); |
| 885 | goto out_err; | 884 | goto out_err; |
| 885 | } else if (err == EINVAL && sample_id_all_avail) { | ||
| 886 | /* | ||
| 887 | * Old kernel, no attr->sample_id_type_all field | ||
| 888 | */ | ||
| 889 | sample_id_all_avail = false; | ||
| 890 | goto retry_sample_id; | ||
| 886 | } | 891 | } |
| 887 | /* | 892 | /* |
| 888 | * If it's cycles then fall back to hrtimer | 893 | * If it's cycles then fall back to hrtimer |
| @@ -928,35 +933,56 @@ out_err: | |||
| 928 | exit(0); | 933 | exit(0); |
| 929 | } | 934 | } |
| 930 | 935 | ||
| 936 | static int setup_sample_type(void) | ||
| 937 | { | ||
| 938 | if (!sort_has_symbols) { | ||
| 939 | if (symbol_conf.use_callchain) { | ||
| 940 | ui__warning("Selected -g but \"sym\" not present in --sort/-s."); | ||
| 941 | return -EINVAL; | ||
| 942 | } | ||
| 943 | } else if (!dont_use_callchains && callchain_param.mode != CHAIN_NONE) { | ||
| 944 | if (callchain_register_param(&callchain_param) < 0) { | ||
| 945 | ui__warning("Can't register callchain params.\n"); | ||
| 946 | return -EINVAL; | ||
| 947 | } | ||
| 948 | } | ||
| 949 | |||
| 950 | return 0; | ||
| 951 | } | ||
| 952 | |||
| 931 | static int __cmd_top(void) | 953 | static int __cmd_top(void) |
| 932 | { | 954 | { |
| 933 | pthread_t thread; | 955 | pthread_t thread; |
| 934 | int ret __used; | 956 | int ret; |
| 935 | /* | 957 | /* |
| 936 | * FIXME: perf_session__new should allow passing a O_MMAP, so that all this | 958 | * 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. | 959 | * mmap reading, etc is encapsulated in it. Use O_WRONLY for now. |
| 938 | */ | 960 | */ |
| 939 | struct perf_session *session = perf_session__new(NULL, O_WRONLY, false, false, NULL); | 961 | top.session = perf_session__new(NULL, O_WRONLY, false, false, NULL); |
| 940 | if (session == NULL) | 962 | if (top.session == NULL) |
| 941 | return -ENOMEM; | 963 | return -ENOMEM; |
| 942 | 964 | ||
| 965 | ret = setup_sample_type(); | ||
| 966 | if (ret) | ||
| 967 | goto out_delete; | ||
| 968 | |||
| 943 | if (top.target_tid != -1) | 969 | if (top.target_tid != -1) |
| 944 | perf_event__synthesize_thread_map(top.evlist->threads, | 970 | perf_event__synthesize_thread_map(top.evlist->threads, |
| 945 | perf_event__process, session); | 971 | perf_event__process, top.session); |
| 946 | else | 972 | else |
| 947 | perf_event__synthesize_threads(perf_event__process, session); | 973 | perf_event__synthesize_threads(perf_event__process, top.session); |
| 948 | 974 | ||
| 949 | start_counters(top.evlist); | 975 | start_counters(top.evlist); |
| 950 | session->evlist = top.evlist; | 976 | top.session->evlist = top.evlist; |
| 951 | perf_session__update_sample_type(session); | 977 | perf_session__update_sample_type(top.session); |
| 952 | 978 | ||
| 953 | /* Wait for a minimal set of events before starting the snapshot */ | 979 | /* Wait for a minimal set of events before starting the snapshot */ |
| 954 | poll(top.evlist->pollfd, top.evlist->nr_fds, 100); | 980 | poll(top.evlist->pollfd, top.evlist->nr_fds, 100); |
| 955 | 981 | ||
| 956 | perf_session__mmap_read(session); | 982 | perf_session__mmap_read(top.session); |
| 957 | 983 | ||
| 958 | if (pthread_create(&thread, NULL, (use_browser > 0 ? display_thread_tui : | 984 | if (pthread_create(&thread, NULL, (use_browser > 0 ? display_thread_tui : |
| 959 | display_thread), session)) { | 985 | display_thread), NULL)) { |
| 960 | printf("Could not create display thread.\n"); | 986 | printf("Could not create display thread.\n"); |
| 961 | exit(-1); | 987 | exit(-1); |
| 962 | } | 988 | } |
| @@ -974,12 +1000,96 @@ static int __cmd_top(void) | |||
| 974 | while (1) { | 1000 | while (1) { |
| 975 | u64 hits = top.samples; | 1001 | u64 hits = top.samples; |
| 976 | 1002 | ||
| 977 | perf_session__mmap_read(session); | 1003 | perf_session__mmap_read(top.session); |
| 978 | 1004 | ||
| 979 | if (hits == top.samples) | 1005 | if (hits == top.samples) |
| 980 | ret = poll(top.evlist->pollfd, top.evlist->nr_fds, 100); | 1006 | ret = poll(top.evlist->pollfd, top.evlist->nr_fds, 100); |
| 981 | } | 1007 | } |
| 982 | 1008 | ||
| 1009 | out_delete: | ||
| 1010 | perf_session__delete(top.session); | ||
| 1011 | top.session = NULL; | ||
| 1012 | |||
| 1013 | return 0; | ||
| 1014 | } | ||
| 1015 | |||
| 1016 | static int | ||
| 1017 | parse_callchain_opt(const struct option *opt __used, const char *arg, | ||
| 1018 | int unset) | ||
| 1019 | { | ||
| 1020 | char *tok, *tok2; | ||
| 1021 | char *endptr; | ||
| 1022 | |||
| 1023 | /* | ||
| 1024 | * --no-call-graph | ||
| 1025 | */ | ||
| 1026 | if (unset) { | ||
| 1027 | dont_use_callchains = true; | ||
| 1028 | return 0; | ||
| 1029 | } | ||
| 1030 | |||
| 1031 | symbol_conf.use_callchain = true; | ||
| 1032 | |||
| 1033 | if (!arg) | ||
| 1034 | return 0; | ||
| 1035 | |||
| 1036 | tok = strtok((char *)arg, ","); | ||
| 1037 | if (!tok) | ||
| 1038 | return -1; | ||
| 1039 | |||
| 1040 | /* get the output mode */ | ||
| 1041 | if (!strncmp(tok, "graph", strlen(arg))) | ||
| 1042 | callchain_param.mode = CHAIN_GRAPH_ABS; | ||
| 1043 | |||
| 1044 | else if (!strncmp(tok, "flat", strlen(arg))) | ||
| 1045 | callchain_param.mode = CHAIN_FLAT; | ||
| 1046 | |||
| 1047 | else if (!strncmp(tok, "fractal", strlen(arg))) | ||
| 1048 | callchain_param.mode = CHAIN_GRAPH_REL; | ||
| 1049 | |||
| 1050 | else if (!strncmp(tok, "none", strlen(arg))) { | ||
| 1051 | callchain_param.mode = CHAIN_NONE; | ||
| 1052 | symbol_conf.use_callchain = false; | ||
| 1053 | |||
| 1054 | return 0; | ||
| 1055 | } | ||
| 1056 | |||
| 1057 | else | ||
| 1058 | return -1; | ||
| 1059 | |||
| 1060 | /* get the min percentage */ | ||
| 1061 | tok = strtok(NULL, ","); | ||
| 1062 | if (!tok) | ||
| 1063 | goto setup; | ||
| 1064 | |||
| 1065 | callchain_param.min_percent = strtod(tok, &endptr); | ||
| 1066 | if (tok == endptr) | ||
| 1067 | return -1; | ||
| 1068 | |||
| 1069 | /* get the print limit */ | ||
| 1070 | tok2 = strtok(NULL, ","); | ||
| 1071 | if (!tok2) | ||
| 1072 | goto setup; | ||
| 1073 | |||
| 1074 | if (tok2[0] != 'c') { | ||
| 1075 | callchain_param.print_limit = strtod(tok2, &endptr); | ||
| 1076 | tok2 = strtok(NULL, ","); | ||
| 1077 | if (!tok2) | ||
| 1078 | goto setup; | ||
| 1079 | } | ||
| 1080 | |||
| 1081 | /* get the call chain order */ | ||
| 1082 | if (!strcmp(tok2, "caller")) | ||
| 1083 | callchain_param.order = ORDER_CALLER; | ||
| 1084 | else if (!strcmp(tok2, "callee")) | ||
| 1085 | callchain_param.order = ORDER_CALLEE; | ||
| 1086 | else | ||
| 1087 | return -1; | ||
| 1088 | setup: | ||
| 1089 | if (callchain_register_param(&callchain_param) < 0) { | ||
| 1090 | fprintf(stderr, "Can't register callchain params\n"); | ||
| 1091 | return -1; | ||
| 1092 | } | ||
| 983 | return 0; | 1093 | return 0; |
| 984 | } | 1094 | } |
| 985 | 1095 | ||
| @@ -1019,7 +1129,7 @@ static const struct option options[] = { | |||
| 1019 | "put the counters into a counter group"), | 1129 | "put the counters into a counter group"), |
| 1020 | OPT_BOOLEAN('i', "inherit", &inherit, | 1130 | OPT_BOOLEAN('i', "inherit", &inherit, |
| 1021 | "child tasks inherit counters"), | 1131 | "child tasks inherit counters"), |
| 1022 | OPT_STRING('s', "sym-annotate", &sym_filter, "symbol name", | 1132 | OPT_STRING(0, "sym-annotate", &sym_filter, "symbol name", |
| 1023 | "symbol to annotate"), | 1133 | "symbol to annotate"), |
| 1024 | OPT_BOOLEAN('z', "zero", &top.zero, | 1134 | OPT_BOOLEAN('z', "zero", &top.zero, |
| 1025 | "zero history across updates"), | 1135 | "zero history across updates"), |
| @@ -1033,6 +1143,28 @@ static const struct option options[] = { | |||
| 1033 | OPT_BOOLEAN(0, "stdio", &use_stdio, "Use the stdio interface"), | 1143 | OPT_BOOLEAN(0, "stdio", &use_stdio, "Use the stdio interface"), |
| 1034 | OPT_INCR('v', "verbose", &verbose, | 1144 | OPT_INCR('v', "verbose", &verbose, |
| 1035 | "be more verbose (show counter open errors, etc)"), | 1145 | "be more verbose (show counter open errors, etc)"), |
| 1146 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", | ||
| 1147 | "sort by key(s): pid, comm, dso, symbol, parent"), | ||
| 1148 | OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples, | ||
| 1149 | "Show a column with the number of samples"), | ||
| 1150 | OPT_CALLBACK_DEFAULT('G', "call-graph", NULL, "output_type,min_percent, call_order", | ||
| 1151 | "Display callchains using output_type (graph, flat, fractal, or none), min percent threshold and callchain order. " | ||
| 1152 | "Default: fractal,0.5,callee", &parse_callchain_opt, | ||
| 1153 | callchain_default_opt), | ||
| 1154 | OPT_BOOLEAN(0, "show-total-period", &symbol_conf.show_total_period, | ||
| 1155 | "Show a column with the sum of periods"), | ||
| 1156 | OPT_STRING(0, "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", | ||
| 1157 | "only consider symbols in these dsos"), | ||
| 1158 | OPT_STRING(0, "comms", &symbol_conf.comm_list_str, "comm[,comm...]", | ||
| 1159 | "only consider symbols in these comms"), | ||
| 1160 | OPT_STRING(0, "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]", | ||
| 1161 | "only consider these symbols"), | ||
| 1162 | OPT_BOOLEAN(0, "source", &symbol_conf.annotate_src, | ||
| 1163 | "Interleave source code with assembly code (default)"), | ||
| 1164 | OPT_BOOLEAN(0, "asm-raw", &symbol_conf.annotate_asm_raw, | ||
| 1165 | "Display raw encoding of assembly instructions (default)"), | ||
| 1166 | OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style", | ||
| 1167 | "Specify disassembler style (e.g. -M intel for intel syntax)"), | ||
| 1036 | OPT_END() | 1168 | OPT_END() |
| 1037 | }; | 1169 | }; |
| 1038 | 1170 | ||
| @@ -1045,18 +1177,16 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
| 1045 | if (top.evlist == NULL) | 1177 | if (top.evlist == NULL) |
| 1046 | return -ENOMEM; | 1178 | return -ENOMEM; |
| 1047 | 1179 | ||
| 1048 | page_size = sysconf(_SC_PAGE_SIZE); | 1180 | symbol_conf.exclude_other = false; |
| 1049 | 1181 | ||
| 1050 | argc = parse_options(argc, argv, options, top_usage, 0); | 1182 | argc = parse_options(argc, argv, options, top_usage, 0); |
| 1051 | if (argc) | 1183 | if (argc) |
| 1052 | usage_with_options(top_usage, options); | 1184 | usage_with_options(top_usage, options); |
| 1053 | 1185 | ||
| 1054 | /* | 1186 | if (sort_order == default_sort_order) |
| 1055 | * XXX For now start disabled, only using TUI if explicitely asked for. | 1187 | sort_order = "dso,symbol"; |
| 1056 | * Change that when handle_keys equivalent gets written, live annotation | 1188 | |
| 1057 | * done, etc. | 1189 | setup_sorting(top_usage, options); |
| 1058 | */ | ||
| 1059 | use_browser = 0; | ||
| 1060 | 1190 | ||
| 1061 | if (use_stdio) | 1191 | if (use_stdio) |
| 1062 | use_browser = 0; | 1192 | use_browser = 0; |
| @@ -1119,13 +1249,22 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
| 1119 | 1249 | ||
| 1120 | top.sym_evsel = list_entry(top.evlist->entries.next, struct perf_evsel, node); | 1250 | top.sym_evsel = list_entry(top.evlist->entries.next, struct perf_evsel, node); |
| 1121 | 1251 | ||
| 1122 | symbol_conf.priv_size = (sizeof(struct sym_entry) + sizeof(struct annotation) + | 1252 | symbol_conf.priv_size = sizeof(struct annotation); |
| 1123 | (top.evlist->nr_entries + 1) * sizeof(unsigned long)); | ||
| 1124 | 1253 | ||
| 1125 | symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL); | 1254 | symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL); |
| 1126 | if (symbol__init() < 0) | 1255 | if (symbol__init() < 0) |
| 1127 | return -1; | 1256 | return -1; |
| 1128 | 1257 | ||
| 1258 | sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso", stdout); | ||
| 1259 | sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, "comm", stdout); | ||
| 1260 | sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list, "symbol", stdout); | ||
| 1261 | |||
| 1262 | /* | ||
| 1263 | * Avoid annotation data structures overhead when symbols aren't on the | ||
| 1264 | * sort list. | ||
| 1265 | */ | ||
| 1266 | sort_has_symbols = sort_sym.list.next != NULL; | ||
| 1267 | |||
| 1129 | get_term_dimensions(&winsize); | 1268 | get_term_dimensions(&winsize); |
| 1130 | if (top.print_entries == 0) { | 1269 | if (top.print_entries == 0) { |
| 1131 | update_print_entries(&winsize); | 1270 | 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.c b/tools/perf/perf.c index ec635b7cc8ea..73d0cac8b67e 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c | |||
| @@ -427,6 +427,24 @@ static void get_debugfs_mntpt(void) | |||
| 427 | debugfs_mntpt[0] = '\0'; | 427 | debugfs_mntpt[0] = '\0'; |
| 428 | } | 428 | } |
| 429 | 429 | ||
| 430 | static void pthread__block_sigwinch(void) | ||
| 431 | { | ||
| 432 | sigset_t set; | ||
| 433 | |||
| 434 | sigemptyset(&set); | ||
| 435 | sigaddset(&set, SIGWINCH); | ||
| 436 | pthread_sigmask(SIG_BLOCK, &set, NULL); | ||
| 437 | } | ||
| 438 | |||
| 439 | void pthread__unblock_sigwinch(void) | ||
| 440 | { | ||
| 441 | sigset_t set; | ||
| 442 | |||
| 443 | sigemptyset(&set); | ||
| 444 | sigaddset(&set, SIGWINCH); | ||
| 445 | pthread_sigmask(SIG_UNBLOCK, &set, NULL); | ||
| 446 | } | ||
| 447 | |||
| 430 | int main(int argc, const char **argv) | 448 | int main(int argc, const char **argv) |
| 431 | { | 449 | { |
| 432 | const char *cmd; | 450 | const char *cmd; |
| @@ -480,6 +498,12 @@ int main(int argc, const char **argv) | |||
| 480 | * time. | 498 | * time. |
| 481 | */ | 499 | */ |
| 482 | setup_path(); | 500 | setup_path(); |
| 501 | /* | ||
| 502 | * Block SIGWINCH notifications so that the thread that wants it can | ||
| 503 | * unblock and get syscalls like select interrupted instead of waiting | ||
| 504 | * forever while the signal goes to some other non interested thread. | ||
| 505 | */ | ||
| 506 | pthread__block_sigwinch(); | ||
| 483 | 507 | ||
| 484 | while (1) { | 508 | while (1) { |
| 485 | static int done_help; | 509 | static int done_help; |
diff --git a/tools/perf/perf.h b/tools/perf/perf.h index a5fc660c1f12..914c895510f7 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,8 @@ 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[]; | ||
| 185 | |||
| 186 | void pthread__unblock_sigwinch(void); | ||
| 174 | 187 | ||
| 175 | #endif | 188 | #endif |
diff --git a/tools/perf/scripts/python/bin/net_dropmonitor-record b/tools/perf/scripts/python/bin/net_dropmonitor-record new file mode 100755 index 000000000000..423fb81dadae --- /dev/null +++ b/tools/perf/scripts/python/bin/net_dropmonitor-record | |||
| @@ -0,0 +1,2 @@ | |||
| 1 | #!/bin/bash | ||
| 2 | perf record -e skb:kfree_skb $@ | ||
diff --git a/tools/perf/scripts/python/bin/net_dropmonitor-report b/tools/perf/scripts/python/bin/net_dropmonitor-report new file mode 100755 index 000000000000..8d698f5a06aa --- /dev/null +++ b/tools/perf/scripts/python/bin/net_dropmonitor-report | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | #!/bin/bash | ||
| 2 | # description: display a table of dropped frames | ||
| 3 | |||
| 4 | perf script -s "$PERF_EXEC_PATH"/scripts/python/net_dropmonitor.py $@ | ||
diff --git a/tools/perf/scripts/python/net_dropmonitor.py b/tools/perf/scripts/python/net_dropmonitor.py new file mode 100755 index 000000000000..a4ffc9500023 --- /dev/null +++ b/tools/perf/scripts/python/net_dropmonitor.py | |||
| @@ -0,0 +1,72 @@ | |||
| 1 | # Monitor the system for dropped packets and proudce a report of drop locations and counts | ||
| 2 | |||
| 3 | import os | ||
| 4 | import sys | ||
| 5 | |||
| 6 | sys.path.append(os.environ['PERF_EXEC_PATH'] + \ | ||
| 7 | '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') | ||
| 8 | |||
| 9 | from perf_trace_context import * | ||
| 10 | from Core import * | ||
| 11 | from Util import * | ||
| 12 | |||
| 13 | drop_log = {} | ||
| 14 | kallsyms = [] | ||
| 15 | |||
| 16 | def get_kallsyms_table(): | ||
| 17 | global kallsyms | ||
| 18 | try: | ||
| 19 | f = open("/proc/kallsyms", "r") | ||
| 20 | linecount = 0 | ||
| 21 | for line in f: | ||
| 22 | linecount = linecount+1 | ||
| 23 | f.seek(0) | ||
| 24 | except: | ||
| 25 | return | ||
| 26 | |||
| 27 | |||
| 28 | j = 0 | ||
| 29 | for line in f: | ||
| 30 | loc = int(line.split()[0], 16) | ||
| 31 | name = line.split()[2] | ||
| 32 | j = j +1 | ||
| 33 | if ((j % 100) == 0): | ||
| 34 | print "\r" + str(j) + "/" + str(linecount), | ||
| 35 | kallsyms.append({ 'loc': loc, 'name' : name}) | ||
| 36 | |||
| 37 | print "\r" + str(j) + "/" + str(linecount) | ||
| 38 | kallsyms.sort() | ||
| 39 | return | ||
| 40 | |||
| 41 | def get_sym(sloc): | ||
| 42 | loc = int(sloc) | ||
| 43 | for i in kallsyms: | ||
| 44 | if (i['loc'] >= loc): | ||
| 45 | return (i['name'], i['loc']-loc) | ||
| 46 | return (None, 0) | ||
| 47 | |||
| 48 | def print_drop_table(): | ||
| 49 | print "%25s %25s %25s" % ("LOCATION", "OFFSET", "COUNT") | ||
| 50 | for i in drop_log.keys(): | ||
| 51 | (sym, off) = get_sym(i) | ||
| 52 | if sym == None: | ||
| 53 | sym = i | ||
| 54 | print "%25s %25s %25s" % (sym, off, drop_log[i]) | ||
| 55 | |||
| 56 | |||
| 57 | def trace_begin(): | ||
| 58 | print "Starting trace (Ctrl-C to dump results)" | ||
| 59 | |||
| 60 | def trace_end(): | ||
| 61 | print "Gathering kallsyms data" | ||
| 62 | get_kallsyms_table() | ||
| 63 | print_drop_table() | ||
| 64 | |||
| 65 | # called from perf, when it finds a correspoinding event | ||
| 66 | def skb__kfree_skb(name, context, cpu, sec, nsec, pid, comm, | ||
| 67 | skbaddr, protocol, location): | ||
| 68 | slocation = str(location) | ||
| 69 | try: | ||
| 70 | drop_log[slocation] = drop_log[slocation] + 1 | ||
| 71 | except: | ||
| 72 | drop_log[slocation] = 1 | ||
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index e01af2b1a469..119e996035c8 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c | |||
| @@ -16,6 +16,8 @@ | |||
| 16 | #include "annotate.h" | 16 | #include "annotate.h" |
| 17 | #include <pthread.h> | 17 | #include <pthread.h> |
| 18 | 18 | ||
| 19 | const char *disassembler_style; | ||
| 20 | |||
| 19 | int symbol__annotate_init(struct map *map __used, struct symbol *sym) | 21 | int symbol__annotate_init(struct map *map __used, struct symbol *sym) |
| 20 | { | 22 | { |
| 21 | struct annotation *notes = symbol__annotation(sym); | 23 | struct annotation *notes = symbol__annotation(sym); |
| @@ -308,9 +310,12 @@ fallback: | |||
| 308 | } | 310 | } |
| 309 | err = -ENOENT; | 311 | err = -ENOENT; |
| 310 | dso->annotate_warned = 1; | 312 | dso->annotate_warned = 1; |
| 311 | pr_err("Can't annotate %s: No vmlinux file%s was found in the " | 313 | pr_err("Can't annotate %s:\n\n" |
| 312 | "path.\nPlease use 'perf buildid-cache -av vmlinux' or " | 314 | "No vmlinux file%s\nwas found in the path.\n\n" |
| 313 | "--vmlinux vmlinux.\n", | 315 | "Please use:\n\n" |
| 316 | " perf buildid-cache -av vmlinux\n\n" | ||
| 317 | "or:\n\n" | ||
| 318 | " --vmlinux vmlinux", | ||
| 314 | sym->name, build_id_msg ?: ""); | 319 | sym->name, build_id_msg ?: ""); |
| 315 | goto out_free_filename; | 320 | goto out_free_filename; |
| 316 | } | 321 | } |
| @@ -323,10 +328,15 @@ fallback: | |||
| 323 | dso, dso->long_name, sym, sym->name); | 328 | dso, dso->long_name, sym, sym->name); |
| 324 | 329 | ||
| 325 | snprintf(command, sizeof(command), | 330 | snprintf(command, sizeof(command), |
| 326 | "objdump --start-address=0x%016" PRIx64 | 331 | "objdump %s%s --start-address=0x%016" PRIx64 |
| 327 | " --stop-address=0x%016" PRIx64 " -dS -C %s|grep -v %s|expand", | 332 | " --stop-address=0x%016" PRIx64 |
| 333 | " -d %s %s -C %s|grep -v %s|expand", | ||
| 334 | disassembler_style ? "-M " : "", | ||
| 335 | disassembler_style ? disassembler_style : "", | ||
| 328 | map__rip_2objdump(map, sym->start), | 336 | map__rip_2objdump(map, sym->start), |
| 329 | map__rip_2objdump(map, sym->end), | 337 | map__rip_2objdump(map, sym->end), |
| 338 | symbol_conf.annotate_asm_raw ? "" : "--no-show-raw", | ||
| 339 | symbol_conf.annotate_src ? "-S" : "", | ||
| 330 | symfs_filename, filename); | 340 | symfs_filename, filename); |
| 331 | 341 | ||
| 332 | pr_debug("Executing: %s\n", command); | 342 | pr_debug("Executing: %s\n", command); |
diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index c2c286896801..d9072523d342 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h | |||
| @@ -91,13 +91,18 @@ 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 | ||
| 106 | extern const char *disassembler_style; | ||
| 107 | |||
| 103 | #endif /* __PERF_ANNOTATE_H */ | 108 | #endif /* __PERF_ANNOTATE_H */ |
diff --git a/tools/perf/util/color.c b/tools/perf/util/color.c index e191eb9a667f..521c38a79190 100644 --- a/tools/perf/util/color.c +++ b/tools/perf/util/color.c | |||
| @@ -200,7 +200,7 @@ static int __color_vfprintf(FILE *fp, const char *color, const char *fmt, | |||
| 200 | * Auto-detect: | 200 | * Auto-detect: |
| 201 | */ | 201 | */ |
| 202 | if (perf_use_color_default < 0) { | 202 | if (perf_use_color_default < 0) { |
| 203 | if (isatty(1) || pager_in_use()) | 203 | if (isatty(fileno(fp)) || pager_in_use()) |
| 204 | perf_use_color_default = 1; | 204 | perf_use_color_default = 1; |
| 205 | else | 205 | else |
| 206 | perf_use_color_default = 0; | 206 | perf_use_color_default = 0; |
diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c index fe02903f7d0f..80d9598db31a 100644 --- a/tools/perf/util/config.c +++ b/tools/perf/util/config.c | |||
| @@ -341,7 +341,7 @@ const char *perf_config_dirname(const char *name, const char *value) | |||
| 341 | 341 | ||
| 342 | static int perf_default_core_config(const char *var __used, const char *value __used) | 342 | static int perf_default_core_config(const char *var __used, const char *value __used) |
| 343 | { | 343 | { |
| 344 | /* Add other config variables here and to Documentation/config.txt. */ | 344 | /* Add other config variables here. */ |
| 345 | return 0; | 345 | return 0; |
| 346 | } | 346 | } |
| 347 | 347 | ||
| @@ -350,7 +350,7 @@ int perf_default_config(const char *var, const char *value, void *dummy __used) | |||
| 350 | if (!prefixcmp(var, "core.")) | 350 | if (!prefixcmp(var, "core.")) |
| 351 | return perf_default_core_config(var, value); | 351 | return perf_default_core_config(var, value); |
| 352 | 352 | ||
| 353 | /* Add other config variables here and to Documentation/config.txt. */ | 353 | /* Add other config variables here. */ |
| 354 | return 0; | 354 | return 0; |
| 355 | } | 355 | } |
| 356 | 356 | ||
diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c index 155749d74350..26817daa2961 100644 --- a/tools/perf/util/debug.c +++ b/tools/perf/util/debug.c | |||
| @@ -47,19 +47,20 @@ int dump_printf(const char *fmt, ...) | |||
| 47 | } | 47 | } |
| 48 | 48 | ||
| 49 | #ifdef NO_NEWT_SUPPORT | 49 | #ifdef NO_NEWT_SUPPORT |
| 50 | void ui__warning(const char *format, ...) | 50 | int ui__warning(const char *format, ...) |
| 51 | { | 51 | { |
| 52 | va_list args; | 52 | va_list args; |
| 53 | 53 | ||
| 54 | va_start(args, format); | 54 | va_start(args, format); |
| 55 | vfprintf(stderr, format, args); | 55 | vfprintf(stderr, format, args); |
| 56 | va_end(args); | 56 | va_end(args); |
| 57 | return 0; | ||
| 57 | } | 58 | } |
| 58 | #endif | 59 | #endif |
| 59 | 60 | ||
| 60 | void ui__warning_paranoid(void) | 61 | int ui__error_paranoid(void) |
| 61 | { | 62 | { |
| 62 | ui__warning("Permission error - are you root?\n" | 63 | return ui__error("Permission error - are you root?\n" |
| 63 | "Consider tweaking /proc/sys/kernel/perf_event_paranoid:\n" | 64 | "Consider tweaking /proc/sys/kernel/perf_event_paranoid:\n" |
| 64 | " -1 - Not paranoid at all\n" | 65 | " -1 - Not paranoid at all\n" |
| 65 | " 0 - Disallow raw tracepoint access for unpriv\n" | 66 | " 0 - Disallow raw tracepoint access for unpriv\n" |
diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h index fd53db47e3de..f2ce88d04f54 100644 --- a/tools/perf/util/debug.h +++ b/tools/perf/util/debug.h | |||
| @@ -19,23 +19,18 @@ static inline int ui_helpline__show_help(const char *format __used, va_list ap _ | |||
| 19 | return 0; | 19 | return 0; |
| 20 | } | 20 | } |
| 21 | 21 | ||
| 22 | static inline struct ui_progress *ui_progress__new(const char *title __used, | 22 | static inline void ui_progress__update(u64 curr __used, u64 total __used, |
| 23 | u64 total __used) | 23 | const char *title __used) {} |
| 24 | { | ||
| 25 | return (struct ui_progress *)1; | ||
| 26 | } | ||
| 27 | |||
| 28 | static inline void ui_progress__update(struct ui_progress *self __used, | ||
| 29 | u64 curr __used) {} | ||
| 30 | 24 | ||
| 31 | static inline void ui_progress__delete(struct ui_progress *self __used) {} | 25 | #define ui__error(format, arg...) ui__warning(format, ##arg) |
| 32 | #else | 26 | #else |
| 33 | extern char ui_helpline__last_msg[]; | 27 | extern char ui_helpline__last_msg[]; |
| 34 | int ui_helpline__show_help(const char *format, va_list ap); | 28 | int ui_helpline__show_help(const char *format, va_list ap); |
| 35 | #include "ui/progress.h" | 29 | #include "ui/progress.h" |
| 30 | int ui__error(const char *format, ...) __attribute__((format(printf, 1, 2))); | ||
| 36 | #endif | 31 | #endif |
| 37 | 32 | ||
| 38 | void ui__warning(const char *format, ...) __attribute__((format(printf, 1, 2))); | 33 | int ui__warning(const char *format, ...) __attribute__((format(printf, 1, 2))); |
| 39 | void ui__warning_paranoid(void); | 34 | int ui__error_paranoid(void); |
| 40 | 35 | ||
| 41 | #endif /* __PERF_DEBUG_H */ | 36 | #endif /* __PERF_DEBUG_H */ |
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 72e9f4886b6d..fbb4b4ab9cc6 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c | |||
| @@ -533,3 +533,39 @@ 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 | } | ||
| 542 | |||
| 543 | int perf_evlist__open(struct perf_evlist *evlist, bool group) | ||
| 544 | { | ||
| 545 | struct perf_evsel *evsel, *first; | ||
| 546 | int err, ncpus, nthreads; | ||
| 547 | |||
| 548 | first = list_entry(evlist->entries.next, struct perf_evsel, node); | ||
| 549 | |||
| 550 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
| 551 | struct xyarray *group_fd = NULL; | ||
| 552 | |||
| 553 | if (group && evsel != first) | ||
| 554 | group_fd = first->fd; | ||
| 555 | |||
| 556 | err = perf_evsel__open(evsel, evlist->cpus, evlist->threads, | ||
| 557 | group, group_fd); | ||
| 558 | if (err < 0) | ||
| 559 | goto out_err; | ||
| 560 | } | ||
| 561 | |||
| 562 | return 0; | ||
| 563 | out_err: | ||
| 564 | ncpus = evlist->cpus ? evlist->cpus->nr : 1; | ||
| 565 | nthreads = evlist->threads ? evlist->threads->nr : 1; | ||
| 566 | |||
| 567 | list_for_each_entry_reverse(evsel, &evlist->entries, node) | ||
| 568 | perf_evsel__close(evsel, ncpus, nthreads); | ||
| 569 | |||
| 570 | return err; | ||
| 571 | } | ||
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index f34915002745..1779ffef7828 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; |
| @@ -49,6 +50,8 @@ struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id); | |||
| 49 | 50 | ||
| 50 | union perf_event *perf_evlist__mmap_read(struct perf_evlist *self, int idx); | 51 | union perf_event *perf_evlist__mmap_read(struct perf_evlist *self, int idx); |
| 51 | 52 | ||
| 53 | int perf_evlist__open(struct perf_evlist *evlist, bool group); | ||
| 54 | |||
| 52 | int perf_evlist__alloc_mmap(struct perf_evlist *evlist); | 55 | int perf_evlist__alloc_mmap(struct perf_evlist *evlist); |
| 53 | int perf_evlist__mmap(struct perf_evlist *evlist, int pages, bool overwrite); | 56 | int perf_evlist__mmap(struct perf_evlist *evlist, int pages, bool overwrite); |
| 54 | void perf_evlist__munmap(struct perf_evlist *evlist); | 57 | void perf_evlist__munmap(struct perf_evlist *evlist); |
| @@ -56,6 +59,9 @@ void perf_evlist__munmap(struct perf_evlist *evlist); | |||
| 56 | void perf_evlist__disable(struct perf_evlist *evlist); | 59 | void perf_evlist__disable(struct perf_evlist *evlist); |
| 57 | void perf_evlist__enable(struct perf_evlist *evlist); | 60 | void perf_evlist__enable(struct perf_evlist *evlist); |
| 58 | 61 | ||
| 62 | void perf_evlist__set_selected(struct perf_evlist *evlist, | ||
| 63 | struct perf_evsel *evsel); | ||
| 64 | |||
| 59 | static inline void perf_evlist__set_maps(struct perf_evlist *evlist, | 65 | static inline void perf_evlist__set_maps(struct perf_evlist *evlist, |
| 60 | struct cpu_map *cpus, | 66 | struct cpu_map *cpus, |
| 61 | struct thread_map *threads) | 67 | struct thread_map *threads) |
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index e389815078d3..e42626422587 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
| @@ -16,6 +16,7 @@ | |||
| 16 | #include "thread_map.h" | 16 | #include "thread_map.h" |
| 17 | 17 | ||
| 18 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) | 18 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) |
| 19 | #define GROUP_FD(group_fd, cpu) (*(int *)xyarray__entry(group_fd, cpu, 0)) | ||
| 19 | 20 | ||
| 20 | int __perf_evsel__sample_size(u64 sample_type) | 21 | int __perf_evsel__sample_size(u64 sample_type) |
| 21 | { | 22 | { |
| @@ -39,6 +40,7 @@ void perf_evsel__init(struct perf_evsel *evsel, | |||
| 39 | evsel->idx = idx; | 40 | evsel->idx = idx; |
| 40 | evsel->attr = *attr; | 41 | evsel->attr = *attr; |
| 41 | INIT_LIST_HEAD(&evsel->node); | 42 | INIT_LIST_HEAD(&evsel->node); |
| 43 | hists__init(&evsel->hists); | ||
| 42 | } | 44 | } |
| 43 | 45 | ||
| 44 | struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx) | 46 | struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx) |
| @@ -203,15 +205,16 @@ int __perf_evsel__read(struct perf_evsel *evsel, | |||
| 203 | } | 205 | } |
| 204 | 206 | ||
| 205 | static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | 207 | static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, |
| 206 | struct thread_map *threads, bool group) | 208 | struct thread_map *threads, bool group, |
| 209 | struct xyarray *group_fds) | ||
| 207 | { | 210 | { |
| 208 | int cpu, thread; | 211 | int cpu, thread; |
| 209 | unsigned long flags = 0; | 212 | unsigned long flags = 0; |
| 210 | int pid = -1; | 213 | int pid = -1, err; |
| 211 | 214 | ||
| 212 | if (evsel->fd == NULL && | 215 | if (evsel->fd == NULL && |
| 213 | perf_evsel__alloc_fd(evsel, cpus->nr, threads->nr) < 0) | 216 | perf_evsel__alloc_fd(evsel, cpus->nr, threads->nr) < 0) |
| 214 | return -1; | 217 | return -ENOMEM; |
| 215 | 218 | ||
| 216 | if (evsel->cgrp) { | 219 | if (evsel->cgrp) { |
| 217 | flags = PERF_FLAG_PID_CGROUP; | 220 | flags = PERF_FLAG_PID_CGROUP; |
| @@ -219,7 +222,7 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | |||
| 219 | } | 222 | } |
| 220 | 223 | ||
| 221 | for (cpu = 0; cpu < cpus->nr; cpu++) { | 224 | for (cpu = 0; cpu < cpus->nr; cpu++) { |
| 222 | int group_fd = -1; | 225 | int group_fd = group_fds ? GROUP_FD(group_fds, cpu) : -1; |
| 223 | 226 | ||
| 224 | for (thread = 0; thread < threads->nr; thread++) { | 227 | for (thread = 0; thread < threads->nr; thread++) { |
| 225 | 228 | ||
| @@ -230,8 +233,10 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | |||
| 230 | pid, | 233 | pid, |
| 231 | cpus->map[cpu], | 234 | cpus->map[cpu], |
| 232 | group_fd, flags); | 235 | group_fd, flags); |
| 233 | if (FD(evsel, cpu, thread) < 0) | 236 | if (FD(evsel, cpu, thread) < 0) { |
| 237 | err = -errno; | ||
| 234 | goto out_close; | 238 | goto out_close; |
| 239 | } | ||
| 235 | 240 | ||
| 236 | if (group && group_fd == -1) | 241 | if (group && group_fd == -1) |
| 237 | group_fd = FD(evsel, cpu, thread); | 242 | group_fd = FD(evsel, cpu, thread); |
| @@ -248,7 +253,17 @@ out_close: | |||
| 248 | } | 253 | } |
| 249 | thread = threads->nr; | 254 | thread = threads->nr; |
| 250 | } while (--cpu >= 0); | 255 | } while (--cpu >= 0); |
| 251 | return -1; | 256 | return err; |
| 257 | } | ||
| 258 | |||
| 259 | void perf_evsel__close(struct perf_evsel *evsel, int ncpus, int nthreads) | ||
| 260 | { | ||
| 261 | if (evsel->fd == NULL) | ||
| 262 | return; | ||
| 263 | |||
| 264 | perf_evsel__close_fd(evsel, ncpus, nthreads); | ||
| 265 | perf_evsel__free_fd(evsel); | ||
| 266 | evsel->fd = NULL; | ||
| 252 | } | 267 | } |
| 253 | 268 | ||
| 254 | static struct { | 269 | static struct { |
| @@ -268,7 +283,8 @@ static struct { | |||
| 268 | }; | 283 | }; |
| 269 | 284 | ||
| 270 | int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | 285 | int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, |
| 271 | struct thread_map *threads, bool group) | 286 | struct thread_map *threads, bool group, |
| 287 | struct xyarray *group_fd) | ||
| 272 | { | 288 | { |
| 273 | if (cpus == NULL) { | 289 | if (cpus == NULL) { |
| 274 | /* Work around old compiler warnings about strict aliasing */ | 290 | /* Work around old compiler warnings about strict aliasing */ |
| @@ -278,19 +294,23 @@ int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | |||
| 278 | if (threads == NULL) | 294 | if (threads == NULL) |
| 279 | threads = &empty_thread_map.map; | 295 | threads = &empty_thread_map.map; |
| 280 | 296 | ||
| 281 | return __perf_evsel__open(evsel, cpus, threads, group); | 297 | return __perf_evsel__open(evsel, cpus, threads, group, group_fd); |
| 282 | } | 298 | } |
| 283 | 299 | ||
| 284 | int perf_evsel__open_per_cpu(struct perf_evsel *evsel, | 300 | int perf_evsel__open_per_cpu(struct perf_evsel *evsel, |
| 285 | struct cpu_map *cpus, bool group) | 301 | struct cpu_map *cpus, bool group, |
| 302 | struct xyarray *group_fd) | ||
| 286 | { | 303 | { |
| 287 | return __perf_evsel__open(evsel, cpus, &empty_thread_map.map, group); | 304 | return __perf_evsel__open(evsel, cpus, &empty_thread_map.map, group, |
| 305 | group_fd); | ||
| 288 | } | 306 | } |
| 289 | 307 | ||
| 290 | int perf_evsel__open_per_thread(struct perf_evsel *evsel, | 308 | int perf_evsel__open_per_thread(struct perf_evsel *evsel, |
| 291 | struct thread_map *threads, bool group) | 309 | struct thread_map *threads, bool group, |
| 310 | struct xyarray *group_fd) | ||
| 292 | { | 311 | { |
| 293 | return __perf_evsel__open(evsel, &empty_cpu_map.map, threads, group); | 312 | return __perf_evsel__open(evsel, &empty_cpu_map.map, threads, group, |
| 313 | group_fd); | ||
| 294 | } | 314 | } |
| 295 | 315 | ||
| 296 | static int perf_event__parse_id_sample(const union perf_event *event, u64 type, | 316 | static int perf_event__parse_id_sample(const union perf_event *event, u64 type, |
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index e9a31554e265..b1d15e6f7ae3 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h | |||
| @@ -82,11 +82,15 @@ void perf_evsel__free_id(struct perf_evsel *evsel); | |||
| 82 | void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads); | 82 | void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads); |
| 83 | 83 | ||
| 84 | int perf_evsel__open_per_cpu(struct perf_evsel *evsel, | 84 | int perf_evsel__open_per_cpu(struct perf_evsel *evsel, |
| 85 | struct cpu_map *cpus, bool group); | 85 | struct cpu_map *cpus, bool group, |
| 86 | struct xyarray *group_fds); | ||
| 86 | int perf_evsel__open_per_thread(struct perf_evsel *evsel, | 87 | int perf_evsel__open_per_thread(struct perf_evsel *evsel, |
| 87 | struct thread_map *threads, bool group); | 88 | struct thread_map *threads, bool group, |
| 89 | struct xyarray *group_fds); | ||
| 88 | int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | 90 | int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, |
| 89 | struct thread_map *threads, bool group); | 91 | struct thread_map *threads, bool group, |
| 92 | struct xyarray *group_fds); | ||
| 93 | void perf_evsel__close(struct perf_evsel *evsel, int ncpus, int nthreads); | ||
| 90 | 94 | ||
| 91 | #define perf_evsel__match(evsel, t, c) \ | 95 | #define perf_evsel__match(evsel, t, c) \ |
| 92 | (evsel->attr.type == PERF_TYPE_##t && \ | 96 | (evsel->attr.type == PERF_TYPE_##t && \ |
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index b6c1ad123ca9..bcd05d05b4f0 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
| @@ -1,5 +1,6 @@ | |||
| 1 | #define _FILE_OFFSET_BITS 64 | 1 | #define _FILE_OFFSET_BITS 64 |
| 2 | 2 | ||
| 3 | #include "util.h" | ||
| 3 | #include <sys/types.h> | 4 | #include <sys/types.h> |
| 4 | #include <byteswap.h> | 5 | #include <byteswap.h> |
| 5 | #include <unistd.h> | 6 | #include <unistd.h> |
| @@ -7,22 +8,29 @@ | |||
| 7 | #include <stdlib.h> | 8 | #include <stdlib.h> |
| 8 | #include <linux/list.h> | 9 | #include <linux/list.h> |
| 9 | #include <linux/kernel.h> | 10 | #include <linux/kernel.h> |
| 11 | #include <sys/utsname.h> | ||
| 10 | 12 | ||
| 11 | #include "evlist.h" | 13 | #include "evlist.h" |
| 12 | #include "evsel.h" | 14 | #include "evsel.h" |
| 13 | #include "util.h" | ||
| 14 | #include "header.h" | 15 | #include "header.h" |
| 15 | #include "../perf.h" | 16 | #include "../perf.h" |
| 16 | #include "trace-event.h" | 17 | #include "trace-event.h" |
| 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) \ |
| @@ -267,7 +1289,7 @@ int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir) | |||
| 267 | if (access(linkname, F_OK)) | 1289 | if (access(linkname, F_OK)) |
| 268 | goto out_free; | 1290 | goto out_free; |
| 269 | 1291 | ||
| 270 | if (readlink(linkname, filename, size) < 0) | 1292 | if (readlink(linkname, filename, size - 1) < 0) |
| 271 | goto out_free; | 1293 | goto out_free; |
| 272 | 1294 | ||
| 273 | if (unlink(linkname)) | 1295 | if (unlink(linkname)) |
| @@ -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 | } |
| @@ -621,21 +1703,41 @@ int perf_file_header__read(struct perf_file_header *header, | |||
| 621 | bitmap_zero(header->adds_features, HEADER_FEAT_BITS); | 1703 | bitmap_zero(header->adds_features, HEADER_FEAT_BITS); |
| 622 | else | 1704 | else |
| 623 | return -1; | 1705 | return -1; |
| 1706 | } else if (ph->needs_swap) { | ||
| 1707 | unsigned int i; | ||
| 1708 | /* | ||
| 1709 | * feature bitmap is declared as an array of unsigned longs -- | ||
| 1710 | * not good since its size can differ between the host that | ||
| 1711 | * generated the data file and the host analyzing the file. | ||
| 1712 | * | ||
| 1713 | * We need to handle endianness, but we don't know the size of | ||
| 1714 | * the unsigned long where the file was generated. Take a best | ||
| 1715 | * guess at determining it: try 64-bit swap first (ie., file | ||
| 1716 | * created on a 64-bit host), and check if the hostname feature | ||
| 1717 | * bit is set (this feature bit is forced on as of fbe96f2). | ||
| 1718 | * If the bit is not, undo the 64-bit swap and try a 32-bit | ||
| 1719 | * swap. If the hostname bit is still not set (e.g., older data | ||
| 1720 | * file), punt and fallback to the original behavior -- | ||
| 1721 | * clearing all feature bits and setting buildid. | ||
| 1722 | */ | ||
| 1723 | for (i = 0; i < BITS_TO_LONGS(HEADER_FEAT_BITS); ++i) | ||
| 1724 | header->adds_features[i] = bswap_64(header->adds_features[i]); | ||
| 1725 | |||
| 1726 | if (!test_bit(HEADER_HOSTNAME, header->adds_features)) { | ||
| 1727 | for (i = 0; i < BITS_TO_LONGS(HEADER_FEAT_BITS); ++i) { | ||
| 1728 | header->adds_features[i] = bswap_64(header->adds_features[i]); | ||
| 1729 | header->adds_features[i] = bswap_32(header->adds_features[i]); | ||
| 1730 | } | ||
| 1731 | } | ||
| 1732 | |||
| 1733 | if (!test_bit(HEADER_HOSTNAME, header->adds_features)) { | ||
| 1734 | bitmap_zero(header->adds_features, HEADER_FEAT_BITS); | ||
| 1735 | set_bit(HEADER_BUILD_ID, header->adds_features); | ||
| 1736 | } | ||
| 624 | } | 1737 | } |
| 625 | 1738 | ||
| 626 | memcpy(&ph->adds_features, &header->adds_features, | 1739 | memcpy(&ph->adds_features, &header->adds_features, |
| 627 | sizeof(ph->adds_features)); | 1740 | sizeof(ph->adds_features)); |
| 628 | /* | ||
| 629 | * FIXME: hack that assumes that if we need swap the perf.data file | ||
| 630 | * may be coming from an arch with a different word-size, ergo different | ||
| 631 | * DEFINE_BITMAP format, investigate more later, but for now its mostly | ||
| 632 | * safe to assume that we have a build-id section. Trace files probably | ||
| 633 | * have several other issues in this realm anyway... | ||
| 634 | */ | ||
| 635 | if (ph->needs_swap) { | ||
| 636 | memset(&ph->adds_features, 0, sizeof(ph->adds_features)); | ||
| 637 | perf_header__set_feat(ph, HEADER_BUILD_ID); | ||
| 638 | } | ||
| 639 | 1741 | ||
| 640 | ph->event_offset = header->event_types.offset; | 1742 | ph->event_offset = header->event_types.offset; |
| 641 | ph->event_size = header->event_types.size; | 1743 | ph->event_size = header->event_types.size; |
| @@ -796,7 +1898,7 @@ out: | |||
| 796 | 1898 | ||
| 797 | static int perf_file_section__process(struct perf_file_section *section, | 1899 | static int perf_file_section__process(struct perf_file_section *section, |
| 798 | struct perf_header *ph, | 1900 | struct perf_header *ph, |
| 799 | int feat, int fd) | 1901 | int feat, int fd, void *data __used) |
| 800 | { | 1902 | { |
| 801 | if (lseek(fd, section->offset, SEEK_SET) == (off_t)-1) { | 1903 | if (lseek(fd, section->offset, SEEK_SET) == (off_t)-1) { |
| 802 | pr_debug("Failed to lseek to %" PRIu64 " offset for feature " | 1904 | pr_debug("Failed to lseek to %" PRIu64 " offset for feature " |
| @@ -813,6 +1915,21 @@ static int perf_file_section__process(struct perf_file_section *section, | |||
| 813 | if (perf_header__read_build_ids(ph, fd, section->offset, section->size)) | 1915 | if (perf_header__read_build_ids(ph, fd, section->offset, section->size)) |
| 814 | pr_debug("Failed to read buildids, continuing...\n"); | 1916 | pr_debug("Failed to read buildids, continuing...\n"); |
| 815 | break; | 1917 | break; |
| 1918 | |||
| 1919 | case HEADER_HOSTNAME: | ||
| 1920 | case HEADER_OSRELEASE: | ||
| 1921 | case HEADER_VERSION: | ||
| 1922 | case HEADER_ARCH: | ||
| 1923 | case HEADER_NRCPUS: | ||
| 1924 | case HEADER_CPUDESC: | ||
| 1925 | case HEADER_CPUID: | ||
| 1926 | case HEADER_TOTAL_MEM: | ||
| 1927 | case HEADER_CMDLINE: | ||
| 1928 | case HEADER_EVENT_DESC: | ||
| 1929 | case HEADER_CPU_TOPOLOGY: | ||
| 1930 | case HEADER_NUMA_TOPOLOGY: | ||
| 1931 | break; | ||
| 1932 | |||
| 816 | default: | 1933 | default: |
| 817 | pr_debug("unknown feature %d, continuing...\n", feat); | 1934 | pr_debug("unknown feature %d, continuing...\n", feat); |
| 818 | } | 1935 | } |
| @@ -935,7 +2052,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); | 2052 | event_count = f_header.event_types.size / sizeof(struct perf_trace_event_type); |
| 936 | } | 2053 | } |
| 937 | 2054 | ||
| 938 | perf_header__process_sections(header, fd, perf_file_section__process); | 2055 | perf_header__process_sections(header, fd, NULL, |
| 2056 | perf_file_section__process); | ||
| 939 | 2057 | ||
| 940 | lseek(fd, header->data_offset, SEEK_SET); | 2058 | lseek(fd, header->data_offset, SEEK_SET); |
| 941 | 2059 | ||
| @@ -1100,15 +2218,29 @@ int perf_event__synthesize_tracing_data(int fd, struct perf_evlist *evlist, | |||
| 1100 | struct perf_session *session __unused) | 2218 | struct perf_session *session __unused) |
| 1101 | { | 2219 | { |
| 1102 | union perf_event ev; | 2220 | union perf_event ev; |
| 2221 | struct tracing_data *tdata; | ||
| 1103 | ssize_t size = 0, aligned_size = 0, padding; | 2222 | ssize_t size = 0, aligned_size = 0, padding; |
| 1104 | int err __used = 0; | 2223 | int err __used = 0; |
| 1105 | 2224 | ||
| 2225 | /* | ||
| 2226 | * We are going to store the size of the data followed | ||
| 2227 | * by the data contents. Since the fd descriptor is a pipe, | ||
| 2228 | * we cannot seek back to store the size of the data once | ||
| 2229 | * we know it. Instead we: | ||
| 2230 | * | ||
| 2231 | * - write the tracing data to the temp file | ||
| 2232 | * - get/write the data size to pipe | ||
| 2233 | * - write the tracing data from the temp file | ||
| 2234 | * to the pipe | ||
| 2235 | */ | ||
| 2236 | tdata = tracing_data_get(&evlist->entries, fd, true); | ||
| 2237 | if (!tdata) | ||
| 2238 | return -1; | ||
| 2239 | |||
| 1106 | memset(&ev, 0, sizeof(ev)); | 2240 | memset(&ev, 0, sizeof(ev)); |
| 1107 | 2241 | ||
| 1108 | ev.tracing_data.header.type = PERF_RECORD_HEADER_TRACING_DATA; | 2242 | ev.tracing_data.header.type = PERF_RECORD_HEADER_TRACING_DATA; |
| 1109 | size = read_tracing_data_size(fd, &evlist->entries); | 2243 | size = tdata->size; |
| 1110 | if (size <= 0) | ||
| 1111 | return size; | ||
| 1112 | aligned_size = ALIGN(size, sizeof(u64)); | 2244 | aligned_size = ALIGN(size, sizeof(u64)); |
| 1113 | padding = aligned_size - size; | 2245 | padding = aligned_size - size; |
| 1114 | ev.tracing_data.header.size = sizeof(ev.tracing_data); | 2246 | ev.tracing_data.header.size = sizeof(ev.tracing_data); |
| @@ -1116,7 +2248,12 @@ int perf_event__synthesize_tracing_data(int fd, struct perf_evlist *evlist, | |||
| 1116 | 2248 | ||
| 1117 | process(&ev, NULL, session); | 2249 | process(&ev, NULL, session); |
| 1118 | 2250 | ||
| 1119 | err = read_tracing_data(fd, &evlist->entries); | 2251 | /* |
| 2252 | * The put function will copy all the tracing data | ||
| 2253 | * stored in temp file to the pipe. | ||
| 2254 | */ | ||
| 2255 | tracing_data_put(tdata); | ||
| 2256 | |||
| 1120 | write_padded(fd, NULL, 0, padding); | 2257 | write_padded(fd, NULL, 0, padding); |
| 1121 | 2258 | ||
| 1122 | return aligned_size; | 2259 | return aligned_size; |
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..a36a3fa81ffb 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
| @@ -6,6 +6,11 @@ | |||
| 6 | #include "sort.h" | 6 | #include "sort.h" |
| 7 | #include <math.h> | 7 | #include <math.h> |
| 8 | 8 | ||
| 9 | static bool hists__filter_entry_by_dso(struct hists *hists, | ||
| 10 | struct hist_entry *he); | ||
| 11 | static bool hists__filter_entry_by_thread(struct hists *hists, | ||
| 12 | struct hist_entry *he); | ||
| 13 | |||
| 9 | enum hist_filter { | 14 | enum hist_filter { |
| 10 | HIST_FILTER__DSO, | 15 | HIST_FILTER__DSO, |
| 11 | HIST_FILTER__THREAD, | 16 | HIST_FILTER__THREAD, |
| @@ -18,56 +23,56 @@ struct callchain_param callchain_param = { | |||
| 18 | .order = ORDER_CALLEE | 23 | .order = ORDER_CALLEE |
| 19 | }; | 24 | }; |
| 20 | 25 | ||
| 21 | u16 hists__col_len(struct hists *self, enum hist_column col) | 26 | u16 hists__col_len(struct hists *hists, enum hist_column col) |
| 22 | { | 27 | { |
| 23 | return self->col_len[col]; | 28 | return hists->col_len[col]; |
| 24 | } | 29 | } |
| 25 | 30 | ||
| 26 | void hists__set_col_len(struct hists *self, enum hist_column col, u16 len) | 31 | void hists__set_col_len(struct hists *hists, enum hist_column col, u16 len) |
| 27 | { | 32 | { |
| 28 | self->col_len[col] = len; | 33 | hists->col_len[col] = len; |
| 29 | } | 34 | } |
| 30 | 35 | ||
| 31 | bool hists__new_col_len(struct hists *self, enum hist_column col, u16 len) | 36 | bool hists__new_col_len(struct hists *hists, enum hist_column col, u16 len) |
| 32 | { | 37 | { |
| 33 | if (len > hists__col_len(self, col)) { | 38 | if (len > hists__col_len(hists, col)) { |
| 34 | hists__set_col_len(self, col, len); | 39 | hists__set_col_len(hists, col, len); |
| 35 | return true; | 40 | return true; |
| 36 | } | 41 | } |
| 37 | return false; | 42 | return false; |
| 38 | } | 43 | } |
| 39 | 44 | ||
| 40 | static void hists__reset_col_len(struct hists *self) | 45 | static void hists__reset_col_len(struct hists *hists) |
| 41 | { | 46 | { |
| 42 | enum hist_column col; | 47 | enum hist_column col; |
| 43 | 48 | ||
| 44 | for (col = 0; col < HISTC_NR_COLS; ++col) | 49 | for (col = 0; col < HISTC_NR_COLS; ++col) |
| 45 | hists__set_col_len(self, col, 0); | 50 | hists__set_col_len(hists, col, 0); |
| 46 | } | 51 | } |
| 47 | 52 | ||
| 48 | static void hists__calc_col_len(struct hists *self, struct hist_entry *h) | 53 | static void hists__calc_col_len(struct hists *hists, struct hist_entry *h) |
| 49 | { | 54 | { |
| 50 | u16 len; | 55 | u16 len; |
| 51 | 56 | ||
| 52 | if (h->ms.sym) | 57 | if (h->ms.sym) |
| 53 | hists__new_col_len(self, HISTC_SYMBOL, h->ms.sym->namelen); | 58 | hists__new_col_len(hists, HISTC_SYMBOL, h->ms.sym->namelen); |
| 54 | else { | 59 | else { |
| 55 | const unsigned int unresolved_col_width = BITS_PER_LONG / 4; | 60 | const unsigned int unresolved_col_width = BITS_PER_LONG / 4; |
| 56 | 61 | ||
| 57 | if (hists__col_len(self, HISTC_DSO) < unresolved_col_width && | 62 | if (hists__col_len(hists, HISTC_DSO) < unresolved_col_width && |
| 58 | !symbol_conf.col_width_list_str && !symbol_conf.field_sep && | 63 | !symbol_conf.col_width_list_str && !symbol_conf.field_sep && |
| 59 | !symbol_conf.dso_list) | 64 | !symbol_conf.dso_list) |
| 60 | hists__set_col_len(self, HISTC_DSO, | 65 | hists__set_col_len(hists, HISTC_DSO, |
| 61 | unresolved_col_width); | 66 | unresolved_col_width); |
| 62 | } | 67 | } |
| 63 | 68 | ||
| 64 | len = thread__comm_len(h->thread); | 69 | len = thread__comm_len(h->thread); |
| 65 | if (hists__new_col_len(self, HISTC_COMM, len)) | 70 | if (hists__new_col_len(hists, HISTC_COMM, len)) |
| 66 | hists__set_col_len(self, HISTC_THREAD, len + 6); | 71 | hists__set_col_len(hists, HISTC_THREAD, len + 6); |
| 67 | 72 | ||
| 68 | if (h->ms.map) { | 73 | if (h->ms.map) { |
| 69 | len = dso__name_len(h->ms.map->dso); | 74 | len = dso__name_len(h->ms.map->dso); |
| 70 | hists__new_col_len(self, HISTC_DSO, len); | 75 | hists__new_col_len(hists, HISTC_DSO, len); |
| 71 | } | 76 | } |
| 72 | } | 77 | } |
| 73 | 78 | ||
| @@ -92,6 +97,67 @@ static void hist_entry__add_cpumode_period(struct hist_entry *self, | |||
| 92 | } | 97 | } |
| 93 | } | 98 | } |
| 94 | 99 | ||
| 100 | static void hist_entry__decay(struct hist_entry *he) | ||
| 101 | { | ||
| 102 | he->period = (he->period * 7) / 8; | ||
| 103 | he->nr_events = (he->nr_events * 7) / 8; | ||
| 104 | } | ||
| 105 | |||
| 106 | static bool hists__decay_entry(struct hists *hists, struct hist_entry *he) | ||
| 107 | { | ||
| 108 | u64 prev_period = he->period; | ||
| 109 | |||
| 110 | if (prev_period == 0) | ||
| 111 | return true; | ||
| 112 | |||
| 113 | hist_entry__decay(he); | ||
| 114 | |||
| 115 | if (!he->filtered) | ||
| 116 | hists->stats.total_period -= prev_period - he->period; | ||
| 117 | |||
| 118 | return he->period == 0; | ||
| 119 | } | ||
| 120 | |||
| 121 | static void __hists__decay_entries(struct hists *hists, bool zap_user, | ||
| 122 | bool zap_kernel, bool threaded) | ||
| 123 | { | ||
| 124 | struct rb_node *next = rb_first(&hists->entries); | ||
| 125 | struct hist_entry *n; | ||
| 126 | |||
| 127 | while (next) { | ||
| 128 | n = rb_entry(next, struct hist_entry, rb_node); | ||
| 129 | next = rb_next(&n->rb_node); | ||
| 130 | /* | ||
| 131 | * We may be annotating this, for instance, so keep it here in | ||
| 132 | * case some it gets new samples, we'll eventually free it when | ||
| 133 | * the user stops browsing and it agains gets fully decayed. | ||
| 134 | */ | ||
| 135 | if (((zap_user && n->level == '.') || | ||
| 136 | (zap_kernel && n->level != '.') || | ||
| 137 | hists__decay_entry(hists, n)) && | ||
| 138 | !n->used) { | ||
| 139 | rb_erase(&n->rb_node, &hists->entries); | ||
| 140 | |||
| 141 | if (sort__need_collapse || threaded) | ||
| 142 | rb_erase(&n->rb_node_in, &hists->entries_collapsed); | ||
| 143 | |||
| 144 | hist_entry__free(n); | ||
| 145 | --hists->nr_entries; | ||
| 146 | } | ||
| 147 | } | ||
| 148 | } | ||
| 149 | |||
| 150 | void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel) | ||
| 151 | { | ||
| 152 | return __hists__decay_entries(hists, zap_user, zap_kernel, false); | ||
| 153 | } | ||
| 154 | |||
| 155 | void hists__decay_entries_threaded(struct hists *hists, | ||
| 156 | bool zap_user, bool zap_kernel) | ||
| 157 | { | ||
| 158 | return __hists__decay_entries(hists, zap_user, zap_kernel, true); | ||
| 159 | } | ||
| 160 | |||
| 95 | /* | 161 | /* |
| 96 | * histogram, sorted on item, collects periods | 162 | * histogram, sorted on item, collects periods |
| 97 | */ | 163 | */ |
| @@ -113,11 +179,12 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template) | |||
| 113 | return self; | 179 | return self; |
| 114 | } | 180 | } |
| 115 | 181 | ||
| 116 | static void hists__inc_nr_entries(struct hists *self, struct hist_entry *h) | 182 | static void hists__inc_nr_entries(struct hists *hists, struct hist_entry *h) |
| 117 | { | 183 | { |
| 118 | if (!h->filtered) { | 184 | if (!h->filtered) { |
| 119 | hists__calc_col_len(self, h); | 185 | hists__calc_col_len(hists, h); |
| 120 | ++self->nr_entries; | 186 | ++hists->nr_entries; |
| 187 | hists->stats.total_period += h->period; | ||
| 121 | } | 188 | } |
| 122 | } | 189 | } |
| 123 | 190 | ||
| @@ -128,11 +195,11 @@ static u8 symbol__parent_filter(const struct symbol *parent) | |||
| 128 | return 0; | 195 | return 0; |
| 129 | } | 196 | } |
| 130 | 197 | ||
| 131 | struct hist_entry *__hists__add_entry(struct hists *self, | 198 | struct hist_entry *__hists__add_entry(struct hists *hists, |
| 132 | struct addr_location *al, | 199 | struct addr_location *al, |
| 133 | struct symbol *sym_parent, u64 period) | 200 | struct symbol *sym_parent, u64 period) |
| 134 | { | 201 | { |
| 135 | struct rb_node **p = &self->entries.rb_node; | 202 | struct rb_node **p; |
| 136 | struct rb_node *parent = NULL; | 203 | struct rb_node *parent = NULL; |
| 137 | struct hist_entry *he; | 204 | struct hist_entry *he; |
| 138 | struct hist_entry entry = { | 205 | struct hist_entry entry = { |
| @@ -150,9 +217,13 @@ struct hist_entry *__hists__add_entry(struct hists *self, | |||
| 150 | }; | 217 | }; |
| 151 | int cmp; | 218 | int cmp; |
| 152 | 219 | ||
| 220 | pthread_mutex_lock(&hists->lock); | ||
| 221 | |||
| 222 | p = &hists->entries_in->rb_node; | ||
| 223 | |||
| 153 | while (*p != NULL) { | 224 | while (*p != NULL) { |
| 154 | parent = *p; | 225 | parent = *p; |
| 155 | he = rb_entry(parent, struct hist_entry, rb_node); | 226 | he = rb_entry(parent, struct hist_entry, rb_node_in); |
| 156 | 227 | ||
| 157 | cmp = hist_entry__cmp(&entry, he); | 228 | cmp = hist_entry__cmp(&entry, he); |
| 158 | 229 | ||
| @@ -170,12 +241,14 @@ struct hist_entry *__hists__add_entry(struct hists *self, | |||
| 170 | 241 | ||
| 171 | he = hist_entry__new(&entry); | 242 | he = hist_entry__new(&entry); |
| 172 | if (!he) | 243 | if (!he) |
| 173 | return NULL; | 244 | goto out_unlock; |
| 174 | rb_link_node(&he->rb_node, parent, p); | 245 | |
| 175 | rb_insert_color(&he->rb_node, &self->entries); | 246 | rb_link_node(&he->rb_node_in, parent, p); |
| 176 | hists__inc_nr_entries(self, he); | 247 | rb_insert_color(&he->rb_node_in, hists->entries_in); |
| 177 | out: | 248 | out: |
| 178 | hist_entry__add_cpumode_period(he, al->cpumode, period); | 249 | hist_entry__add_cpumode_period(he, al->cpumode, period); |
| 250 | out_unlock: | ||
| 251 | pthread_mutex_unlock(&hists->lock); | ||
| 179 | return he; | 252 | return he; |
| 180 | } | 253 | } |
| 181 | 254 | ||
| @@ -222,7 +295,7 @@ void hist_entry__free(struct hist_entry *he) | |||
| 222 | * collapse the histogram | 295 | * collapse the histogram |
| 223 | */ | 296 | */ |
| 224 | 297 | ||
| 225 | static bool hists__collapse_insert_entry(struct hists *self, | 298 | static bool hists__collapse_insert_entry(struct hists *hists, |
| 226 | struct rb_root *root, | 299 | struct rb_root *root, |
| 227 | struct hist_entry *he) | 300 | struct hist_entry *he) |
| 228 | { | 301 | { |
| @@ -233,15 +306,16 @@ static bool hists__collapse_insert_entry(struct hists *self, | |||
| 233 | 306 | ||
| 234 | while (*p != NULL) { | 307 | while (*p != NULL) { |
| 235 | parent = *p; | 308 | parent = *p; |
| 236 | iter = rb_entry(parent, struct hist_entry, rb_node); | 309 | iter = rb_entry(parent, struct hist_entry, rb_node_in); |
| 237 | 310 | ||
| 238 | cmp = hist_entry__collapse(iter, he); | 311 | cmp = hist_entry__collapse(iter, he); |
| 239 | 312 | ||
| 240 | if (!cmp) { | 313 | if (!cmp) { |
| 241 | iter->period += he->period; | 314 | iter->period += he->period; |
| 315 | iter->nr_events += he->nr_events; | ||
| 242 | if (symbol_conf.use_callchain) { | 316 | if (symbol_conf.use_callchain) { |
| 243 | callchain_cursor_reset(&self->callchain_cursor); | 317 | callchain_cursor_reset(&hists->callchain_cursor); |
| 244 | callchain_merge(&self->callchain_cursor, iter->callchain, | 318 | callchain_merge(&hists->callchain_cursor, iter->callchain, |
| 245 | he->callchain); | 319 | he->callchain); |
| 246 | } | 320 | } |
| 247 | hist_entry__free(he); | 321 | hist_entry__free(he); |
| @@ -254,35 +328,68 @@ static bool hists__collapse_insert_entry(struct hists *self, | |||
| 254 | p = &(*p)->rb_right; | 328 | p = &(*p)->rb_right; |
| 255 | } | 329 | } |
| 256 | 330 | ||
| 257 | rb_link_node(&he->rb_node, parent, p); | 331 | rb_link_node(&he->rb_node_in, parent, p); |
| 258 | rb_insert_color(&he->rb_node, root); | 332 | rb_insert_color(&he->rb_node_in, root); |
| 259 | return true; | 333 | return true; |
| 260 | } | 334 | } |
| 261 | 335 | ||
| 262 | void hists__collapse_resort(struct hists *self) | 336 | static struct rb_root *hists__get_rotate_entries_in(struct hists *hists) |
| 337 | { | ||
| 338 | struct rb_root *root; | ||
| 339 | |||
| 340 | pthread_mutex_lock(&hists->lock); | ||
| 341 | |||
| 342 | root = hists->entries_in; | ||
| 343 | if (++hists->entries_in > &hists->entries_in_array[1]) | ||
| 344 | hists->entries_in = &hists->entries_in_array[0]; | ||
| 345 | |||
| 346 | pthread_mutex_unlock(&hists->lock); | ||
| 347 | |||
| 348 | return root; | ||
| 349 | } | ||
| 350 | |||
| 351 | static void hists__apply_filters(struct hists *hists, struct hist_entry *he) | ||
| 352 | { | ||
| 353 | hists__filter_entry_by_dso(hists, he); | ||
| 354 | hists__filter_entry_by_thread(hists, he); | ||
| 355 | } | ||
| 356 | |||
| 357 | static void __hists__collapse_resort(struct hists *hists, bool threaded) | ||
| 263 | { | 358 | { |
| 264 | struct rb_root tmp; | 359 | struct rb_root *root; |
| 265 | struct rb_node *next; | 360 | struct rb_node *next; |
| 266 | struct hist_entry *n; | 361 | struct hist_entry *n; |
| 267 | 362 | ||
| 268 | if (!sort__need_collapse) | 363 | if (!sort__need_collapse && !threaded) |
| 269 | return; | 364 | return; |
| 270 | 365 | ||
| 271 | tmp = RB_ROOT; | 366 | root = hists__get_rotate_entries_in(hists); |
| 272 | next = rb_first(&self->entries); | 367 | next = rb_first(root); |
| 273 | self->nr_entries = 0; | ||
| 274 | hists__reset_col_len(self); | ||
| 275 | 368 | ||
| 276 | while (next) { | 369 | while (next) { |
| 277 | n = rb_entry(next, struct hist_entry, rb_node); | 370 | n = rb_entry(next, struct hist_entry, rb_node_in); |
| 278 | next = rb_next(&n->rb_node); | 371 | next = rb_next(&n->rb_node_in); |
| 279 | 372 | ||
| 280 | rb_erase(&n->rb_node, &self->entries); | 373 | rb_erase(&n->rb_node_in, root); |
| 281 | if (hists__collapse_insert_entry(self, &tmp, n)) | 374 | if (hists__collapse_insert_entry(hists, &hists->entries_collapsed, n)) { |
| 282 | hists__inc_nr_entries(self, n); | 375 | /* |
| 376 | * If it wasn't combined with one of the entries already | ||
| 377 | * collapsed, we need to apply the filters that may have | ||
| 378 | * been set by, say, the hist_browser. | ||
| 379 | */ | ||
| 380 | hists__apply_filters(hists, n); | ||
| 381 | } | ||
| 283 | } | 382 | } |
| 383 | } | ||
| 284 | 384 | ||
| 285 | self->entries = tmp; | 385 | void hists__collapse_resort(struct hists *hists) |
| 386 | { | ||
| 387 | return __hists__collapse_resort(hists, false); | ||
| 388 | } | ||
| 389 | |||
| 390 | void hists__collapse_resort_threaded(struct hists *hists) | ||
| 391 | { | ||
| 392 | return __hists__collapse_resort(hists, true); | ||
| 286 | } | 393 | } |
| 287 | 394 | ||
| 288 | /* | 395 | /* |
| @@ -315,31 +422,44 @@ static void __hists__insert_output_entry(struct rb_root *entries, | |||
| 315 | rb_insert_color(&he->rb_node, entries); | 422 | rb_insert_color(&he->rb_node, entries); |
| 316 | } | 423 | } |
| 317 | 424 | ||
| 318 | void hists__output_resort(struct hists *self) | 425 | static void __hists__output_resort(struct hists *hists, bool threaded) |
| 319 | { | 426 | { |
| 320 | struct rb_root tmp; | 427 | struct rb_root *root; |
| 321 | struct rb_node *next; | 428 | struct rb_node *next; |
| 322 | struct hist_entry *n; | 429 | struct hist_entry *n; |
| 323 | u64 min_callchain_hits; | 430 | u64 min_callchain_hits; |
| 324 | 431 | ||
| 325 | min_callchain_hits = self->stats.total_period * (callchain_param.min_percent / 100); | 432 | min_callchain_hits = hists->stats.total_period * (callchain_param.min_percent / 100); |
| 433 | |||
| 434 | if (sort__need_collapse || threaded) | ||
| 435 | root = &hists->entries_collapsed; | ||
| 436 | else | ||
| 437 | root = hists->entries_in; | ||
| 326 | 438 | ||
| 327 | tmp = RB_ROOT; | 439 | next = rb_first(root); |
| 328 | next = rb_first(&self->entries); | 440 | hists->entries = RB_ROOT; |
| 329 | 441 | ||
| 330 | self->nr_entries = 0; | 442 | hists->nr_entries = 0; |
| 331 | hists__reset_col_len(self); | 443 | hists->stats.total_period = 0; |
| 444 | hists__reset_col_len(hists); | ||
| 332 | 445 | ||
| 333 | while (next) { | 446 | while (next) { |
| 334 | n = rb_entry(next, struct hist_entry, rb_node); | 447 | n = rb_entry(next, struct hist_entry, rb_node_in); |
| 335 | next = rb_next(&n->rb_node); | 448 | next = rb_next(&n->rb_node_in); |
| 336 | 449 | ||
| 337 | rb_erase(&n->rb_node, &self->entries); | 450 | __hists__insert_output_entry(&hists->entries, n, min_callchain_hits); |
| 338 | __hists__insert_output_entry(&tmp, n, min_callchain_hits); | 451 | hists__inc_nr_entries(hists, n); |
| 339 | hists__inc_nr_entries(self, n); | ||
| 340 | } | 452 | } |
| 453 | } | ||
| 341 | 454 | ||
| 342 | self->entries = tmp; | 455 | void hists__output_resort(struct hists *hists) |
| 456 | { | ||
| 457 | return __hists__output_resort(hists, false); | ||
| 458 | } | ||
| 459 | |||
| 460 | void hists__output_resort_threaded(struct hists *hists) | ||
| 461 | { | ||
| 462 | return __hists__output_resort(hists, true); | ||
| 343 | } | 463 | } |
| 344 | 464 | ||
| 345 | static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin) | 465 | static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin) |
| @@ -594,12 +714,27 @@ static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, | |||
| 594 | return ret; | 714 | return ret; |
| 595 | } | 715 | } |
| 596 | 716 | ||
| 597 | int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, | 717 | void hists__output_recalc_col_len(struct hists *hists, int max_rows) |
| 598 | struct hists *hists, struct hists *pair_hists, | 718 | { |
| 599 | bool show_displacement, long displacement, | 719 | struct rb_node *next = rb_first(&hists->entries); |
| 600 | bool color, u64 session_total) | 720 | struct hist_entry *n; |
| 721 | int row = 0; | ||
| 722 | |||
| 723 | hists__reset_col_len(hists); | ||
| 724 | |||
| 725 | while (next && row++ < max_rows) { | ||
| 726 | n = rb_entry(next, struct hist_entry, rb_node); | ||
| 727 | if (!n->filtered) | ||
| 728 | hists__calc_col_len(hists, n); | ||
| 729 | next = rb_next(&n->rb_node); | ||
| 730 | } | ||
| 731 | } | ||
| 732 | |||
| 733 | static int hist_entry__pcnt_snprintf(struct hist_entry *self, char *s, | ||
| 734 | size_t size, struct hists *pair_hists, | ||
| 735 | bool show_displacement, long displacement, | ||
| 736 | bool color, u64 session_total) | ||
| 601 | { | 737 | { |
| 602 | struct sort_entry *se; | ||
| 603 | u64 period, total, period_sys, period_us, period_guest_sys, period_guest_us; | 738 | u64 period, total, period_sys, period_us, period_guest_sys, period_guest_us; |
| 604 | u64 nr_events; | 739 | u64 nr_events; |
| 605 | const char *sep = symbol_conf.field_sep; | 740 | const char *sep = symbol_conf.field_sep; |
| @@ -664,6 +799,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); | 799 | ret += snprintf(s + ret, size - ret, "%11" PRIu64, nr_events); |
| 665 | } | 800 | } |
| 666 | 801 | ||
| 802 | if (symbol_conf.show_total_period) { | ||
| 803 | if (sep) | ||
| 804 | ret += snprintf(s + ret, size - ret, "%c%" PRIu64, *sep, period); | ||
| 805 | else | ||
| 806 | ret += snprintf(s + ret, size - ret, " %12" PRIu64, period); | ||
| 807 | } | ||
| 808 | |||
| 667 | if (pair_hists) { | 809 | if (pair_hists) { |
| 668 | char bf[32]; | 810 | char bf[32]; |
| 669 | double old_percent = 0, new_percent = 0, diff; | 811 | double old_percent = 0, new_percent = 0, diff; |
| @@ -698,26 +840,42 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, | |||
| 698 | } | 840 | } |
| 699 | } | 841 | } |
| 700 | 842 | ||
| 843 | return ret; | ||
| 844 | } | ||
| 845 | |||
| 846 | int hist_entry__snprintf(struct hist_entry *he, char *s, size_t size, | ||
| 847 | struct hists *hists) | ||
| 848 | { | ||
| 849 | const char *sep = symbol_conf.field_sep; | ||
| 850 | struct sort_entry *se; | ||
| 851 | int ret = 0; | ||
| 852 | |||
| 701 | list_for_each_entry(se, &hist_entry__sort_list, list) { | 853 | list_for_each_entry(se, &hist_entry__sort_list, list) { |
| 702 | if (se->elide) | 854 | if (se->elide) |
| 703 | continue; | 855 | continue; |
| 704 | 856 | ||
| 705 | ret += snprintf(s + ret, size - ret, "%s", sep ?: " "); | 857 | ret += snprintf(s + ret, size - ret, "%s", sep ?: " "); |
| 706 | ret += se->se_snprintf(self, s + ret, size - ret, | 858 | ret += se->se_snprintf(he, s + ret, size - ret, |
| 707 | hists__col_len(hists, se->se_width_idx)); | 859 | hists__col_len(hists, se->se_width_idx)); |
| 708 | } | 860 | } |
| 709 | 861 | ||
| 710 | return ret; | 862 | return ret; |
| 711 | } | 863 | } |
| 712 | 864 | ||
| 713 | int hist_entry__fprintf(struct hist_entry *self, struct hists *hists, | 865 | int hist_entry__fprintf(struct hist_entry *he, size_t size, struct hists *hists, |
| 714 | struct hists *pair_hists, bool show_displacement, | 866 | struct hists *pair_hists, bool show_displacement, |
| 715 | long displacement, FILE *fp, u64 session_total) | 867 | long displacement, FILE *fp, u64 session_total) |
| 716 | { | 868 | { |
| 717 | char bf[512]; | 869 | char bf[512]; |
| 718 | hist_entry__snprintf(self, bf, sizeof(bf), hists, pair_hists, | 870 | int ret; |
| 719 | show_displacement, displacement, | 871 | |
| 720 | true, session_total); | 872 | if (size == 0 || size > sizeof(bf)) |
| 873 | size = sizeof(bf); | ||
| 874 | |||
| 875 | ret = hist_entry__pcnt_snprintf(he, bf, size, pair_hists, | ||
| 876 | show_displacement, displacement, | ||
| 877 | true, session_total); | ||
| 878 | hist_entry__snprintf(he, bf + ret, size - ret, hists); | ||
| 721 | return fprintf(fp, "%s\n", bf); | 879 | return fprintf(fp, "%s\n", bf); |
| 722 | } | 880 | } |
| 723 | 881 | ||
| @@ -738,8 +896,9 @@ static size_t hist_entry__fprintf_callchain(struct hist_entry *self, | |||
| 738 | left_margin); | 896 | left_margin); |
| 739 | } | 897 | } |
| 740 | 898 | ||
| 741 | size_t hists__fprintf(struct hists *self, struct hists *pair, | 899 | size_t hists__fprintf(struct hists *hists, struct hists *pair, |
| 742 | bool show_displacement, FILE *fp) | 900 | bool show_displacement, bool show_header, int max_rows, |
| 901 | int max_cols, FILE *fp) | ||
| 743 | { | 902 | { |
| 744 | struct sort_entry *se; | 903 | struct sort_entry *se; |
| 745 | struct rb_node *nd; | 904 | struct rb_node *nd; |
| @@ -749,9 +908,13 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, | |||
| 749 | unsigned int width; | 908 | unsigned int width; |
| 750 | const char *sep = symbol_conf.field_sep; | 909 | const char *sep = symbol_conf.field_sep; |
| 751 | const char *col_width = symbol_conf.col_width_list_str; | 910 | const char *col_width = symbol_conf.col_width_list_str; |
| 911 | int nr_rows = 0; | ||
| 752 | 912 | ||
| 753 | init_rem_hits(); | 913 | init_rem_hits(); |
| 754 | 914 | ||
| 915 | if (!show_header) | ||
| 916 | goto print_entries; | ||
| 917 | |||
| 755 | fprintf(fp, "# %s", pair ? "Baseline" : "Overhead"); | 918 | fprintf(fp, "# %s", pair ? "Baseline" : "Overhead"); |
| 756 | 919 | ||
| 757 | if (symbol_conf.show_nr_samples) { | 920 | if (symbol_conf.show_nr_samples) { |
| @@ -761,6 +924,13 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, | |||
| 761 | fputs(" Samples ", fp); | 924 | fputs(" Samples ", fp); |
| 762 | } | 925 | } |
| 763 | 926 | ||
| 927 | if (symbol_conf.show_total_period) { | ||
| 928 | if (sep) | ||
| 929 | ret += fprintf(fp, "%cPeriod", *sep); | ||
| 930 | else | ||
| 931 | ret += fprintf(fp, " Period "); | ||
| 932 | } | ||
| 933 | |||
| 764 | if (symbol_conf.show_cpu_utilization) { | 934 | if (symbol_conf.show_cpu_utilization) { |
| 765 | if (sep) { | 935 | if (sep) { |
| 766 | ret += fprintf(fp, "%csys", *sep); | 936 | ret += fprintf(fp, "%csys", *sep); |
| @@ -803,18 +973,21 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, | |||
| 803 | width = strlen(se->se_header); | 973 | width = strlen(se->se_header); |
| 804 | if (symbol_conf.col_width_list_str) { | 974 | if (symbol_conf.col_width_list_str) { |
| 805 | if (col_width) { | 975 | if (col_width) { |
| 806 | hists__set_col_len(self, se->se_width_idx, | 976 | hists__set_col_len(hists, se->se_width_idx, |
| 807 | atoi(col_width)); | 977 | atoi(col_width)); |
| 808 | col_width = strchr(col_width, ','); | 978 | col_width = strchr(col_width, ','); |
| 809 | if (col_width) | 979 | if (col_width) |
| 810 | ++col_width; | 980 | ++col_width; |
| 811 | } | 981 | } |
| 812 | } | 982 | } |
| 813 | if (!hists__new_col_len(self, se->se_width_idx, width)) | 983 | if (!hists__new_col_len(hists, se->se_width_idx, width)) |
| 814 | width = hists__col_len(self, se->se_width_idx); | 984 | width = hists__col_len(hists, se->se_width_idx); |
| 815 | fprintf(fp, " %*s", width, se->se_header); | 985 | fprintf(fp, " %*s", width, se->se_header); |
| 816 | } | 986 | } |
| 987 | |||
| 817 | fprintf(fp, "\n"); | 988 | fprintf(fp, "\n"); |
| 989 | if (max_rows && ++nr_rows >= max_rows) | ||
| 990 | goto out; | ||
| 818 | 991 | ||
| 819 | if (sep) | 992 | if (sep) |
| 820 | goto print_entries; | 993 | goto print_entries; |
| @@ -822,6 +995,8 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, | |||
| 822 | fprintf(fp, "# ........"); | 995 | fprintf(fp, "# ........"); |
| 823 | if (symbol_conf.show_nr_samples) | 996 | if (symbol_conf.show_nr_samples) |
| 824 | fprintf(fp, " .........."); | 997 | fprintf(fp, " .........."); |
| 998 | if (symbol_conf.show_total_period) | ||
| 999 | fprintf(fp, " ............"); | ||
| 825 | if (pair) { | 1000 | if (pair) { |
| 826 | fprintf(fp, " .........."); | 1001 | fprintf(fp, " .........."); |
| 827 | if (show_displacement) | 1002 | if (show_displacement) |
| @@ -834,17 +1009,23 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, | |||
| 834 | continue; | 1009 | continue; |
| 835 | 1010 | ||
| 836 | fprintf(fp, " "); | 1011 | fprintf(fp, " "); |
| 837 | width = hists__col_len(self, se->se_width_idx); | 1012 | width = hists__col_len(hists, se->se_width_idx); |
| 838 | if (width == 0) | 1013 | if (width == 0) |
| 839 | width = strlen(se->se_header); | 1014 | width = strlen(se->se_header); |
| 840 | for (i = 0; i < width; i++) | 1015 | for (i = 0; i < width; i++) |
| 841 | fprintf(fp, "."); | 1016 | fprintf(fp, "."); |
| 842 | } | 1017 | } |
| 843 | 1018 | ||
| 844 | fprintf(fp, "\n#\n"); | 1019 | fprintf(fp, "\n"); |
| 1020 | if (max_rows && ++nr_rows >= max_rows) | ||
| 1021 | goto out; | ||
| 1022 | |||
| 1023 | fprintf(fp, "#\n"); | ||
| 1024 | if (max_rows && ++nr_rows >= max_rows) | ||
| 1025 | goto out; | ||
| 845 | 1026 | ||
| 846 | print_entries: | 1027 | print_entries: |
| 847 | for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { | 1028 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { |
| 848 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | 1029 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); |
| 849 | 1030 | ||
| 850 | if (h->filtered) | 1031 | if (h->filtered) |
| @@ -858,19 +1039,22 @@ print_entries: | |||
| 858 | displacement = 0; | 1039 | displacement = 0; |
| 859 | ++position; | 1040 | ++position; |
| 860 | } | 1041 | } |
| 861 | ret += hist_entry__fprintf(h, self, pair, show_displacement, | 1042 | ret += hist_entry__fprintf(h, max_cols, hists, pair, show_displacement, |
| 862 | displacement, fp, self->stats.total_period); | 1043 | displacement, fp, hists->stats.total_period); |
| 863 | 1044 | ||
| 864 | if (symbol_conf.use_callchain) | 1045 | if (symbol_conf.use_callchain) |
| 865 | ret += hist_entry__fprintf_callchain(h, self, fp, | 1046 | ret += hist_entry__fprintf_callchain(h, hists, fp, |
| 866 | self->stats.total_period); | 1047 | hists->stats.total_period); |
| 1048 | if (max_rows && ++nr_rows >= max_rows) | ||
| 1049 | goto out; | ||
| 1050 | |||
| 867 | if (h->ms.map == NULL && verbose > 1) { | 1051 | if (h->ms.map == NULL && verbose > 1) { |
| 868 | __map_groups__fprintf_maps(&h->thread->mg, | 1052 | __map_groups__fprintf_maps(&h->thread->mg, |
| 869 | MAP__FUNCTION, verbose, fp); | 1053 | MAP__FUNCTION, verbose, fp); |
| 870 | fprintf(fp, "%.10s end\n", graph_dotted_line); | 1054 | fprintf(fp, "%.10s end\n", graph_dotted_line); |
| 871 | } | 1055 | } |
| 872 | } | 1056 | } |
| 873 | 1057 | out: | |
| 874 | free(rem_sq_bracket); | 1058 | free(rem_sq_bracket); |
| 875 | 1059 | ||
| 876 | return ret; | 1060 | return ret; |
| @@ -879,7 +1063,7 @@ print_entries: | |||
| 879 | /* | 1063 | /* |
| 880 | * See hists__fprintf to match the column widths | 1064 | * See hists__fprintf to match the column widths |
| 881 | */ | 1065 | */ |
| 882 | unsigned int hists__sort_list_width(struct hists *self) | 1066 | unsigned int hists__sort_list_width(struct hists *hists) |
| 883 | { | 1067 | { |
| 884 | struct sort_entry *se; | 1068 | struct sort_entry *se; |
| 885 | int ret = 9; /* total % */ | 1069 | int ret = 9; /* total % */ |
| @@ -896,9 +1080,12 @@ unsigned int hists__sort_list_width(struct hists *self) | |||
| 896 | if (symbol_conf.show_nr_samples) | 1080 | if (symbol_conf.show_nr_samples) |
| 897 | ret += 11; | 1081 | ret += 11; |
| 898 | 1082 | ||
| 1083 | if (symbol_conf.show_total_period) | ||
| 1084 | ret += 13; | ||
| 1085 | |||
| 899 | list_for_each_entry(se, &hist_entry__sort_list, list) | 1086 | list_for_each_entry(se, &hist_entry__sort_list, list) |
| 900 | if (!se->elide) | 1087 | if (!se->elide) |
| 901 | ret += 2 + hists__col_len(self, se->se_width_idx); | 1088 | ret += 2 + hists__col_len(hists, se->se_width_idx); |
| 902 | 1089 | ||
| 903 | if (verbose) /* Addr + origin */ | 1090 | if (verbose) /* Addr + origin */ |
| 904 | ret += 3 + BITS_PER_LONG / 4; | 1091 | ret += 3 + BITS_PER_LONG / 4; |
| @@ -906,63 +1093,84 @@ unsigned int hists__sort_list_width(struct hists *self) | |||
| 906 | return ret; | 1093 | return ret; |
| 907 | } | 1094 | } |
| 908 | 1095 | ||
| 909 | static void hists__remove_entry_filter(struct hists *self, struct hist_entry *h, | 1096 | static void hists__remove_entry_filter(struct hists *hists, struct hist_entry *h, |
| 910 | enum hist_filter filter) | 1097 | enum hist_filter filter) |
| 911 | { | 1098 | { |
| 912 | h->filtered &= ~(1 << filter); | 1099 | h->filtered &= ~(1 << filter); |
| 913 | if (h->filtered) | 1100 | if (h->filtered) |
| 914 | return; | 1101 | return; |
| 915 | 1102 | ||
| 916 | ++self->nr_entries; | 1103 | ++hists->nr_entries; |
| 917 | if (h->ms.unfolded) | 1104 | if (h->ms.unfolded) |
| 918 | self->nr_entries += h->nr_rows; | 1105 | hists->nr_entries += h->nr_rows; |
| 919 | h->row_offset = 0; | 1106 | h->row_offset = 0; |
| 920 | self->stats.total_period += h->period; | 1107 | hists->stats.total_period += h->period; |
| 921 | self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events; | 1108 | hists->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events; |
| 922 | 1109 | ||
| 923 | hists__calc_col_len(self, h); | 1110 | hists__calc_col_len(hists, h); |
| 924 | } | 1111 | } |
| 925 | 1112 | ||
| 926 | void hists__filter_by_dso(struct hists *self, const struct dso *dso) | 1113 | |
| 1114 | static bool hists__filter_entry_by_dso(struct hists *hists, | ||
| 1115 | struct hist_entry *he) | ||
| 1116 | { | ||
| 1117 | if (hists->dso_filter != NULL && | ||
| 1118 | (he->ms.map == NULL || he->ms.map->dso != hists->dso_filter)) { | ||
| 1119 | he->filtered |= (1 << HIST_FILTER__DSO); | ||
| 1120 | return true; | ||
| 1121 | } | ||
| 1122 | |||
| 1123 | return false; | ||
| 1124 | } | ||
| 1125 | |||
| 1126 | void hists__filter_by_dso(struct hists *hists) | ||
| 927 | { | 1127 | { |
| 928 | struct rb_node *nd; | 1128 | struct rb_node *nd; |
| 929 | 1129 | ||
| 930 | self->nr_entries = self->stats.total_period = 0; | 1130 | hists->nr_entries = hists->stats.total_period = 0; |
| 931 | self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; | 1131 | hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0; |
| 932 | hists__reset_col_len(self); | 1132 | hists__reset_col_len(hists); |
| 933 | 1133 | ||
| 934 | for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { | 1134 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { |
| 935 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | 1135 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); |
| 936 | 1136 | ||
| 937 | if (symbol_conf.exclude_other && !h->parent) | 1137 | if (symbol_conf.exclude_other && !h->parent) |
| 938 | continue; | 1138 | continue; |
| 939 | 1139 | ||
| 940 | if (dso != NULL && (h->ms.map == NULL || h->ms.map->dso != dso)) { | 1140 | if (hists__filter_entry_by_dso(hists, h)) |
| 941 | h->filtered |= (1 << HIST_FILTER__DSO); | ||
| 942 | continue; | 1141 | continue; |
| 943 | } | ||
| 944 | 1142 | ||
| 945 | hists__remove_entry_filter(self, h, HIST_FILTER__DSO); | 1143 | hists__remove_entry_filter(hists, h, HIST_FILTER__DSO); |
| 946 | } | 1144 | } |
| 947 | } | 1145 | } |
| 948 | 1146 | ||
| 949 | void hists__filter_by_thread(struct hists *self, const struct thread *thread) | 1147 | static bool hists__filter_entry_by_thread(struct hists *hists, |
| 1148 | struct hist_entry *he) | ||
| 1149 | { | ||
| 1150 | if (hists->thread_filter != NULL && | ||
| 1151 | he->thread != hists->thread_filter) { | ||
| 1152 | he->filtered |= (1 << HIST_FILTER__THREAD); | ||
| 1153 | return true; | ||
| 1154 | } | ||
| 1155 | |||
| 1156 | return false; | ||
| 1157 | } | ||
| 1158 | |||
| 1159 | void hists__filter_by_thread(struct hists *hists) | ||
| 950 | { | 1160 | { |
| 951 | struct rb_node *nd; | 1161 | struct rb_node *nd; |
| 952 | 1162 | ||
| 953 | self->nr_entries = self->stats.total_period = 0; | 1163 | hists->nr_entries = hists->stats.total_period = 0; |
| 954 | self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; | 1164 | hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0; |
| 955 | hists__reset_col_len(self); | 1165 | hists__reset_col_len(hists); |
| 956 | 1166 | ||
| 957 | for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { | 1167 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { |
| 958 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | 1168 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); |
| 959 | 1169 | ||
| 960 | if (thread != NULL && h->thread != thread) { | 1170 | if (hists__filter_entry_by_thread(hists, h)) |
| 961 | h->filtered |= (1 << HIST_FILTER__THREAD); | ||
| 962 | continue; | 1171 | continue; |
| 963 | } | ||
| 964 | 1172 | ||
| 965 | hists__remove_entry_filter(self, h, HIST_FILTER__THREAD); | 1173 | hists__remove_entry_filter(hists, h, HIST_FILTER__THREAD); |
| 966 | } | 1174 | } |
| 967 | } | 1175 | } |
| 968 | 1176 | ||
| @@ -976,13 +1184,13 @@ int hist_entry__annotate(struct hist_entry *he, size_t privsize) | |||
| 976 | return symbol__annotate(he->ms.sym, he->ms.map, privsize); | 1184 | return symbol__annotate(he->ms.sym, he->ms.map, privsize); |
| 977 | } | 1185 | } |
| 978 | 1186 | ||
| 979 | void hists__inc_nr_events(struct hists *self, u32 type) | 1187 | void hists__inc_nr_events(struct hists *hists, u32 type) |
| 980 | { | 1188 | { |
| 981 | ++self->stats.nr_events[0]; | 1189 | ++hists->stats.nr_events[0]; |
| 982 | ++self->stats.nr_events[type]; | 1190 | ++hists->stats.nr_events[type]; |
| 983 | } | 1191 | } |
| 984 | 1192 | ||
| 985 | size_t hists__fprintf_nr_events(struct hists *self, FILE *fp) | 1193 | size_t hists__fprintf_nr_events(struct hists *hists, FILE *fp) |
| 986 | { | 1194 | { |
| 987 | int i; | 1195 | int i; |
| 988 | size_t ret = 0; | 1196 | size_t ret = 0; |
| @@ -990,7 +1198,7 @@ size_t hists__fprintf_nr_events(struct hists *self, FILE *fp) | |||
| 990 | for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) { | 1198 | for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) { |
| 991 | const char *name; | 1199 | const char *name; |
| 992 | 1200 | ||
| 993 | if (self->stats.nr_events[i] == 0) | 1201 | if (hists->stats.nr_events[i] == 0) |
| 994 | continue; | 1202 | continue; |
| 995 | 1203 | ||
| 996 | name = perf_event__name(i); | 1204 | name = perf_event__name(i); |
| @@ -998,8 +1206,18 @@ size_t hists__fprintf_nr_events(struct hists *self, FILE *fp) | |||
| 998 | continue; | 1206 | continue; |
| 999 | 1207 | ||
| 1000 | ret += fprintf(fp, "%16s events: %10d\n", name, | 1208 | ret += fprintf(fp, "%16s events: %10d\n", name, |
| 1001 | self->stats.nr_events[i]); | 1209 | hists->stats.nr_events[i]); |
| 1002 | } | 1210 | } |
| 1003 | 1211 | ||
| 1004 | return ret; | 1212 | return ret; |
| 1005 | } | 1213 | } |
| 1214 | |||
| 1215 | void hists__init(struct hists *hists) | ||
| 1216 | { | ||
| 1217 | memset(hists, 0, sizeof(*hists)); | ||
| 1218 | hists->entries_in_array[0] = hists->entries_in_array[1] = RB_ROOT; | ||
| 1219 | hists->entries_in = &hists->entries_in_array[0]; | ||
| 1220 | hists->entries_collapsed = RB_ROOT; | ||
| 1221 | hists->entries = RB_ROOT; | ||
| 1222 | pthread_mutex_init(&hists->lock, NULL); | ||
| 1223 | } | ||
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 3beb97c4d822..c86c1d27bd1e 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; |
| @@ -27,6 +28,7 @@ struct events_stats { | |||
| 27 | u64 total_lost; | 28 | u64 total_lost; |
| 28 | u64 total_invalid_chains; | 29 | u64 total_invalid_chains; |
| 29 | u32 nr_events[PERF_RECORD_HEADER_MAX]; | 30 | u32 nr_events[PERF_RECORD_HEADER_MAX]; |
| 31 | u32 nr_lost_warned; | ||
| 30 | u32 nr_unknown_events; | 32 | u32 nr_unknown_events; |
| 31 | u32 nr_invalid_chains; | 33 | u32 nr_invalid_chains; |
| 32 | u32 nr_unknown_id; | 34 | u32 nr_unknown_id; |
| @@ -42,9 +44,18 @@ enum hist_column { | |||
| 42 | HISTC_NR_COLS, /* Last entry */ | 44 | HISTC_NR_COLS, /* Last entry */ |
| 43 | }; | 45 | }; |
| 44 | 46 | ||
| 47 | struct thread; | ||
| 48 | struct dso; | ||
| 49 | |||
| 45 | struct hists { | 50 | struct hists { |
| 51 | struct rb_root entries_in_array[2]; | ||
| 52 | struct rb_root *entries_in; | ||
| 46 | struct rb_root entries; | 53 | struct rb_root entries; |
| 54 | struct rb_root entries_collapsed; | ||
| 47 | u64 nr_entries; | 55 | u64 nr_entries; |
| 56 | const struct thread *thread_filter; | ||
| 57 | const struct dso *dso_filter; | ||
| 58 | pthread_mutex_t lock; | ||
| 48 | struct events_stats stats; | 59 | struct events_stats stats; |
| 49 | u64 event_stream; | 60 | u64 event_stream; |
| 50 | u16 col_len[HISTC_NR_COLS]; | 61 | u16 col_len[HISTC_NR_COLS]; |
| @@ -52,34 +63,42 @@ struct hists { | |||
| 52 | struct callchain_cursor callchain_cursor; | 63 | struct callchain_cursor callchain_cursor; |
| 53 | }; | 64 | }; |
| 54 | 65 | ||
| 66 | void hists__init(struct hists *hists); | ||
| 67 | |||
| 55 | struct hist_entry *__hists__add_entry(struct hists *self, | 68 | struct hist_entry *__hists__add_entry(struct hists *self, |
| 56 | struct addr_location *al, | 69 | struct addr_location *al, |
| 57 | struct symbol *parent, u64 period); | 70 | struct symbol *parent, u64 period); |
| 58 | extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *); | 71 | 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 *); | 72 | extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *); |
| 60 | int hist_entry__fprintf(struct hist_entry *self, struct hists *hists, | 73 | int hist_entry__fprintf(struct hist_entry *he, size_t size, struct hists *hists, |
| 61 | struct hists *pair_hists, bool show_displacement, | 74 | struct hists *pair_hists, bool show_displacement, |
| 62 | long displacement, FILE *fp, u64 total); | 75 | long displacement, FILE *fp, u64 session_total); |
| 63 | int hist_entry__snprintf(struct hist_entry *self, char *bf, size_t size, | 76 | int hist_entry__snprintf(struct hist_entry *self, char *bf, size_t size, |
| 64 | struct hists *hists, struct hists *pair_hists, | 77 | struct hists *hists); |
| 65 | bool show_displacement, long displacement, | ||
| 66 | bool color, u64 total); | ||
| 67 | void hist_entry__free(struct hist_entry *); | 78 | void hist_entry__free(struct hist_entry *); |
| 68 | 79 | ||
| 69 | void hists__output_resort(struct hists *self); | 80 | void hists__output_resort(struct hists *self); |
| 81 | void hists__output_resort_threaded(struct hists *hists); | ||
| 70 | void hists__collapse_resort(struct hists *self); | 82 | void hists__collapse_resort(struct hists *self); |
| 83 | void hists__collapse_resort_threaded(struct hists *hists); | ||
| 84 | |||
| 85 | void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel); | ||
| 86 | void hists__decay_entries_threaded(struct hists *hists, bool zap_user, | ||
| 87 | bool zap_kernel); | ||
| 88 | void hists__output_recalc_col_len(struct hists *hists, int max_rows); | ||
| 71 | 89 | ||
| 72 | void hists__inc_nr_events(struct hists *self, u32 type); | 90 | void hists__inc_nr_events(struct hists *self, u32 type); |
| 73 | size_t hists__fprintf_nr_events(struct hists *self, FILE *fp); | 91 | size_t hists__fprintf_nr_events(struct hists *self, FILE *fp); |
| 74 | 92 | ||
| 75 | size_t hists__fprintf(struct hists *self, struct hists *pair, | 93 | size_t hists__fprintf(struct hists *self, struct hists *pair, |
| 76 | bool show_displacement, FILE *fp); | 94 | bool show_displacement, bool show_header, |
| 95 | int max_rows, int max_cols, FILE *fp); | ||
| 77 | 96 | ||
| 78 | int hist_entry__inc_addr_samples(struct hist_entry *self, int evidx, u64 addr); | 97 | 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); | 98 | int hist_entry__annotate(struct hist_entry *self, size_t privsize); |
| 80 | 99 | ||
| 81 | void hists__filter_by_dso(struct hists *self, const struct dso *dso); | 100 | void hists__filter_by_dso(struct hists *hists); |
| 82 | void hists__filter_by_thread(struct hists *self, const struct thread *thread); | 101 | void hists__filter_by_thread(struct hists *hists); |
| 83 | 102 | ||
| 84 | u16 hists__col_len(struct hists *self, enum hist_column col); | 103 | u16 hists__col_len(struct hists *self, enum hist_column col); |
| 85 | void hists__set_col_len(struct hists *self, enum hist_column col, u16 len); | 104 | void hists__set_col_len(struct hists *self, enum hist_column col, u16 len); |
| @@ -90,26 +109,33 @@ struct perf_evlist; | |||
| 90 | #ifdef NO_NEWT_SUPPORT | 109 | #ifdef NO_NEWT_SUPPORT |
| 91 | static inline | 110 | static inline |
| 92 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __used, | 111 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __used, |
| 93 | const char *help __used) | 112 | const char *help __used, |
| 113 | void(*timer)(void *arg) __used, | ||
| 114 | void *arg __used, | ||
| 115 | int refresh __used) | ||
| 94 | { | 116 | { |
| 95 | return 0; | 117 | return 0; |
| 96 | } | 118 | } |
| 97 | 119 | ||
| 98 | static inline int hist_entry__tui_annotate(struct hist_entry *self __used, | 120 | static inline int hist_entry__tui_annotate(struct hist_entry *self __used, |
| 99 | int evidx __used) | 121 | int evidx __used, |
| 122 | int nr_events __used, | ||
| 123 | void(*timer)(void *arg) __used, | ||
| 124 | void *arg __used, | ||
| 125 | int delay_secs __used) | ||
| 100 | { | 126 | { |
| 101 | return 0; | 127 | return 0; |
| 102 | } | 128 | } |
| 103 | #define KEY_LEFT -1 | 129 | #define K_LEFT -1 |
| 104 | #define KEY_RIGHT -2 | 130 | #define K_RIGHT -2 |
| 105 | #else | 131 | #else |
| 106 | #include <newt.h> | 132 | #include "ui/keysyms.h" |
| 107 | int hist_entry__tui_annotate(struct hist_entry *self, int evidx); | 133 | int hist_entry__tui_annotate(struct hist_entry *he, int evidx, int nr_events, |
| 108 | 134 | void(*timer)(void *arg), void *arg, int delay_secs); | |
| 109 | #define KEY_LEFT NEWT_KEY_LEFT | ||
| 110 | #define KEY_RIGHT NEWT_KEY_RIGHT | ||
| 111 | 135 | ||
| 112 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help); | 136 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, |
| 137 | void(*timer)(void *arg), void *arg, | ||
| 138 | int refresh); | ||
| 113 | #endif | 139 | #endif |
| 114 | 140 | ||
| 115 | unsigned int hists__sort_list_width(struct hists *self); | 141 | unsigned int hists__sort_list_width(struct hists *self); |
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index a16ecab5229d..78284b13e808 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c | |||
| @@ -18,6 +18,13 @@ static inline int is_anon_memory(const char *filename) | |||
| 18 | return strcmp(filename, "//anon") == 0; | 18 | return strcmp(filename, "//anon") == 0; |
| 19 | } | 19 | } |
| 20 | 20 | ||
| 21 | static inline int is_no_dso_memory(const char *filename) | ||
| 22 | { | ||
| 23 | return !strcmp(filename, "[stack]") || | ||
| 24 | !strcmp(filename, "[vdso]") || | ||
| 25 | !strcmp(filename, "[heap]"); | ||
| 26 | } | ||
| 27 | |||
| 21 | void map__init(struct map *self, enum map_type type, | 28 | void map__init(struct map *self, enum map_type type, |
| 22 | u64 start, u64 end, u64 pgoff, struct dso *dso) | 29 | u64 start, u64 end, u64 pgoff, struct dso *dso) |
| 23 | { | 30 | { |
| @@ -42,9 +49,10 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, | |||
| 42 | if (self != NULL) { | 49 | if (self != NULL) { |
| 43 | char newfilename[PATH_MAX]; | 50 | char newfilename[PATH_MAX]; |
| 44 | struct dso *dso; | 51 | struct dso *dso; |
| 45 | int anon; | 52 | int anon, no_dso; |
| 46 | 53 | ||
| 47 | anon = is_anon_memory(filename); | 54 | anon = is_anon_memory(filename); |
| 55 | no_dso = is_no_dso_memory(filename); | ||
| 48 | 56 | ||
| 49 | if (anon) { | 57 | if (anon) { |
| 50 | snprintf(newfilename, sizeof(newfilename), "/tmp/perf-%d.map", pid); | 58 | snprintf(newfilename, sizeof(newfilename), "/tmp/perf-%d.map", pid); |
| @@ -57,12 +65,16 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, | |||
| 57 | 65 | ||
| 58 | map__init(self, type, start, start + len, pgoff, dso); | 66 | map__init(self, type, start, start + len, pgoff, dso); |
| 59 | 67 | ||
| 60 | if (anon) { | 68 | if (anon || no_dso) { |
| 61 | set_identity: | ||
| 62 | self->map_ip = self->unmap_ip = identity__map_ip; | 69 | self->map_ip = self->unmap_ip = identity__map_ip; |
| 63 | } else if (strcmp(filename, "[vdso]") == 0) { | 70 | |
| 64 | dso__set_loaded(dso, self->type); | 71 | /* |
| 65 | goto set_identity; | 72 | * Set memory without DSO as loaded. All map__find_* |
| 73 | * functions still return NULL, and we avoid the | ||
| 74 | * unnecessary map__load warning. | ||
| 75 | */ | ||
| 76 | if (no_dso) | ||
| 77 | dso__set_loaded(dso, self->type); | ||
| 66 | } | 78 | } |
| 67 | } | 79 | } |
| 68 | return self; | 80 | return self; |
| @@ -127,8 +139,8 @@ int map__load(struct map *self, symbol_filter_t filter) | |||
| 127 | 139 | ||
| 128 | if (len > sizeof(DSO__DELETED) && | 140 | if (len > sizeof(DSO__DELETED) && |
| 129 | strcmp(name + real_len + 1, DSO__DELETED) == 0) { | 141 | strcmp(name + real_len + 1, DSO__DELETED) == 0) { |
| 130 | pr_warning("%.*s was updated, restart the long " | 142 | pr_warning("%.*s was updated (is prelink enabled?). " |
| 131 | "running apps that use it!\n", | 143 | "Restart the long running apps that use it!\n", |
| 132 | (int)real_len, name); | 144 | (int)real_len, name); |
| 133 | } else { | 145 | } else { |
| 134 | pr_warning("no symbols found in %s, maybe install " | 146 | pr_warning("no symbols found in %s, maybe install " |
| @@ -220,55 +232,55 @@ u64 map__objdump_2ip(struct map *map, u64 addr) | |||
| 220 | return ip; | 232 | return ip; |
| 221 | } | 233 | } |
| 222 | 234 | ||
| 223 | void map_groups__init(struct map_groups *self) | 235 | void map_groups__init(struct map_groups *mg) |
| 224 | { | 236 | { |
| 225 | int i; | 237 | int i; |
| 226 | for (i = 0; i < MAP__NR_TYPES; ++i) { | 238 | for (i = 0; i < MAP__NR_TYPES; ++i) { |
| 227 | self->maps[i] = RB_ROOT; | 239 | mg->maps[i] = RB_ROOT; |
| 228 | INIT_LIST_HEAD(&self->removed_maps[i]); | 240 | INIT_LIST_HEAD(&mg->removed_maps[i]); |
| 229 | } | 241 | } |
| 230 | self->machine = NULL; | 242 | mg->machine = NULL; |
| 231 | } | 243 | } |
| 232 | 244 | ||
| 233 | static void maps__delete(struct rb_root *self) | 245 | static void maps__delete(struct rb_root *maps) |
| 234 | { | 246 | { |
| 235 | struct rb_node *next = rb_first(self); | 247 | struct rb_node *next = rb_first(maps); |
| 236 | 248 | ||
| 237 | while (next) { | 249 | while (next) { |
| 238 | struct map *pos = rb_entry(next, struct map, rb_node); | 250 | struct map *pos = rb_entry(next, struct map, rb_node); |
| 239 | 251 | ||
| 240 | next = rb_next(&pos->rb_node); | 252 | next = rb_next(&pos->rb_node); |
| 241 | rb_erase(&pos->rb_node, self); | 253 | rb_erase(&pos->rb_node, maps); |
| 242 | map__delete(pos); | 254 | map__delete(pos); |
| 243 | } | 255 | } |
| 244 | } | 256 | } |
| 245 | 257 | ||
| 246 | static void maps__delete_removed(struct list_head *self) | 258 | static void maps__delete_removed(struct list_head *maps) |
| 247 | { | 259 | { |
| 248 | struct map *pos, *n; | 260 | struct map *pos, *n; |
| 249 | 261 | ||
| 250 | list_for_each_entry_safe(pos, n, self, node) { | 262 | list_for_each_entry_safe(pos, n, maps, node) { |
| 251 | list_del(&pos->node); | 263 | list_del(&pos->node); |
| 252 | map__delete(pos); | 264 | map__delete(pos); |
| 253 | } | 265 | } |
| 254 | } | 266 | } |
| 255 | 267 | ||
| 256 | void map_groups__exit(struct map_groups *self) | 268 | void map_groups__exit(struct map_groups *mg) |
| 257 | { | 269 | { |
| 258 | int i; | 270 | int i; |
| 259 | 271 | ||
| 260 | for (i = 0; i < MAP__NR_TYPES; ++i) { | 272 | for (i = 0; i < MAP__NR_TYPES; ++i) { |
| 261 | maps__delete(&self->maps[i]); | 273 | maps__delete(&mg->maps[i]); |
| 262 | maps__delete_removed(&self->removed_maps[i]); | 274 | maps__delete_removed(&mg->removed_maps[i]); |
| 263 | } | 275 | } |
| 264 | } | 276 | } |
| 265 | 277 | ||
| 266 | void map_groups__flush(struct map_groups *self) | 278 | void map_groups__flush(struct map_groups *mg) |
| 267 | { | 279 | { |
| 268 | int type; | 280 | int type; |
| 269 | 281 | ||
| 270 | for (type = 0; type < MAP__NR_TYPES; type++) { | 282 | for (type = 0; type < MAP__NR_TYPES; type++) { |
| 271 | struct rb_root *root = &self->maps[type]; | 283 | struct rb_root *root = &mg->maps[type]; |
| 272 | struct rb_node *next = rb_first(root); | 284 | struct rb_node *next = rb_first(root); |
| 273 | 285 | ||
| 274 | while (next) { | 286 | while (next) { |
| @@ -280,17 +292,17 @@ void map_groups__flush(struct map_groups *self) | |||
| 280 | * instance in some hist_entry instances, so | 292 | * instance in some hist_entry instances, so |
| 281 | * just move them to a separate list. | 293 | * just move them to a separate list. |
| 282 | */ | 294 | */ |
| 283 | list_add_tail(&pos->node, &self->removed_maps[pos->type]); | 295 | list_add_tail(&pos->node, &mg->removed_maps[pos->type]); |
| 284 | } | 296 | } |
| 285 | } | 297 | } |
| 286 | } | 298 | } |
| 287 | 299 | ||
| 288 | struct symbol *map_groups__find_symbol(struct map_groups *self, | 300 | struct symbol *map_groups__find_symbol(struct map_groups *mg, |
| 289 | enum map_type type, u64 addr, | 301 | enum map_type type, u64 addr, |
| 290 | struct map **mapp, | 302 | struct map **mapp, |
| 291 | symbol_filter_t filter) | 303 | symbol_filter_t filter) |
| 292 | { | 304 | { |
| 293 | struct map *map = map_groups__find(self, type, addr); | 305 | struct map *map = map_groups__find(mg, type, addr); |
| 294 | 306 | ||
| 295 | if (map != NULL) { | 307 | if (map != NULL) { |
| 296 | if (mapp != NULL) | 308 | if (mapp != NULL) |
| @@ -301,7 +313,7 @@ struct symbol *map_groups__find_symbol(struct map_groups *self, | |||
| 301 | return NULL; | 313 | return NULL; |
| 302 | } | 314 | } |
| 303 | 315 | ||
| 304 | struct symbol *map_groups__find_symbol_by_name(struct map_groups *self, | 316 | struct symbol *map_groups__find_symbol_by_name(struct map_groups *mg, |
| 305 | enum map_type type, | 317 | enum map_type type, |
| 306 | const char *name, | 318 | const char *name, |
| 307 | struct map **mapp, | 319 | struct map **mapp, |
| @@ -309,7 +321,7 @@ struct symbol *map_groups__find_symbol_by_name(struct map_groups *self, | |||
| 309 | { | 321 | { |
| 310 | struct rb_node *nd; | 322 | struct rb_node *nd; |
| 311 | 323 | ||
| 312 | for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) { | 324 | for (nd = rb_first(&mg->maps[type]); nd; nd = rb_next(nd)) { |
| 313 | struct map *pos = rb_entry(nd, struct map, rb_node); | 325 | struct map *pos = rb_entry(nd, struct map, rb_node); |
| 314 | struct symbol *sym = map__find_symbol_by_name(pos, name, filter); | 326 | struct symbol *sym = map__find_symbol_by_name(pos, name, filter); |
| 315 | 327 | ||
| @@ -323,13 +335,13 @@ struct symbol *map_groups__find_symbol_by_name(struct map_groups *self, | |||
| 323 | return NULL; | 335 | return NULL; |
| 324 | } | 336 | } |
| 325 | 337 | ||
| 326 | size_t __map_groups__fprintf_maps(struct map_groups *self, | 338 | size_t __map_groups__fprintf_maps(struct map_groups *mg, |
| 327 | enum map_type type, int verbose, FILE *fp) | 339 | enum map_type type, int verbose, FILE *fp) |
| 328 | { | 340 | { |
| 329 | size_t printed = fprintf(fp, "%s:\n", map_type__name[type]); | 341 | size_t printed = fprintf(fp, "%s:\n", map_type__name[type]); |
| 330 | struct rb_node *nd; | 342 | struct rb_node *nd; |
| 331 | 343 | ||
| 332 | for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) { | 344 | for (nd = rb_first(&mg->maps[type]); nd; nd = rb_next(nd)) { |
| 333 | struct map *pos = rb_entry(nd, struct map, rb_node); | 345 | struct map *pos = rb_entry(nd, struct map, rb_node); |
| 334 | printed += fprintf(fp, "Map:"); | 346 | printed += fprintf(fp, "Map:"); |
| 335 | printed += map__fprintf(pos, fp); | 347 | printed += map__fprintf(pos, fp); |
| @@ -342,22 +354,22 @@ size_t __map_groups__fprintf_maps(struct map_groups *self, | |||
| 342 | return printed; | 354 | return printed; |
| 343 | } | 355 | } |
| 344 | 356 | ||
| 345 | size_t map_groups__fprintf_maps(struct map_groups *self, int verbose, FILE *fp) | 357 | size_t map_groups__fprintf_maps(struct map_groups *mg, int verbose, FILE *fp) |
| 346 | { | 358 | { |
| 347 | size_t printed = 0, i; | 359 | size_t printed = 0, i; |
| 348 | for (i = 0; i < MAP__NR_TYPES; ++i) | 360 | for (i = 0; i < MAP__NR_TYPES; ++i) |
| 349 | printed += __map_groups__fprintf_maps(self, i, verbose, fp); | 361 | printed += __map_groups__fprintf_maps(mg, i, verbose, fp); |
| 350 | return printed; | 362 | return printed; |
| 351 | } | 363 | } |
| 352 | 364 | ||
| 353 | static size_t __map_groups__fprintf_removed_maps(struct map_groups *self, | 365 | static size_t __map_groups__fprintf_removed_maps(struct map_groups *mg, |
| 354 | enum map_type type, | 366 | enum map_type type, |
| 355 | int verbose, FILE *fp) | 367 | int verbose, FILE *fp) |
| 356 | { | 368 | { |
| 357 | struct map *pos; | 369 | struct map *pos; |
| 358 | size_t printed = 0; | 370 | size_t printed = 0; |
| 359 | 371 | ||
| 360 | list_for_each_entry(pos, &self->removed_maps[type], node) { | 372 | list_for_each_entry(pos, &mg->removed_maps[type], node) { |
| 361 | printed += fprintf(fp, "Map:"); | 373 | printed += fprintf(fp, "Map:"); |
| 362 | printed += map__fprintf(pos, fp); | 374 | printed += map__fprintf(pos, fp); |
| 363 | if (verbose > 1) { | 375 | if (verbose > 1) { |
| @@ -368,26 +380,26 @@ static size_t __map_groups__fprintf_removed_maps(struct map_groups *self, | |||
| 368 | return printed; | 380 | return printed; |
| 369 | } | 381 | } |
| 370 | 382 | ||
| 371 | static size_t map_groups__fprintf_removed_maps(struct map_groups *self, | 383 | static size_t map_groups__fprintf_removed_maps(struct map_groups *mg, |
| 372 | int verbose, FILE *fp) | 384 | int verbose, FILE *fp) |
| 373 | { | 385 | { |
| 374 | size_t printed = 0, i; | 386 | size_t printed = 0, i; |
| 375 | for (i = 0; i < MAP__NR_TYPES; ++i) | 387 | for (i = 0; i < MAP__NR_TYPES; ++i) |
| 376 | printed += __map_groups__fprintf_removed_maps(self, i, verbose, fp); | 388 | printed += __map_groups__fprintf_removed_maps(mg, i, verbose, fp); |
| 377 | return printed; | 389 | return printed; |
| 378 | } | 390 | } |
| 379 | 391 | ||
| 380 | size_t map_groups__fprintf(struct map_groups *self, int verbose, FILE *fp) | 392 | size_t map_groups__fprintf(struct map_groups *mg, int verbose, FILE *fp) |
| 381 | { | 393 | { |
| 382 | size_t printed = map_groups__fprintf_maps(self, verbose, fp); | 394 | size_t printed = map_groups__fprintf_maps(mg, verbose, fp); |
| 383 | printed += fprintf(fp, "Removed maps:\n"); | 395 | printed += fprintf(fp, "Removed maps:\n"); |
| 384 | return printed + map_groups__fprintf_removed_maps(self, verbose, fp); | 396 | return printed + map_groups__fprintf_removed_maps(mg, verbose, fp); |
| 385 | } | 397 | } |
| 386 | 398 | ||
| 387 | int map_groups__fixup_overlappings(struct map_groups *self, struct map *map, | 399 | int map_groups__fixup_overlappings(struct map_groups *mg, struct map *map, |
| 388 | int verbose, FILE *fp) | 400 | int verbose, FILE *fp) |
| 389 | { | 401 | { |
| 390 | struct rb_root *root = &self->maps[map->type]; | 402 | struct rb_root *root = &mg->maps[map->type]; |
| 391 | struct rb_node *next = rb_first(root); | 403 | struct rb_node *next = rb_first(root); |
| 392 | int err = 0; | 404 | int err = 0; |
| 393 | 405 | ||
| @@ -418,7 +430,7 @@ int map_groups__fixup_overlappings(struct map_groups *self, struct map *map, | |||
| 418 | } | 430 | } |
| 419 | 431 | ||
| 420 | before->end = map->start - 1; | 432 | before->end = map->start - 1; |
| 421 | map_groups__insert(self, before); | 433 | map_groups__insert(mg, before); |
| 422 | if (verbose >= 2) | 434 | if (verbose >= 2) |
| 423 | map__fprintf(before, fp); | 435 | map__fprintf(before, fp); |
| 424 | } | 436 | } |
| @@ -432,7 +444,7 @@ int map_groups__fixup_overlappings(struct map_groups *self, struct map *map, | |||
| 432 | } | 444 | } |
| 433 | 445 | ||
| 434 | after->start = map->end + 1; | 446 | after->start = map->end + 1; |
| 435 | map_groups__insert(self, after); | 447 | map_groups__insert(mg, after); |
| 436 | if (verbose >= 2) | 448 | if (verbose >= 2) |
| 437 | map__fprintf(after, fp); | 449 | map__fprintf(after, fp); |
| 438 | } | 450 | } |
| @@ -441,7 +453,7 @@ move_map: | |||
| 441 | * If we have references, just move them to a separate list. | 453 | * If we have references, just move them to a separate list. |
| 442 | */ | 454 | */ |
| 443 | if (pos->referenced) | 455 | if (pos->referenced) |
| 444 | list_add_tail(&pos->node, &self->removed_maps[map->type]); | 456 | list_add_tail(&pos->node, &mg->removed_maps[map->type]); |
| 445 | else | 457 | else |
| 446 | map__delete(pos); | 458 | map__delete(pos); |
| 447 | 459 | ||
| @@ -455,7 +467,7 @@ move_map: | |||
| 455 | /* | 467 | /* |
| 456 | * XXX This should not really _copy_ te maps, but refcount them. | 468 | * XXX This should not really _copy_ te maps, but refcount them. |
| 457 | */ | 469 | */ |
| 458 | int map_groups__clone(struct map_groups *self, | 470 | int map_groups__clone(struct map_groups *mg, |
| 459 | struct map_groups *parent, enum map_type type) | 471 | struct map_groups *parent, enum map_type type) |
| 460 | { | 472 | { |
| 461 | struct rb_node *nd; | 473 | struct rb_node *nd; |
| @@ -464,7 +476,7 @@ int map_groups__clone(struct map_groups *self, | |||
| 464 | struct map *new = map__clone(map); | 476 | struct map *new = map__clone(map); |
| 465 | if (new == NULL) | 477 | if (new == NULL) |
| 466 | return -ENOMEM; | 478 | return -ENOMEM; |
| 467 | map_groups__insert(self, new); | 479 | map_groups__insert(mg, new); |
| 468 | } | 480 | } |
| 469 | return 0; | 481 | return 0; |
| 470 | } | 482 | } |
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index b397c0383728..890d85545d0f 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h | |||
| @@ -123,17 +123,17 @@ void map__fixup_end(struct map *self); | |||
| 123 | 123 | ||
| 124 | void map__reloc_vmlinux(struct map *self); | 124 | void map__reloc_vmlinux(struct map *self); |
| 125 | 125 | ||
| 126 | size_t __map_groups__fprintf_maps(struct map_groups *self, | 126 | size_t __map_groups__fprintf_maps(struct map_groups *mg, |
| 127 | enum map_type type, int verbose, FILE *fp); | 127 | enum map_type type, int verbose, FILE *fp); |
| 128 | void maps__insert(struct rb_root *maps, struct map *map); | 128 | void maps__insert(struct rb_root *maps, struct map *map); |
| 129 | void maps__remove(struct rb_root *self, struct map *map); | 129 | void maps__remove(struct rb_root *maps, struct map *map); |
| 130 | struct map *maps__find(struct rb_root *maps, u64 addr); | 130 | struct map *maps__find(struct rb_root *maps, u64 addr); |
| 131 | void map_groups__init(struct map_groups *self); | 131 | void map_groups__init(struct map_groups *mg); |
| 132 | void map_groups__exit(struct map_groups *self); | 132 | void map_groups__exit(struct map_groups *mg); |
| 133 | int map_groups__clone(struct map_groups *self, | 133 | int map_groups__clone(struct map_groups *mg, |
| 134 | struct map_groups *parent, enum map_type type); | 134 | struct map_groups *parent, enum map_type type); |
| 135 | size_t map_groups__fprintf(struct map_groups *self, int verbose, FILE *fp); | 135 | size_t map_groups__fprintf(struct map_groups *mg, int verbose, FILE *fp); |
| 136 | size_t map_groups__fprintf_maps(struct map_groups *self, int verbose, FILE *fp); | 136 | size_t map_groups__fprintf_maps(struct map_groups *mg, int verbose, FILE *fp); |
| 137 | 137 | ||
| 138 | typedef void (*machine__process_t)(struct machine *self, void *data); | 138 | typedef void (*machine__process_t)(struct machine *self, void *data); |
| 139 | 139 | ||
| @@ -162,29 +162,29 @@ static inline bool machine__is_host(struct machine *self) | |||
| 162 | return self ? self->pid == HOST_KERNEL_ID : false; | 162 | return self ? self->pid == HOST_KERNEL_ID : false; |
| 163 | } | 163 | } |
| 164 | 164 | ||
| 165 | static inline void map_groups__insert(struct map_groups *self, struct map *map) | 165 | static inline void map_groups__insert(struct map_groups *mg, struct map *map) |
| 166 | { | 166 | { |
| 167 | maps__insert(&self->maps[map->type], map); | 167 | maps__insert(&mg->maps[map->type], map); |
| 168 | map->groups = self; | 168 | map->groups = mg; |
| 169 | } | 169 | } |
| 170 | 170 | ||
| 171 | static inline void map_groups__remove(struct map_groups *self, struct map *map) | 171 | static inline void map_groups__remove(struct map_groups *mg, struct map *map) |
| 172 | { | 172 | { |
| 173 | maps__remove(&self->maps[map->type], map); | 173 | maps__remove(&mg->maps[map->type], map); |
| 174 | } | 174 | } |
| 175 | 175 | ||
| 176 | static inline struct map *map_groups__find(struct map_groups *self, | 176 | static inline struct map *map_groups__find(struct map_groups *mg, |
| 177 | enum map_type type, u64 addr) | 177 | enum map_type type, u64 addr) |
| 178 | { | 178 | { |
| 179 | return maps__find(&self->maps[type], addr); | 179 | return maps__find(&mg->maps[type], addr); |
| 180 | } | 180 | } |
| 181 | 181 | ||
| 182 | struct symbol *map_groups__find_symbol(struct map_groups *self, | 182 | struct symbol *map_groups__find_symbol(struct map_groups *mg, |
| 183 | enum map_type type, u64 addr, | 183 | enum map_type type, u64 addr, |
| 184 | struct map **mapp, | 184 | struct map **mapp, |
| 185 | symbol_filter_t filter); | 185 | symbol_filter_t filter); |
| 186 | 186 | ||
| 187 | struct symbol *map_groups__find_symbol_by_name(struct map_groups *self, | 187 | struct symbol *map_groups__find_symbol_by_name(struct map_groups *mg, |
| 188 | enum map_type type, | 188 | enum map_type type, |
| 189 | const char *name, | 189 | const char *name, |
| 190 | struct map **mapp, | 190 | struct map **mapp, |
| @@ -208,11 +208,11 @@ struct symbol *machine__find_kernel_function(struct machine *self, u64 addr, | |||
| 208 | } | 208 | } |
| 209 | 209 | ||
| 210 | static inline | 210 | static inline |
| 211 | struct symbol *map_groups__find_function_by_name(struct map_groups *self, | 211 | struct symbol *map_groups__find_function_by_name(struct map_groups *mg, |
| 212 | const char *name, struct map **mapp, | 212 | const char *name, struct map **mapp, |
| 213 | symbol_filter_t filter) | 213 | symbol_filter_t filter) |
| 214 | { | 214 | { |
| 215 | return map_groups__find_symbol_by_name(self, MAP__FUNCTION, name, mapp, filter); | 215 | return map_groups__find_symbol_by_name(mg, MAP__FUNCTION, name, mapp, filter); |
| 216 | } | 216 | } |
| 217 | 217 | ||
| 218 | static inline | 218 | static inline |
| @@ -225,13 +225,13 @@ struct symbol *machine__find_kernel_function_by_name(struct machine *self, | |||
| 225 | filter); | 225 | filter); |
| 226 | } | 226 | } |
| 227 | 227 | ||
| 228 | int map_groups__fixup_overlappings(struct map_groups *self, struct map *map, | 228 | int map_groups__fixup_overlappings(struct map_groups *mg, struct map *map, |
| 229 | int verbose, FILE *fp); | 229 | int verbose, FILE *fp); |
| 230 | 230 | ||
| 231 | struct map *map_groups__find_by_name(struct map_groups *self, | 231 | struct map *map_groups__find_by_name(struct map_groups *mg, |
| 232 | enum map_type type, const char *name); | 232 | enum map_type type, const char *name); |
| 233 | struct map *machine__new_module(struct machine *self, u64 start, const char *filename); | 233 | struct map *machine__new_module(struct machine *self, u64 start, const char *filename); |
| 234 | 234 | ||
| 235 | void map_groups__flush(struct map_groups *self); | 235 | void map_groups__flush(struct map_groups *mg); |
| 236 | 236 | ||
| 237 | #endif /* __PERF_MAP_H */ | 237 | #endif /* __PERF_MAP_H */ |
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 1c7bfa5fe0a8..eb25900e2211 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
| @@ -1956,8 +1956,10 @@ static int __del_trace_probe_event(int fd, struct str_node *ent) | |||
| 1956 | 1956 | ||
| 1957 | pr_debug("Writing event: %s\n", buf); | 1957 | pr_debug("Writing event: %s\n", buf); |
| 1958 | ret = write(fd, buf, strlen(buf)); | 1958 | ret = write(fd, buf, strlen(buf)); |
| 1959 | if (ret < 0) | 1959 | if (ret < 0) { |
| 1960 | ret = -errno; | ||
| 1960 | goto error; | 1961 | goto error; |
| 1962 | } | ||
| 1961 | 1963 | ||
| 1962 | printf("Remove event: %s\n", ent->s); | 1964 | printf("Remove event: %s\n", ent->s); |
| 1963 | return 0; | 1965 | return 0; |
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 7624324efad4..9dd47a4f2596 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c | |||
| @@ -623,7 +623,11 @@ static PyObject *pyrf_evsel__open(struct pyrf_evsel *pevsel, | |||
| 623 | cpus = ((struct pyrf_cpu_map *)pcpus)->cpus; | 623 | cpus = ((struct pyrf_cpu_map *)pcpus)->cpus; |
| 624 | 624 | ||
| 625 | evsel->attr.inherit = inherit; | 625 | evsel->attr.inherit = inherit; |
| 626 | if (perf_evsel__open(evsel, cpus, threads, group) < 0) { | 626 | /* |
| 627 | * This will group just the fds for this single evsel, to group | ||
| 628 | * multiple events, use evlist.open(). | ||
| 629 | */ | ||
| 630 | if (perf_evsel__open(evsel, cpus, threads, group, NULL) < 0) { | ||
| 627 | PyErr_SetFromErrno(PyExc_OSError); | 631 | PyErr_SetFromErrno(PyExc_OSError); |
| 628 | return NULL; | 632 | return NULL; |
| 629 | } | 633 | } |
| @@ -814,6 +818,25 @@ static PyObject *pyrf_evlist__read_on_cpu(struct pyrf_evlist *pevlist, | |||
| 814 | return Py_None; | 818 | return Py_None; |
| 815 | } | 819 | } |
| 816 | 820 | ||
| 821 | static PyObject *pyrf_evlist__open(struct pyrf_evlist *pevlist, | ||
| 822 | PyObject *args, PyObject *kwargs) | ||
| 823 | { | ||
| 824 | struct perf_evlist *evlist = &pevlist->evlist; | ||
| 825 | int group = 0; | ||
| 826 | static char *kwlist[] = { "group", NULL }; | ||
| 827 | |||
| 828 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOii", kwlist, &group)) | ||
| 829 | return NULL; | ||
| 830 | |||
| 831 | if (perf_evlist__open(evlist, group) < 0) { | ||
| 832 | PyErr_SetFromErrno(PyExc_OSError); | ||
| 833 | return NULL; | ||
| 834 | } | ||
| 835 | |||
| 836 | Py_INCREF(Py_None); | ||
| 837 | return Py_None; | ||
| 838 | } | ||
| 839 | |||
| 817 | static PyMethodDef pyrf_evlist__methods[] = { | 840 | static PyMethodDef pyrf_evlist__methods[] = { |
| 818 | { | 841 | { |
| 819 | .ml_name = "mmap", | 842 | .ml_name = "mmap", |
| @@ -822,6 +845,12 @@ static PyMethodDef pyrf_evlist__methods[] = { | |||
| 822 | .ml_doc = PyDoc_STR("mmap the file descriptor table.") | 845 | .ml_doc = PyDoc_STR("mmap the file descriptor table.") |
| 823 | }, | 846 | }, |
| 824 | { | 847 | { |
| 848 | .ml_name = "open", | ||
| 849 | .ml_meth = (PyCFunction)pyrf_evlist__open, | ||
| 850 | .ml_flags = METH_VARARGS | METH_KEYWORDS, | ||
| 851 | .ml_doc = PyDoc_STR("open the file descriptors.") | ||
| 852 | }, | ||
| 853 | { | ||
| 825 | .ml_name = "poll", | 854 | .ml_name = "poll", |
| 826 | .ml_meth = (PyCFunction)pyrf_evlist__poll, | 855 | .ml_meth = (PyCFunction)pyrf_evlist__poll, |
| 827 | .ml_flags = METH_VARARGS | METH_KEYWORDS, | 856 | .ml_flags = METH_VARARGS | METH_KEYWORDS, |
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 72458d9da5b1..85c1e6b76f0a 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
| @@ -502,6 +502,7 @@ static void flush_sample_queue(struct perf_session *s, | |||
| 502 | struct perf_sample sample; | 502 | struct perf_sample sample; |
| 503 | u64 limit = os->next_flush; | 503 | u64 limit = os->next_flush; |
| 504 | u64 last_ts = os->last_sample ? os->last_sample->timestamp : 0ULL; | 504 | u64 last_ts = os->last_sample ? os->last_sample->timestamp : 0ULL; |
| 505 | unsigned idx = 0, progress_next = os->nr_samples / 16; | ||
| 505 | int ret; | 506 | int ret; |
| 506 | 507 | ||
| 507 | if (!ops->ordered_samples || !limit) | 508 | if (!ops->ordered_samples || !limit) |
| @@ -521,6 +522,11 @@ static void flush_sample_queue(struct perf_session *s, | |||
| 521 | os->last_flush = iter->timestamp; | 522 | os->last_flush = iter->timestamp; |
| 522 | list_del(&iter->list); | 523 | list_del(&iter->list); |
| 523 | list_add(&iter->list, &os->sample_cache); | 524 | list_add(&iter->list, &os->sample_cache); |
| 525 | if (++idx >= progress_next) { | ||
| 526 | progress_next += os->nr_samples / 16; | ||
| 527 | ui_progress__update(idx, os->nr_samples, | ||
| 528 | "Processing time ordered events..."); | ||
| 529 | } | ||
| 524 | } | 530 | } |
| 525 | 531 | ||
| 526 | if (list_empty(head)) { | 532 | if (list_empty(head)) { |
| @@ -529,6 +535,8 @@ static void flush_sample_queue(struct perf_session *s, | |||
| 529 | os->last_sample = | 535 | os->last_sample = |
| 530 | list_entry(head->prev, struct sample_queue, list); | 536 | list_entry(head->prev, struct sample_queue, list); |
| 531 | } | 537 | } |
| 538 | |||
| 539 | os->nr_samples = 0; | ||
| 532 | } | 540 | } |
| 533 | 541 | ||
| 534 | /* | 542 | /* |
| @@ -588,6 +596,7 @@ static void __queue_event(struct sample_queue *new, struct perf_session *s) | |||
| 588 | u64 timestamp = new->timestamp; | 596 | u64 timestamp = new->timestamp; |
| 589 | struct list_head *p; | 597 | struct list_head *p; |
| 590 | 598 | ||
| 599 | ++os->nr_samples; | ||
| 591 | os->last_sample = new; | 600 | os->last_sample = new; |
| 592 | 601 | ||
| 593 | if (!sample) { | 602 | if (!sample) { |
| @@ -738,10 +747,27 @@ static int perf_session_deliver_event(struct perf_session *session, | |||
| 738 | 747 | ||
| 739 | dump_event(session, event, file_offset, sample); | 748 | dump_event(session, event, file_offset, sample); |
| 740 | 749 | ||
| 750 | evsel = perf_evlist__id2evsel(session->evlist, sample->id); | ||
| 751 | if (evsel != NULL && event->header.type != PERF_RECORD_SAMPLE) { | ||
| 752 | /* | ||
| 753 | * XXX We're leaving PERF_RECORD_SAMPLE unnacounted here | ||
| 754 | * because the tools right now may apply filters, discarding | ||
| 755 | * some of the samples. For consistency, in the future we | ||
| 756 | * should have something like nr_filtered_samples and remove | ||
| 757 | * the sample->period from total_sample_period, etc, KISS for | ||
| 758 | * now tho. | ||
| 759 | * | ||
| 760 | * Also testing against NULL allows us to handle files without | ||
| 761 | * attr.sample_id_all and/or without PERF_SAMPLE_ID. In the | ||
| 762 | * future probably it'll be a good idea to restrict event | ||
| 763 | * processing via perf_session to files with both set. | ||
| 764 | */ | ||
| 765 | hists__inc_nr_events(&evsel->hists, event->header.type); | ||
| 766 | } | ||
| 767 | |||
| 741 | switch (event->header.type) { | 768 | switch (event->header.type) { |
| 742 | case PERF_RECORD_SAMPLE: | 769 | case PERF_RECORD_SAMPLE: |
| 743 | dump_sample(session, event, sample); | 770 | dump_sample(session, event, sample); |
| 744 | evsel = perf_evlist__id2evsel(session->evlist, sample->id); | ||
| 745 | if (evsel == NULL) { | 771 | if (evsel == NULL) { |
| 746 | ++session->hists.stats.nr_unknown_id; | 772 | ++session->hists.stats.nr_unknown_id; |
| 747 | return -1; | 773 | return -1; |
| @@ -874,11 +900,11 @@ static void perf_session__warn_about_errors(const struct perf_session *session, | |||
| 874 | const struct perf_event_ops *ops) | 900 | const struct perf_event_ops *ops) |
| 875 | { | 901 | { |
| 876 | if (ops->lost == perf_event__process_lost && | 902 | if (ops->lost == perf_event__process_lost && |
| 877 | session->hists.stats.total_lost != 0) { | 903 | session->hists.stats.nr_events[PERF_RECORD_LOST] != 0) { |
| 878 | ui__warning("Processed %" PRIu64 " events and LOST %" PRIu64 | 904 | ui__warning("Processed %d events and lost %d chunks!\n\n" |
| 879 | "!\n\nCheck IO/CPU overload!\n\n", | 905 | "Check IO/CPU overload!\n\n", |
| 880 | session->hists.stats.total_period, | 906 | session->hists.stats.nr_events[0], |
| 881 | session->hists.stats.total_lost); | 907 | session->hists.stats.nr_events[PERF_RECORD_LOST]); |
| 882 | } | 908 | } |
| 883 | 909 | ||
| 884 | if (session->hists.stats.nr_unknown_events != 0) { | 910 | if (session->hists.stats.nr_unknown_events != 0) { |
| @@ -1012,7 +1038,6 @@ int __perf_session__process_events(struct perf_session *session, | |||
| 1012 | { | 1038 | { |
| 1013 | u64 head, page_offset, file_offset, file_pos, progress_next; | 1039 | u64 head, page_offset, file_offset, file_pos, progress_next; |
| 1014 | int err, mmap_prot, mmap_flags, map_idx = 0; | 1040 | int err, mmap_prot, mmap_flags, map_idx = 0; |
| 1015 | struct ui_progress *progress; | ||
| 1016 | size_t page_size, mmap_size; | 1041 | size_t page_size, mmap_size; |
| 1017 | char *buf, *mmaps[8]; | 1042 | char *buf, *mmaps[8]; |
| 1018 | union perf_event *event; | 1043 | union perf_event *event; |
| @@ -1030,9 +1055,6 @@ int __perf_session__process_events(struct perf_session *session, | |||
| 1030 | file_size = data_offset + data_size; | 1055 | file_size = data_offset + data_size; |
| 1031 | 1056 | ||
| 1032 | progress_next = file_size / 16; | 1057 | progress_next = file_size / 16; |
| 1033 | progress = ui_progress__new("Processing events...", file_size); | ||
| 1034 | if (progress == NULL) | ||
| 1035 | return -1; | ||
| 1036 | 1058 | ||
| 1037 | mmap_size = session->mmap_window; | 1059 | mmap_size = session->mmap_window; |
| 1038 | if (mmap_size > file_size) | 1060 | if (mmap_size > file_size) |
| @@ -1095,7 +1117,8 @@ more: | |||
| 1095 | 1117 | ||
| 1096 | if (file_pos >= progress_next) { | 1118 | if (file_pos >= progress_next) { |
| 1097 | progress_next += file_size / 16; | 1119 | progress_next += file_size / 16; |
| 1098 | ui_progress__update(progress, file_pos); | 1120 | ui_progress__update(file_pos, file_size, |
| 1121 | "Processing events..."); | ||
| 1099 | } | 1122 | } |
| 1100 | 1123 | ||
| 1101 | if (file_pos < file_size) | 1124 | if (file_pos < file_size) |
| @@ -1106,7 +1129,6 @@ more: | |||
| 1106 | session->ordered_samples.next_flush = ULLONG_MAX; | 1129 | session->ordered_samples.next_flush = ULLONG_MAX; |
| 1107 | flush_sample_queue(session, ops); | 1130 | flush_sample_queue(session, ops); |
| 1108 | out_err: | 1131 | out_err: |
| 1109 | ui_progress__delete(progress); | ||
| 1110 | perf_session__warn_about_errors(session, ops); | 1132 | perf_session__warn_about_errors(session, ops); |
| 1111 | perf_session_free_sample_buffers(session); | 1133 | perf_session_free_sample_buffers(session); |
| 1112 | return err; | 1134 | return err; |
| @@ -1326,3 +1348,22 @@ int perf_session__cpu_bitmap(struct perf_session *session, | |||
| 1326 | 1348 | ||
| 1327 | return 0; | 1349 | return 0; |
| 1328 | } | 1350 | } |
| 1351 | |||
| 1352 | void perf_session__fprintf_info(struct perf_session *session, FILE *fp, | ||
| 1353 | bool full) | ||
| 1354 | { | ||
| 1355 | struct stat st; | ||
| 1356 | int ret; | ||
| 1357 | |||
| 1358 | if (session == NULL || fp == NULL) | ||
| 1359 | return; | ||
| 1360 | |||
| 1361 | ret = fstat(session->fd, &st); | ||
| 1362 | if (ret == -1) | ||
| 1363 | return; | ||
| 1364 | |||
| 1365 | fprintf(fp, "# ========\n"); | ||
| 1366 | fprintf(fp, "# captured on: %s", ctime(&st.st_ctime)); | ||
| 1367 | perf_header__fprintf_info(session, fp, full); | ||
| 1368 | fprintf(fp, "# ========\n#\n"); | ||
| 1369 | } | ||
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 974d0cbee5e9..6e393c98eb34 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h | |||
| @@ -23,6 +23,7 @@ struct ordered_samples { | |||
| 23 | struct sample_queue *sample_buffer; | 23 | struct sample_queue *sample_buffer; |
| 24 | struct sample_queue *last_sample; | 24 | struct sample_queue *last_sample; |
| 25 | int sample_buffer_idx; | 25 | int sample_buffer_idx; |
| 26 | unsigned int nr_samples; | ||
| 26 | }; | 27 | }; |
| 27 | 28 | ||
| 28 | struct perf_session { | 29 | struct perf_session { |
| @@ -177,4 +178,5 @@ void perf_session__print_ip(union perf_event *event, | |||
| 177 | int perf_session__cpu_bitmap(struct perf_session *session, | 178 | int perf_session__cpu_bitmap(struct perf_session *session, |
| 178 | const char *cpu_list, unsigned long *cpu_bitmap); | 179 | const char *cpu_list, unsigned long *cpu_bitmap); |
| 179 | 180 | ||
| 181 | void perf_session__fprintf_info(struct perf_session *s, FILE *fp, bool full); | ||
| 180 | #endif /* __PERF_SESSION_H */ | 182 | #endif /* __PERF_SESSION_H */ |
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 1ee8f1e40f18..16da30d8d765 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c | |||
| @@ -177,7 +177,9 @@ static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, | |||
| 177 | BITS_PER_LONG / 4, self->ip, o); | 177 | BITS_PER_LONG / 4, self->ip, o); |
| 178 | } | 178 | } |
| 179 | 179 | ||
| 180 | ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", self->level); | 180 | if (!sort_dso.elide) |
| 181 | ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", self->level); | ||
| 182 | |||
| 181 | if (self->ms.sym) | 183 | if (self->ms.sym) |
| 182 | ret += repsep_snprintf(bf + ret, size - ret, "%s", | 184 | ret += repsep_snprintf(bf + ret, size - ret, "%s", |
| 183 | self->ms.sym->name); | 185 | self->ms.sym->name); |
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 77d0388ad415..3f67ae395752 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; |
| @@ -63,6 +64,7 @@ struct hist_entry { | |||
| 63 | 64 | ||
| 64 | bool init_have_children; | 65 | bool init_have_children; |
| 65 | char level; | 66 | char level; |
| 67 | bool used; | ||
| 66 | u8 filtered; | 68 | u8 filtered; |
| 67 | struct symbol *parent; | 69 | struct symbol *parent; |
| 68 | union { | 70 | union { |
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 40eeaf07725b..632b50c7bc26 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
| @@ -24,7 +24,7 @@ | |||
| 24 | #include <sys/utsname.h> | 24 | #include <sys/utsname.h> |
| 25 | 25 | ||
| 26 | #ifndef KSYM_NAME_LEN | 26 | #ifndef KSYM_NAME_LEN |
| 27 | #define KSYM_NAME_LEN 128 | 27 | #define KSYM_NAME_LEN 256 |
| 28 | #endif | 28 | #endif |
| 29 | 29 | ||
| 30 | #ifndef NT_GNU_BUILD_ID | 30 | #ifndef NT_GNU_BUILD_ID |
| @@ -46,6 +46,7 @@ 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_src = true, | ||
| 49 | .symfs = "", | 50 | .symfs = "", |
| 50 | }; | 51 | }; |
| 51 | 52 | ||
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 4f377d92e75a..29f8d742e92f 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
| @@ -72,11 +72,14 @@ 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, |
| 78 | initialized, | 79 | initialized, |
| 79 | kptr_restrict; | 80 | kptr_restrict, |
| 81 | annotate_asm_raw, | ||
| 82 | annotate_src; | ||
| 80 | const char *vmlinux_name, | 83 | const char *vmlinux_name, |
| 81 | *kallsyms_name, | 84 | *kallsyms_name, |
| 82 | *source_prefix, | 85 | *source_prefix, |
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 bfbf95bcc603..399650967958 100644 --- a/tools/perf/util/top.h +++ b/tools/perf/util/top.h | |||
| @@ -4,26 +4,10 @@ | |||
| 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 | 10 | struct perf_session; | |
| 14 | struct sym_entry { | ||
| 15 | struct rb_node rb_node; | ||
| 16 | struct list_head node; | ||
| 17 | unsigned long snap_count; | ||
| 18 | double weight; | ||
| 19 | struct map *map; | ||
| 20 | unsigned long count[0]; | ||
| 21 | }; | ||
| 22 | |||
| 23 | static inline struct symbol *sym_entry__symbol(struct sym_entry *self) | ||
| 24 | { | ||
| 25 | return ((void *)self) + symbol_conf.priv_size; | ||
| 26 | } | ||
| 27 | 11 | ||
| 28 | struct perf_top { | 12 | struct perf_top { |
| 29 | struct perf_evlist *evlist; | 13 | struct perf_evlist *evlist; |
| @@ -31,34 +15,20 @@ struct perf_top { | |||
| 31 | * 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 |
| 32 | * get out after decayed. | 16 | * get out after decayed. |
| 33 | */ | 17 | */ |
| 34 | struct list_head active_symbols; | ||
| 35 | pthread_mutex_t active_symbols_lock; | ||
| 36 | pthread_cond_t active_symbols_cond; | ||
| 37 | u64 samples; | 18 | u64 samples; |
| 38 | u64 kernel_samples, us_samples; | 19 | u64 kernel_samples, us_samples; |
| 39 | u64 exact_samples; | 20 | u64 exact_samples; |
| 40 | u64 guest_us_samples, guest_kernel_samples; | 21 | u64 guest_us_samples, guest_kernel_samples; |
| 41 | int print_entries, count_filter, delay_secs; | 22 | int print_entries, count_filter, delay_secs; |
| 42 | int display_weighted, freq, rb_entries; | 23 | int freq; |
| 43 | pid_t target_pid, target_tid; | 24 | pid_t target_pid, target_tid; |
| 44 | bool hide_kernel_symbols, hide_user_symbols, zero; | 25 | bool hide_kernel_symbols, hide_user_symbols, zero; |
| 45 | const char *cpu_list; | 26 | const char *cpu_list; |
| 46 | struct sym_entry *sym_filter_entry; | 27 | struct hist_entry *sym_filter_entry; |
| 47 | struct perf_evsel *sym_evsel; | 28 | struct perf_evsel *sym_evsel; |
| 29 | struct perf_session *session; | ||
| 48 | }; | 30 | }; |
| 49 | 31 | ||
| 50 | size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size); | 32 | size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size); |
| 51 | void perf_top__reset_sample_counters(struct perf_top *top); | 33 | void perf_top__reset_sample_counters(struct perf_top *top); |
| 52 | float perf_top__decay_samples(struct perf_top *top, struct rb_root *root); | ||
| 53 | void perf_top__find_widths(struct perf_top *top, struct rb_root *root, | ||
| 54 | int *dso_width, int *dso_short_width, int *sym_width); | ||
| 55 | |||
| 56 | #ifdef NO_NEWT_SUPPORT | ||
| 57 | static inline int perf_top__tui_browser(struct perf_top *top __used) | ||
| 58 | { | ||
| 59 | return 0; | ||
| 60 | } | ||
| 61 | #else | ||
| 62 | int perf_top__tui_browser(struct perf_top *top); | ||
| 63 | #endif | ||
| 64 | #endif /* __PERF_TOP_H */ | 34 | #endif /* __PERF_TOP_H */ |
diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c index 3403f814ad72..d2655f08bcc0 100644 --- a/tools/perf/util/trace-event-info.c +++ b/tools/perf/util/trace-event-info.c | |||
| @@ -80,7 +80,7 @@ static void die(const char *fmt, ...) | |||
| 80 | int ret = errno; | 80 | int ret = errno; |
| 81 | 81 | ||
| 82 | if (errno) | 82 | if (errno) |
| 83 | perror("trace-cmd"); | 83 | perror("perf"); |
| 84 | else | 84 | else |
| 85 | ret = -1; | 85 | ret = -1; |
| 86 | 86 | ||
| @@ -196,7 +196,8 @@ static void record_file(const char *file, size_t hdr_sz) | |||
| 196 | die("Can't read '%s'", file); | 196 | die("Can't read '%s'", file); |
| 197 | 197 | ||
| 198 | /* put in zeros for file size, then fill true size later */ | 198 | /* put in zeros for file size, then fill true size later */ |
| 199 | write_or_die(&size, hdr_sz); | 199 | if (hdr_sz) |
| 200 | write_or_die(&size, hdr_sz); | ||
| 200 | 201 | ||
| 201 | do { | 202 | do { |
| 202 | r = read(fd, buf, BUFSIZ); | 203 | r = read(fd, buf, BUFSIZ); |
| @@ -212,7 +213,7 @@ static void record_file(const char *file, size_t hdr_sz) | |||
| 212 | if (bigendian()) | 213 | if (bigendian()) |
| 213 | sizep += sizeof(u64) - hdr_sz; | 214 | sizep += sizeof(u64) - hdr_sz; |
| 214 | 215 | ||
| 215 | if (pwrite(output_fd, sizep, hdr_sz, hdr_pos) < 0) | 216 | if (hdr_sz && pwrite(output_fd, sizep, hdr_sz, hdr_pos) < 0) |
| 216 | die("writing to %s", output_file); | 217 | die("writing to %s", output_file); |
| 217 | } | 218 | } |
| 218 | 219 | ||
| @@ -428,6 +429,19 @@ get_tracepoints_path(struct list_head *pattrs) | |||
| 428 | return nr_tracepoints > 0 ? path.next : NULL; | 429 | return nr_tracepoints > 0 ? path.next : NULL; |
| 429 | } | 430 | } |
| 430 | 431 | ||
| 432 | static void | ||
| 433 | put_tracepoints_path(struct tracepoint_path *tps) | ||
| 434 | { | ||
| 435 | while (tps) { | ||
| 436 | struct tracepoint_path *t = tps; | ||
| 437 | |||
| 438 | tps = tps->next; | ||
| 439 | free(t->name); | ||
| 440 | free(t->system); | ||
| 441 | free(t); | ||
| 442 | } | ||
| 443 | } | ||
| 444 | |||
| 431 | bool have_tracepoints(struct list_head *pattrs) | 445 | bool have_tracepoints(struct list_head *pattrs) |
| 432 | { | 446 | { |
| 433 | struct perf_evsel *pos; | 447 | struct perf_evsel *pos; |
| @@ -439,19 +453,11 @@ bool have_tracepoints(struct list_head *pattrs) | |||
| 439 | return false; | 453 | return false; |
| 440 | } | 454 | } |
| 441 | 455 | ||
| 442 | int read_tracing_data(int fd, struct list_head *pattrs) | 456 | static void tracing_data_header(void) |
| 443 | { | 457 | { |
| 444 | char buf[BUFSIZ]; | 458 | char buf[20]; |
| 445 | struct tracepoint_path *tps = get_tracepoints_path(pattrs); | ||
| 446 | |||
| 447 | /* | ||
| 448 | * What? No tracepoints? No sense writing anything here, bail out. | ||
| 449 | */ | ||
| 450 | if (tps == NULL) | ||
| 451 | return -1; | ||
| 452 | |||
| 453 | output_fd = fd; | ||
| 454 | 459 | ||
| 460 | /* just guessing this is someone's birthday.. ;) */ | ||
| 455 | buf[0] = 23; | 461 | buf[0] = 23; |
| 456 | buf[1] = 8; | 462 | buf[1] = 8; |
| 457 | buf[2] = 68; | 463 | buf[2] = 68; |
| @@ -476,28 +482,86 @@ int read_tracing_data(int fd, struct list_head *pattrs) | |||
| 476 | /* save page_size */ | 482 | /* save page_size */ |
| 477 | page_size = sysconf(_SC_PAGESIZE); | 483 | page_size = sysconf(_SC_PAGESIZE); |
| 478 | write_or_die(&page_size, 4); | 484 | write_or_die(&page_size, 4); |
| 485 | } | ||
| 486 | |||
| 487 | struct tracing_data *tracing_data_get(struct list_head *pattrs, | ||
| 488 | int fd, bool temp) | ||
| 489 | { | ||
| 490 | struct tracepoint_path *tps; | ||
| 491 | struct tracing_data *tdata; | ||
| 492 | |||
| 493 | output_fd = fd; | ||
| 494 | |||
| 495 | tps = get_tracepoints_path(pattrs); | ||
| 496 | if (!tps) | ||
| 497 | return NULL; | ||
| 479 | 498 | ||
| 499 | tdata = malloc_or_die(sizeof(*tdata)); | ||
| 500 | tdata->temp = temp; | ||
| 501 | tdata->size = 0; | ||
| 502 | |||
| 503 | if (temp) { | ||
| 504 | int temp_fd; | ||
| 505 | |||
| 506 | snprintf(tdata->temp_file, sizeof(tdata->temp_file), | ||
| 507 | "/tmp/perf-XXXXXX"); | ||
| 508 | if (!mkstemp(tdata->temp_file)) | ||
| 509 | die("Can't make temp file"); | ||
| 510 | |||
| 511 | temp_fd = open(tdata->temp_file, O_RDWR); | ||
| 512 | if (temp_fd < 0) | ||
| 513 | die("Can't read '%s'", tdata->temp_file); | ||
| 514 | |||
| 515 | /* | ||
| 516 | * Set the temp file the default output, so all the | ||
| 517 | * tracing data are stored into it. | ||
| 518 | */ | ||
| 519 | output_fd = temp_fd; | ||
| 520 | } | ||
| 521 | |||
| 522 | tracing_data_header(); | ||
| 480 | read_header_files(); | 523 | read_header_files(); |
| 481 | read_ftrace_files(tps); | 524 | read_ftrace_files(tps); |
| 482 | read_event_files(tps); | 525 | read_event_files(tps); |
| 483 | read_proc_kallsyms(); | 526 | read_proc_kallsyms(); |
| 484 | read_ftrace_printk(); | 527 | read_ftrace_printk(); |
| 485 | 528 | ||
| 486 | return 0; | 529 | /* |
| 530 | * All tracing data are stored by now, we can restore | ||
| 531 | * the default output file in case we used temp file. | ||
| 532 | */ | ||
| 533 | if (temp) { | ||
| 534 | tdata->size = lseek(output_fd, 0, SEEK_CUR); | ||
| 535 | close(output_fd); | ||
| 536 | output_fd = fd; | ||
| 537 | } | ||
| 538 | |||
| 539 | put_tracepoints_path(tps); | ||
| 540 | return tdata; | ||
| 487 | } | 541 | } |
| 488 | 542 | ||
| 489 | ssize_t read_tracing_data_size(int fd, struct list_head *pattrs) | 543 | void tracing_data_put(struct tracing_data *tdata) |
| 490 | { | 544 | { |
| 491 | ssize_t size; | 545 | if (tdata->temp) { |
| 492 | int err = 0; | 546 | record_file(tdata->temp_file, 0); |
| 547 | unlink(tdata->temp_file); | ||
| 548 | } | ||
| 493 | 549 | ||
| 494 | calc_data_size = 1; | 550 | free(tdata); |
| 495 | err = read_tracing_data(fd, pattrs); | 551 | } |
| 496 | size = calc_data_size - 1; | ||
| 497 | calc_data_size = 0; | ||
| 498 | 552 | ||
| 499 | if (err < 0) | 553 | int read_tracing_data(int fd, struct list_head *pattrs) |
| 500 | return err; | 554 | { |
| 555 | struct tracing_data *tdata; | ||
| 501 | 556 | ||
| 502 | return size; | 557 | /* |
| 558 | * We work over the real file, so we can write data | ||
| 559 | * directly, no temp file is needed. | ||
| 560 | */ | ||
| 561 | tdata = tracing_data_get(pattrs, fd, false); | ||
| 562 | if (!tdata) | ||
| 563 | return -ENOMEM; | ||
| 564 | |||
| 565 | tracing_data_put(tdata); | ||
| 566 | return 0; | ||
| 503 | } | 567 | } |
diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h index f674dda3363b..a84100817649 100644 --- a/tools/perf/util/trace-event.h +++ b/tools/perf/util/trace-event.h | |||
| @@ -263,7 +263,18 @@ void *raw_field_ptr(struct event *event, const char *name, void *data); | |||
| 263 | unsigned long long eval_flag(const char *flag); | 263 | unsigned long long eval_flag(const char *flag); |
| 264 | 264 | ||
| 265 | int read_tracing_data(int fd, struct list_head *pattrs); | 265 | int read_tracing_data(int fd, struct list_head *pattrs); |
| 266 | ssize_t read_tracing_data_size(int fd, struct list_head *pattrs); | 266 | |
| 267 | struct tracing_data { | ||
| 268 | /* size is only valid if temp is 'true' */ | ||
| 269 | ssize_t size; | ||
| 270 | bool temp; | ||
| 271 | char temp_file[50]; | ||
| 272 | }; | ||
| 273 | |||
| 274 | struct tracing_data *tracing_data_get(struct list_head *pattrs, | ||
| 275 | int fd, bool temp); | ||
| 276 | void tracing_data_put(struct tracing_data *tdata); | ||
| 277 | |||
| 267 | 278 | ||
| 268 | /* taken from kernel/trace/trace.h */ | 279 | /* taken from kernel/trace/trace.h */ |
| 269 | enum trace_flag_type { | 280 | enum trace_flag_type { |
diff --git a/tools/perf/util/ui/browser.c b/tools/perf/util/ui/browser.c index 611219f80680..556829124b02 100644 --- a/tools/perf/util/ui/browser.c +++ b/tools/perf/util/ui/browser.c | |||
| @@ -1,5 +1,10 @@ | |||
| 1 | #include "../util.h" | ||
| 2 | #include "../cache.h" | ||
| 3 | #include "../../perf.h" | ||
| 1 | #include "libslang.h" | 4 | #include "libslang.h" |
| 5 | #include <newt.h> | ||
| 2 | #include "ui.h" | 6 | #include "ui.h" |
| 7 | #include "util.h" | ||
| 3 | #include <linux/compiler.h> | 8 | #include <linux/compiler.h> |
| 4 | #include <linux/list.h> | 9 | #include <linux/list.h> |
| 5 | #include <linux/rbtree.h> | 10 | #include <linux/rbtree.h> |
| @@ -7,13 +12,13 @@ | |||
| 7 | #include <sys/ttydefaults.h> | 12 | #include <sys/ttydefaults.h> |
| 8 | #include "browser.h" | 13 | #include "browser.h" |
| 9 | #include "helpline.h" | 14 | #include "helpline.h" |
| 15 | #include "keysyms.h" | ||
| 10 | #include "../color.h" | 16 | #include "../color.h" |
| 11 | #include "../util.h" | ||
| 12 | #include <stdio.h> | ||
| 13 | 17 | ||
| 14 | static int ui_browser__percent_color(double percent, bool current) | 18 | static int ui_browser__percent_color(struct ui_browser *browser, |
| 19 | double percent, bool current) | ||
| 15 | { | 20 | { |
| 16 | if (current) | 21 | if (current && (!browser->use_navkeypressed || browser->navkeypressed)) |
| 17 | return HE_COLORSET_SELECTED; | 22 | return HE_COLORSET_SELECTED; |
| 18 | if (percent >= MIN_RED) | 23 | if (percent >= MIN_RED) |
| 19 | return HE_COLORSET_TOP; | 24 | return HE_COLORSET_TOP; |
| @@ -30,7 +35,7 @@ void ui_browser__set_color(struct ui_browser *self __used, int color) | |||
| 30 | void ui_browser__set_percent_color(struct ui_browser *self, | 35 | void ui_browser__set_percent_color(struct ui_browser *self, |
| 31 | double percent, bool current) | 36 | double percent, bool current) |
| 32 | { | 37 | { |
| 33 | int color = ui_browser__percent_color(percent, current); | 38 | int color = ui_browser__percent_color(self, percent, current); |
| 34 | ui_browser__set_color(self, color); | 39 | ui_browser__set_color(self, color); |
| 35 | } | 40 | } |
| 36 | 41 | ||
| @@ -39,31 +44,62 @@ void ui_browser__gotorc(struct ui_browser *self, int y, int x) | |||
| 39 | SLsmg_gotorc(self->y + y, self->x + x); | 44 | SLsmg_gotorc(self->y + y, self->x + x); |
| 40 | } | 45 | } |
| 41 | 46 | ||
| 47 | static struct list_head * | ||
| 48 | ui_browser__list_head_filter_entries(struct ui_browser *browser, | ||
| 49 | struct list_head *pos) | ||
| 50 | { | ||
| 51 | do { | ||
| 52 | if (!browser->filter || !browser->filter(browser, pos)) | ||
| 53 | return pos; | ||
| 54 | pos = pos->next; | ||
| 55 | } while (pos != browser->entries); | ||
| 56 | |||
| 57 | return NULL; | ||
| 58 | } | ||
| 59 | |||
| 60 | static struct list_head * | ||
| 61 | ui_browser__list_head_filter_prev_entries(struct ui_browser *browser, | ||
| 62 | struct list_head *pos) | ||
| 63 | { | ||
| 64 | do { | ||
| 65 | if (!browser->filter || !browser->filter(browser, pos)) | ||
| 66 | return pos; | ||
| 67 | pos = pos->prev; | ||
| 68 | } while (pos != browser->entries); | ||
| 69 | |||
| 70 | return NULL; | ||
| 71 | } | ||
| 72 | |||
| 42 | void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whence) | 73 | void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whence) |
| 43 | { | 74 | { |
| 44 | struct list_head *head = self->entries; | 75 | struct list_head *head = self->entries; |
| 45 | struct list_head *pos; | 76 | struct list_head *pos; |
| 46 | 77 | ||
| 78 | if (self->nr_entries == 0) | ||
| 79 | return; | ||
| 80 | |||
| 47 | switch (whence) { | 81 | switch (whence) { |
| 48 | case SEEK_SET: | 82 | case SEEK_SET: |
| 49 | pos = head->next; | 83 | pos = ui_browser__list_head_filter_entries(self, head->next); |
| 50 | break; | 84 | break; |
| 51 | case SEEK_CUR: | 85 | case SEEK_CUR: |
| 52 | pos = self->top; | 86 | pos = self->top; |
| 53 | break; | 87 | break; |
| 54 | case SEEK_END: | 88 | case SEEK_END: |
| 55 | pos = head->prev; | 89 | pos = ui_browser__list_head_filter_prev_entries(self, head->prev); |
| 56 | break; | 90 | break; |
| 57 | default: | 91 | default: |
| 58 | return; | 92 | return; |
| 59 | } | 93 | } |
| 60 | 94 | ||
| 95 | assert(pos != NULL); | ||
| 96 | |||
| 61 | if (offset > 0) { | 97 | if (offset > 0) { |
| 62 | while (offset-- != 0) | 98 | while (offset-- != 0) |
| 63 | pos = pos->next; | 99 | pos = ui_browser__list_head_filter_entries(self, pos->next); |
| 64 | } else { | 100 | } else { |
| 65 | while (offset++ != 0) | 101 | while (offset++ != 0) |
| 66 | pos = pos->prev; | 102 | pos = ui_browser__list_head_filter_prev_entries(self, pos->prev); |
| 67 | } | 103 | } |
| 68 | 104 | ||
| 69 | self->top = pos; | 105 | self->top = pos; |
| @@ -127,41 +163,76 @@ bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row) | |||
| 127 | 163 | ||
| 128 | void ui_browser__refresh_dimensions(struct ui_browser *self) | 164 | void ui_browser__refresh_dimensions(struct ui_browser *self) |
| 129 | { | 165 | { |
| 130 | int cols, rows; | 166 | self->width = SLtt_Screen_Cols - 1; |
| 131 | newtGetScreenSize(&cols, &rows); | 167 | self->height = SLtt_Screen_Rows - 2; |
| 132 | |||
| 133 | self->width = cols - 1; | ||
| 134 | self->height = rows - 2; | ||
| 135 | self->y = 1; | 168 | self->y = 1; |
| 136 | self->x = 0; | 169 | self->x = 0; |
| 137 | } | 170 | } |
| 138 | 171 | ||
| 139 | void ui_browser__reset_index(struct ui_browser *self) | 172 | void ui_browser__handle_resize(struct ui_browser *browser) |
| 140 | { | 173 | { |
| 141 | self->index = self->top_idx = 0; | 174 | ui__refresh_dimensions(false); |
| 142 | self->seek(self, 0, SEEK_SET); | 175 | ui_browser__show(browser, browser->title, ui_helpline__current); |
| 176 | ui_browser__refresh(browser); | ||
| 143 | } | 177 | } |
| 144 | 178 | ||
| 145 | void ui_browser__add_exit_key(struct ui_browser *self, int key) | 179 | int ui_browser__warning(struct ui_browser *browser, int timeout, |
| 180 | const char *format, ...) | ||
| 146 | { | 181 | { |
| 147 | newtFormAddHotKey(self->form, key); | 182 | va_list args; |
| 183 | char *text; | ||
| 184 | int key = 0, err; | ||
| 185 | |||
| 186 | va_start(args, format); | ||
| 187 | err = vasprintf(&text, format, args); | ||
| 188 | va_end(args); | ||
| 189 | |||
| 190 | if (err < 0) { | ||
| 191 | va_start(args, format); | ||
| 192 | ui_helpline__vpush(format, args); | ||
| 193 | va_end(args); | ||
| 194 | } else { | ||
| 195 | while ((key == ui__question_window("Warning!", text, | ||
| 196 | "Press any key...", | ||
| 197 | timeout)) == K_RESIZE) | ||
| 198 | ui_browser__handle_resize(browser); | ||
| 199 | free(text); | ||
| 200 | } | ||
| 201 | |||
| 202 | return key; | ||
| 148 | } | 203 | } |
| 149 | 204 | ||
| 150 | void ui_browser__add_exit_keys(struct ui_browser *self, int keys[]) | 205 | int ui_browser__help_window(struct ui_browser *browser, const char *text) |
| 151 | { | 206 | { |
| 152 | int i = 0; | 207 | int key; |
| 153 | 208 | ||
| 154 | while (keys[i] && i < 64) { | 209 | while ((key = ui__help_window(text)) == K_RESIZE) |
| 155 | ui_browser__add_exit_key(self, keys[i]); | 210 | ui_browser__handle_resize(browser); |
| 156 | ++i; | 211 | |
| 157 | } | 212 | return key; |
| 213 | } | ||
| 214 | |||
| 215 | bool ui_browser__dialog_yesno(struct ui_browser *browser, const char *text) | ||
| 216 | { | ||
| 217 | int key; | ||
| 218 | |||
| 219 | while ((key = ui__dialog_yesno(text)) == K_RESIZE) | ||
| 220 | ui_browser__handle_resize(browser); | ||
| 221 | |||
| 222 | return key == K_ENTER || toupper(key) == 'Y'; | ||
| 223 | } | ||
| 224 | |||
| 225 | void ui_browser__reset_index(struct ui_browser *self) | ||
| 226 | { | ||
| 227 | self->index = self->top_idx = 0; | ||
| 228 | self->seek(self, 0, SEEK_SET); | ||
| 158 | } | 229 | } |
| 159 | 230 | ||
| 160 | void __ui_browser__show_title(struct ui_browser *browser, const char *title) | 231 | void __ui_browser__show_title(struct ui_browser *browser, const char *title) |
| 161 | { | 232 | { |
| 162 | SLsmg_gotorc(0, 0); | 233 | SLsmg_gotorc(0, 0); |
| 163 | ui_browser__set_color(browser, NEWT_COLORSET_ROOT); | 234 | ui_browser__set_color(browser, NEWT_COLORSET_ROOT); |
| 164 | slsmg_write_nstring(title, browser->width); | 235 | slsmg_write_nstring(title, browser->width + 1); |
| 165 | } | 236 | } |
| 166 | 237 | ||
| 167 | void ui_browser__show_title(struct ui_browser *browser, const char *title) | 238 | void ui_browser__show_title(struct ui_browser *browser, const char *title) |
| @@ -174,78 +245,145 @@ void ui_browser__show_title(struct ui_browser *browser, const char *title) | |||
| 174 | int ui_browser__show(struct ui_browser *self, const char *title, | 245 | int ui_browser__show(struct ui_browser *self, const char *title, |
| 175 | const char *helpline, ...) | 246 | const char *helpline, ...) |
| 176 | { | 247 | { |
| 248 | int err; | ||
| 177 | va_list ap; | 249 | va_list ap; |
| 178 | int keys[] = { NEWT_KEY_UP, NEWT_KEY_DOWN, NEWT_KEY_PGUP, | ||
| 179 | NEWT_KEY_PGDN, NEWT_KEY_HOME, NEWT_KEY_END, ' ', | ||
| 180 | NEWT_KEY_LEFT, NEWT_KEY_ESCAPE, 'q', CTRL('c'), 0 }; | ||
| 181 | |||
| 182 | if (self->form != NULL) | ||
| 183 | newtFormDestroy(self->form); | ||
| 184 | 250 | ||
| 185 | ui_browser__refresh_dimensions(self); | 251 | ui_browser__refresh_dimensions(self); |
| 186 | self->form = newtForm(NULL, NULL, 0); | ||
| 187 | if (self->form == NULL) | ||
| 188 | return -1; | ||
| 189 | |||
| 190 | self->sb = newtVerticalScrollbar(self->width, 1, self->height, | ||
| 191 | HE_COLORSET_NORMAL, | ||
| 192 | HE_COLORSET_SELECTED); | ||
| 193 | if (self->sb == NULL) | ||
| 194 | return -1; | ||
| 195 | 252 | ||
| 196 | pthread_mutex_lock(&ui__lock); | 253 | pthread_mutex_lock(&ui__lock); |
| 197 | __ui_browser__show_title(self, title); | 254 | __ui_browser__show_title(self, title); |
| 198 | 255 | ||
| 199 | ui_browser__add_exit_keys(self, keys); | 256 | self->title = title; |
| 200 | newtFormAddComponent(self->form, self->sb); | 257 | free(self->helpline); |
| 258 | self->helpline = NULL; | ||
| 201 | 259 | ||
| 202 | va_start(ap, helpline); | 260 | va_start(ap, helpline); |
| 203 | ui_helpline__vpush(helpline, ap); | 261 | err = vasprintf(&self->helpline, helpline, ap); |
| 204 | va_end(ap); | 262 | va_end(ap); |
| 263 | if (err > 0) | ||
| 264 | ui_helpline__push(self->helpline); | ||
| 205 | pthread_mutex_unlock(&ui__lock); | 265 | pthread_mutex_unlock(&ui__lock); |
| 206 | return 0; | 266 | return err ? 0 : -1; |
| 207 | } | 267 | } |
| 208 | 268 | ||
| 209 | void ui_browser__hide(struct ui_browser *self) | 269 | void ui_browser__hide(struct ui_browser *browser __used) |
| 210 | { | 270 | { |
| 211 | pthread_mutex_lock(&ui__lock); | 271 | pthread_mutex_lock(&ui__lock); |
| 212 | newtFormDestroy(self->form); | ||
| 213 | self->form = NULL; | ||
| 214 | ui_helpline__pop(); | 272 | ui_helpline__pop(); |
| 215 | pthread_mutex_unlock(&ui__lock); | 273 | pthread_mutex_unlock(&ui__lock); |
| 216 | } | 274 | } |
| 217 | 275 | ||
| 218 | int ui_browser__refresh(struct ui_browser *self) | 276 | static void ui_browser__scrollbar_set(struct ui_browser *browser) |
| 277 | { | ||
| 278 | int height = browser->height, h = 0, pct = 0, | ||
| 279 | col = browser->width, | ||
| 280 | row = browser->y - 1; | ||
| 281 | |||
| 282 | if (browser->nr_entries > 1) { | ||
| 283 | pct = ((browser->index * (browser->height - 1)) / | ||
| 284 | (browser->nr_entries - 1)); | ||
| 285 | } | ||
| 286 | |||
| 287 | SLsmg_set_char_set(1); | ||
| 288 | |||
| 289 | while (h < height) { | ||
| 290 | ui_browser__gotorc(browser, row++, col); | ||
| 291 | SLsmg_write_char(h == pct ? SLSMG_DIAMOND_CHAR : SLSMG_CKBRD_CHAR); | ||
| 292 | ++h; | ||
| 293 | } | ||
| 294 | |||
| 295 | SLsmg_set_char_set(0); | ||
| 296 | } | ||
| 297 | |||
| 298 | static int __ui_browser__refresh(struct ui_browser *browser) | ||
| 219 | { | 299 | { |
| 220 | int row; | 300 | int row; |
| 301 | int width = browser->width; | ||
| 302 | |||
| 303 | row = browser->refresh(browser); | ||
| 304 | ui_browser__set_color(browser, HE_COLORSET_NORMAL); | ||
| 305 | |||
| 306 | if (!browser->use_navkeypressed || browser->navkeypressed) | ||
| 307 | ui_browser__scrollbar_set(browser); | ||
| 308 | else | ||
| 309 | width += 1; | ||
| 310 | |||
| 311 | SLsmg_fill_region(browser->y + row, browser->x, | ||
| 312 | browser->height - row, width, ' '); | ||
| 313 | |||
| 314 | return 0; | ||
| 315 | } | ||
| 221 | 316 | ||
| 317 | int ui_browser__refresh(struct ui_browser *browser) | ||
| 318 | { | ||
| 222 | pthread_mutex_lock(&ui__lock); | 319 | pthread_mutex_lock(&ui__lock); |
| 223 | newtScrollbarSet(self->sb, self->index, self->nr_entries - 1); | 320 | __ui_browser__refresh(browser); |
| 224 | row = self->refresh(self); | ||
| 225 | ui_browser__set_color(self, HE_COLORSET_NORMAL); | ||
| 226 | SLsmg_fill_region(self->y + row, self->x, | ||
| 227 | self->height - row, self->width, ' '); | ||
| 228 | pthread_mutex_unlock(&ui__lock); | 321 | pthread_mutex_unlock(&ui__lock); |
| 229 | 322 | ||
| 230 | return 0; | 323 | return 0; |
| 231 | } | 324 | } |
| 232 | 325 | ||
| 233 | int ui_browser__run(struct ui_browser *self) | 326 | /* |
| 327 | * Here we're updating nr_entries _after_ we started browsing, i.e. we have to | ||
| 328 | * forget about any reference to any entry in the underlying data structure, | ||
| 329 | * that is why we do a SEEK_SET. Think about 'perf top' in the hists browser | ||
| 330 | * after an output_resort and hist decay. | ||
| 331 | */ | ||
| 332 | void ui_browser__update_nr_entries(struct ui_browser *browser, u32 nr_entries) | ||
| 234 | { | 333 | { |
| 235 | struct newtExitStruct es; | 334 | off_t offset = nr_entries - browser->nr_entries; |
| 335 | |||
| 336 | browser->nr_entries = nr_entries; | ||
| 236 | 337 | ||
| 237 | if (ui_browser__refresh(self) < 0) | 338 | if (offset < 0) { |
| 238 | return -1; | 339 | if (browser->top_idx < (u64)-offset) |
| 340 | offset = -browser->top_idx; | ||
| 341 | |||
| 342 | browser->index += offset; | ||
| 343 | browser->top_idx += offset; | ||
| 344 | } | ||
| 345 | |||
| 346 | browser->top = NULL; | ||
| 347 | browser->seek(browser, browser->top_idx, SEEK_SET); | ||
| 348 | } | ||
| 349 | |||
| 350 | int ui_browser__run(struct ui_browser *self, int delay_secs) | ||
| 351 | { | ||
| 352 | int err, key; | ||
| 239 | 353 | ||
| 240 | while (1) { | 354 | while (1) { |
| 241 | off_t offset; | 355 | off_t offset; |
| 242 | 356 | ||
| 243 | newtFormRun(self->form, &es); | 357 | pthread_mutex_lock(&ui__lock); |
| 244 | 358 | err = __ui_browser__refresh(self); | |
| 245 | if (es.reason != NEWT_EXIT_HOTKEY) | 359 | SLsmg_refresh(); |
| 360 | pthread_mutex_unlock(&ui__lock); | ||
| 361 | if (err < 0) | ||
| 246 | break; | 362 | break; |
| 247 | switch (es.u.key) { | 363 | |
| 248 | case NEWT_KEY_DOWN: | 364 | key = ui__getch(delay_secs); |
| 365 | |||
| 366 | if (key == K_RESIZE) { | ||
| 367 | ui__refresh_dimensions(false); | ||
| 368 | ui_browser__refresh_dimensions(self); | ||
| 369 | __ui_browser__show_title(self, self->title); | ||
| 370 | ui_helpline__puts(self->helpline); | ||
| 371 | continue; | ||
| 372 | } | ||
| 373 | |||
| 374 | if (self->use_navkeypressed && !self->navkeypressed) { | ||
| 375 | if (key == K_DOWN || key == K_UP || | ||
| 376 | key == K_PGDN || key == K_PGUP || | ||
| 377 | key == K_HOME || key == K_END || | ||
| 378 | key == ' ') { | ||
| 379 | self->navkeypressed = true; | ||
| 380 | continue; | ||
| 381 | } else | ||
| 382 | return key; | ||
| 383 | } | ||
| 384 | |||
| 385 | switch (key) { | ||
| 386 | case K_DOWN: | ||
| 249 | if (self->index == self->nr_entries - 1) | 387 | if (self->index == self->nr_entries - 1) |
| 250 | break; | 388 | break; |
| 251 | ++self->index; | 389 | ++self->index; |
| @@ -254,7 +392,7 @@ int ui_browser__run(struct ui_browser *self) | |||
| 254 | self->seek(self, +1, SEEK_CUR); | 392 | self->seek(self, +1, SEEK_CUR); |
| 255 | } | 393 | } |
| 256 | break; | 394 | break; |
| 257 | case NEWT_KEY_UP: | 395 | case K_UP: |
| 258 | if (self->index == 0) | 396 | if (self->index == 0) |
| 259 | break; | 397 | break; |
| 260 | --self->index; | 398 | --self->index; |
| @@ -263,7 +401,7 @@ int ui_browser__run(struct ui_browser *self) | |||
| 263 | self->seek(self, -1, SEEK_CUR); | 401 | self->seek(self, -1, SEEK_CUR); |
| 264 | } | 402 | } |
| 265 | break; | 403 | break; |
| 266 | case NEWT_KEY_PGDN: | 404 | case K_PGDN: |
| 267 | case ' ': | 405 | case ' ': |
| 268 | if (self->top_idx + self->height > self->nr_entries - 1) | 406 | if (self->top_idx + self->height > self->nr_entries - 1) |
| 269 | break; | 407 | break; |
| @@ -275,7 +413,7 @@ int ui_browser__run(struct ui_browser *self) | |||
| 275 | self->top_idx += offset; | 413 | self->top_idx += offset; |
| 276 | self->seek(self, +offset, SEEK_CUR); | 414 | self->seek(self, +offset, SEEK_CUR); |
| 277 | break; | 415 | break; |
| 278 | case NEWT_KEY_PGUP: | 416 | case K_PGUP: |
| 279 | if (self->top_idx == 0) | 417 | if (self->top_idx == 0) |
| 280 | break; | 418 | break; |
| 281 | 419 | ||
| @@ -288,10 +426,10 @@ int ui_browser__run(struct ui_browser *self) | |||
| 288 | self->top_idx -= offset; | 426 | self->top_idx -= offset; |
| 289 | self->seek(self, -offset, SEEK_CUR); | 427 | self->seek(self, -offset, SEEK_CUR); |
| 290 | break; | 428 | break; |
| 291 | case NEWT_KEY_HOME: | 429 | case K_HOME: |
| 292 | ui_browser__reset_index(self); | 430 | ui_browser__reset_index(self); |
| 293 | break; | 431 | break; |
| 294 | case NEWT_KEY_END: | 432 | case K_END: |
| 295 | offset = self->height - 1; | 433 | offset = self->height - 1; |
| 296 | if (offset >= self->nr_entries) | 434 | if (offset >= self->nr_entries) |
| 297 | offset = self->nr_entries - 1; | 435 | offset = self->nr_entries - 1; |
| @@ -301,10 +439,8 @@ int ui_browser__run(struct ui_browser *self) | |||
| 301 | self->seek(self, -offset, SEEK_END); | 439 | self->seek(self, -offset, SEEK_END); |
| 302 | break; | 440 | break; |
| 303 | default: | 441 | default: |
| 304 | return es.u.key; | 442 | return key; |
| 305 | } | 443 | } |
| 306 | if (ui_browser__refresh(self) < 0) | ||
| 307 | return -1; | ||
| 308 | } | 444 | } |
| 309 | return -1; | 445 | return -1; |
| 310 | } | 446 | } |
| @@ -316,41 +452,146 @@ unsigned int ui_browser__list_head_refresh(struct ui_browser *self) | |||
| 316 | int row = 0; | 452 | int row = 0; |
| 317 | 453 | ||
| 318 | if (self->top == NULL || self->top == self->entries) | 454 | if (self->top == NULL || self->top == self->entries) |
| 319 | self->top = head->next; | 455 | self->top = ui_browser__list_head_filter_entries(self, head->next); |
| 320 | 456 | ||
| 321 | pos = self->top; | 457 | pos = self->top; |
| 322 | 458 | ||
| 323 | list_for_each_from(pos, head) { | 459 | list_for_each_from(pos, head) { |
| 324 | ui_browser__gotorc(self, row, 0); | 460 | if (!self->filter || !self->filter(self, pos)) { |
| 325 | self->write(self, pos, row); | 461 | ui_browser__gotorc(self, row, 0); |
| 326 | if (++row == self->height) | 462 | self->write(self, pos, row); |
| 327 | break; | 463 | if (++row == self->height) |
| 464 | break; | ||
| 465 | } | ||
| 328 | } | 466 | } |
| 329 | 467 | ||
| 330 | return row; | 468 | return row; |
| 331 | } | 469 | } |
| 332 | 470 | ||
| 333 | static struct newtPercentTreeColors { | 471 | static struct ui_browser__colorset { |
| 334 | const char *topColorFg, *topColorBg; | 472 | const char *name, *fg, *bg; |
| 335 | const char *mediumColorFg, *mediumColorBg; | 473 | int colorset; |
| 336 | const char *normalColorFg, *normalColorBg; | 474 | } ui_browser__colorsets[] = { |
| 337 | const char *selColorFg, *selColorBg; | 475 | { |
| 338 | const char *codeColorFg, *codeColorBg; | 476 | .colorset = HE_COLORSET_TOP, |
| 339 | } defaultPercentTreeColors = { | 477 | .name = "top", |
| 340 | "red", "lightgray", | 478 | .fg = "red", |
| 341 | "green", "lightgray", | 479 | .bg = "default", |
| 342 | "black", "lightgray", | 480 | }, |
| 343 | "lightgray", "magenta", | 481 | { |
| 344 | "blue", "lightgray", | 482 | .colorset = HE_COLORSET_MEDIUM, |
| 483 | .name = "medium", | ||
| 484 | .fg = "green", | ||
| 485 | .bg = "default", | ||
| 486 | }, | ||
| 487 | { | ||
| 488 | .colorset = HE_COLORSET_NORMAL, | ||
| 489 | .name = "normal", | ||
| 490 | .fg = "default", | ||
| 491 | .bg = "default", | ||
| 492 | }, | ||
| 493 | { | ||
| 494 | .colorset = HE_COLORSET_SELECTED, | ||
| 495 | .name = "selected", | ||
| 496 | .fg = "black", | ||
| 497 | .bg = "lightgray", | ||
| 498 | }, | ||
| 499 | { | ||
| 500 | .colorset = HE_COLORSET_CODE, | ||
| 501 | .name = "code", | ||
| 502 | .fg = "blue", | ||
| 503 | .bg = "default", | ||
| 504 | }, | ||
| 505 | { | ||
| 506 | .name = NULL, | ||
| 507 | } | ||
| 345 | }; | 508 | }; |
| 346 | 509 | ||
| 510 | |||
| 511 | static int ui_browser__color_config(const char *var, const char *value, | ||
| 512 | void *data __used) | ||
| 513 | { | ||
| 514 | char *fg = NULL, *bg; | ||
| 515 | int i; | ||
| 516 | |||
| 517 | /* same dir for all commands */ | ||
| 518 | if (prefixcmp(var, "colors.") != 0) | ||
| 519 | return 0; | ||
| 520 | |||
| 521 | for (i = 0; ui_browser__colorsets[i].name != NULL; ++i) { | ||
| 522 | const char *name = var + 7; | ||
| 523 | |||
| 524 | if (strcmp(ui_browser__colorsets[i].name, name) != 0) | ||
| 525 | continue; | ||
| 526 | |||
| 527 | fg = strdup(value); | ||
| 528 | if (fg == NULL) | ||
| 529 | break; | ||
| 530 | |||
| 531 | bg = strchr(fg, ','); | ||
| 532 | if (bg == NULL) | ||
| 533 | break; | ||
| 534 | |||
| 535 | *bg = '\0'; | ||
| 536 | while (isspace(*++bg)); | ||
| 537 | ui_browser__colorsets[i].bg = bg; | ||
| 538 | ui_browser__colorsets[i].fg = fg; | ||
| 539 | return 0; | ||
| 540 | } | ||
| 541 | |||
| 542 | free(fg); | ||
| 543 | return -1; | ||
| 544 | } | ||
| 545 | |||
| 546 | void ui_browser__argv_seek(struct ui_browser *browser, off_t offset, int whence) | ||
| 547 | { | ||
| 548 | switch (whence) { | ||
| 549 | case SEEK_SET: | ||
| 550 | browser->top = browser->entries; | ||
| 551 | break; | ||
| 552 | case SEEK_CUR: | ||
| 553 | browser->top = browser->top + browser->top_idx + offset; | ||
| 554 | break; | ||
| 555 | case SEEK_END: | ||
| 556 | browser->top = browser->top + browser->nr_entries + offset; | ||
| 557 | break; | ||
| 558 | default: | ||
| 559 | return; | ||
| 560 | } | ||
| 561 | } | ||
| 562 | |||
| 563 | unsigned int ui_browser__argv_refresh(struct ui_browser *browser) | ||
| 564 | { | ||
| 565 | unsigned int row = 0, idx = browser->top_idx; | ||
| 566 | char **pos; | ||
| 567 | |||
| 568 | if (browser->top == NULL) | ||
| 569 | browser->top = browser->entries; | ||
| 570 | |||
| 571 | pos = (char **)browser->top; | ||
| 572 | while (idx < browser->nr_entries) { | ||
| 573 | if (!browser->filter || !browser->filter(browser, *pos)) { | ||
| 574 | ui_browser__gotorc(browser, row, 0); | ||
| 575 | browser->write(browser, pos, row); | ||
| 576 | if (++row == browser->height) | ||
| 577 | break; | ||
| 578 | } | ||
| 579 | |||
| 580 | ++idx; | ||
| 581 | ++pos; | ||
| 582 | } | ||
| 583 | |||
| 584 | return row; | ||
| 585 | } | ||
| 586 | |||
| 347 | void ui_browser__init(void) | 587 | void ui_browser__init(void) |
| 348 | { | 588 | { |
| 349 | struct newtPercentTreeColors *c = &defaultPercentTreeColors; | 589 | int i = 0; |
| 590 | |||
| 591 | perf_config(ui_browser__color_config, NULL); | ||
| 350 | 592 | ||
| 351 | sltt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg); | 593 | while (ui_browser__colorsets[i].name) { |
| 352 | sltt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg); | 594 | struct ui_browser__colorset *c = &ui_browser__colorsets[i++]; |
| 353 | sltt_set_color(HE_COLORSET_NORMAL, NULL, c->normalColorFg, c->normalColorBg); | 595 | sltt_set_color(c->colorset, c->name, c->fg, c->bg); |
| 354 | sltt_set_color(HE_COLORSET_SELECTED, NULL, c->selColorFg, c->selColorBg); | 596 | } |
| 355 | sltt_set_color(HE_COLORSET_CODE, NULL, c->codeColorFg, c->codeColorBg); | ||
| 356 | } | 597 | } |
diff --git a/tools/perf/util/ui/browser.h b/tools/perf/util/ui/browser.h index fc63dda10910..84d761b730c1 100644 --- a/tools/perf/util/ui/browser.h +++ b/tools/perf/util/ui/browser.h | |||
| @@ -2,7 +2,6 @@ | |||
| 2 | #define _PERF_UI_BROWSER_H_ 1 | 2 | #define _PERF_UI_BROWSER_H_ 1 |
| 3 | 3 | ||
| 4 | #include <stdbool.h> | 4 | #include <stdbool.h> |
| 5 | #include <newt.h> | ||
| 6 | #include <sys/types.h> | 5 | #include <sys/types.h> |
| 7 | #include "../types.h" | 6 | #include "../types.h" |
| 8 | 7 | ||
| @@ -13,15 +12,19 @@ | |||
| 13 | #define HE_COLORSET_CODE 54 | 12 | #define HE_COLORSET_CODE 54 |
| 14 | 13 | ||
| 15 | struct ui_browser { | 14 | struct ui_browser { |
| 16 | newtComponent form, sb; | ||
| 17 | u64 index, top_idx; | 15 | u64 index, top_idx; |
| 18 | void *top, *entries; | 16 | void *top, *entries; |
| 19 | u16 y, x, width, height; | 17 | u16 y, x, width, height; |
| 20 | void *priv; | 18 | void *priv; |
| 19 | const char *title; | ||
| 20 | char *helpline; | ||
| 21 | unsigned int (*refresh)(struct ui_browser *self); | 21 | unsigned int (*refresh)(struct ui_browser *self); |
| 22 | void (*write)(struct ui_browser *self, void *entry, int row); | 22 | void (*write)(struct ui_browser *self, void *entry, int row); |
| 23 | void (*seek)(struct ui_browser *self, off_t offset, int whence); | 23 | void (*seek)(struct ui_browser *self, off_t offset, int whence); |
| 24 | bool (*filter)(struct ui_browser *self, void *entry); | ||
| 24 | u32 nr_entries; | 25 | u32 nr_entries; |
| 26 | bool navkeypressed; | ||
| 27 | bool use_navkeypressed; | ||
| 25 | }; | 28 | }; |
| 26 | 29 | ||
| 27 | void ui_browser__set_color(struct ui_browser *self, int color); | 30 | void ui_browser__set_color(struct ui_browser *self, int color); |
| @@ -32,15 +35,23 @@ void ui_browser__refresh_dimensions(struct ui_browser *self); | |||
| 32 | void ui_browser__reset_index(struct ui_browser *self); | 35 | void ui_browser__reset_index(struct ui_browser *self); |
| 33 | 36 | ||
| 34 | void ui_browser__gotorc(struct ui_browser *self, int y, int x); | 37 | void ui_browser__gotorc(struct ui_browser *self, int y, int x); |
| 35 | void ui_browser__add_exit_key(struct ui_browser *self, int key); | ||
| 36 | void ui_browser__add_exit_keys(struct ui_browser *self, int keys[]); | ||
| 37 | void __ui_browser__show_title(struct ui_browser *browser, const char *title); | 38 | void __ui_browser__show_title(struct ui_browser *browser, const char *title); |
| 38 | void ui_browser__show_title(struct ui_browser *browser, const char *title); | 39 | void ui_browser__show_title(struct ui_browser *browser, const char *title); |
| 39 | int ui_browser__show(struct ui_browser *self, const char *title, | 40 | int ui_browser__show(struct ui_browser *self, const char *title, |
| 40 | const char *helpline, ...); | 41 | const char *helpline, ...); |
| 41 | void ui_browser__hide(struct ui_browser *self); | 42 | void ui_browser__hide(struct ui_browser *self); |
| 42 | int ui_browser__refresh(struct ui_browser *self); | 43 | int ui_browser__refresh(struct ui_browser *self); |
| 43 | int ui_browser__run(struct ui_browser *self); | 44 | int ui_browser__run(struct ui_browser *browser, int delay_secs); |
| 45 | void ui_browser__update_nr_entries(struct ui_browser *browser, u32 nr_entries); | ||
| 46 | void ui_browser__handle_resize(struct ui_browser *browser); | ||
| 47 | |||
| 48 | int ui_browser__warning(struct ui_browser *browser, int timeout, | ||
| 49 | const char *format, ...); | ||
| 50 | int ui_browser__help_window(struct ui_browser *browser, const char *text); | ||
| 51 | bool ui_browser__dialog_yesno(struct ui_browser *browser, const char *text); | ||
| 52 | |||
| 53 | void ui_browser__argv_seek(struct ui_browser *browser, off_t offset, int whence); | ||
| 54 | unsigned int ui_browser__argv_refresh(struct ui_browser *browser); | ||
| 44 | 55 | ||
| 45 | void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence); | 56 | void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence); |
| 46 | unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self); | 57 | unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self); |
diff --git a/tools/perf/util/ui/browsers/annotate.c b/tools/perf/util/ui/browsers/annotate.c index 0229723aceb3..0575905d1205 100644 --- a/tools/perf/util/ui/browsers/annotate.c +++ b/tools/perf/util/ui/browsers/annotate.c | |||
| @@ -1,31 +1,31 @@ | |||
| 1 | #include "../../util.h" | ||
| 1 | #include "../browser.h" | 2 | #include "../browser.h" |
| 2 | #include "../helpline.h" | 3 | #include "../helpline.h" |
| 3 | #include "../libslang.h" | 4 | #include "../libslang.h" |
| 5 | #include "../ui.h" | ||
| 6 | #include "../util.h" | ||
| 4 | #include "../../annotate.h" | 7 | #include "../../annotate.h" |
| 5 | #include "../../hist.h" | 8 | #include "../../hist.h" |
| 6 | #include "../../sort.h" | 9 | #include "../../sort.h" |
| 7 | #include "../../symbol.h" | 10 | #include "../../symbol.h" |
| 8 | #include <pthread.h> | 11 | #include <pthread.h> |
| 9 | 12 | #include <newt.h> | |
| 10 | static void ui__error_window(const char *fmt, ...) | ||
| 11 | { | ||
| 12 | va_list ap; | ||
| 13 | |||
| 14 | va_start(ap, fmt); | ||
| 15 | newtWinMessagev((char *)"Error", (char *)"Ok", (char *)fmt, ap); | ||
| 16 | va_end(ap); | ||
| 17 | } | ||
| 18 | 13 | ||
| 19 | struct annotate_browser { | 14 | struct annotate_browser { |
| 20 | struct ui_browser b; | 15 | struct ui_browser b; |
| 21 | struct rb_root entries; | 16 | struct rb_root entries; |
| 22 | struct rb_node *curr_hot; | 17 | struct rb_node *curr_hot; |
| 18 | struct objdump_line *selection; | ||
| 19 | int nr_asm_entries; | ||
| 20 | int nr_entries; | ||
| 21 | bool hide_src_code; | ||
| 23 | }; | 22 | }; |
| 24 | 23 | ||
| 25 | struct objdump_line_rb_node { | 24 | struct objdump_line_rb_node { |
| 26 | struct rb_node rb_node; | 25 | struct rb_node rb_node; |
| 27 | double percent; | 26 | double percent; |
| 28 | u32 idx; | 27 | u32 idx; |
| 28 | int idx_asm; | ||
| 29 | }; | 29 | }; |
| 30 | 30 | ||
| 31 | static inline | 31 | static inline |
| @@ -34,9 +34,22 @@ struct objdump_line_rb_node *objdump_line__rb(struct objdump_line *self) | |||
| 34 | return (struct objdump_line_rb_node *)(self + 1); | 34 | return (struct objdump_line_rb_node *)(self + 1); |
| 35 | } | 35 | } |
| 36 | 36 | ||
| 37 | static bool objdump_line__filter(struct ui_browser *browser, void *entry) | ||
| 38 | { | ||
| 39 | struct annotate_browser *ab = container_of(browser, struct annotate_browser, b); | ||
| 40 | |||
| 41 | if (ab->hide_src_code) { | ||
| 42 | struct objdump_line *ol = list_entry(entry, struct objdump_line, node); | ||
| 43 | return ol->offset == -1; | ||
| 44 | } | ||
| 45 | |||
| 46 | return false; | ||
| 47 | } | ||
| 48 | |||
| 37 | static void annotate_browser__write(struct ui_browser *self, void *entry, int row) | 49 | static void annotate_browser__write(struct ui_browser *self, void *entry, int row) |
| 38 | { | 50 | { |
| 39 | struct objdump_line *ol = rb_entry(entry, struct objdump_line, node); | 51 | struct annotate_browser *ab = container_of(self, struct annotate_browser, b); |
| 52 | struct objdump_line *ol = list_entry(entry, struct objdump_line, node); | ||
| 40 | bool current_entry = ui_browser__is_current_entry(self, row); | 53 | bool current_entry = ui_browser__is_current_entry(self, row); |
| 41 | int width = self->width; | 54 | int width = self->width; |
| 42 | 55 | ||
| @@ -51,6 +64,11 @@ static void annotate_browser__write(struct ui_browser *self, void *entry, int ro | |||
| 51 | 64 | ||
| 52 | SLsmg_write_char(':'); | 65 | SLsmg_write_char(':'); |
| 53 | slsmg_write_nstring(" ", 8); | 66 | slsmg_write_nstring(" ", 8); |
| 67 | |||
| 68 | /* The scroll bar isn't being used */ | ||
| 69 | if (!self->navkeypressed) | ||
| 70 | width += 1; | ||
| 71 | |||
| 54 | if (!*ol->line) | 72 | if (!*ol->line) |
| 55 | slsmg_write_nstring(" ", width - 18); | 73 | slsmg_write_nstring(" ", width - 18); |
| 56 | else | 74 | else |
| @@ -58,6 +76,8 @@ static void annotate_browser__write(struct ui_browser *self, void *entry, int ro | |||
| 58 | 76 | ||
| 59 | if (!current_entry) | 77 | if (!current_entry) |
| 60 | ui_browser__set_color(self, HE_COLORSET_CODE); | 78 | ui_browser__set_color(self, HE_COLORSET_CODE); |
| 79 | else | ||
| 80 | ab->selection = ol; | ||
| 61 | } | 81 | } |
| 62 | 82 | ||
| 63 | static double objdump_line__calc_percent(struct objdump_line *self, | 83 | static double objdump_line__calc_percent(struct objdump_line *self, |
| @@ -141,7 +161,8 @@ static void annotate_browser__set_top(struct annotate_browser *self, | |||
| 141 | static void annotate_browser__calc_percent(struct annotate_browser *browser, | 161 | static void annotate_browser__calc_percent(struct annotate_browser *browser, |
| 142 | int evidx) | 162 | int evidx) |
| 143 | { | 163 | { |
| 144 | struct symbol *sym = browser->b.priv; | 164 | struct map_symbol *ms = browser->b.priv; |
| 165 | struct symbol *sym = ms->sym; | ||
| 145 | struct annotation *notes = symbol__annotation(sym); | 166 | struct annotation *notes = symbol__annotation(sym); |
| 146 | struct objdump_line *pos; | 167 | struct objdump_line *pos; |
| 147 | 168 | ||
| @@ -163,25 +184,60 @@ static void annotate_browser__calc_percent(struct annotate_browser *browser, | |||
| 163 | browser->curr_hot = rb_last(&browser->entries); | 184 | browser->curr_hot = rb_last(&browser->entries); |
| 164 | } | 185 | } |
| 165 | 186 | ||
| 187 | static bool annotate_browser__toggle_source(struct annotate_browser *browser) | ||
| 188 | { | ||
| 189 | struct objdump_line *ol; | ||
| 190 | struct objdump_line_rb_node *olrb; | ||
| 191 | off_t offset = browser->b.index - browser->b.top_idx; | ||
| 192 | |||
| 193 | browser->b.seek(&browser->b, offset, SEEK_CUR); | ||
| 194 | ol = list_entry(browser->b.top, struct objdump_line, node); | ||
| 195 | olrb = objdump_line__rb(ol); | ||
| 196 | |||
| 197 | if (browser->hide_src_code) { | ||
| 198 | if (olrb->idx_asm < offset) | ||
| 199 | offset = olrb->idx; | ||
| 200 | |||
| 201 | browser->b.nr_entries = browser->nr_entries; | ||
| 202 | browser->hide_src_code = false; | ||
| 203 | browser->b.seek(&browser->b, -offset, SEEK_CUR); | ||
| 204 | browser->b.top_idx = olrb->idx - offset; | ||
| 205 | browser->b.index = olrb->idx; | ||
| 206 | } else { | ||
| 207 | if (olrb->idx_asm < 0) { | ||
| 208 | ui_helpline__puts("Only available for assembly lines."); | ||
| 209 | browser->b.seek(&browser->b, -offset, SEEK_CUR); | ||
| 210 | return false; | ||
| 211 | } | ||
| 212 | |||
| 213 | if (olrb->idx_asm < offset) | ||
| 214 | offset = olrb->idx_asm; | ||
| 215 | |||
| 216 | browser->b.nr_entries = browser->nr_asm_entries; | ||
| 217 | browser->hide_src_code = true; | ||
| 218 | browser->b.seek(&browser->b, -offset, SEEK_CUR); | ||
| 219 | browser->b.top_idx = olrb->idx_asm - offset; | ||
| 220 | browser->b.index = olrb->idx_asm; | ||
| 221 | } | ||
| 222 | |||
| 223 | return true; | ||
| 224 | } | ||
| 225 | |||
| 166 | static int annotate_browser__run(struct annotate_browser *self, int evidx, | 226 | static int annotate_browser__run(struct annotate_browser *self, int evidx, |
| 167 | int refresh) | 227 | int nr_events, void(*timer)(void *arg), |
| 228 | void *arg, int delay_secs) | ||
| 168 | { | 229 | { |
| 169 | struct rb_node *nd = NULL; | 230 | struct rb_node *nd = NULL; |
| 170 | struct symbol *sym = self->b.priv; | 231 | struct map_symbol *ms = self->b.priv; |
| 171 | /* | 232 | struct symbol *sym = ms->sym; |
| 172 | * RIGHT To allow builtin-annotate to cycle thru multiple symbols by | 233 | const char *help = "<-, ESC: exit, TAB/shift+TAB: cycle hottest lines, " |
| 173 | * examining the exit key for this function. | 234 | "H: Hottest, -> Line action, S -> Toggle source " |
| 174 | */ | 235 | "code view"; |
| 175 | int exit_keys[] = { 'H', NEWT_KEY_TAB, NEWT_KEY_UNTAB, | ||
| 176 | NEWT_KEY_RIGHT, 0 }; | ||
| 177 | int key; | 236 | int key; |
| 178 | 237 | ||
| 179 | if (ui_browser__show(&self->b, sym->name, | 238 | if (ui_browser__show(&self->b, sym->name, help) < 0) |
| 180 | "<-, -> or ESC: exit, TAB/shift+TAB: " | ||
| 181 | "cycle hottest lines, H: Hottest") < 0) | ||
| 182 | return -1; | 239 | return -1; |
| 183 | 240 | ||
| 184 | ui_browser__add_exit_keys(&self->b, exit_keys); | ||
| 185 | annotate_browser__calc_percent(self, evidx); | 241 | annotate_browser__calc_percent(self, evidx); |
| 186 | 242 | ||
| 187 | if (self->curr_hot) | 243 | if (self->curr_hot) |
| @@ -189,13 +245,10 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx, | |||
| 189 | 245 | ||
| 190 | nd = self->curr_hot; | 246 | nd = self->curr_hot; |
| 191 | 247 | ||
| 192 | if (refresh != 0) | ||
| 193 | newtFormSetTimer(self->b.form, refresh); | ||
| 194 | |||
| 195 | while (1) { | 248 | while (1) { |
| 196 | key = ui_browser__run(&self->b); | 249 | key = ui_browser__run(&self->b, delay_secs); |
| 197 | 250 | ||
| 198 | if (refresh != 0) { | 251 | if (delay_secs != 0) { |
| 199 | annotate_browser__calc_percent(self, evidx); | 252 | annotate_browser__calc_percent(self, evidx); |
| 200 | /* | 253 | /* |
| 201 | * Current line focus got out of the list of most active | 254 | * Current line focus got out of the list of most active |
| @@ -207,15 +260,14 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx, | |||
| 207 | } | 260 | } |
| 208 | 261 | ||
| 209 | switch (key) { | 262 | switch (key) { |
| 210 | case -1: | 263 | case K_TIMER: |
| 211 | /* | 264 | if (timer != NULL) |
| 212 | * FIXME we need to check if it was | 265 | timer(arg); |
| 213 | * es.reason == NEWT_EXIT_TIMER | 266 | |
| 214 | */ | 267 | if (delay_secs != 0) |
| 215 | if (refresh != 0) | ||
| 216 | symbol__annotate_decay_histogram(sym, evidx); | 268 | symbol__annotate_decay_histogram(sym, evidx); |
| 217 | continue; | 269 | continue; |
| 218 | case NEWT_KEY_TAB: | 270 | case K_TAB: |
| 219 | if (nd != NULL) { | 271 | if (nd != NULL) { |
| 220 | nd = rb_prev(nd); | 272 | nd = rb_prev(nd); |
| 221 | if (nd == NULL) | 273 | if (nd == NULL) |
| @@ -223,7 +275,7 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx, | |||
| 223 | } else | 275 | } else |
| 224 | nd = self->curr_hot; | 276 | nd = self->curr_hot; |
| 225 | break; | 277 | break; |
| 226 | case NEWT_KEY_UNTAB: | 278 | case K_UNTAB: |
| 227 | if (nd != NULL) | 279 | if (nd != NULL) |
| 228 | nd = rb_next(nd); | 280 | nd = rb_next(nd); |
| 229 | if (nd == NULL) | 281 | if (nd == NULL) |
| @@ -234,8 +286,68 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx, | |||
| 234 | case 'H': | 286 | case 'H': |
| 235 | nd = self->curr_hot; | 287 | nd = self->curr_hot; |
| 236 | break; | 288 | break; |
| 237 | default: | 289 | case 'S': |
| 290 | if (annotate_browser__toggle_source(self)) | ||
| 291 | ui_helpline__puts(help); | ||
| 292 | continue; | ||
| 293 | case K_ENTER: | ||
| 294 | case K_RIGHT: | ||
| 295 | if (self->selection == NULL) { | ||
| 296 | ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org"); | ||
| 297 | continue; | ||
| 298 | } | ||
| 299 | |||
| 300 | if (self->selection->offset == -1) { | ||
| 301 | ui_helpline__puts("Actions are only available for assembly lines."); | ||
| 302 | continue; | ||
| 303 | } else { | ||
| 304 | char *s = strstr(self->selection->line, "callq "); | ||
| 305 | struct annotation *notes; | ||
| 306 | struct symbol *target; | ||
| 307 | u64 ip; | ||
| 308 | |||
| 309 | if (s == NULL) { | ||
| 310 | ui_helpline__puts("Actions are only available for the 'callq' instruction."); | ||
| 311 | continue; | ||
| 312 | } | ||
| 313 | |||
| 314 | s = strchr(s, ' '); | ||
| 315 | if (s++ == NULL) { | ||
| 316 | ui_helpline__puts("Invallid callq instruction."); | ||
| 317 | continue; | ||
| 318 | } | ||
| 319 | |||
| 320 | ip = strtoull(s, NULL, 16); | ||
| 321 | ip = ms->map->map_ip(ms->map, ip); | ||
| 322 | target = map__find_symbol(ms->map, ip, NULL); | ||
| 323 | if (target == NULL) { | ||
| 324 | ui_helpline__puts("The called function was not found."); | ||
| 325 | continue; | ||
| 326 | } | ||
| 327 | |||
| 328 | notes = symbol__annotation(target); | ||
| 329 | pthread_mutex_lock(¬es->lock); | ||
| 330 | |||
| 331 | if (notes->src == NULL && | ||
| 332 | symbol__alloc_hist(target, nr_events) < 0) { | ||
| 333 | pthread_mutex_unlock(¬es->lock); | ||
| 334 | ui__warning("Not enough memory for annotating '%s' symbol!\n", | ||
| 335 | target->name); | ||
| 336 | continue; | ||
| 337 | } | ||
| 338 | |||
| 339 | pthread_mutex_unlock(¬es->lock); | ||
| 340 | symbol__tui_annotate(target, ms->map, evidx, nr_events, | ||
| 341 | timer, arg, delay_secs); | ||
| 342 | } | ||
| 343 | continue; | ||
| 344 | case K_LEFT: | ||
| 345 | case K_ESC: | ||
| 346 | case 'q': | ||
| 347 | case CTRL('c'): | ||
| 238 | goto out; | 348 | goto out; |
| 349 | default: | ||
| 350 | continue; | ||
| 239 | } | 351 | } |
| 240 | 352 | ||
| 241 | if (nd != NULL) | 353 | if (nd != NULL) |
| @@ -246,22 +358,31 @@ out: | |||
| 246 | return key; | 358 | return key; |
| 247 | } | 359 | } |
| 248 | 360 | ||
| 249 | int hist_entry__tui_annotate(struct hist_entry *he, int evidx) | 361 | int hist_entry__tui_annotate(struct hist_entry *he, int evidx, int nr_events, |
| 362 | void(*timer)(void *arg), void *arg, int delay_secs) | ||
| 250 | { | 363 | { |
| 251 | return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx, 0); | 364 | return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx, nr_events, |
| 365 | timer, arg, delay_secs); | ||
| 252 | } | 366 | } |
| 253 | 367 | ||
| 254 | int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, | 368 | int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, |
| 255 | int refresh) | 369 | int nr_events, void(*timer)(void *arg), void *arg, |
| 370 | int delay_secs) | ||
| 256 | { | 371 | { |
| 257 | struct objdump_line *pos, *n; | 372 | struct objdump_line *pos, *n; |
| 258 | struct annotation *notes; | 373 | struct annotation *notes; |
| 374 | struct map_symbol ms = { | ||
| 375 | .map = map, | ||
| 376 | .sym = sym, | ||
| 377 | }; | ||
| 259 | struct annotate_browser browser = { | 378 | struct annotate_browser browser = { |
| 260 | .b = { | 379 | .b = { |
| 261 | .refresh = ui_browser__list_head_refresh, | 380 | .refresh = ui_browser__list_head_refresh, |
| 262 | .seek = ui_browser__list_head_seek, | 381 | .seek = ui_browser__list_head_seek, |
| 263 | .write = annotate_browser__write, | 382 | .write = annotate_browser__write, |
| 264 | .priv = sym, | 383 | .filter = objdump_line__filter, |
| 384 | .priv = &ms, | ||
| 385 | .use_navkeypressed = true, | ||
| 265 | }, | 386 | }, |
| 266 | }; | 387 | }; |
| 267 | int ret; | 388 | int ret; |
| @@ -273,7 +394,7 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, | |||
| 273 | return -1; | 394 | return -1; |
| 274 | 395 | ||
| 275 | if (symbol__annotate(sym, map, sizeof(struct objdump_line_rb_node)) < 0) { | 396 | if (symbol__annotate(sym, map, sizeof(struct objdump_line_rb_node)) < 0) { |
| 276 | ui__error_window(ui_helpline__last_msg); | 397 | ui__error("%s", ui_helpline__last_msg); |
| 277 | return -1; | 398 | return -1; |
| 278 | } | 399 | } |
| 279 | 400 | ||
| @@ -288,12 +409,18 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, | |||
| 288 | if (browser.b.width < line_len) | 409 | if (browser.b.width < line_len) |
| 289 | browser.b.width = line_len; | 410 | browser.b.width = line_len; |
| 290 | rbpos = objdump_line__rb(pos); | 411 | rbpos = objdump_line__rb(pos); |
| 291 | rbpos->idx = browser.b.nr_entries++; | 412 | rbpos->idx = browser.nr_entries++; |
| 413 | if (pos->offset != -1) | ||
| 414 | rbpos->idx_asm = browser.nr_asm_entries++; | ||
| 415 | else | ||
| 416 | rbpos->idx_asm = -1; | ||
| 292 | } | 417 | } |
| 293 | 418 | ||
| 419 | browser.b.nr_entries = browser.nr_entries; | ||
| 294 | browser.b.entries = ¬es->src->source, | 420 | browser.b.entries = ¬es->src->source, |
| 295 | browser.b.width += 18; /* Percentage */ | 421 | browser.b.width += 18; /* Percentage */ |
| 296 | ret = annotate_browser__run(&browser, evidx, refresh); | 422 | ret = annotate_browser__run(&browser, evidx, nr_events, |
| 423 | timer, arg, delay_secs); | ||
| 297 | list_for_each_entry_safe(pos, n, ¬es->src->source, node) { | 424 | list_for_each_entry_safe(pos, n, ¬es->src->source, node) { |
| 298 | list_del(&pos->node); | 425 | list_del(&pos->node); |
| 299 | objdump_line__free(pos); | 426 | objdump_line__free(pos); |
diff --git a/tools/perf/util/ui/browsers/hists.c b/tools/perf/util/ui/browsers/hists.c index 5d767c622dfc..d0c94b459685 100644 --- a/tools/perf/util/ui/browsers/hists.c +++ b/tools/perf/util/ui/browsers/hists.c | |||
| @@ -17,6 +17,7 @@ | |||
| 17 | #include "../browser.h" | 17 | #include "../browser.h" |
| 18 | #include "../helpline.h" | 18 | #include "../helpline.h" |
| 19 | #include "../util.h" | 19 | #include "../util.h" |
| 20 | #include "../ui.h" | ||
| 20 | #include "map.h" | 21 | #include "map.h" |
| 21 | 22 | ||
| 22 | struct hist_browser { | 23 | struct hist_browser { |
| @@ -24,8 +25,12 @@ struct hist_browser { | |||
| 24 | struct hists *hists; | 25 | struct hists *hists; |
| 25 | struct hist_entry *he_selection; | 26 | struct hist_entry *he_selection; |
| 26 | struct map_symbol *selection; | 27 | struct map_symbol *selection; |
| 28 | bool has_symbols; | ||
| 27 | }; | 29 | }; |
| 28 | 30 | ||
| 31 | static int hists__browser_title(struct hists *self, char *bf, size_t size, | ||
| 32 | const char *ev_name); | ||
| 33 | |||
| 29 | static void hist_browser__refresh_dimensions(struct hist_browser *self) | 34 | static void hist_browser__refresh_dimensions(struct hist_browser *self) |
| 30 | { | 35 | { |
| 31 | /* 3 == +/- toggle symbol before actual hist_entry rendering */ | 36 | /* 3 == +/- toggle symbol before actual hist_entry rendering */ |
| @@ -290,28 +295,49 @@ static void hist_browser__set_folding(struct hist_browser *self, bool unfold) | |||
| 290 | ui_browser__reset_index(&self->b); | 295 | ui_browser__reset_index(&self->b); |
| 291 | } | 296 | } |
| 292 | 297 | ||
| 293 | static int hist_browser__run(struct hist_browser *self, const char *title) | 298 | static void ui_browser__warn_lost_events(struct ui_browser *browser) |
| 299 | { | ||
| 300 | ui_browser__warning(browser, 4, | ||
| 301 | "Events are being lost, check IO/CPU overload!\n\n" | ||
| 302 | "You may want to run 'perf' using a RT scheduler policy:\n\n" | ||
| 303 | " perf top -r 80\n\n" | ||
| 304 | "Or reduce the sampling frequency."); | ||
| 305 | } | ||
| 306 | |||
| 307 | static int hist_browser__run(struct hist_browser *self, const char *ev_name, | ||
| 308 | void(*timer)(void *arg), void *arg, int delay_secs) | ||
| 294 | { | 309 | { |
| 295 | int key; | 310 | int key; |
| 296 | int exit_keys[] = { 'a', '?', 'h', 'C', 'd', 'D', 'E', 't', | 311 | char title[160]; |
| 297 | NEWT_KEY_ENTER, NEWT_KEY_RIGHT, NEWT_KEY_LEFT, | ||
| 298 | NEWT_KEY_TAB, NEWT_KEY_UNTAB, 0, }; | ||
| 299 | 312 | ||
| 300 | self->b.entries = &self->hists->entries; | 313 | self->b.entries = &self->hists->entries; |
| 301 | self->b.nr_entries = self->hists->nr_entries; | 314 | self->b.nr_entries = self->hists->nr_entries; |
| 302 | 315 | ||
| 303 | hist_browser__refresh_dimensions(self); | 316 | hist_browser__refresh_dimensions(self); |
| 317 | hists__browser_title(self->hists, title, sizeof(title), ev_name); | ||
| 304 | 318 | ||
| 305 | if (ui_browser__show(&self->b, title, | 319 | if (ui_browser__show(&self->b, title, |
| 306 | "Press '?' for help on key bindings") < 0) | 320 | "Press '?' for help on key bindings") < 0) |
| 307 | return -1; | 321 | return -1; |
| 308 | 322 | ||
| 309 | ui_browser__add_exit_keys(&self->b, exit_keys); | ||
| 310 | |||
| 311 | while (1) { | 323 | while (1) { |
| 312 | key = ui_browser__run(&self->b); | 324 | key = ui_browser__run(&self->b, delay_secs); |
| 313 | 325 | ||
| 314 | switch (key) { | 326 | switch (key) { |
| 327 | case K_TIMER: | ||
| 328 | timer(arg); | ||
| 329 | ui_browser__update_nr_entries(&self->b, self->hists->nr_entries); | ||
| 330 | |||
| 331 | if (self->hists->stats.nr_lost_warned != | ||
| 332 | self->hists->stats.nr_events[PERF_RECORD_LOST]) { | ||
| 333 | self->hists->stats.nr_lost_warned = | ||
| 334 | self->hists->stats.nr_events[PERF_RECORD_LOST]; | ||
| 335 | ui_browser__warn_lost_events(&self->b); | ||
| 336 | } | ||
| 337 | |||
| 338 | hists__browser_title(self->hists, title, sizeof(title), ev_name); | ||
| 339 | ui_browser__show_title(&self->b, title); | ||
| 340 | continue; | ||
| 315 | case 'D': { /* Debug */ | 341 | case 'D': { /* Debug */ |
| 316 | static int seq; | 342 | static int seq; |
| 317 | struct hist_entry *h = rb_entry(self->b.top, | 343 | struct hist_entry *h = rb_entry(self->b.top, |
| @@ -334,7 +360,7 @@ static int hist_browser__run(struct hist_browser *self, const char *title) | |||
| 334 | /* Expand the whole world. */ | 360 | /* Expand the whole world. */ |
| 335 | hist_browser__set_folding(self, true); | 361 | hist_browser__set_folding(self, true); |
| 336 | break; | 362 | break; |
| 337 | case NEWT_KEY_ENTER: | 363 | case K_ENTER: |
| 338 | if (hist_browser__toggle_fold(self)) | 364 | if (hist_browser__toggle_fold(self)) |
| 339 | break; | 365 | break; |
| 340 | /* fall thru */ | 366 | /* fall thru */ |
| @@ -532,7 +558,7 @@ static int hist_browser__show_entry(struct hist_browser *self, | |||
| 532 | char s[256]; | 558 | char s[256]; |
| 533 | double percent; | 559 | double percent; |
| 534 | int printed = 0; | 560 | int printed = 0; |
| 535 | int color, width = self->b.width; | 561 | int width = self->b.width - 6; /* The percentage */ |
| 536 | char folded_sign = ' '; | 562 | char folded_sign = ' '; |
| 537 | bool current_entry = ui_browser__is_current_entry(&self->b, row); | 563 | bool current_entry = ui_browser__is_current_entry(&self->b, row); |
| 538 | off_t row_offset = entry->row_offset; | 564 | off_t row_offset = entry->row_offset; |
| @@ -548,26 +574,35 @@ static int hist_browser__show_entry(struct hist_browser *self, | |||
| 548 | } | 574 | } |
| 549 | 575 | ||
| 550 | if (row_offset == 0) { | 576 | if (row_offset == 0) { |
| 551 | hist_entry__snprintf(entry, s, sizeof(s), self->hists, NULL, false, | 577 | hist_entry__snprintf(entry, s, sizeof(s), self->hists); |
| 552 | 0, false, self->hists->stats.total_period); | ||
| 553 | percent = (entry->period * 100.0) / self->hists->stats.total_period; | 578 | percent = (entry->period * 100.0) / self->hists->stats.total_period; |
| 554 | 579 | ||
| 555 | color = HE_COLORSET_SELECTED; | 580 | ui_browser__set_percent_color(&self->b, percent, current_entry); |
| 556 | if (!current_entry) { | ||
| 557 | if (percent >= MIN_RED) | ||
| 558 | color = HE_COLORSET_TOP; | ||
| 559 | else if (percent >= MIN_GREEN) | ||
| 560 | color = HE_COLORSET_MEDIUM; | ||
| 561 | else | ||
| 562 | color = HE_COLORSET_NORMAL; | ||
| 563 | } | ||
| 564 | |||
| 565 | ui_browser__set_color(&self->b, color); | ||
| 566 | ui_browser__gotorc(&self->b, row, 0); | 581 | ui_browser__gotorc(&self->b, row, 0); |
| 567 | if (symbol_conf.use_callchain) { | 582 | if (symbol_conf.use_callchain) { |
| 568 | slsmg_printf("%c ", folded_sign); | 583 | slsmg_printf("%c ", folded_sign); |
| 569 | width -= 2; | 584 | width -= 2; |
| 570 | } | 585 | } |
| 586 | |||
| 587 | slsmg_printf(" %5.2f%%", percent); | ||
| 588 | |||
| 589 | /* The scroll bar isn't being used */ | ||
| 590 | if (!self->b.navkeypressed) | ||
| 591 | width += 1; | ||
| 592 | |||
| 593 | if (!current_entry || !self->b.navkeypressed) | ||
| 594 | ui_browser__set_color(&self->b, HE_COLORSET_NORMAL); | ||
| 595 | |||
| 596 | if (symbol_conf.show_nr_samples) { | ||
| 597 | slsmg_printf(" %11u", entry->nr_events); | ||
| 598 | width -= 12; | ||
| 599 | } | ||
| 600 | |||
| 601 | if (symbol_conf.show_total_period) { | ||
| 602 | slsmg_printf(" %12" PRIu64, entry->period); | ||
| 603 | width -= 13; | ||
| 604 | } | ||
| 605 | |||
| 571 | slsmg_write_nstring(s, width); | 606 | slsmg_write_nstring(s, width); |
| 572 | ++row; | 607 | ++row; |
| 573 | ++printed; | 608 | ++printed; |
| @@ -585,14 +620,23 @@ static int hist_browser__show_entry(struct hist_browser *self, | |||
| 585 | return printed; | 620 | return printed; |
| 586 | } | 621 | } |
| 587 | 622 | ||
| 623 | static void ui_browser__hists_init_top(struct ui_browser *browser) | ||
| 624 | { | ||
| 625 | if (browser->top == NULL) { | ||
| 626 | struct hist_browser *hb; | ||
| 627 | |||
| 628 | hb = container_of(browser, struct hist_browser, b); | ||
| 629 | browser->top = rb_first(&hb->hists->entries); | ||
| 630 | } | ||
| 631 | } | ||
| 632 | |||
| 588 | static unsigned int hist_browser__refresh(struct ui_browser *self) | 633 | static unsigned int hist_browser__refresh(struct ui_browser *self) |
| 589 | { | 634 | { |
| 590 | unsigned row = 0; | 635 | unsigned row = 0; |
| 591 | struct rb_node *nd; | 636 | struct rb_node *nd; |
| 592 | struct hist_browser *hb = container_of(self, struct hist_browser, b); | 637 | struct hist_browser *hb = container_of(self, struct hist_browser, b); |
| 593 | 638 | ||
| 594 | if (self->top == NULL) | 639 | ui_browser__hists_init_top(self); |
| 595 | self->top = rb_first(&hb->hists->entries); | ||
| 596 | 640 | ||
| 597 | for (nd = self->top; nd; nd = rb_next(nd)) { | 641 | for (nd = self->top; nd; nd = rb_next(nd)) { |
| 598 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | 642 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); |
| @@ -644,6 +688,8 @@ static void ui_browser__hists_seek(struct ui_browser *self, | |||
| 644 | if (self->nr_entries == 0) | 688 | if (self->nr_entries == 0) |
| 645 | return; | 689 | return; |
| 646 | 690 | ||
| 691 | ui_browser__hists_init_top(self); | ||
| 692 | |||
| 647 | switch (whence) { | 693 | switch (whence) { |
| 648 | case SEEK_SET: | 694 | case SEEK_SET: |
| 649 | nd = hists__filter_entries(rb_first(self->entries)); | 695 | nd = hists__filter_entries(rb_first(self->entries)); |
| @@ -761,6 +807,8 @@ static struct hist_browser *hist_browser__new(struct hists *hists) | |||
| 761 | self->hists = hists; | 807 | self->hists = hists; |
| 762 | self->b.refresh = hist_browser__refresh; | 808 | self->b.refresh = hist_browser__refresh; |
| 763 | self->b.seek = ui_browser__hists_seek; | 809 | self->b.seek = ui_browser__hists_seek; |
| 810 | self->b.use_navkeypressed = true, | ||
| 811 | self->has_symbols = sort_sym.list.next != NULL; | ||
| 764 | } | 812 | } |
| 765 | 813 | ||
| 766 | return self; | 814 | return self; |
| @@ -782,11 +830,12 @@ static struct thread *hist_browser__selected_thread(struct hist_browser *self) | |||
| 782 | } | 830 | } |
| 783 | 831 | ||
| 784 | static int hists__browser_title(struct hists *self, char *bf, size_t size, | 832 | static int hists__browser_title(struct hists *self, char *bf, size_t size, |
| 785 | const char *ev_name, const struct dso *dso, | 833 | const char *ev_name) |
| 786 | const struct thread *thread) | ||
| 787 | { | 834 | { |
| 788 | char unit; | 835 | char unit; |
| 789 | int printed; | 836 | int printed; |
| 837 | const struct dso *dso = self->dso_filter; | ||
| 838 | const struct thread *thread = self->thread_filter; | ||
| 790 | unsigned long nr_events = self->stats.nr_events[PERF_RECORD_SAMPLE]; | 839 | unsigned long nr_events = self->stats.nr_events[PERF_RECORD_SAMPLE]; |
| 791 | 840 | ||
| 792 | nr_events = convert_unit(nr_events, &unit); | 841 | nr_events = convert_unit(nr_events, &unit); |
| @@ -803,16 +852,15 @@ static int hists__browser_title(struct hists *self, char *bf, size_t size, | |||
| 803 | return printed; | 852 | return printed; |
| 804 | } | 853 | } |
| 805 | 854 | ||
| 806 | static int perf_evsel__hists_browse(struct perf_evsel *evsel, | 855 | static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, |
| 807 | const char *helpline, const char *ev_name, | 856 | const char *helpline, const char *ev_name, |
| 808 | bool left_exits) | 857 | bool left_exits, |
| 858 | void(*timer)(void *arg), void *arg, | ||
| 859 | int delay_secs) | ||
| 809 | { | 860 | { |
| 810 | struct hists *self = &evsel->hists; | 861 | struct hists *self = &evsel->hists; |
| 811 | struct hist_browser *browser = hist_browser__new(self); | 862 | struct hist_browser *browser = hist_browser__new(self); |
| 812 | struct pstack *fstack; | 863 | 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; | 864 | int key = -1; |
| 817 | 865 | ||
| 818 | if (browser == NULL) | 866 | if (browser == NULL) |
| @@ -824,8 +872,6 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, | |||
| 824 | 872 | ||
| 825 | ui_helpline__push(helpline); | 873 | ui_helpline__push(helpline); |
| 826 | 874 | ||
| 827 | hists__browser_title(self, msg, sizeof(msg), ev_name, | ||
| 828 | dso_filter, thread_filter); | ||
| 829 | while (1) { | 875 | while (1) { |
| 830 | const struct thread *thread = NULL; | 876 | const struct thread *thread = NULL; |
| 831 | const struct dso *dso = NULL; | 877 | const struct dso *dso = NULL; |
| @@ -834,7 +880,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, | |||
| 834 | annotate = -2, zoom_dso = -2, zoom_thread = -2, | 880 | annotate = -2, zoom_dso = -2, zoom_thread = -2, |
| 835 | browse_map = -2; | 881 | browse_map = -2; |
| 836 | 882 | ||
| 837 | key = hist_browser__run(browser, msg); | 883 | key = hist_browser__run(browser, ev_name, timer, arg, delay_secs); |
| 838 | 884 | ||
| 839 | if (browser->he_selection != NULL) { | 885 | if (browser->he_selection != NULL) { |
| 840 | thread = hist_browser__selected_thread(browser); | 886 | thread = hist_browser__selected_thread(browser); |
| @@ -842,14 +888,23 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, | |||
| 842 | } | 888 | } |
| 843 | 889 | ||
| 844 | switch (key) { | 890 | switch (key) { |
| 845 | case NEWT_KEY_TAB: | 891 | case K_TAB: |
| 846 | case NEWT_KEY_UNTAB: | 892 | case K_UNTAB: |
| 893 | if (nr_events == 1) | ||
| 894 | continue; | ||
| 847 | /* | 895 | /* |
| 848 | * Exit the browser, let hists__browser_tree | 896 | * Exit the browser, let hists__browser_tree |
| 849 | * go to the next or previous | 897 | * go to the next or previous |
| 850 | */ | 898 | */ |
| 851 | goto out_free_stack; | 899 | goto out_free_stack; |
| 852 | case 'a': | 900 | case 'a': |
| 901 | if (!browser->has_symbols) { | ||
| 902 | ui_browser__warning(&browser->b, delay_secs * 2, | ||
| 903 | "Annotation is only available for symbolic views, " | ||
| 904 | "include \"sym\" in --sort to use it."); | ||
| 905 | continue; | ||
| 906 | } | ||
| 907 | |||
| 853 | if (browser->selection == NULL || | 908 | if (browser->selection == NULL || |
| 854 | browser->selection->sym == NULL || | 909 | browser->selection->sym == NULL || |
| 855 | browser->selection->map->dso->annotate_warned) | 910 | browser->selection->map->dso->annotate_warned) |
| @@ -859,25 +914,30 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, | |||
| 859 | goto zoom_dso; | 914 | goto zoom_dso; |
| 860 | case 't': | 915 | case 't': |
| 861 | goto zoom_thread; | 916 | goto zoom_thread; |
| 862 | case NEWT_KEY_F1: | 917 | case K_F1: |
| 863 | case 'h': | 918 | case 'h': |
| 864 | case '?': | 919 | case '?': |
| 865 | ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n" | 920 | ui_browser__help_window(&browser->b, |
| 866 | "<- Zoom out\n" | 921 | "h/?/F1 Show this window\n" |
| 867 | "a Annotate current symbol\n" | 922 | "UP/DOWN/PGUP\n" |
| 868 | "h/?/F1 Show this window\n" | 923 | "PGDN/SPACE Navigate\n" |
| 869 | "C Collapse all callchains\n" | 924 | "q/ESC/CTRL+C Exit browser\n\n" |
| 870 | "E Expand all callchains\n" | 925 | "For multiple event sessions:\n\n" |
| 871 | "d Zoom into current DSO\n" | 926 | "TAB/UNTAB Switch events\n\n" |
| 872 | "t Zoom into current Thread\n" | 927 | "For symbolic views (--sort has sym):\n\n" |
| 873 | "TAB/UNTAB Switch events\n" | 928 | "-> Zoom into DSO/Threads & Annotate current symbol\n" |
| 874 | "q/CTRL+C Exit browser"); | 929 | "<- Zoom out\n" |
| 930 | "a Annotate current symbol\n" | ||
| 931 | "C Collapse all callchains\n" | ||
| 932 | "E Expand all callchains\n" | ||
| 933 | "d Zoom into current DSO\n" | ||
| 934 | "t Zoom into current Thread"); | ||
| 875 | continue; | 935 | continue; |
| 876 | case NEWT_KEY_ENTER: | 936 | case K_ENTER: |
| 877 | case NEWT_KEY_RIGHT: | 937 | case K_RIGHT: |
| 878 | /* menu */ | 938 | /* menu */ |
| 879 | break; | 939 | break; |
| 880 | case NEWT_KEY_LEFT: { | 940 | case K_LEFT: { |
| 881 | const void *top; | 941 | const void *top; |
| 882 | 942 | ||
| 883 | if (pstack__empty(fstack)) { | 943 | if (pstack__empty(fstack)) { |
| @@ -889,21 +949,28 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, | |||
| 889 | continue; | 949 | continue; |
| 890 | } | 950 | } |
| 891 | top = pstack__pop(fstack); | 951 | top = pstack__pop(fstack); |
| 892 | if (top == &dso_filter) | 952 | if (top == &browser->hists->dso_filter) |
| 893 | goto zoom_out_dso; | 953 | goto zoom_out_dso; |
| 894 | if (top == &thread_filter) | 954 | if (top == &browser->hists->thread_filter) |
| 895 | goto zoom_out_thread; | 955 | goto zoom_out_thread; |
| 896 | continue; | 956 | continue; |
| 897 | } | 957 | } |
| 898 | case NEWT_KEY_ESCAPE: | 958 | case K_ESC: |
| 899 | if (!left_exits && | 959 | if (!left_exits && |
| 900 | !ui__dialog_yesno("Do you really want to exit?")) | 960 | !ui_browser__dialog_yesno(&browser->b, |
| 961 | "Do you really want to exit?")) | ||
| 901 | continue; | 962 | continue; |
| 902 | /* Fall thru */ | 963 | /* Fall thru */ |
| 903 | default: | 964 | case 'q': |
| 965 | case CTRL('c'): | ||
| 904 | goto out_free_stack; | 966 | goto out_free_stack; |
| 967 | default: | ||
| 968 | continue; | ||
| 905 | } | 969 | } |
| 906 | 970 | ||
| 971 | if (!browser->has_symbols) | ||
| 972 | goto add_exit_option; | ||
| 973 | |||
| 907 | if (browser->selection != NULL && | 974 | if (browser->selection != NULL && |
| 908 | browser->selection->sym != NULL && | 975 | browser->selection->sym != NULL && |
| 909 | !browser->selection->map->dso->annotate_warned && | 976 | !browser->selection->map->dso->annotate_warned && |
| @@ -913,14 +980,14 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, | |||
| 913 | 980 | ||
| 914 | if (thread != NULL && | 981 | if (thread != NULL && |
| 915 | asprintf(&options[nr_options], "Zoom %s %s(%d) thread", | 982 | asprintf(&options[nr_options], "Zoom %s %s(%d) thread", |
| 916 | (thread_filter ? "out of" : "into"), | 983 | (browser->hists->thread_filter ? "out of" : "into"), |
| 917 | (thread->comm_set ? thread->comm : ""), | 984 | (thread->comm_set ? thread->comm : ""), |
| 918 | thread->pid) > 0) | 985 | thread->pid) > 0) |
| 919 | zoom_thread = nr_options++; | 986 | zoom_thread = nr_options++; |
| 920 | 987 | ||
| 921 | if (dso != NULL && | 988 | if (dso != NULL && |
| 922 | asprintf(&options[nr_options], "Zoom %s %s DSO", | 989 | asprintf(&options[nr_options], "Zoom %s %s DSO", |
| 923 | (dso_filter ? "out of" : "into"), | 990 | (browser->hists->dso_filter ? "out of" : "into"), |
| 924 | (dso->kernel ? "the Kernel" : dso->short_name)) > 0) | 991 | (dso->kernel ? "the Kernel" : dso->short_name)) > 0) |
| 925 | zoom_dso = nr_options++; | 992 | zoom_dso = nr_options++; |
| 926 | 993 | ||
| @@ -928,7 +995,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, | |||
| 928 | browser->selection->map != NULL && | 995 | browser->selection->map != NULL && |
| 929 | asprintf(&options[nr_options], "Browse map details") > 0) | 996 | asprintf(&options[nr_options], "Browse map details") > 0) |
| 930 | browse_map = nr_options++; | 997 | browse_map = nr_options++; |
| 931 | 998 | add_exit_option: | |
| 932 | options[nr_options++] = (char *)"Exit"; | 999 | options[nr_options++] = (char *)"Exit"; |
| 933 | 1000 | ||
| 934 | choice = ui__popup_menu(nr_options, options); | 1001 | choice = ui__popup_menu(nr_options, options); |
| @@ -944,50 +1011,59 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, | |||
| 944 | 1011 | ||
| 945 | if (choice == annotate) { | 1012 | if (choice == annotate) { |
| 946 | struct hist_entry *he; | 1013 | struct hist_entry *he; |
| 1014 | int err; | ||
| 947 | do_annotate: | 1015 | do_annotate: |
| 948 | he = hist_browser__selected_entry(browser); | 1016 | he = hist_browser__selected_entry(browser); |
| 949 | if (he == NULL) | 1017 | if (he == NULL) |
| 950 | continue; | 1018 | continue; |
| 951 | 1019 | /* | |
| 952 | hist_entry__tui_annotate(he, evsel->idx); | 1020 | * Don't let this be freed, say, by hists__decay_entry. |
| 1021 | */ | ||
| 1022 | he->used = true; | ||
| 1023 | err = hist_entry__tui_annotate(he, evsel->idx, nr_events, | ||
| 1024 | timer, arg, delay_secs); | ||
| 1025 | he->used = false; | ||
| 1026 | ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries); | ||
| 1027 | if (err) | ||
| 1028 | ui_browser__handle_resize(&browser->b); | ||
| 953 | } else if (choice == browse_map) | 1029 | } else if (choice == browse_map) |
| 954 | map__browse(browser->selection->map); | 1030 | map__browse(browser->selection->map); |
| 955 | else if (choice == zoom_dso) { | 1031 | else if (choice == zoom_dso) { |
| 956 | zoom_dso: | 1032 | zoom_dso: |
| 957 | if (dso_filter) { | 1033 | if (browser->hists->dso_filter) { |
| 958 | pstack__remove(fstack, &dso_filter); | 1034 | pstack__remove(fstack, &browser->hists->dso_filter); |
| 959 | zoom_out_dso: | 1035 | zoom_out_dso: |
| 960 | ui_helpline__pop(); | 1036 | ui_helpline__pop(); |
| 961 | dso_filter = NULL; | 1037 | browser->hists->dso_filter = NULL; |
| 1038 | sort_dso.elide = false; | ||
| 962 | } else { | 1039 | } else { |
| 963 | if (dso == NULL) | 1040 | if (dso == NULL) |
| 964 | continue; | 1041 | continue; |
| 965 | ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"", | 1042 | ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"", |
| 966 | dso->kernel ? "the Kernel" : dso->short_name); | 1043 | dso->kernel ? "the Kernel" : dso->short_name); |
| 967 | dso_filter = dso; | 1044 | browser->hists->dso_filter = dso; |
| 968 | pstack__push(fstack, &dso_filter); | 1045 | sort_dso.elide = true; |
| 1046 | pstack__push(fstack, &browser->hists->dso_filter); | ||
| 969 | } | 1047 | } |
| 970 | hists__filter_by_dso(self, dso_filter); | 1048 | hists__filter_by_dso(self); |
| 971 | hists__browser_title(self, msg, sizeof(msg), ev_name, | ||
| 972 | dso_filter, thread_filter); | ||
| 973 | hist_browser__reset(browser); | 1049 | hist_browser__reset(browser); |
| 974 | } else if (choice == zoom_thread) { | 1050 | } else if (choice == zoom_thread) { |
| 975 | zoom_thread: | 1051 | zoom_thread: |
| 976 | if (thread_filter) { | 1052 | if (browser->hists->thread_filter) { |
| 977 | pstack__remove(fstack, &thread_filter); | 1053 | pstack__remove(fstack, &browser->hists->thread_filter); |
| 978 | zoom_out_thread: | 1054 | zoom_out_thread: |
| 979 | ui_helpline__pop(); | 1055 | ui_helpline__pop(); |
| 980 | thread_filter = NULL; | 1056 | browser->hists->thread_filter = NULL; |
| 1057 | sort_thread.elide = false; | ||
| 981 | } else { | 1058 | } else { |
| 982 | ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"", | 1059 | ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"", |
| 983 | thread->comm_set ? thread->comm : "", | 1060 | thread->comm_set ? thread->comm : "", |
| 984 | thread->pid); | 1061 | thread->pid); |
| 985 | thread_filter = thread; | 1062 | browser->hists->thread_filter = thread; |
| 986 | pstack__push(fstack, &thread_filter); | 1063 | sort_thread.elide = true; |
| 1064 | pstack__push(fstack, &browser->hists->thread_filter); | ||
| 987 | } | 1065 | } |
| 988 | hists__filter_by_thread(self, thread_filter); | 1066 | hists__filter_by_thread(self); |
| 989 | hists__browser_title(self, msg, sizeof(msg), ev_name, | ||
| 990 | dso_filter, thread_filter); | ||
| 991 | hist_browser__reset(browser); | 1067 | hist_browser__reset(browser); |
| 992 | } | 1068 | } |
| 993 | } | 1069 | } |
| @@ -1001,6 +1077,7 @@ out: | |||
| 1001 | struct perf_evsel_menu { | 1077 | struct perf_evsel_menu { |
| 1002 | struct ui_browser b; | 1078 | struct ui_browser b; |
| 1003 | struct perf_evsel *selection; | 1079 | struct perf_evsel *selection; |
| 1080 | bool lost_events, lost_events_warned; | ||
| 1004 | }; | 1081 | }; |
| 1005 | 1082 | ||
| 1006 | static void perf_evsel_menu__write(struct ui_browser *browser, | 1083 | static void perf_evsel_menu__write(struct ui_browser *browser, |
| @@ -1013,22 +1090,38 @@ static void perf_evsel_menu__write(struct ui_browser *browser, | |||
| 1013 | unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE]; | 1090 | unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE]; |
| 1014 | const char *ev_name = event_name(evsel); | 1091 | const char *ev_name = event_name(evsel); |
| 1015 | char bf[256], unit; | 1092 | char bf[256], unit; |
| 1093 | const char *warn = " "; | ||
| 1094 | size_t printed; | ||
| 1016 | 1095 | ||
| 1017 | ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED : | 1096 | ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED : |
| 1018 | HE_COLORSET_NORMAL); | 1097 | HE_COLORSET_NORMAL); |
| 1019 | 1098 | ||
| 1020 | nr_events = convert_unit(nr_events, &unit); | 1099 | nr_events = convert_unit(nr_events, &unit); |
| 1021 | snprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events, | 1100 | printed = snprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events, |
| 1022 | unit, unit == ' ' ? "" : " ", ev_name); | 1101 | unit, unit == ' ' ? "" : " ", ev_name); |
| 1023 | slsmg_write_nstring(bf, browser->width); | 1102 | slsmg_printf("%s", bf); |
| 1103 | |||
| 1104 | nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST]; | ||
| 1105 | if (nr_events != 0) { | ||
| 1106 | menu->lost_events = true; | ||
| 1107 | if (!current_entry) | ||
| 1108 | ui_browser__set_color(browser, HE_COLORSET_TOP); | ||
| 1109 | nr_events = convert_unit(nr_events, &unit); | ||
| 1110 | snprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!", nr_events, | ||
| 1111 | unit, unit == ' ' ? "" : " "); | ||
| 1112 | warn = bf; | ||
| 1113 | } | ||
| 1114 | |||
| 1115 | slsmg_write_nstring(warn, browser->width - printed); | ||
| 1024 | 1116 | ||
| 1025 | if (current_entry) | 1117 | if (current_entry) |
| 1026 | menu->selection = evsel; | 1118 | menu->selection = evsel; |
| 1027 | } | 1119 | } |
| 1028 | 1120 | ||
| 1029 | static int perf_evsel_menu__run(struct perf_evsel_menu *menu, const char *help) | 1121 | static int perf_evsel_menu__run(struct perf_evsel_menu *menu, |
| 1122 | int nr_events, const char *help, | ||
| 1123 | void(*timer)(void *arg), void *arg, int delay_secs) | ||
| 1030 | { | 1124 | { |
| 1031 | int exit_keys[] = { NEWT_KEY_ENTER, NEWT_KEY_RIGHT, 0, }; | ||
| 1032 | struct perf_evlist *evlist = menu->b.priv; | 1125 | struct perf_evlist *evlist = menu->b.priv; |
| 1033 | struct perf_evsel *pos; | 1126 | struct perf_evsel *pos; |
| 1034 | const char *ev_name, *title = "Available samples"; | 1127 | const char *ev_name, *title = "Available samples"; |
| @@ -1038,50 +1131,72 @@ static int perf_evsel_menu__run(struct perf_evsel_menu *menu, const char *help) | |||
| 1038 | "ESC: exit, ENTER|->: Browse histograms") < 0) | 1131 | "ESC: exit, ENTER|->: Browse histograms") < 0) |
| 1039 | return -1; | 1132 | return -1; |
| 1040 | 1133 | ||
| 1041 | ui_browser__add_exit_keys(&menu->b, exit_keys); | ||
| 1042 | |||
| 1043 | while (1) { | 1134 | while (1) { |
| 1044 | key = ui_browser__run(&menu->b); | 1135 | key = ui_browser__run(&menu->b, delay_secs); |
| 1045 | 1136 | ||
| 1046 | switch (key) { | 1137 | switch (key) { |
| 1047 | case NEWT_KEY_RIGHT: | 1138 | case K_TIMER: |
| 1048 | case NEWT_KEY_ENTER: | 1139 | timer(arg); |
| 1140 | |||
| 1141 | if (!menu->lost_events_warned && menu->lost_events) { | ||
| 1142 | ui_browser__warn_lost_events(&menu->b); | ||
| 1143 | menu->lost_events_warned = true; | ||
| 1144 | } | ||
| 1145 | continue; | ||
| 1146 | case K_RIGHT: | ||
| 1147 | case K_ENTER: | ||
| 1049 | if (!menu->selection) | 1148 | if (!menu->selection) |
| 1050 | continue; | 1149 | continue; |
| 1051 | pos = menu->selection; | 1150 | pos = menu->selection; |
| 1052 | browse_hists: | 1151 | browse_hists: |
| 1152 | perf_evlist__set_selected(evlist, pos); | ||
| 1153 | /* | ||
| 1154 | * Give the calling tool a chance to populate the non | ||
| 1155 | * default evsel resorted hists tree. | ||
| 1156 | */ | ||
| 1157 | if (timer) | ||
| 1158 | timer(arg); | ||
| 1053 | ev_name = event_name(pos); | 1159 | ev_name = event_name(pos); |
| 1054 | key = perf_evsel__hists_browse(pos, help, ev_name, true); | 1160 | key = perf_evsel__hists_browse(pos, nr_events, help, |
| 1161 | ev_name, true, timer, | ||
| 1162 | arg, delay_secs); | ||
| 1055 | ui_browser__show_title(&menu->b, title); | 1163 | ui_browser__show_title(&menu->b, title); |
| 1056 | break; | 1164 | switch (key) { |
| 1057 | case NEWT_KEY_LEFT: | 1165 | case K_TAB: |
| 1166 | if (pos->node.next == &evlist->entries) | ||
| 1167 | pos = list_entry(evlist->entries.next, struct perf_evsel, node); | ||
| 1168 | else | ||
| 1169 | pos = list_entry(pos->node.next, struct perf_evsel, node); | ||
| 1170 | goto browse_hists; | ||
| 1171 | case K_UNTAB: | ||
| 1172 | if (pos->node.prev == &evlist->entries) | ||
| 1173 | pos = list_entry(evlist->entries.prev, struct perf_evsel, node); | ||
| 1174 | else | ||
| 1175 | pos = list_entry(pos->node.prev, struct perf_evsel, node); | ||
| 1176 | goto browse_hists; | ||
| 1177 | case K_ESC: | ||
| 1178 | if (!ui_browser__dialog_yesno(&menu->b, | ||
| 1179 | "Do you really want to exit?")) | ||
| 1180 | continue; | ||
| 1181 | /* Fall thru */ | ||
| 1182 | case 'q': | ||
| 1183 | case CTRL('c'): | ||
| 1184 | goto out; | ||
| 1185 | default: | ||
| 1186 | continue; | ||
| 1187 | } | ||
| 1188 | case K_LEFT: | ||
| 1058 | continue; | 1189 | continue; |
| 1059 | case NEWT_KEY_ESCAPE: | 1190 | case K_ESC: |
| 1060 | if (!ui__dialog_yesno("Do you really want to exit?")) | 1191 | if (!ui_browser__dialog_yesno(&menu->b, |
| 1192 | "Do you really want to exit?")) | ||
| 1061 | continue; | 1193 | continue; |
| 1062 | /* Fall thru */ | 1194 | /* Fall thru */ |
| 1063 | default: | ||
| 1064 | goto out; | ||
| 1065 | } | ||
| 1066 | |||
| 1067 | switch (key) { | ||
| 1068 | case NEWT_KEY_TAB: | ||
| 1069 | if (pos->node.next == &evlist->entries) | ||
| 1070 | pos = list_entry(evlist->entries.next, struct perf_evsel, node); | ||
| 1071 | else | ||
| 1072 | pos = list_entry(pos->node.next, struct perf_evsel, node); | ||
| 1073 | goto browse_hists; | ||
| 1074 | case NEWT_KEY_UNTAB: | ||
| 1075 | if (pos->node.prev == &evlist->entries) | ||
| 1076 | pos = list_entry(evlist->entries.prev, struct perf_evsel, node); | ||
| 1077 | else | ||
| 1078 | pos = list_entry(pos->node.prev, struct perf_evsel, node); | ||
| 1079 | goto browse_hists; | ||
| 1080 | case 'q': | 1195 | case 'q': |
| 1081 | case CTRL('c'): | 1196 | case CTRL('c'): |
| 1082 | goto out; | 1197 | goto out; |
| 1083 | default: | 1198 | default: |
| 1084 | break; | 1199 | continue; |
| 1085 | } | 1200 | } |
| 1086 | } | 1201 | } |
| 1087 | 1202 | ||
| @@ -1091,7 +1206,9 @@ out: | |||
| 1091 | } | 1206 | } |
| 1092 | 1207 | ||
| 1093 | static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, | 1208 | static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, |
| 1094 | const char *help) | 1209 | const char *help, |
| 1210 | void(*timer)(void *arg), void *arg, | ||
| 1211 | int delay_secs) | ||
| 1095 | { | 1212 | { |
| 1096 | struct perf_evsel *pos; | 1213 | struct perf_evsel *pos; |
| 1097 | struct perf_evsel_menu menu = { | 1214 | struct perf_evsel_menu menu = { |
| @@ -1121,18 +1238,24 @@ static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, | |||
| 1121 | pos->name = strdup(ev_name); | 1238 | pos->name = strdup(ev_name); |
| 1122 | } | 1239 | } |
| 1123 | 1240 | ||
| 1124 | return perf_evsel_menu__run(&menu, help); | 1241 | return perf_evsel_menu__run(&menu, evlist->nr_entries, help, timer, |
| 1242 | arg, delay_secs); | ||
| 1125 | } | 1243 | } |
| 1126 | 1244 | ||
| 1127 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help) | 1245 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, |
| 1246 | void(*timer)(void *arg), void *arg, | ||
| 1247 | int delay_secs) | ||
| 1128 | { | 1248 | { |
| 1129 | 1249 | ||
| 1130 | if (evlist->nr_entries == 1) { | 1250 | if (evlist->nr_entries == 1) { |
| 1131 | struct perf_evsel *first = list_entry(evlist->entries.next, | 1251 | struct perf_evsel *first = list_entry(evlist->entries.next, |
| 1132 | struct perf_evsel, node); | 1252 | struct perf_evsel, node); |
| 1133 | const char *ev_name = event_name(first); | 1253 | const char *ev_name = event_name(first); |
| 1134 | return perf_evsel__hists_browse(first, help, ev_name, false); | 1254 | return perf_evsel__hists_browse(first, evlist->nr_entries, help, |
| 1255 | ev_name, false, timer, arg, | ||
| 1256 | delay_secs); | ||
| 1135 | } | 1257 | } |
| 1136 | 1258 | ||
| 1137 | return __perf_evlist__tui_browse_hists(evlist, help); | 1259 | return __perf_evlist__tui_browse_hists(evlist, help, |
| 1260 | timer, arg, delay_secs); | ||
| 1138 | } | 1261 | } |
diff --git a/tools/perf/util/ui/browsers/map.c b/tools/perf/util/ui/browsers/map.c index 8462bffe20bc..6905bcc8be2d 100644 --- a/tools/perf/util/ui/browsers/map.c +++ b/tools/perf/util/ui/browsers/map.c | |||
| @@ -1,5 +1,6 @@ | |||
| 1 | #include "../libslang.h" | 1 | #include "../libslang.h" |
| 2 | #include <elf.h> | 2 | #include <elf.h> |
| 3 | #include <newt.h> | ||
| 3 | #include <inttypes.h> | 4 | #include <inttypes.h> |
| 4 | #include <sys/ttydefaults.h> | 5 | #include <sys/ttydefaults.h> |
| 5 | #include <ctype.h> | 6 | #include <ctype.h> |
| @@ -108,11 +109,8 @@ static int map_browser__run(struct map_browser *self) | |||
| 108 | verbose ? "" : "restart with -v to use") < 0) | 109 | verbose ? "" : "restart with -v to use") < 0) |
| 109 | return -1; | 110 | return -1; |
| 110 | 111 | ||
| 111 | if (verbose) | ||
| 112 | ui_browser__add_exit_key(&self->b, '/'); | ||
| 113 | |||
| 114 | while (1) { | 112 | while (1) { |
| 115 | key = ui_browser__run(&self->b); | 113 | key = ui_browser__run(&self->b, 0); |
| 116 | 114 | ||
| 117 | if (verbose && key == '/') | 115 | if (verbose && key == '/') |
| 118 | map_browser__search(self); | 116 | map_browser__search(self); |
diff --git a/tools/perf/util/ui/browsers/top.c b/tools/perf/util/ui/browsers/top.c deleted file mode 100644 index 88403cf8396a..000000000000 --- a/tools/perf/util/ui/browsers/top.c +++ /dev/null | |||
| @@ -1,212 +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 "../../evlist.h" | ||
| 15 | #include "../../hist.h" | ||
| 16 | #include "../../sort.h" | ||
| 17 | #include "../../symbol.h" | ||
| 18 | #include "../../top.h" | ||
| 19 | |||
| 20 | struct perf_top_browser { | ||
| 21 | struct ui_browser b; | ||
| 22 | struct rb_root root; | ||
| 23 | struct sym_entry *selection; | ||
| 24 | float sum_ksamples; | ||
| 25 | int dso_width; | ||
| 26 | int dso_short_width; | ||
| 27 | int sym_width; | ||
| 28 | }; | ||
| 29 | |||
| 30 | static void perf_top_browser__write(struct ui_browser *browser, void *entry, int row) | ||
| 31 | { | ||
| 32 | struct perf_top_browser *top_browser = container_of(browser, struct perf_top_browser, b); | ||
| 33 | struct sym_entry *syme = rb_entry(entry, struct sym_entry, rb_node); | ||
| 34 | bool current_entry = ui_browser__is_current_entry(browser, row); | ||
| 35 | struct symbol *symbol = sym_entry__symbol(syme); | ||
| 36 | struct perf_top *top = browser->priv; | ||
| 37 | int width = browser->width; | ||
| 38 | double pcnt; | ||
| 39 | |||
| 40 | pcnt = 100.0 - (100.0 * ((top_browser->sum_ksamples - syme->snap_count) / | ||
| 41 | top_browser->sum_ksamples)); | ||
| 42 | ui_browser__set_percent_color(browser, pcnt, current_entry); | ||
| 43 | |||
| 44 | if (top->evlist->nr_entries == 1 || !top->display_weighted) { | ||
| 45 | slsmg_printf("%20.2f ", syme->weight); | ||
| 46 | width -= 24; | ||
| 47 | } else { | ||
| 48 | slsmg_printf("%9.1f %10ld ", syme->weight, syme->snap_count); | ||
| 49 | width -= 23; | ||
| 50 | } | ||
| 51 | |||
| 52 | slsmg_printf("%4.1f%%", pcnt); | ||
| 53 | width -= 7; | ||
| 54 | |||
| 55 | if (verbose) { | ||
| 56 | slsmg_printf(" %016" PRIx64, symbol->start); | ||
| 57 | width -= 17; | ||
| 58 | } | ||
| 59 | |||
| 60 | slsmg_printf(" %-*.*s ", top_browser->sym_width, top_browser->sym_width, | ||
| 61 | symbol->name); | ||
| 62 | width -= top_browser->sym_width; | ||
| 63 | slsmg_write_nstring(width >= syme->map->dso->long_name_len ? | ||
| 64 | syme->map->dso->long_name : | ||
| 65 | syme->map->dso->short_name, width); | ||
| 66 | |||
| 67 | if (current_entry) | ||
| 68 | top_browser->selection = syme; | ||
| 69 | } | ||
| 70 | |||
| 71 | static void perf_top_browser__update_rb_tree(struct perf_top_browser *browser) | ||
| 72 | { | ||
| 73 | struct perf_top *top = browser->b.priv; | ||
| 74 | u64 top_idx = browser->b.top_idx; | ||
| 75 | |||
| 76 | browser->root = RB_ROOT; | ||
| 77 | browser->b.top = NULL; | ||
| 78 | browser->sum_ksamples = perf_top__decay_samples(top, &browser->root); | ||
| 79 | /* | ||
| 80 | * No active symbols | ||
| 81 | */ | ||
| 82 | if (top->rb_entries == 0) | ||
| 83 | return; | ||
| 84 | |||
| 85 | perf_top__find_widths(top, &browser->root, &browser->dso_width, | ||
| 86 | &browser->dso_short_width, | ||
| 87 | &browser->sym_width); | ||
| 88 | if (browser->sym_width + browser->dso_width > browser->b.width - 29) { | ||
| 89 | browser->dso_width = browser->dso_short_width; | ||
| 90 | if (browser->sym_width + browser->dso_width > browser->b.width - 29) | ||
| 91 | browser->sym_width = browser->b.width - browser->dso_width - 29; | ||
| 92 | } | ||
| 93 | |||
| 94 | /* | ||
| 95 | * Adjust the ui_browser indexes since the entries in the browser->root | ||
| 96 | * rb_tree may have changed, then seek it from start, so that we get a | ||
| 97 | * possible new top of the screen. | ||
| 98 | */ | ||
| 99 | browser->b.nr_entries = top->rb_entries; | ||
| 100 | |||
| 101 | if (top_idx >= browser->b.nr_entries) { | ||
| 102 | if (browser->b.height >= browser->b.nr_entries) | ||
| 103 | top_idx = browser->b.nr_entries - browser->b.height; | ||
| 104 | else | ||
| 105 | top_idx = 0; | ||
| 106 | } | ||
| 107 | |||
| 108 | if (browser->b.index >= top_idx + browser->b.height) | ||
| 109 | browser->b.index = top_idx + browser->b.index - browser->b.top_idx; | ||
| 110 | |||
| 111 | if (browser->b.index >= browser->b.nr_entries) | ||
| 112 | browser->b.index = browser->b.nr_entries - 1; | ||
| 113 | |||
| 114 | browser->b.top_idx = top_idx; | ||
| 115 | browser->b.seek(&browser->b, top_idx, SEEK_SET); | ||
| 116 | } | ||
| 117 | |||
| 118 | static void perf_top_browser__annotate(struct perf_top_browser *browser) | ||
| 119 | { | ||
| 120 | struct sym_entry *syme = browser->selection; | ||
| 121 | struct symbol *sym = sym_entry__symbol(syme); | ||
| 122 | struct annotation *notes = symbol__annotation(sym); | ||
| 123 | struct perf_top *top = browser->b.priv; | ||
| 124 | |||
| 125 | if (notes->src != NULL) | ||
| 126 | goto do_annotation; | ||
| 127 | |||
| 128 | pthread_mutex_lock(¬es->lock); | ||
| 129 | |||
| 130 | top->sym_filter_entry = NULL; | ||
| 131 | |||
| 132 | if (symbol__alloc_hist(sym, top->evlist->nr_entries) < 0) { | ||
| 133 | pr_err("Not enough memory for annotating '%s' symbol!\n", | ||
| 134 | sym->name); | ||
| 135 | pthread_mutex_unlock(¬es->lock); | ||
| 136 | return; | ||
| 137 | } | ||
| 138 | |||
| 139 | top->sym_filter_entry = syme; | ||
| 140 | |||
| 141 | pthread_mutex_unlock(¬es->lock); | ||
| 142 | do_annotation: | ||
| 143 | symbol__tui_annotate(sym, syme->map, 0, top->delay_secs * 1000); | ||
| 144 | } | ||
| 145 | |||
| 146 | static int perf_top_browser__run(struct perf_top_browser *browser) | ||
| 147 | { | ||
| 148 | int key; | ||
| 149 | char title[160]; | ||
| 150 | struct perf_top *top = browser->b.priv; | ||
| 151 | int delay_msecs = top->delay_secs * 1000; | ||
| 152 | int exit_keys[] = { 'a', NEWT_KEY_ENTER, NEWT_KEY_RIGHT, 0, }; | ||
| 153 | |||
| 154 | perf_top_browser__update_rb_tree(browser); | ||
| 155 | perf_top__header_snprintf(top, title, sizeof(title)); | ||
| 156 | perf_top__reset_sample_counters(top); | ||
| 157 | |||
| 158 | if (ui_browser__show(&browser->b, title, | ||
| 159 | "ESC: exit, ENTER|->|a: Live Annotate") < 0) | ||
| 160 | return -1; | ||
| 161 | |||
| 162 | newtFormSetTimer(browser->b.form, delay_msecs); | ||
| 163 | ui_browser__add_exit_keys(&browser->b, exit_keys); | ||
| 164 | |||
| 165 | while (1) { | ||
| 166 | key = ui_browser__run(&browser->b); | ||
| 167 | |||
| 168 | switch (key) { | ||
| 169 | case -1: | ||
| 170 | /* FIXME we need to check if it was es.reason == NEWT_EXIT_TIMER */ | ||
| 171 | perf_top_browser__update_rb_tree(browser); | ||
| 172 | perf_top__header_snprintf(top, title, sizeof(title)); | ||
| 173 | perf_top__reset_sample_counters(top); | ||
| 174 | ui_browser__set_color(&browser->b, NEWT_COLORSET_ROOT); | ||
| 175 | SLsmg_gotorc(0, 0); | ||
| 176 | slsmg_write_nstring(title, browser->b.width); | ||
| 177 | break; | ||
| 178 | case 'a': | ||
| 179 | case NEWT_KEY_RIGHT: | ||
| 180 | case NEWT_KEY_ENTER: | ||
| 181 | if (browser->selection) | ||
| 182 | perf_top_browser__annotate(browser); | ||
| 183 | break; | ||
| 184 | case NEWT_KEY_LEFT: | ||
| 185 | continue; | ||
| 186 | case NEWT_KEY_ESCAPE: | ||
| 187 | if (!ui__dialog_yesno("Do you really want to exit?")) | ||
| 188 | continue; | ||
| 189 | /* Fall thru */ | ||
| 190 | default: | ||
| 191 | goto out; | ||
| 192 | } | ||
| 193 | } | ||
| 194 | out: | ||
| 195 | ui_browser__hide(&browser->b); | ||
| 196 | return key; | ||
| 197 | } | ||
| 198 | |||
| 199 | int perf_top__tui_browser(struct perf_top *top) | ||
| 200 | { | ||
| 201 | struct perf_top_browser browser = { | ||
| 202 | .b = { | ||
| 203 | .entries = &browser.root, | ||
| 204 | .refresh = ui_browser__rb_tree_refresh, | ||
| 205 | .seek = ui_browser__rb_tree_seek, | ||
| 206 | .write = perf_top_browser__write, | ||
| 207 | .priv = top, | ||
| 208 | }, | ||
| 209 | }; | ||
| 210 | |||
| 211 | return perf_top_browser__run(&browser); | ||
| 212 | } | ||
diff --git a/tools/perf/util/ui/helpline.c b/tools/perf/util/ui/helpline.c index f36d2ff509ed..6ef3c5691762 100644 --- a/tools/perf/util/ui/helpline.c +++ b/tools/perf/util/ui/helpline.c | |||
| @@ -1,20 +1,28 @@ | |||
| 1 | #define _GNU_SOURCE | 1 | #define _GNU_SOURCE |
| 2 | #include <stdio.h> | 2 | #include <stdio.h> |
| 3 | #include <stdlib.h> | 3 | #include <stdlib.h> |
| 4 | #include <newt.h> | 4 | #include <string.h> |
| 5 | 5 | ||
| 6 | #include "../debug.h" | 6 | #include "../debug.h" |
| 7 | #include "helpline.h" | 7 | #include "helpline.h" |
| 8 | #include "ui.h" | 8 | #include "ui.h" |
| 9 | #include "libslang.h" | ||
| 9 | 10 | ||
| 10 | void ui_helpline__pop(void) | 11 | void ui_helpline__pop(void) |
| 11 | { | 12 | { |
| 12 | newtPopHelpLine(); | ||
| 13 | } | 13 | } |
| 14 | 14 | ||
| 15 | char ui_helpline__current[512]; | ||
| 16 | |||
| 15 | void ui_helpline__push(const char *msg) | 17 | void ui_helpline__push(const char *msg) |
| 16 | { | 18 | { |
| 17 | newtPushHelpLine(msg); | 19 | const size_t sz = sizeof(ui_helpline__current); |
| 20 | |||
| 21 | SLsmg_gotorc(SLtt_Screen_Rows - 1, 0); | ||
| 22 | SLsmg_set_color(0); | ||
| 23 | SLsmg_write_nstring((char *)msg, SLtt_Screen_Cols); | ||
| 24 | SLsmg_refresh(); | ||
| 25 | strncpy(ui_helpline__current, msg, sz)[sz - 1] = '\0'; | ||
| 18 | } | 26 | } |
| 19 | 27 | ||
| 20 | void ui_helpline__vpush(const char *fmt, va_list ap) | 28 | void ui_helpline__vpush(const char *fmt, va_list ap) |
| @@ -63,7 +71,7 @@ int ui_helpline__show_help(const char *format, va_list ap) | |||
| 63 | 71 | ||
| 64 | if (ui_helpline__last_msg[backlog - 1] == '\n') { | 72 | if (ui_helpline__last_msg[backlog - 1] == '\n') { |
| 65 | ui_helpline__puts(ui_helpline__last_msg); | 73 | ui_helpline__puts(ui_helpline__last_msg); |
| 66 | newtRefresh(); | 74 | SLsmg_refresh(); |
| 67 | backlog = 0; | 75 | backlog = 0; |
| 68 | } | 76 | } |
| 69 | pthread_mutex_unlock(&ui__lock); | 77 | pthread_mutex_unlock(&ui__lock); |
diff --git a/tools/perf/util/ui/helpline.h b/tools/perf/util/ui/helpline.h index ab6028d0c401..7bab6b34e35e 100644 --- a/tools/perf/util/ui/helpline.h +++ b/tools/perf/util/ui/helpline.h | |||
| @@ -1,6 +1,9 @@ | |||
| 1 | #ifndef _PERF_UI_HELPLINE_H_ | 1 | #ifndef _PERF_UI_HELPLINE_H_ |
| 2 | #define _PERF_UI_HELPLINE_H_ 1 | 2 | #define _PERF_UI_HELPLINE_H_ 1 |
| 3 | 3 | ||
| 4 | #include <stdio.h> | ||
| 5 | #include <stdarg.h> | ||
| 6 | |||
| 4 | void ui_helpline__init(void); | 7 | void ui_helpline__init(void); |
| 5 | void ui_helpline__pop(void); | 8 | void ui_helpline__pop(void); |
| 6 | void ui_helpline__push(const char *msg); | 9 | void ui_helpline__push(const char *msg); |
| @@ -8,4 +11,6 @@ void ui_helpline__vpush(const char *fmt, va_list ap); | |||
| 8 | void ui_helpline__fpush(const char *fmt, ...); | 11 | void ui_helpline__fpush(const char *fmt, ...); |
| 9 | void ui_helpline__puts(const char *msg); | 12 | void ui_helpline__puts(const char *msg); |
| 10 | 13 | ||
| 14 | extern char ui_helpline__current[]; | ||
| 15 | |||
| 11 | #endif /* _PERF_UI_HELPLINE_H_ */ | 16 | #endif /* _PERF_UI_HELPLINE_H_ */ |
diff --git a/tools/perf/util/ui/keysyms.h b/tools/perf/util/ui/keysyms.h new file mode 100644 index 000000000000..3458b1985761 --- /dev/null +++ b/tools/perf/util/ui/keysyms.h | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | #ifndef _PERF_KEYSYMS_H_ | ||
| 2 | #define _PERF_KEYSYMS_H_ 1 | ||
| 3 | |||
| 4 | #include "libslang.h" | ||
| 5 | |||
| 6 | #define K_DOWN SL_KEY_DOWN | ||
| 7 | #define K_END SL_KEY_END | ||
| 8 | #define K_ENTER '\r' | ||
| 9 | #define K_ESC 033 | ||
| 10 | #define K_F1 SL_KEY_F(1) | ||
| 11 | #define K_HOME SL_KEY_HOME | ||
| 12 | #define K_LEFT SL_KEY_LEFT | ||
| 13 | #define K_PGDN SL_KEY_NPAGE | ||
| 14 | #define K_PGUP SL_KEY_PPAGE | ||
| 15 | #define K_RIGHT SL_KEY_RIGHT | ||
| 16 | #define K_TAB '\t' | ||
| 17 | #define K_UNTAB SL_KEY_UNTAB | ||
| 18 | #define K_UP SL_KEY_UP | ||
| 19 | |||
| 20 | /* Not really keys */ | ||
| 21 | #define K_TIMER -1 | ||
| 22 | #define K_ERROR -2 | ||
| 23 | #define K_RESIZE -3 | ||
| 24 | |||
| 25 | #endif /* _PERF_KEYSYMS_H_ */ | ||
diff --git a/tools/perf/util/ui/libslang.h b/tools/perf/util/ui/libslang.h index 2b63e1c9b181..4d54b6450f5b 100644 --- a/tools/perf/util/ui/libslang.h +++ b/tools/perf/util/ui/libslang.h | |||
| @@ -24,4 +24,6 @@ | |||
| 24 | #define sltt_set_color SLtt_set_color | 24 | #define sltt_set_color SLtt_set_color |
| 25 | #endif | 25 | #endif |
| 26 | 26 | ||
| 27 | #define SL_KEY_UNTAB 0x1000 | ||
| 28 | |||
| 27 | #endif /* _PERF_UI_SLANG_H_ */ | 29 | #endif /* _PERF_UI_SLANG_H_ */ |
diff --git a/tools/perf/util/ui/progress.c b/tools/perf/util/ui/progress.c index d7fc399d36b3..295e366b6311 100644 --- a/tools/perf/util/ui/progress.c +++ b/tools/perf/util/ui/progress.c | |||
| @@ -1,60 +1,29 @@ | |||
| 1 | #include <stdlib.h> | ||
| 2 | #include <newt.h> | ||
| 3 | #include "../cache.h" | 1 | #include "../cache.h" |
| 4 | #include "progress.h" | 2 | #include "progress.h" |
| 3 | #include "libslang.h" | ||
| 4 | #include "ui.h" | ||
| 5 | #include "browser.h" | ||
| 5 | 6 | ||
| 6 | struct ui_progress { | 7 | void ui_progress__update(u64 curr, u64 total, const char *title) |
| 7 | newtComponent form, scale; | ||
| 8 | }; | ||
| 9 | |||
| 10 | struct ui_progress *ui_progress__new(const char *title, u64 total) | ||
| 11 | { | ||
| 12 | struct ui_progress *self = malloc(sizeof(*self)); | ||
| 13 | |||
| 14 | if (self != NULL) { | ||
| 15 | int cols; | ||
| 16 | |||
| 17 | if (use_browser <= 0) | ||
| 18 | return self; | ||
| 19 | newtGetScreenSize(&cols, NULL); | ||
| 20 | cols -= 4; | ||
| 21 | newtCenteredWindow(cols, 1, title); | ||
| 22 | self->form = newtForm(NULL, NULL, 0); | ||
| 23 | if (self->form == NULL) | ||
| 24 | goto out_free_self; | ||
| 25 | self->scale = newtScale(0, 0, cols, total); | ||
| 26 | if (self->scale == NULL) | ||
| 27 | goto out_free_form; | ||
| 28 | newtFormAddComponent(self->form, self->scale); | ||
| 29 | newtRefresh(); | ||
| 30 | } | ||
| 31 | |||
| 32 | return self; | ||
| 33 | |||
| 34 | out_free_form: | ||
| 35 | newtFormDestroy(self->form); | ||
| 36 | out_free_self: | ||
| 37 | free(self); | ||
| 38 | return NULL; | ||
| 39 | } | ||
| 40 | |||
| 41 | void ui_progress__update(struct ui_progress *self, u64 curr) | ||
| 42 | { | 8 | { |
| 9 | int bar, y; | ||
| 43 | /* | 10 | /* |
| 44 | * FIXME: We should have a per UI backend way of showing progress, | 11 | * FIXME: We should have a per UI backend way of showing progress, |
| 45 | * stdio will just show a percentage as NN%, etc. | 12 | * stdio will just show a percentage as NN%, etc. |
| 46 | */ | 13 | */ |
| 47 | if (use_browser <= 0) | 14 | if (use_browser <= 0) |
| 48 | return; | 15 | return; |
| 49 | newtScaleSet(self->scale, curr); | ||
| 50 | newtRefresh(); | ||
| 51 | } | ||
| 52 | 16 | ||
| 53 | void ui_progress__delete(struct ui_progress *self) | 17 | ui__refresh_dimensions(true); |
| 54 | { | 18 | pthread_mutex_lock(&ui__lock); |
| 55 | if (use_browser > 0) { | 19 | y = SLtt_Screen_Rows / 2 - 2; |
| 56 | newtFormDestroy(self->form); | 20 | SLsmg_set_color(0); |
| 57 | newtPopWindow(); | 21 | SLsmg_draw_box(y, 0, 3, SLtt_Screen_Cols); |
| 58 | } | 22 | SLsmg_gotorc(y++, 1); |
| 59 | free(self); | 23 | SLsmg_write_string((char *)title); |
| 24 | SLsmg_set_color(HE_COLORSET_SELECTED); | ||
| 25 | bar = ((SLtt_Screen_Cols - 2) * curr) / total; | ||
| 26 | SLsmg_fill_region(y, 1, 1, bar, ' '); | ||
| 27 | SLsmg_refresh(); | ||
| 28 | pthread_mutex_unlock(&ui__lock); | ||
| 60 | } | 29 | } |
diff --git a/tools/perf/util/ui/progress.h b/tools/perf/util/ui/progress.h index a3820a0beb5b..d9c205b59aa1 100644 --- a/tools/perf/util/ui/progress.h +++ b/tools/perf/util/ui/progress.h | |||
| @@ -1,11 +1,8 @@ | |||
| 1 | #ifndef _PERF_UI_PROGRESS_H_ | 1 | #ifndef _PERF_UI_PROGRESS_H_ |
| 2 | #define _PERF_UI_PROGRESS_H_ 1 | 2 | #define _PERF_UI_PROGRESS_H_ 1 |
| 3 | 3 | ||
| 4 | struct ui_progress; | 4 | #include <../types.h> |
| 5 | 5 | ||
| 6 | struct ui_progress *ui_progress__new(const char *title, u64 total); | 6 | void ui_progress__update(u64 curr, u64 total, const char *title); |
| 7 | void ui_progress__delete(struct ui_progress *self); | ||
| 8 | |||
| 9 | void ui_progress__update(struct ui_progress *self, u64 curr); | ||
| 10 | 7 | ||
| 11 | #endif | 8 | #endif |
diff --git a/tools/perf/util/ui/setup.c b/tools/perf/util/ui/setup.c index ee46d671db59..85a69faa09aa 100644 --- a/tools/perf/util/ui/setup.c +++ b/tools/perf/util/ui/setup.c | |||
| @@ -7,9 +7,85 @@ | |||
| 7 | #include "browser.h" | 7 | #include "browser.h" |
| 8 | #include "helpline.h" | 8 | #include "helpline.h" |
| 9 | #include "ui.h" | 9 | #include "ui.h" |
| 10 | #include "util.h" | ||
| 11 | #include "libslang.h" | ||
| 12 | #include "keysyms.h" | ||
| 10 | 13 | ||
| 11 | pthread_mutex_t ui__lock = PTHREAD_MUTEX_INITIALIZER; | 14 | pthread_mutex_t ui__lock = PTHREAD_MUTEX_INITIALIZER; |
| 12 | 15 | ||
| 16 | static volatile int ui__need_resize; | ||
| 17 | |||
| 18 | void ui__refresh_dimensions(bool force) | ||
| 19 | { | ||
| 20 | if (force || ui__need_resize) { | ||
| 21 | ui__need_resize = 0; | ||
| 22 | pthread_mutex_lock(&ui__lock); | ||
| 23 | SLtt_get_screen_size(); | ||
| 24 | SLsmg_reinit_smg(); | ||
| 25 | pthread_mutex_unlock(&ui__lock); | ||
| 26 | } | ||
| 27 | } | ||
| 28 | |||
| 29 | static void ui__sigwinch(int sig __used) | ||
| 30 | { | ||
| 31 | ui__need_resize = 1; | ||
| 32 | } | ||
| 33 | |||
| 34 | static void ui__setup_sigwinch(void) | ||
| 35 | { | ||
| 36 | static bool done; | ||
| 37 | |||
| 38 | if (done) | ||
| 39 | return; | ||
| 40 | |||
| 41 | done = true; | ||
| 42 | pthread__unblock_sigwinch(); | ||
| 43 | signal(SIGWINCH, ui__sigwinch); | ||
| 44 | } | ||
| 45 | |||
| 46 | int ui__getch(int delay_secs) | ||
| 47 | { | ||
| 48 | struct timeval timeout, *ptimeout = delay_secs ? &timeout : NULL; | ||
| 49 | fd_set read_set; | ||
| 50 | int err, key; | ||
| 51 | |||
| 52 | ui__setup_sigwinch(); | ||
| 53 | |||
| 54 | FD_ZERO(&read_set); | ||
| 55 | FD_SET(0, &read_set); | ||
| 56 | |||
| 57 | if (delay_secs) { | ||
| 58 | timeout.tv_sec = delay_secs; | ||
| 59 | timeout.tv_usec = 0; | ||
| 60 | } | ||
| 61 | |||
| 62 | err = select(1, &read_set, NULL, NULL, ptimeout); | ||
| 63 | |||
| 64 | if (err == 0) | ||
| 65 | return K_TIMER; | ||
| 66 | |||
| 67 | if (err == -1) { | ||
| 68 | if (errno == EINTR) | ||
| 69 | return K_RESIZE; | ||
| 70 | return K_ERROR; | ||
| 71 | } | ||
| 72 | |||
| 73 | key = SLang_getkey(); | ||
| 74 | if (key != K_ESC) | ||
| 75 | return key; | ||
| 76 | |||
| 77 | FD_ZERO(&read_set); | ||
| 78 | FD_SET(0, &read_set); | ||
| 79 | timeout.tv_sec = 0; | ||
| 80 | timeout.tv_usec = 20; | ||
| 81 | err = select(1, &read_set, NULL, NULL, &timeout); | ||
| 82 | if (err == 0) | ||
| 83 | return K_ESC; | ||
| 84 | |||
| 85 | SLang_ungetkey(key); | ||
| 86 | return SLkp_getkey(); | ||
| 87 | } | ||
| 88 | |||
| 13 | static void newt_suspend(void *d __used) | 89 | static void newt_suspend(void *d __used) |
| 14 | { | 90 | { |
| 15 | newtSuspend(); | 91 | newtSuspend(); |
| @@ -17,6 +93,33 @@ static void newt_suspend(void *d __used) | |||
| 17 | newtResume(); | 93 | newtResume(); |
| 18 | } | 94 | } |
| 19 | 95 | ||
| 96 | static int ui__init(void) | ||
| 97 | { | ||
| 98 | int err = SLkp_init(); | ||
| 99 | |||
| 100 | if (err < 0) | ||
| 101 | goto out; | ||
| 102 | |||
| 103 | SLkp_define_keysym((char *)"^(kB)", SL_KEY_UNTAB); | ||
| 104 | out: | ||
| 105 | return err; | ||
| 106 | } | ||
| 107 | |||
| 108 | static void ui__exit(void) | ||
| 109 | { | ||
| 110 | SLtt_set_cursor_visibility(1); | ||
| 111 | SLsmg_refresh(); | ||
| 112 | SLsmg_reset_smg(); | ||
| 113 | SLang_reset_tty(); | ||
| 114 | } | ||
| 115 | |||
| 116 | static void ui__signal(int sig) | ||
| 117 | { | ||
| 118 | ui__exit(); | ||
| 119 | psignal(sig, "perf"); | ||
| 120 | exit(0); | ||
| 121 | } | ||
| 122 | |||
| 20 | void setup_browser(bool fallback_to_pager) | 123 | void setup_browser(bool fallback_to_pager) |
| 21 | { | 124 | { |
| 22 | if (!isatty(1) || !use_browser || dump_trace) { | 125 | if (!isatty(1) || !use_browser || dump_trace) { |
| @@ -28,19 +131,25 @@ void setup_browser(bool fallback_to_pager) | |||
| 28 | 131 | ||
| 29 | use_browser = 1; | 132 | use_browser = 1; |
| 30 | newtInit(); | 133 | newtInit(); |
| 31 | newtCls(); | 134 | ui__init(); |
| 32 | newtSetSuspendCallback(newt_suspend, NULL); | 135 | newtSetSuspendCallback(newt_suspend, NULL); |
| 33 | ui_helpline__init(); | 136 | ui_helpline__init(); |
| 34 | ui_browser__init(); | 137 | ui_browser__init(); |
| 138 | |||
| 139 | signal(SIGSEGV, ui__signal); | ||
| 140 | signal(SIGFPE, ui__signal); | ||
| 141 | signal(SIGINT, ui__signal); | ||
| 142 | signal(SIGQUIT, ui__signal); | ||
| 143 | signal(SIGTERM, ui__signal); | ||
| 35 | } | 144 | } |
| 36 | 145 | ||
| 37 | void exit_browser(bool wait_for_ok) | 146 | void exit_browser(bool wait_for_ok) |
| 38 | { | 147 | { |
| 39 | if (use_browser > 0) { | 148 | if (use_browser > 0) { |
| 40 | if (wait_for_ok) { | 149 | if (wait_for_ok) |
| 41 | char title[] = "Fatal Error", ok[] = "Ok"; | 150 | ui__question_window("Fatal Error", |
| 42 | newtWinMessage(title, ok, ui_helpline__last_msg); | 151 | ui_helpline__last_msg, |
| 43 | } | 152 | "Press any key...", 0); |
| 44 | newtFinished(); | 153 | ui__exit(); |
| 45 | } | 154 | } |
| 46 | } | 155 | } |
diff --git a/tools/perf/util/ui/ui.h b/tools/perf/util/ui/ui.h index d264e059c829..7b67045479f6 100644 --- a/tools/perf/util/ui/ui.h +++ b/tools/perf/util/ui/ui.h | |||
| @@ -2,7 +2,10 @@ | |||
| 2 | #define _PERF_UI_H_ 1 | 2 | #define _PERF_UI_H_ 1 |
| 3 | 3 | ||
| 4 | #include <pthread.h> | 4 | #include <pthread.h> |
| 5 | #include <stdbool.h> | ||
| 5 | 6 | ||
| 6 | extern pthread_mutex_t ui__lock; | 7 | extern pthread_mutex_t ui__lock; |
| 7 | 8 | ||
| 9 | void ui__refresh_dimensions(bool force); | ||
| 10 | |||
| 8 | #endif /* _PERF_UI_H_ */ | 11 | #endif /* _PERF_UI_H_ */ |
diff --git a/tools/perf/util/ui/util.c b/tools/perf/util/ui/util.c index fdf1fc8f08bc..45daa7c41dad 100644 --- a/tools/perf/util/ui/util.c +++ b/tools/perf/util/ui/util.c | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | #include <newt.h> | 1 | #include "../util.h" |
| 2 | #include <signal.h> | 2 | #include <signal.h> |
| 3 | #include <stdio.h> | ||
| 4 | #include <stdbool.h> | 3 | #include <stdbool.h> |
| 5 | #include <string.h> | 4 | #include <string.h> |
| 6 | #include <sys/ttydefaults.h> | 5 | #include <sys/ttydefaults.h> |
| @@ -8,72 +7,75 @@ | |||
| 8 | #include "../cache.h" | 7 | #include "../cache.h" |
| 9 | #include "../debug.h" | 8 | #include "../debug.h" |
| 10 | #include "browser.h" | 9 | #include "browser.h" |
| 10 | #include "keysyms.h" | ||
| 11 | #include "helpline.h" | 11 | #include "helpline.h" |
| 12 | #include "ui.h" | 12 | #include "ui.h" |
| 13 | #include "util.h" | 13 | #include "util.h" |
| 14 | #include "libslang.h" | ||
| 14 | 15 | ||
| 15 | static void newt_form__set_exit_keys(newtComponent self) | 16 | static void ui_browser__argv_write(struct ui_browser *browser, |
| 17 | void *entry, int row) | ||
| 16 | { | 18 | { |
| 17 | newtFormAddHotKey(self, NEWT_KEY_LEFT); | 19 | char **arg = entry; |
| 18 | newtFormAddHotKey(self, NEWT_KEY_ESCAPE); | 20 | bool current_entry = ui_browser__is_current_entry(browser, row); |
| 19 | newtFormAddHotKey(self, 'Q'); | ||
| 20 | newtFormAddHotKey(self, 'q'); | ||
| 21 | newtFormAddHotKey(self, CTRL('c')); | ||
| 22 | } | ||
| 23 | 21 | ||
| 24 | static newtComponent newt_form__new(void) | 22 | ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED : |
| 25 | { | 23 | HE_COLORSET_NORMAL); |
| 26 | newtComponent self = newtForm(NULL, NULL, 0); | 24 | slsmg_write_nstring(*arg, browser->width); |
| 27 | if (self) | ||
| 28 | newt_form__set_exit_keys(self); | ||
| 29 | return self; | ||
| 30 | } | 25 | } |
| 31 | 26 | ||
| 32 | int ui__popup_menu(int argc, char * const argv[]) | 27 | static int popup_menu__run(struct ui_browser *menu) |
| 33 | { | 28 | { |
| 34 | struct newtExitStruct es; | 29 | int key; |
| 35 | int i, rc = -1, max_len = 5; | ||
| 36 | newtComponent listbox, form = newt_form__new(); | ||
| 37 | 30 | ||
| 38 | if (form == NULL) | 31 | if (ui_browser__show(menu, " ", "ESC: exit, ENTER|->: Select option") < 0) |
| 39 | return -1; | 32 | return -1; |
| 40 | 33 | ||
| 41 | listbox = newtListbox(0, 0, argc, NEWT_FLAG_RETURNEXIT); | 34 | while (1) { |
| 42 | if (listbox == NULL) | 35 | key = ui_browser__run(menu, 0); |
| 43 | goto out_destroy_form; | ||
| 44 | 36 | ||
| 45 | newtFormAddComponent(form, listbox); | 37 | switch (key) { |
| 38 | case K_RIGHT: | ||
| 39 | case K_ENTER: | ||
| 40 | key = menu->index; | ||
| 41 | break; | ||
| 42 | case K_LEFT: | ||
| 43 | case K_ESC: | ||
| 44 | case 'q': | ||
| 45 | case CTRL('c'): | ||
| 46 | key = -1; | ||
| 47 | break; | ||
| 48 | default: | ||
| 49 | continue; | ||
| 50 | } | ||
| 46 | 51 | ||
| 47 | for (i = 0; i < argc; ++i) { | 52 | break; |
| 48 | int len = strlen(argv[i]); | ||
| 49 | if (len > max_len) | ||
| 50 | max_len = len; | ||
| 51 | if (newtListboxAddEntry(listbox, argv[i], (void *)(long)i)) | ||
| 52 | goto out_destroy_form; | ||
| 53 | } | 53 | } |
| 54 | 54 | ||
| 55 | newtCenteredWindow(max_len, argc, NULL); | 55 | ui_browser__hide(menu); |
| 56 | newtFormRun(form, &es); | 56 | return key; |
| 57 | rc = newtListboxGetCurrent(listbox) - NULL; | ||
| 58 | if (es.reason == NEWT_EXIT_HOTKEY) | ||
| 59 | rc = -1; | ||
| 60 | newtPopWindow(); | ||
| 61 | out_destroy_form: | ||
| 62 | newtFormDestroy(form); | ||
| 63 | return rc; | ||
| 64 | } | 57 | } |
| 65 | 58 | ||
| 66 | int ui__help_window(const char *text) | 59 | int ui__popup_menu(int argc, char * const argv[]) |
| 67 | { | 60 | { |
| 68 | struct newtExitStruct es; | 61 | struct ui_browser menu = { |
| 69 | newtComponent tb, form = newt_form__new(); | 62 | .entries = (void *)argv, |
| 70 | int rc = -1; | 63 | .refresh = ui_browser__argv_refresh, |
| 64 | .seek = ui_browser__argv_seek, | ||
| 65 | .write = ui_browser__argv_write, | ||
| 66 | .nr_entries = argc, | ||
| 67 | }; | ||
| 68 | |||
| 69 | return popup_menu__run(&menu); | ||
| 70 | } | ||
| 71 | |||
| 72 | int ui__question_window(const char *title, const char *text, | ||
| 73 | const char *exit_msg, int delay_secs) | ||
| 74 | { | ||
| 75 | int x, y; | ||
| 71 | int max_len = 0, nr_lines = 0; | 76 | int max_len = 0, nr_lines = 0; |
| 72 | const char *t; | 77 | const char *t; |
| 73 | 78 | ||
| 74 | if (form == NULL) | ||
| 75 | return -1; | ||
| 76 | |||
| 77 | t = text; | 79 | t = text; |
| 78 | while (1) { | 80 | while (1) { |
| 79 | const char *sep = strchr(t, '\n'); | 81 | const char *sep = strchr(t, '\n'); |
| @@ -90,41 +92,77 @@ int ui__help_window(const char *text) | |||
| 90 | t = sep + 1; | 92 | t = sep + 1; |
| 91 | } | 93 | } |
| 92 | 94 | ||
| 93 | tb = newtTextbox(0, 0, max_len, nr_lines, 0); | 95 | max_len += 2; |
| 94 | if (tb == NULL) | 96 | nr_lines += 4; |
| 95 | goto out_destroy_form; | 97 | y = SLtt_Screen_Rows / 2 - nr_lines / 2, |
| 96 | 98 | x = SLtt_Screen_Cols / 2 - max_len / 2; | |
| 97 | newtTextboxSetText(tb, text); | 99 | |
| 98 | newtFormAddComponent(form, tb); | 100 | SLsmg_set_color(0); |
| 99 | newtCenteredWindow(max_len, nr_lines, NULL); | 101 | SLsmg_draw_box(y, x++, nr_lines, max_len); |
| 100 | newtFormRun(form, &es); | 102 | if (title) { |
| 101 | newtPopWindow(); | 103 | SLsmg_gotorc(y, x + 1); |
| 102 | rc = 0; | 104 | SLsmg_write_string((char *)title); |
| 103 | out_destroy_form: | 105 | } |
| 104 | newtFormDestroy(form); | 106 | SLsmg_gotorc(++y, x); |
| 105 | return rc; | 107 | nr_lines -= 2; |
| 108 | max_len -= 2; | ||
| 109 | SLsmg_write_wrapped_string((unsigned char *)text, y, x, | ||
| 110 | nr_lines, max_len, 1); | ||
| 111 | SLsmg_gotorc(y + nr_lines - 2, x); | ||
| 112 | SLsmg_write_nstring((char *)" ", max_len); | ||
| 113 | SLsmg_gotorc(y + nr_lines - 1, x); | ||
| 114 | SLsmg_write_nstring((char *)exit_msg, max_len); | ||
| 115 | SLsmg_refresh(); | ||
| 116 | return ui__getch(delay_secs); | ||
| 106 | } | 117 | } |
| 107 | 118 | ||
| 108 | static const char yes[] = "Yes", no[] = "No", | 119 | int ui__help_window(const char *text) |
| 109 | warning_str[] = "Warning!", ok[] = "Ok"; | 120 | { |
| 121 | return ui__question_window("Help", text, "Press any key...", 0); | ||
| 122 | } | ||
| 110 | 123 | ||
| 111 | bool ui__dialog_yesno(const char *msg) | 124 | int ui__dialog_yesno(const char *msg) |
| 112 | { | 125 | { |
| 113 | /* newtWinChoice should really be accepting const char pointers... */ | 126 | return ui__question_window(NULL, msg, "Enter: Yes, ESC: No", 0); |
| 114 | return newtWinChoice(NULL, (char *)yes, (char *)no, (char *)msg) == 1; | ||
| 115 | } | 127 | } |
| 116 | 128 | ||
| 117 | void ui__warning(const char *format, ...) | 129 | int __ui__warning(const char *title, const char *format, va_list args) |
| 118 | { | 130 | { |
| 119 | va_list args; | 131 | char *s; |
| 132 | |||
| 133 | if (use_browser > 0 && vasprintf(&s, format, args) > 0) { | ||
| 134 | int key; | ||
| 120 | 135 | ||
| 121 | va_start(args, format); | ||
| 122 | if (use_browser > 0) { | ||
| 123 | pthread_mutex_lock(&ui__lock); | 136 | pthread_mutex_lock(&ui__lock); |
| 124 | newtWinMessagev((char *)warning_str, (char *)ok, | 137 | key = ui__question_window(title, s, "Press any key...", 0); |
| 125 | (char *)format, args); | ||
| 126 | pthread_mutex_unlock(&ui__lock); | 138 | pthread_mutex_unlock(&ui__lock); |
| 127 | } else | 139 | free(s); |
| 128 | vfprintf(stderr, format, args); | 140 | return key; |
| 141 | } | ||
| 142 | |||
| 143 | fprintf(stderr, "%s:\n", title); | ||
| 144 | vfprintf(stderr, format, args); | ||
| 145 | return K_ESC; | ||
| 146 | } | ||
| 147 | |||
| 148 | int ui__warning(const char *format, ...) | ||
| 149 | { | ||
| 150 | int key; | ||
| 151 | va_list args; | ||
| 152 | |||
| 153 | va_start(args, format); | ||
| 154 | key = __ui__warning("Warning", format, args); | ||
| 155 | va_end(args); | ||
| 156 | return key; | ||
| 157 | } | ||
| 158 | |||
| 159 | int ui__error(const char *format, ...) | ||
| 160 | { | ||
| 161 | int key; | ||
| 162 | va_list args; | ||
| 163 | |||
| 164 | va_start(args, format); | ||
| 165 | key = __ui__warning("Error", format, args); | ||
| 129 | va_end(args); | 166 | va_end(args); |
| 167 | return key; | ||
| 130 | } | 168 | } |
diff --git a/tools/perf/util/ui/util.h b/tools/perf/util/ui/util.h index afcbc1d99531..2d1738bd71c8 100644 --- a/tools/perf/util/ui/util.h +++ b/tools/perf/util/ui/util.h | |||
| @@ -1,10 +1,14 @@ | |||
| 1 | #ifndef _PERF_UI_UTIL_H_ | 1 | #ifndef _PERF_UI_UTIL_H_ |
| 2 | #define _PERF_UI_UTIL_H_ 1 | 2 | #define _PERF_UI_UTIL_H_ 1 |
| 3 | 3 | ||
| 4 | #include <stdbool.h> | 4 | #include <stdarg.h> |
| 5 | 5 | ||
| 6 | int ui__getch(int delay_secs); | ||
| 6 | int ui__popup_menu(int argc, char * const argv[]); | 7 | int ui__popup_menu(int argc, char * const argv[]); |
| 7 | int ui__help_window(const char *text); | 8 | int ui__help_window(const char *text); |
| 8 | bool ui__dialog_yesno(const char *msg); | 9 | int ui__dialog_yesno(const char *msg); |
| 10 | int ui__question_window(const char *title, const char *text, | ||
| 11 | const char *exit_msg, int delay_secs); | ||
| 12 | int __ui__warning(const char *title, const char *format, va_list args); | ||
| 9 | 13 | ||
| 10 | #endif /* _PERF_UI_UTIL_H_ */ | 14 | #endif /* _PERF_UI_UTIL_H_ */ |
