diff options
Diffstat (limited to 'tools')
30 files changed, 1955 insertions, 322 deletions
diff --git a/tools/perf/Documentation/Makefile b/tools/perf/Documentation/Makefile index 5457192e1b41..bdd3b7ecad0a 100644 --- a/tools/perf/Documentation/Makefile +++ b/tools/perf/Documentation/Makefile | |||
| @@ -35,7 +35,7 @@ man7dir=$(mandir)/man7 | |||
| 35 | # DESTDIR= | 35 | # DESTDIR= |
| 36 | 36 | ||
| 37 | ASCIIDOC=asciidoc | 37 | ASCIIDOC=asciidoc |
| 38 | ASCIIDOC_EXTRA = | 38 | ASCIIDOC_EXTRA = --unsafe |
| 39 | MANPAGE_XSL = manpage-normal.xsl | 39 | MANPAGE_XSL = manpage-normal.xsl |
| 40 | XMLTO_EXTRA = | 40 | XMLTO_EXTRA = |
| 41 | INSTALL?=install | 41 | INSTALL?=install |
diff --git a/tools/perf/Documentation/examples.txt b/tools/perf/Documentation/examples.txt new file mode 100644 index 000000000000..8eb6c489fb15 --- /dev/null +++ b/tools/perf/Documentation/examples.txt | |||
| @@ -0,0 +1,225 @@ | |||
| 1 | |||
| 2 | ------------------------------ | ||
| 3 | ****** perf by examples ****** | ||
| 4 | ------------------------------ | ||
| 5 | |||
| 6 | [ From an e-mail by Ingo Molnar, http://lkml.org/lkml/2009/8/4/346 ] | ||
| 7 | |||
| 8 | |||
| 9 | First, discovery/enumeration of available counters can be done via | ||
| 10 | 'perf list': | ||
| 11 | |||
| 12 | titan:~> perf list | ||
| 13 | [...] | ||
| 14 | kmem:kmalloc [Tracepoint event] | ||
| 15 | kmem:kmem_cache_alloc [Tracepoint event] | ||
| 16 | kmem:kmalloc_node [Tracepoint event] | ||
| 17 | kmem:kmem_cache_alloc_node [Tracepoint event] | ||
| 18 | kmem:kfree [Tracepoint event] | ||
| 19 | kmem:kmem_cache_free [Tracepoint event] | ||
| 20 | kmem:mm_page_free_direct [Tracepoint event] | ||
| 21 | kmem:mm_pagevec_free [Tracepoint event] | ||
| 22 | kmem:mm_page_alloc [Tracepoint event] | ||
| 23 | kmem:mm_page_alloc_zone_locked [Tracepoint event] | ||
| 24 | kmem:mm_page_pcpu_drain [Tracepoint event] | ||
| 25 | kmem:mm_page_alloc_extfrag [Tracepoint event] | ||
| 26 | |||
| 27 | Then any (or all) of the above event sources can be activated and | ||
| 28 | measured. For example the page alloc/free properties of a 'hackbench | ||
| 29 | run' are: | ||
| 30 | |||
| 31 | titan:~> perf stat -e kmem:mm_page_pcpu_drain -e kmem:mm_page_alloc | ||
| 32 | -e kmem:mm_pagevec_free -e kmem:mm_page_free_direct ./hackbench 10 | ||
| 33 | Time: 0.575 | ||
| 34 | |||
| 35 | Performance counter stats for './hackbench 10': | ||
| 36 | |||
| 37 | 13857 kmem:mm_page_pcpu_drain | ||
| 38 | 27576 kmem:mm_page_alloc | ||
| 39 | 6025 kmem:mm_pagevec_free | ||
| 40 | 20934 kmem:mm_page_free_direct | ||
| 41 | |||
| 42 | 0.613972165 seconds time elapsed | ||
| 43 | |||
| 44 | You can observe the statistical properties as well, by using the | ||
| 45 | 'repeat the workload N times' feature of perf stat: | ||
| 46 | |||
| 47 | titan:~> perf stat --repeat 5 -e kmem:mm_page_pcpu_drain -e | ||
| 48 | kmem:mm_page_alloc -e kmem:mm_pagevec_free -e | ||
| 49 | kmem:mm_page_free_direct ./hackbench 10 | ||
| 50 | Time: 0.627 | ||
| 51 | Time: 0.644 | ||
| 52 | Time: 0.564 | ||
| 53 | Time: 0.559 | ||
| 54 | Time: 0.626 | ||
| 55 | |||
| 56 | Performance counter stats for './hackbench 10' (5 runs): | ||
| 57 | |||
| 58 | 12920 kmem:mm_page_pcpu_drain ( +- 3.359% ) | ||
| 59 | 25035 kmem:mm_page_alloc ( +- 3.783% ) | ||
| 60 | 6104 kmem:mm_pagevec_free ( +- 0.934% ) | ||
| 61 | 18376 kmem:mm_page_free_direct ( +- 4.941% ) | ||
| 62 | |||
| 63 | 0.643954516 seconds time elapsed ( +- 2.363% ) | ||
| 64 | |||
| 65 | Furthermore, these tracepoints can be used to sample the workload as | ||
| 66 | well. For example the page allocations done by a 'git gc' can be | ||
| 67 | captured the following way: | ||
| 68 | |||
| 69 | titan:~/git> perf record -f -e kmem:mm_page_alloc -c 1 ./git gc | ||
| 70 | Counting objects: 1148, done. | ||
| 71 | Delta compression using up to 2 threads. | ||
| 72 | Compressing objects: 100% (450/450), done. | ||
| 73 | Writing objects: 100% (1148/1148), done. | ||
| 74 | Total 1148 (delta 690), reused 1148 (delta 690) | ||
| 75 | [ perf record: Captured and wrote 0.267 MB perf.data (~11679 samples) ] | ||
| 76 | |||
| 77 | To check which functions generated page allocations: | ||
| 78 | |||
| 79 | titan:~/git> perf report | ||
| 80 | # Samples: 10646 | ||
| 81 | # | ||
| 82 | # Overhead Command Shared Object | ||
| 83 | # ........ ............... .......................... | ||
| 84 | # | ||
| 85 | 23.57% git-repack /lib64/libc-2.5.so | ||
| 86 | 21.81% git /lib64/libc-2.5.so | ||
| 87 | 14.59% git ./git | ||
| 88 | 11.79% git-repack ./git | ||
| 89 | 7.12% git /lib64/ld-2.5.so | ||
| 90 | 3.16% git-repack /lib64/libpthread-2.5.so | ||
| 91 | 2.09% git-repack /bin/bash | ||
| 92 | 1.97% rm /lib64/libc-2.5.so | ||
| 93 | 1.39% mv /lib64/ld-2.5.so | ||
| 94 | 1.37% mv /lib64/libc-2.5.so | ||
| 95 | 1.12% git-repack /lib64/ld-2.5.so | ||
| 96 | 0.95% rm /lib64/ld-2.5.so | ||
| 97 | 0.90% git-update-serv /lib64/libc-2.5.so | ||
| 98 | 0.73% git-update-serv /lib64/ld-2.5.so | ||
| 99 | 0.68% perf /lib64/libpthread-2.5.so | ||
| 100 | 0.64% git-repack /usr/lib64/libz.so.1.2.3 | ||
| 101 | |||
| 102 | Or to see it on a more finegrained level: | ||
| 103 | |||
| 104 | titan:~/git> perf report --sort comm,dso,symbol | ||
| 105 | # Samples: 10646 | ||
| 106 | # | ||
| 107 | # Overhead Command Shared Object Symbol | ||
| 108 | # ........ ............... .......................... ...... | ||
| 109 | # | ||
| 110 | 9.35% git-repack ./git [.] insert_obj_hash | ||
| 111 | 9.12% git ./git [.] insert_obj_hash | ||
| 112 | 7.31% git /lib64/libc-2.5.so [.] memcpy | ||
| 113 | 6.34% git-repack /lib64/libc-2.5.so [.] _int_malloc | ||
| 114 | 6.24% git-repack /lib64/libc-2.5.so [.] memcpy | ||
| 115 | 5.82% git-repack /lib64/libc-2.5.so [.] __GI___fork | ||
| 116 | 5.47% git /lib64/libc-2.5.so [.] _int_malloc | ||
| 117 | 2.99% git /lib64/libc-2.5.so [.] memset | ||
| 118 | |||
| 119 | Furthermore, call-graph sampling can be done too, of page | ||
| 120 | allocations - to see precisely what kind of page allocations there | ||
| 121 | are: | ||
| 122 | |||
| 123 | titan:~/git> perf record -f -g -e kmem:mm_page_alloc -c 1 ./git gc | ||
| 124 | Counting objects: 1148, done. | ||
| 125 | Delta compression using up to 2 threads. | ||
| 126 | Compressing objects: 100% (450/450), done. | ||
| 127 | Writing objects: 100% (1148/1148), done. | ||
| 128 | Total 1148 (delta 690), reused 1148 (delta 690) | ||
| 129 | [ perf record: Captured and wrote 0.963 MB perf.data (~42069 samples) ] | ||
| 130 | |||
| 131 | titan:~/git> perf report -g | ||
| 132 | # Samples: 10686 | ||
| 133 | # | ||
| 134 | # Overhead Command Shared Object | ||
| 135 | # ........ ............... .......................... | ||
| 136 | # | ||
| 137 | 23.25% git-repack /lib64/libc-2.5.so | ||
| 138 | | | ||
| 139 | |--50.00%-- _int_free | ||
| 140 | | | ||
| 141 | |--37.50%-- __GI___fork | ||
| 142 | | make_child | ||
| 143 | | | ||
| 144 | |--12.50%-- ptmalloc_unlock_all2 | ||
| 145 | | make_child | ||
| 146 | | | ||
| 147 | --6.25%-- __GI_strcpy | ||
| 148 | 21.61% git /lib64/libc-2.5.so | ||
| 149 | | | ||
| 150 | |--30.00%-- __GI_read | ||
| 151 | | | | ||
| 152 | | --83.33%-- git_config_from_file | ||
| 153 | | git_config | ||
| 154 | | | | ||
| 155 | [...] | ||
| 156 | |||
| 157 | Or you can observe the whole system's page allocations for 10 | ||
| 158 | seconds: | ||
| 159 | |||
| 160 | titan:~/git> perf stat -a -e kmem:mm_page_pcpu_drain -e | ||
| 161 | kmem:mm_page_alloc -e kmem:mm_pagevec_free -e | ||
| 162 | kmem:mm_page_free_direct sleep 10 | ||
| 163 | |||
| 164 | Performance counter stats for 'sleep 10': | ||
| 165 | |||
| 166 | 171585 kmem:mm_page_pcpu_drain | ||
| 167 | 322114 kmem:mm_page_alloc | ||
| 168 | 73623 kmem:mm_pagevec_free | ||
| 169 | 254115 kmem:mm_page_free_direct | ||
| 170 | |||
| 171 | 10.000591410 seconds time elapsed | ||
| 172 | |||
| 173 | Or observe how fluctuating the page allocations are, via statistical | ||
| 174 | analysis done over ten 1-second intervals: | ||
| 175 | |||
| 176 | titan:~/git> perf stat --repeat 10 -a -e kmem:mm_page_pcpu_drain -e | ||
| 177 | kmem:mm_page_alloc -e kmem:mm_pagevec_free -e | ||
| 178 | kmem:mm_page_free_direct sleep 1 | ||
| 179 | |||
| 180 | Performance counter stats for 'sleep 1' (10 runs): | ||
| 181 | |||
| 182 | 17254 kmem:mm_page_pcpu_drain ( +- 3.709% ) | ||
| 183 | 34394 kmem:mm_page_alloc ( +- 4.617% ) | ||
| 184 | 7509 kmem:mm_pagevec_free ( +- 4.820% ) | ||
| 185 | 25653 kmem:mm_page_free_direct ( +- 3.672% ) | ||
| 186 | |||
| 187 | 1.058135029 seconds time elapsed ( +- 3.089% ) | ||
| 188 | |||
| 189 | Or you can annotate the recorded 'git gc' run on a per symbol basis | ||
| 190 | and check which instructions/source-code generated page allocations: | ||
| 191 | |||
| 192 | titan:~/git> perf annotate __GI___fork | ||
| 193 | ------------------------------------------------ | ||
| 194 | Percent | Source code & Disassembly of libc-2.5.so | ||
| 195 | ------------------------------------------------ | ||
| 196 | : | ||
| 197 | : | ||
| 198 | : Disassembly of section .plt: | ||
| 199 | : Disassembly of section .text: | ||
| 200 | : | ||
| 201 | : 00000031a2e95560 <__fork>: | ||
| 202 | [...] | ||
| 203 | 0.00 : 31a2e95602: b8 38 00 00 00 mov $0x38,%eax | ||
| 204 | 0.00 : 31a2e95607: 0f 05 syscall | ||
| 205 | 83.42 : 31a2e95609: 48 3d 00 f0 ff ff cmp $0xfffffffffffff000,%rax | ||
| 206 | 0.00 : 31a2e9560f: 0f 87 4d 01 00 00 ja 31a2e95762 <__fork+0x202> | ||
| 207 | 0.00 : 31a2e95615: 85 c0 test %eax,%eax | ||
| 208 | |||
| 209 | ( this shows that 83.42% of __GI___fork's page allocations come from | ||
| 210 | the 0x38 system call it performs. ) | ||
| 211 | |||
| 212 | etc. etc. - a lot more is possible. I could list a dozen of | ||
| 213 | other different usecases straight away - neither of which is | ||
| 214 | possible via /proc/vmstat. | ||
| 215 | |||
| 216 | /proc/vmstat is not in the same league really, in terms of | ||
| 217 | expressive power of system analysis and performance | ||
| 218 | analysis. | ||
| 219 | |||
| 220 | All that the above results needed were those new tracepoints | ||
| 221 | in include/tracing/events/kmem.h. | ||
| 222 | |||
| 223 | Ingo | ||
| 224 | |||
| 225 | |||
diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index 1dbc1eeb4c01..6be696b0a2bb 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt | |||
| @@ -29,13 +29,67 @@ OPTIONS | |||
| 29 | Select the PMU event. Selection can be a symbolic event name | 29 | Select the PMU event. Selection can be a symbolic event name |
| 30 | (use 'perf list' to list all events) or a raw PMU | 30 | (use 'perf list' to list all events) or a raw PMU |
| 31 | event (eventsel+umask) in the form of rNNN where NNN is a | 31 | event (eventsel+umask) in the form of rNNN where NNN is a |
| 32 | hexadecimal event descriptor. | 32 | hexadecimal event descriptor. |
| 33 | 33 | ||
| 34 | -a:: | 34 | -a:: |
| 35 | system-wide collection | 35 | System-wide collection. |
| 36 | 36 | ||
| 37 | -l:: | 37 | -l:: |
| 38 | scale counter values | 38 | Scale counter values. |
| 39 | |||
| 40 | -p:: | ||
| 41 | --pid=:: | ||
| 42 | Record events on existing pid. | ||
| 43 | |||
| 44 | -r:: | ||
| 45 | --realtime=:: | ||
| 46 | Collect data with this RT SCHED_FIFO priority. | ||
| 47 | -A:: | ||
| 48 | --append:: | ||
| 49 | Append to the output file to do incremental profiling. | ||
| 50 | |||
| 51 | -f:: | ||
| 52 | --force:: | ||
| 53 | Overwrite existing data file. | ||
| 54 | |||
| 55 | -c:: | ||
| 56 | --count=:: | ||
| 57 | Event period to sample. | ||
| 58 | |||
| 59 | -o:: | ||
| 60 | --output=:: | ||
| 61 | Output file name. | ||
| 62 | |||
| 63 | -i:: | ||
| 64 | --inherit:: | ||
| 65 | Child tasks inherit counters. | ||
| 66 | -F:: | ||
| 67 | --freq=:: | ||
| 68 | Profile at this frequency. | ||
| 69 | |||
| 70 | -m:: | ||
| 71 | --mmap-pages=:: | ||
| 72 | Number of mmap data pages. | ||
| 73 | |||
| 74 | -g:: | ||
| 75 | --call-graph:: | ||
| 76 | Do call-graph (stack chain/backtrace) recording. | ||
| 77 | |||
| 78 | -v:: | ||
| 79 | --verbose:: | ||
| 80 | Be more verbose (show counter open errors, etc). | ||
| 81 | |||
| 82 | -s:: | ||
| 83 | --stat:: | ||
| 84 | Per thread counts. | ||
| 85 | |||
| 86 | -d:: | ||
| 87 | --data:: | ||
| 88 | Sample addresses. | ||
| 89 | |||
| 90 | -n:: | ||
| 91 | --no-samples:: | ||
| 92 | Don't sample. | ||
| 39 | 93 | ||
| 40 | SEE ALSO | 94 | SEE ALSO |
| 41 | -------- | 95 | -------- |
diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index 8aa3f8c88707..e72e93110782 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt | |||
| @@ -24,6 +24,9 @@ OPTIONS | |||
| 24 | --dsos=:: | 24 | --dsos=:: |
| 25 | Only consider symbols in these dsos. CSV that understands | 25 | Only consider symbols in these dsos. CSV that understands |
| 26 | file://filename entries. | 26 | file://filename entries. |
| 27 | -n | ||
| 28 | --show-nr-samples | ||
| 29 | Show the number of samples for each symbol | ||
| 27 | -C:: | 30 | -C:: |
| 28 | --comms=:: | 31 | --comms=:: |
| 29 | Only consider symbols in these comms. CSV that understands | 32 | Only consider symbols in these comms. CSV that understands |
| @@ -33,6 +36,18 @@ OPTIONS | |||
| 33 | Only consider these symbols. CSV that understands | 36 | Only consider these symbols. CSV that understands |
| 34 | file://filename entries. | 37 | file://filename entries. |
| 35 | 38 | ||
| 39 | -w:: | ||
| 40 | --field-width=:: | ||
| 41 | Force each column width to the provided list, for large terminal | ||
| 42 | readability. | ||
| 43 | |||
| 44 | -t:: | ||
| 45 | --field-separator=:: | ||
| 46 | |||
| 47 | Use a special separator character and don't pad with spaces, replacing | ||
| 48 | all occurances of this separator in symbol names (and other output) | ||
| 49 | with a '.' character, that thus it's the only non valid separator. | ||
| 50 | |||
| 36 | SEE ALSO | 51 | SEE ALSO |
| 37 | -------- | 52 | -------- |
| 38 | linkperf:perf-stat[1] | 53 | linkperf:perf-stat[1] |
diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt index 0d74346d21ab..484080dd5b6f 100644 --- a/tools/perf/Documentation/perf-stat.txt +++ b/tools/perf/Documentation/perf-stat.txt | |||
| @@ -40,7 +40,7 @@ OPTIONS | |||
| 40 | -a:: | 40 | -a:: |
| 41 | system-wide collection | 41 | system-wide collection |
| 42 | 42 | ||
| 43 | -S:: | 43 | -c:: |
| 44 | scale counter values | 44 | scale counter values |
| 45 | 45 | ||
| 46 | EXAMPLES | 46 | EXAMPLES |
diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt index 539d01289725..4a7d558dc309 100644 --- a/tools/perf/Documentation/perf-top.txt +++ b/tools/perf/Documentation/perf-top.txt | |||
| @@ -3,36 +3,122 @@ perf-top(1) | |||
| 3 | 3 | ||
| 4 | NAME | 4 | NAME |
| 5 | ---- | 5 | ---- |
| 6 | perf-top - Run a command and profile it | 6 | perf-top - System profiling tool. |
| 7 | 7 | ||
| 8 | SYNOPSIS | 8 | SYNOPSIS |
| 9 | -------- | 9 | -------- |
| 10 | [verse] | 10 | [verse] |
| 11 | 'perf top' [-e <EVENT> | --event=EVENT] [-l] [-a] <command> | 11 | 'perf top' [-e <EVENT> | --event=EVENT] [<options>] |
| 12 | 12 | ||
| 13 | DESCRIPTION | 13 | DESCRIPTION |
| 14 | ----------- | 14 | ----------- |
| 15 | This command runs a command and gathers a performance counter profile | 15 | This command generates and displays a performance counter profile in realtime. |
| 16 | from it. | ||
| 17 | 16 | ||
| 18 | 17 | ||
| 19 | OPTIONS | 18 | OPTIONS |
| 20 | ------- | 19 | ------- |
| 21 | <command>...:: | 20 | -a:: |
| 22 | Any command you can specify in a shell. | 21 | --all-cpus:: |
| 22 | System-wide collection. (default) | ||
| 23 | |||
| 24 | -c <count>:: | ||
| 25 | --count=<count>:: | ||
| 26 | Event period to sample. | ||
| 27 | |||
| 28 | -C <cpu>:: | ||
| 29 | --CPU=<cpu>:: | ||
| 30 | CPU to profile. | ||
| 31 | |||
| 32 | -d <seconds>:: | ||
| 33 | --delay=<seconds>:: | ||
| 34 | Number of seconds to delay between refreshes. | ||
| 23 | 35 | ||
| 24 | -e:: | 36 | -e <event>:: |
| 25 | --event=:: | 37 | --event=<event>:: |
| 26 | Select the PMU event. Selection can be a symbolic event name | 38 | Select the PMU event. Selection can be a symbolic event name |
| 27 | (use 'perf list' to list all events) or a raw PMU | 39 | (use 'perf list' to list all events) or a raw PMU |
| 28 | event (eventsel+umask) in the form of rNNN where NNN is a | 40 | event (eventsel+umask) in the form of rNNN where NNN is a |
| 29 | hexadecimal event descriptor. | 41 | hexadecimal event descriptor. |
| 30 | 42 | ||
| 31 | -a:: | 43 | -E <entries>:: |
| 32 | system-wide collection | 44 | --entries=<entries>:: |
| 45 | Display this many functions. | ||
| 46 | |||
| 47 | -f <count>:: | ||
| 48 | --count-filter=<count>:: | ||
| 49 | Only display functions with more events than this. | ||
| 50 | |||
| 51 | -F <freq>:: | ||
| 52 | --freq=<freq>:: | ||
| 53 | Profile at this frequency. | ||
| 54 | |||
| 55 | -i:: | ||
| 56 | --inherit:: | ||
| 57 | Child tasks inherit counters, only makes sens with -p option. | ||
| 58 | |||
| 59 | -k <path>:: | ||
| 60 | --vmlinux=<path>:: | ||
| 61 | Path to vmlinux. Required for annotation functionality. | ||
| 62 | |||
| 63 | -m <pages>:: | ||
| 64 | --mmap-pages=<pages>:: | ||
| 65 | Number of mmapped data pages. | ||
| 66 | |||
| 67 | -p <pid>:: | ||
| 68 | --pid=<pid>:: | ||
| 69 | Profile events on existing pid. | ||
| 70 | |||
| 71 | -r <priority>:: | ||
| 72 | --realtime=<priority>:: | ||
| 73 | Collect data with this RT SCHED_FIFO priority. | ||
| 74 | |||
| 75 | -s <symbol>:: | ||
| 76 | --sym-annotate=<symbol>:: | ||
| 77 | Annotate this symbol. Requires -k option. | ||
| 78 | |||
| 79 | -v:: | ||
| 80 | --verbose:: | ||
| 81 | Be more verbose (show counter open errors, etc). | ||
| 82 | |||
| 83 | -z:: | ||
| 84 | --zero:: | ||
| 85 | Zero history across display updates. | ||
| 86 | |||
| 87 | INTERACTIVE PROMPTING KEYS | ||
| 88 | -------------------------- | ||
| 89 | |||
| 90 | [d]:: | ||
| 91 | Display refresh delay. | ||
| 92 | |||
| 93 | [e]:: | ||
| 94 | Number of entries to display. | ||
| 95 | |||
| 96 | [E]:: | ||
| 97 | Event to display when multiple counters are active. | ||
| 98 | |||
| 99 | [f]:: | ||
| 100 | Profile display filter (>= hit count). | ||
| 101 | |||
| 102 | [F]:: | ||
| 103 | Annotation display filter (>= % of total). | ||
| 104 | |||
| 105 | [s]:: | ||
| 106 | Annotate symbol. | ||
| 107 | |||
| 108 | [S]:: | ||
| 109 | Stop annotation, return to full profile display. | ||
| 110 | |||
| 111 | [w]:: | ||
| 112 | Toggle between weighted sum and individual count[E]r profile. | ||
| 113 | |||
| 114 | [z]:: | ||
| 115 | Toggle event count zeroing across display updates. | ||
| 116 | |||
| 117 | [qQ]:: | ||
| 118 | Quit. | ||
| 119 | |||
| 120 | Pressing any unmapped key displays a menu, and prompts for input. | ||
| 33 | 121 | ||
| 34 | -l:: | ||
| 35 | scale counter values | ||
| 36 | 122 | ||
| 37 | SEE ALSO | 123 | SEE ALSO |
| 38 | -------- | 124 | -------- |
diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 7822b3d6baca..c045b4271e57 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile | |||
| @@ -158,8 +158,10 @@ uname_P := $(shell sh -c 'uname -p 2>/dev/null || echo not') | |||
| 158 | uname_V := $(shell sh -c 'uname -v 2>/dev/null || echo not') | 158 | uname_V := $(shell sh -c 'uname -v 2>/dev/null || echo not') |
| 159 | 159 | ||
| 160 | # If we're on a 64-bit kernel, use -m64 | 160 | # If we're on a 64-bit kernel, use -m64 |
| 161 | ifneq ($(patsubst %64,%,$(uname_M)),$(uname_M)) | 161 | ifndef NO_64BIT |
| 162 | M64 := -m64 | 162 | ifneq ($(patsubst %64,%,$(uname_M)),$(uname_M)) |
| 163 | M64 := -m64 | ||
| 164 | endif | ||
| 163 | endif | 165 | endif |
| 164 | 166 | ||
| 165 | # CFLAGS and LDFLAGS are for the users to override from the command line. | 167 | # CFLAGS and LDFLAGS are for the users to override from the command line. |
| @@ -345,7 +347,6 @@ BUILTIN_OBJS += builtin-stat.o | |||
| 345 | BUILTIN_OBJS += builtin-top.o | 347 | BUILTIN_OBJS += builtin-top.o |
| 346 | 348 | ||
| 347 | PERFLIBS = $(LIB_FILE) | 349 | PERFLIBS = $(LIB_FILE) |
| 348 | EXTLIBS = | ||
| 349 | 350 | ||
| 350 | # | 351 | # |
| 351 | # Platform specific tweaks | 352 | # Platform specific tweaks |
| @@ -374,6 +375,39 @@ ifeq ($(uname_S),Darwin) | |||
| 374 | PTHREAD_LIBS = | 375 | PTHREAD_LIBS = |
| 375 | endif | 376 | endif |
| 376 | 377 | ||
| 378 | ifneq ($(shell sh -c "(echo '\#include <libelf.h>'; echo 'int main(void) { Elf * elf = elf_begin(0, ELF_C_READ_MMAP, 0); return (long)elf; }') | $(CC) -x c - $(ALL_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -o /dev/null $(ALL_LDFLAGS) > /dev/null 2>&1 && echo y"), y) | ||
| 379 | msg := $(error No libelf.h/libelf found, please install libelf-dev/elfutils-libelf-devel); | ||
| 380 | endif | ||
| 381 | |||
| 382 | ifdef NO_DEMANGLE | ||
| 383 | BASIC_CFLAGS += -DNO_DEMANGLE | ||
| 384 | else | ||
| 385 | has_bfd := $(shell sh -c "(echo '\#include <bfd.h>'; echo 'int main(void) { bfd_demangle(0, 0, 0); return 0; }') | $(CC) -x c - $(ALL_CFLAGS) -o /dev/null $(ALL_LDFLAGS) -lbfd > /dev/null 2>&1 && echo y") | ||
| 386 | |||
| 387 | ifeq ($(has_bfd),y) | ||
| 388 | EXTLIBS += -lbfd | ||
| 389 | else | ||
| 390 | has_bfd_iberty := $(shell sh -c "(echo '\#include <bfd.h>'; echo 'int main(void) { bfd_demangle(0, 0, 0); return 0; }') | $(CC) -x c - $(ALL_CFLAGS) -o /dev/null $(ALL_LDFLAGS) -lbfd -liberty > /dev/null 2>&1 && echo y") | ||
| 391 | ifeq ($(has_bfd_iberty),y) | ||
| 392 | EXTLIBS += -lbfd -liberty | ||
| 393 | else | ||
| 394 | has_bfd_iberty_z := $(shell sh -c "(echo '\#include <bfd.h>'; echo 'int main(void) { bfd_demangle(0, 0, 0); return 0; }') | $(CC) -x c - $(ALL_CFLAGS) -o /dev/null $(ALL_LDFLAGS) -lbfd -liberty -lz > /dev/null 2>&1 && echo y") | ||
| 395 | ifeq ($(has_bfd_iberty_z),y) | ||
| 396 | EXTLIBS += -lbfd -liberty -lz | ||
| 397 | else | ||
| 398 | has_cplus_demangle := $(shell sh -c "(echo 'extern char *cplus_demangle(const char *, int);'; echo 'int main(void) { cplus_demangle(0, 0); return 0; }') | $(CC) -x c - $(ALL_CFLAGS) -o /dev/null $(ALL_LDFLAGS) -liberty > /dev/null 2>&1 && echo y") | ||
| 399 | ifeq ($(has_cplus_demangle),y) | ||
| 400 | EXTLIBS += -liberty | ||
| 401 | BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE | ||
| 402 | else | ||
| 403 | msg := $(warning No bfd.h/libbfd found, install binutils-dev[el] to gain symbol demangling) | ||
| 404 | BASIC_CFLAGS += -DNO_DEMANGLE | ||
| 405 | endif | ||
| 406 | endif | ||
| 407 | endif | ||
| 408 | endif | ||
| 409 | endif | ||
| 410 | |||
| 377 | ifndef CC_LD_DYNPATH | 411 | ifndef CC_LD_DYNPATH |
| 378 | ifdef NO_R_TO_GCC_LINKER | 412 | ifdef NO_R_TO_GCC_LINKER |
| 379 | # Some gcc does not accept and pass -R to the linker to specify | 413 | # Some gcc does not accept and pass -R to the linker to specify |
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 5f9eefecc574..5e17de984dc8 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c | |||
| @@ -31,6 +31,7 @@ static char *vmlinux = "vmlinux"; | |||
| 31 | static char default_sort_order[] = "comm,symbol"; | 31 | static char default_sort_order[] = "comm,symbol"; |
| 32 | static char *sort_order = default_sort_order; | 32 | static char *sort_order = default_sort_order; |
| 33 | 33 | ||
| 34 | static int force; | ||
| 34 | static int input; | 35 | static int input; |
| 35 | static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV; | 36 | static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV; |
| 36 | 37 | ||
| @@ -74,20 +75,12 @@ struct fork_event { | |||
| 74 | u32 pid, ppid; | 75 | u32 pid, ppid; |
| 75 | }; | 76 | }; |
| 76 | 77 | ||
| 77 | struct period_event { | ||
| 78 | struct perf_event_header header; | ||
| 79 | u64 time; | ||
| 80 | u64 id; | ||
| 81 | u64 sample_period; | ||
| 82 | }; | ||
| 83 | |||
| 84 | typedef union event_union { | 78 | typedef union event_union { |
| 85 | struct perf_event_header header; | 79 | struct perf_event_header header; |
| 86 | struct ip_event ip; | 80 | struct ip_event ip; |
| 87 | struct mmap_event mmap; | 81 | struct mmap_event mmap; |
| 88 | struct comm_event comm; | 82 | struct comm_event comm; |
| 89 | struct fork_event fork; | 83 | struct fork_event fork; |
| 90 | struct period_event period; | ||
| 91 | } event_t; | 84 | } event_t; |
| 92 | 85 | ||
| 93 | 86 | ||
| @@ -988,6 +981,13 @@ process_fork_event(event_t *event, unsigned long offset, unsigned long head) | |||
| 988 | (void *)(long)(event->header.size), | 981 | (void *)(long)(event->header.size), |
| 989 | event->fork.pid, event->fork.ppid); | 982 | event->fork.pid, event->fork.ppid); |
| 990 | 983 | ||
| 984 | /* | ||
| 985 | * A thread clone will have the same PID for both | ||
| 986 | * parent and child. | ||
| 987 | */ | ||
| 988 | if (thread == parent) | ||
| 989 | return 0; | ||
| 990 | |||
| 991 | if (!thread || !parent || thread__fork(thread, parent)) { | 991 | if (!thread || !parent || thread__fork(thread, parent)) { |
| 992 | dprintf("problem processing PERF_EVENT_FORK, skipping event.\n"); | 992 | dprintf("problem processing PERF_EVENT_FORK, skipping event.\n"); |
| 993 | return -1; | 993 | return -1; |
| @@ -998,19 +998,6 @@ process_fork_event(event_t *event, unsigned long offset, unsigned long head) | |||
| 998 | } | 998 | } |
| 999 | 999 | ||
| 1000 | static int | 1000 | static int |
| 1001 | process_period_event(event_t *event, unsigned long offset, unsigned long head) | ||
| 1002 | { | ||
| 1003 | dprintf("%p [%p]: PERF_EVENT_PERIOD: time:%Ld, id:%Ld: period:%Ld\n", | ||
| 1004 | (void *)(offset + head), | ||
| 1005 | (void *)(long)(event->header.size), | ||
| 1006 | event->period.time, | ||
| 1007 | event->period.id, | ||
| 1008 | event->period.sample_period); | ||
| 1009 | |||
| 1010 | return 0; | ||
| 1011 | } | ||
| 1012 | |||
| 1013 | static int | ||
| 1014 | process_event(event_t *event, unsigned long offset, unsigned long head) | 1001 | process_event(event_t *event, unsigned long offset, unsigned long head) |
| 1015 | { | 1002 | { |
| 1016 | switch (event->header.type) { | 1003 | switch (event->header.type) { |
| @@ -1025,9 +1012,6 @@ process_event(event_t *event, unsigned long offset, unsigned long head) | |||
| 1025 | 1012 | ||
| 1026 | case PERF_EVENT_FORK: | 1013 | case PERF_EVENT_FORK: |
| 1027 | return process_fork_event(event, offset, head); | 1014 | return process_fork_event(event, offset, head); |
| 1028 | |||
| 1029 | case PERF_EVENT_PERIOD: | ||
| 1030 | return process_period_event(event, offset, head); | ||
| 1031 | /* | 1015 | /* |
| 1032 | * We dont process them right now but they are fine: | 1016 | * We dont process them right now but they are fine: |
| 1033 | */ | 1017 | */ |
| @@ -1351,6 +1335,11 @@ static int __cmd_annotate(void) | |||
| 1351 | exit(-1); | 1335 | exit(-1); |
| 1352 | } | 1336 | } |
| 1353 | 1337 | ||
| 1338 | if (!force && (stat.st_uid != geteuid())) { | ||
| 1339 | fprintf(stderr, "file: %s not owned by current user\n", input_name); | ||
| 1340 | exit(-1); | ||
| 1341 | } | ||
| 1342 | |||
| 1354 | if (!stat.st_size) { | 1343 | if (!stat.st_size) { |
| 1355 | fprintf(stderr, "zero-sized file, nothing to do!\n"); | 1344 | fprintf(stderr, "zero-sized file, nothing to do!\n"); |
| 1356 | exit(0); | 1345 | exit(0); |
| @@ -1456,6 +1445,7 @@ static const struct option options[] = { | |||
| 1456 | "input file name"), | 1445 | "input file name"), |
| 1457 | OPT_STRING('s', "symbol", &sym_hist_filter, "symbol", | 1446 | OPT_STRING('s', "symbol", &sym_hist_filter, "symbol", |
| 1458 | "symbol to annotate"), | 1447 | "symbol to annotate"), |
| 1448 | OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), | ||
| 1459 | OPT_BOOLEAN('v', "verbose", &verbose, | 1449 | OPT_BOOLEAN('v', "verbose", &verbose, |
| 1460 | "be more verbose (show symbol address, etc)"), | 1450 | "be more verbose (show symbol address, etc)"), |
| 1461 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, | 1451 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, |
diff --git a/tools/perf/builtin-list.c b/tools/perf/builtin-list.c index f990fa8a35c9..d88c6961274c 100644 --- a/tools/perf/builtin-list.c +++ b/tools/perf/builtin-list.c | |||
| @@ -10,11 +10,12 @@ | |||
| 10 | 10 | ||
| 11 | #include "perf.h" | 11 | #include "perf.h" |
| 12 | 12 | ||
| 13 | #include "util/parse-options.h" | ||
| 14 | #include "util/parse-events.h" | 13 | #include "util/parse-events.h" |
| 14 | #include "util/cache.h" | ||
| 15 | 15 | ||
| 16 | int cmd_list(int argc __used, const char **argv __used, const char *prefix __used) | 16 | int cmd_list(int argc __used, const char **argv __used, const char *prefix __used) |
| 17 | { | 17 | { |
| 18 | setup_pager(); | ||
| 18 | print_events(); | 19 | print_events(); |
| 19 | return 0; | 20 | return 0; |
| 20 | } | 21 | } |
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 4ef78a5e6f32..89a5ddcd1ded 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
| @@ -34,7 +34,9 @@ static int output; | |||
| 34 | static const char *output_name = "perf.data"; | 34 | static const char *output_name = "perf.data"; |
| 35 | static int group = 0; | 35 | static int group = 0; |
| 36 | static unsigned int realtime_prio = 0; | 36 | static unsigned int realtime_prio = 0; |
| 37 | static int raw_samples = 0; | ||
| 37 | static int system_wide = 0; | 38 | static int system_wide = 0; |
| 39 | static int profile_cpu = -1; | ||
| 38 | static pid_t target_pid = -1; | 40 | static pid_t target_pid = -1; |
| 39 | static int inherit = 1; | 41 | static int inherit = 1; |
| 40 | static int force = 0; | 42 | static int force = 0; |
| @@ -43,6 +45,7 @@ static int call_graph = 0; | |||
| 43 | static int verbose = 0; | 45 | static int verbose = 0; |
| 44 | static int inherit_stat = 0; | 46 | static int inherit_stat = 0; |
| 45 | static int no_samples = 0; | 47 | static int no_samples = 0; |
| 48 | static int sample_address = 0; | ||
| 46 | 49 | ||
| 47 | static long samples; | 50 | static long samples; |
| 48 | static struct timeval last_read; | 51 | static struct timeval last_read; |
| @@ -202,46 +205,48 @@ static void sig_atexit(void) | |||
| 202 | kill(getpid(), signr); | 205 | kill(getpid(), signr); |
| 203 | } | 206 | } |
| 204 | 207 | ||
| 205 | static void pid_synthesize_comm_event(pid_t pid, int full) | 208 | static pid_t pid_synthesize_comm_event(pid_t pid, int full) |
| 206 | { | 209 | { |
| 207 | struct comm_event comm_ev; | 210 | struct comm_event comm_ev; |
| 208 | char filename[PATH_MAX]; | 211 | char filename[PATH_MAX]; |
| 209 | char bf[BUFSIZ]; | 212 | char bf[BUFSIZ]; |
| 210 | int fd; | 213 | FILE *fp; |
| 211 | size_t size; | 214 | size_t size = 0; |
| 212 | char *field, *sep; | ||
| 213 | DIR *tasks; | 215 | DIR *tasks; |
| 214 | struct dirent dirent, *next; | 216 | struct dirent dirent, *next; |
| 217 | pid_t tgid = 0; | ||
| 215 | 218 | ||
| 216 | snprintf(filename, sizeof(filename), "/proc/%d/stat", pid); | 219 | snprintf(filename, sizeof(filename), "/proc/%d/status", pid); |
| 217 | 220 | ||
| 218 | fd = open(filename, O_RDONLY); | 221 | fp = fopen(filename, "r"); |
| 219 | if (fd < 0) { | 222 | if (fp == NULL) { |
| 220 | /* | 223 | /* |
| 221 | * We raced with a task exiting - just return: | 224 | * We raced with a task exiting - just return: |
| 222 | */ | 225 | */ |
| 223 | if (verbose) | 226 | if (verbose) |
| 224 | fprintf(stderr, "couldn't open %s\n", filename); | 227 | fprintf(stderr, "couldn't open %s\n", filename); |
| 225 | return; | 228 | return 0; |
| 226 | } | ||
| 227 | if (read(fd, bf, sizeof(bf)) < 0) { | ||
| 228 | fprintf(stderr, "couldn't read %s\n", filename); | ||
| 229 | exit(EXIT_FAILURE); | ||
| 230 | } | 229 | } |
| 231 | close(fd); | ||
| 232 | 230 | ||
| 233 | /* 9027 (cat) R 6747 9027 6747 34816 9027 ... */ | ||
| 234 | memset(&comm_ev, 0, sizeof(comm_ev)); | 231 | memset(&comm_ev, 0, sizeof(comm_ev)); |
| 235 | field = strchr(bf, '('); | 232 | while (!comm_ev.comm[0] || !comm_ev.pid) { |
| 236 | if (field == NULL) | 233 | if (fgets(bf, sizeof(bf), fp) == NULL) |
| 237 | goto out_failure; | 234 | goto out_failure; |
| 238 | sep = strchr(++field, ')'); | 235 | |
| 239 | if (sep == NULL) | 236 | if (memcmp(bf, "Name:", 5) == 0) { |
| 240 | goto out_failure; | 237 | char *name = bf + 5; |
| 241 | size = sep - field; | 238 | while (*name && isspace(*name)) |
| 242 | memcpy(comm_ev.comm, field, size++); | 239 | ++name; |
| 243 | 240 | size = strlen(name) - 1; | |
| 244 | comm_ev.pid = pid; | 241 | memcpy(comm_ev.comm, name, size++); |
| 242 | } else if (memcmp(bf, "Tgid:", 5) == 0) { | ||
| 243 | char *tgids = bf + 5; | ||
| 244 | while (*tgids && isspace(*tgids)) | ||
| 245 | ++tgids; | ||
| 246 | tgid = comm_ev.pid = atoi(tgids); | ||
| 247 | } | ||
| 248 | } | ||
| 249 | |||
| 245 | comm_ev.header.type = PERF_EVENT_COMM; | 250 | comm_ev.header.type = PERF_EVENT_COMM; |
| 246 | size = ALIGN(size, sizeof(u64)); | 251 | size = ALIGN(size, sizeof(u64)); |
| 247 | comm_ev.header.size = sizeof(comm_ev) - (sizeof(comm_ev.comm) - size); | 252 | comm_ev.header.size = sizeof(comm_ev) - (sizeof(comm_ev.comm) - size); |
| @@ -250,7 +255,7 @@ static void pid_synthesize_comm_event(pid_t pid, int full) | |||
| 250 | comm_ev.tid = pid; | 255 | comm_ev.tid = pid; |
| 251 | 256 | ||
| 252 | write_output(&comm_ev, comm_ev.header.size); | 257 | write_output(&comm_ev, comm_ev.header.size); |
| 253 | return; | 258 | goto out_fclose; |
| 254 | } | 259 | } |
| 255 | 260 | ||
| 256 | snprintf(filename, sizeof(filename), "/proc/%d/task", pid); | 261 | snprintf(filename, sizeof(filename), "/proc/%d/task", pid); |
| @@ -267,7 +272,10 @@ static void pid_synthesize_comm_event(pid_t pid, int full) | |||
| 267 | write_output(&comm_ev, comm_ev.header.size); | 272 | write_output(&comm_ev, comm_ev.header.size); |
| 268 | } | 273 | } |
| 269 | closedir(tasks); | 274 | closedir(tasks); |
| 270 | return; | 275 | |
| 276 | out_fclose: | ||
| 277 | fclose(fp); | ||
| 278 | return tgid; | ||
| 271 | 279 | ||
| 272 | out_failure: | 280 | out_failure: |
| 273 | fprintf(stderr, "couldn't get COMM and pgid, malformed %s\n", | 281 | fprintf(stderr, "couldn't get COMM and pgid, malformed %s\n", |
| @@ -275,7 +283,7 @@ out_failure: | |||
| 275 | exit(EXIT_FAILURE); | 283 | exit(EXIT_FAILURE); |
| 276 | } | 284 | } |
| 277 | 285 | ||
| 278 | static void pid_synthesize_mmap_samples(pid_t pid) | 286 | static void pid_synthesize_mmap_samples(pid_t pid, pid_t tgid) |
| 279 | { | 287 | { |
| 280 | char filename[PATH_MAX]; | 288 | char filename[PATH_MAX]; |
| 281 | FILE *fp; | 289 | FILE *fp; |
| @@ -313,6 +321,10 @@ static void pid_synthesize_mmap_samples(pid_t pid) | |||
| 313 | if (*pbf == 'x') { /* vm_exec */ | 321 | if (*pbf == 'x') { /* vm_exec */ |
| 314 | char *execname = strchr(bf, '/'); | 322 | char *execname = strchr(bf, '/'); |
| 315 | 323 | ||
| 324 | /* Catch VDSO */ | ||
| 325 | if (execname == NULL) | ||
| 326 | execname = strstr(bf, "[vdso]"); | ||
| 327 | |||
| 316 | if (execname == NULL) | 328 | if (execname == NULL) |
| 317 | continue; | 329 | continue; |
| 318 | 330 | ||
| @@ -323,7 +335,7 @@ static void pid_synthesize_mmap_samples(pid_t pid) | |||
| 323 | mmap_ev.len -= mmap_ev.start; | 335 | mmap_ev.len -= mmap_ev.start; |
| 324 | mmap_ev.header.size = (sizeof(mmap_ev) - | 336 | mmap_ev.header.size = (sizeof(mmap_ev) - |
| 325 | (sizeof(mmap_ev.filename) - size)); | 337 | (sizeof(mmap_ev.filename) - size)); |
| 326 | mmap_ev.pid = pid; | 338 | mmap_ev.pid = tgid; |
| 327 | mmap_ev.tid = pid; | 339 | mmap_ev.tid = pid; |
| 328 | 340 | ||
| 329 | write_output(&mmap_ev, mmap_ev.header.size); | 341 | write_output(&mmap_ev, mmap_ev.header.size); |
| @@ -342,14 +354,14 @@ static void synthesize_all(void) | |||
| 342 | 354 | ||
| 343 | while (!readdir_r(proc, &dirent, &next) && next) { | 355 | while (!readdir_r(proc, &dirent, &next) && next) { |
| 344 | char *end; | 356 | char *end; |
| 345 | pid_t pid; | 357 | pid_t pid, tgid; |
| 346 | 358 | ||
| 347 | pid = strtol(dirent.d_name, &end, 10); | 359 | pid = strtol(dirent.d_name, &end, 10); |
| 348 | if (*end) /* only interested in proper numerical dirents */ | 360 | if (*end) /* only interested in proper numerical dirents */ |
| 349 | continue; | 361 | continue; |
| 350 | 362 | ||
| 351 | pid_synthesize_comm_event(pid, 1); | 363 | tgid = pid_synthesize_comm_event(pid, 1); |
| 352 | pid_synthesize_mmap_samples(pid); | 364 | pid_synthesize_mmap_samples(pid, tgid); |
| 353 | } | 365 | } |
| 354 | 366 | ||
| 355 | closedir(proc); | 367 | closedir(proc); |
| @@ -387,7 +399,7 @@ static void create_counter(int counter, int cpu, pid_t pid) | |||
| 387 | PERF_FORMAT_TOTAL_TIME_RUNNING | | 399 | PERF_FORMAT_TOTAL_TIME_RUNNING | |
| 388 | PERF_FORMAT_ID; | 400 | PERF_FORMAT_ID; |
| 389 | 401 | ||
| 390 | attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID; | 402 | attr->sample_type |= PERF_SAMPLE_IP | PERF_SAMPLE_TID; |
| 391 | 403 | ||
| 392 | if (freq) { | 404 | if (freq) { |
| 393 | attr->sample_type |= PERF_SAMPLE_PERIOD; | 405 | attr->sample_type |= PERF_SAMPLE_PERIOD; |
| @@ -401,9 +413,15 @@ static void create_counter(int counter, int cpu, pid_t pid) | |||
| 401 | if (inherit_stat) | 413 | if (inherit_stat) |
| 402 | attr->inherit_stat = 1; | 414 | attr->inherit_stat = 1; |
| 403 | 415 | ||
| 416 | if (sample_address) | ||
| 417 | attr->sample_type |= PERF_SAMPLE_ADDR; | ||
| 418 | |||
| 404 | if (call_graph) | 419 | if (call_graph) |
| 405 | attr->sample_type |= PERF_SAMPLE_CALLCHAIN; | 420 | attr->sample_type |= PERF_SAMPLE_CALLCHAIN; |
| 406 | 421 | ||
| 422 | if (raw_samples) | ||
| 423 | attr->sample_type |= PERF_SAMPLE_RAW; | ||
| 424 | |||
| 407 | attr->mmap = track; | 425 | attr->mmap = track; |
| 408 | attr->comm = track; | 426 | attr->comm = track; |
| 409 | attr->inherit = (cpu < 0) && inherit; | 427 | attr->inherit = (cpu < 0) && inherit; |
| @@ -417,6 +435,8 @@ try_again: | |||
| 417 | 435 | ||
| 418 | if (err == EPERM) | 436 | if (err == EPERM) |
| 419 | die("Permission error - are you root?\n"); | 437 | die("Permission error - are you root?\n"); |
| 438 | else if (err == ENODEV && profile_cpu != -1) | ||
| 439 | die("No such device - did you specify an out-of-range profile CPU?\n"); | ||
| 420 | 440 | ||
| 421 | /* | 441 | /* |
| 422 | * If it's cycles then fall back to hrtimer | 442 | * If it's cycles then fall back to hrtimer |
| @@ -516,10 +536,14 @@ static int __cmd_record(int argc, const char **argv) | |||
| 516 | signal(SIGCHLD, sig_handler); | 536 | signal(SIGCHLD, sig_handler); |
| 517 | signal(SIGINT, sig_handler); | 537 | signal(SIGINT, sig_handler); |
| 518 | 538 | ||
| 519 | if (!stat(output_name, &st) && !force && !append_file) { | 539 | if (!stat(output_name, &st) && st.st_size) { |
| 520 | fprintf(stderr, "Error, output file %s exists, use -A to append or -f to overwrite.\n", | 540 | if (!force && !append_file) { |
| 521 | output_name); | 541 | fprintf(stderr, "Error, output file %s exists, use -A to append or -f to overwrite.\n", |
| 522 | exit(-1); | 542 | output_name); |
| 543 | exit(-1); | ||
| 544 | } | ||
| 545 | } else { | ||
| 546 | append_file = 0; | ||
| 523 | } | 547 | } |
| 524 | 548 | ||
| 525 | flags = O_CREAT|O_RDWR; | 549 | flags = O_CREAT|O_RDWR; |
| @@ -546,16 +570,22 @@ static int __cmd_record(int argc, const char **argv) | |||
| 546 | if (pid == -1) | 570 | if (pid == -1) |
| 547 | pid = getpid(); | 571 | pid = getpid(); |
| 548 | 572 | ||
| 549 | open_counters(-1, pid); | 573 | open_counters(profile_cpu, pid); |
| 550 | } else for (i = 0; i < nr_cpus; i++) | 574 | } else { |
| 551 | open_counters(i, target_pid); | 575 | if (profile_cpu != -1) { |
| 576 | open_counters(profile_cpu, target_pid); | ||
| 577 | } else { | ||
| 578 | for (i = 0; i < nr_cpus; i++) | ||
| 579 | open_counters(i, target_pid); | ||
| 580 | } | ||
| 581 | } | ||
| 552 | 582 | ||
| 553 | if (file_new) | 583 | if (file_new) |
| 554 | perf_header__write(header, output); | 584 | perf_header__write(header, output); |
| 555 | 585 | ||
| 556 | if (!system_wide) { | 586 | if (!system_wide) { |
| 557 | pid_synthesize_comm_event(pid, 0); | 587 | pid_t tgid = pid_synthesize_comm_event(pid, 0); |
| 558 | pid_synthesize_mmap_samples(pid); | 588 | pid_synthesize_mmap_samples(pid, tgid); |
| 559 | } else | 589 | } else |
| 560 | synthesize_all(); | 590 | synthesize_all(); |
| 561 | 591 | ||
| @@ -623,10 +653,14 @@ static const struct option options[] = { | |||
| 623 | "record events on existing pid"), | 653 | "record events on existing pid"), |
| 624 | OPT_INTEGER('r', "realtime", &realtime_prio, | 654 | OPT_INTEGER('r', "realtime", &realtime_prio, |
| 625 | "collect data with this RT SCHED_FIFO priority"), | 655 | "collect data with this RT SCHED_FIFO priority"), |
| 656 | OPT_BOOLEAN('R', "raw-samples", &raw_samples, | ||
| 657 | "collect raw sample records from all opened counters"), | ||
| 626 | OPT_BOOLEAN('a', "all-cpus", &system_wide, | 658 | OPT_BOOLEAN('a', "all-cpus", &system_wide, |
| 627 | "system-wide collection from all CPUs"), | 659 | "system-wide collection from all CPUs"), |
| 628 | OPT_BOOLEAN('A', "append", &append_file, | 660 | OPT_BOOLEAN('A', "append", &append_file, |
| 629 | "append to the output file to do incremental profiling"), | 661 | "append to the output file to do incremental profiling"), |
| 662 | OPT_INTEGER('C', "profile_cpu", &profile_cpu, | ||
| 663 | "CPU to profile on"), | ||
| 630 | OPT_BOOLEAN('f', "force", &force, | 664 | OPT_BOOLEAN('f', "force", &force, |
| 631 | "overwrite existing data file"), | 665 | "overwrite existing data file"), |
| 632 | OPT_LONG('c', "count", &default_interval, | 666 | OPT_LONG('c', "count", &default_interval, |
| @@ -645,6 +679,8 @@ static const struct option options[] = { | |||
| 645 | "be more verbose (show counter open errors, etc)"), | 679 | "be more verbose (show counter open errors, etc)"), |
| 646 | OPT_BOOLEAN('s', "stat", &inherit_stat, | 680 | OPT_BOOLEAN('s', "stat", &inherit_stat, |
| 647 | "per thread counts"), | 681 | "per thread counts"), |
| 682 | OPT_BOOLEAN('d', "data", &sample_address, | ||
| 683 | "Sample addresses"), | ||
| 648 | OPT_BOOLEAN('n', "no-samples", &no_samples, | 684 | OPT_BOOLEAN('n', "no-samples", &no_samples, |
| 649 | "don't sample"), | 685 | "don't sample"), |
| 650 | OPT_END() | 686 | OPT_END() |
| @@ -654,7 +690,8 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) | |||
| 654 | { | 690 | { |
| 655 | int counter; | 691 | int counter; |
| 656 | 692 | ||
| 657 | argc = parse_options(argc, argv, options, record_usage, 0); | 693 | argc = parse_options(argc, argv, options, record_usage, |
| 694 | PARSE_OPT_STOP_AT_NON_OPTION); | ||
| 658 | if (!argc && target_pid == -1 && !system_wide) | 695 | if (!argc && target_pid == -1 && !system_wide) |
| 659 | usage_with_options(record_usage, options); | 696 | usage_with_options(record_usage, options); |
| 660 | 697 | ||
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 4e5cc266311e..8b2ec882e6e0 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
| @@ -31,11 +31,14 @@ | |||
| 31 | static char const *input_name = "perf.data"; | 31 | static char const *input_name = "perf.data"; |
| 32 | static char *vmlinux = NULL; | 32 | static char *vmlinux = NULL; |
| 33 | 33 | ||
| 34 | static char default_sort_order[] = "comm,dso"; | 34 | static char default_sort_order[] = "comm,dso,symbol"; |
| 35 | static char *sort_order = default_sort_order; | 35 | static char *sort_order = default_sort_order; |
| 36 | static char *dso_list_str, *comm_list_str, *sym_list_str; | 36 | static char *dso_list_str, *comm_list_str, *sym_list_str, |
| 37 | *col_width_list_str; | ||
| 37 | static struct strlist *dso_list, *comm_list, *sym_list; | 38 | static struct strlist *dso_list, *comm_list, *sym_list; |
| 39 | static char *field_sep; | ||
| 38 | 40 | ||
| 41 | static int force; | ||
| 39 | static int input; | 42 | static int input; |
| 40 | static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV; | 43 | static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV; |
| 41 | 44 | ||
| @@ -49,6 +52,7 @@ static int verbose; | |||
| 49 | static int modules; | 52 | static int modules; |
| 50 | 53 | ||
| 51 | static int full_paths; | 54 | static int full_paths; |
| 55 | static int show_nr_samples; | ||
| 52 | 56 | ||
| 53 | static unsigned long page_size; | 57 | static unsigned long page_size; |
| 54 | static unsigned long mmap_window = 32; | 58 | static unsigned long mmap_window = 32; |
| @@ -65,7 +69,7 @@ static int callchain; | |||
| 65 | 69 | ||
| 66 | static | 70 | static |
| 67 | struct callchain_param callchain_param = { | 71 | struct callchain_param callchain_param = { |
| 68 | .mode = CHAIN_GRAPH_ABS, | 72 | .mode = CHAIN_GRAPH_REL, |
| 69 | .min_percent = 0.5 | 73 | .min_percent = 0.5 |
| 70 | }; | 74 | }; |
| 71 | 75 | ||
| @@ -96,13 +100,7 @@ struct comm_event { | |||
| 96 | struct fork_event { | 100 | struct fork_event { |
| 97 | struct perf_event_header header; | 101 | struct perf_event_header header; |
| 98 | u32 pid, ppid; | 102 | u32 pid, ppid; |
| 99 | }; | 103 | u32 tid, ptid; |
| 100 | |||
| 101 | struct period_event { | ||
| 102 | struct perf_event_header header; | ||
| 103 | u64 time; | ||
| 104 | u64 id; | ||
| 105 | u64 sample_period; | ||
| 106 | }; | 104 | }; |
| 107 | 105 | ||
| 108 | struct lost_event { | 106 | struct lost_event { |
| @@ -115,7 +113,9 @@ struct read_event { | |||
| 115 | struct perf_event_header header; | 113 | struct perf_event_header header; |
| 116 | u32 pid,tid; | 114 | u32 pid,tid; |
| 117 | u64 value; | 115 | u64 value; |
| 118 | u64 format[3]; | 116 | u64 time_enabled; |
| 117 | u64 time_running; | ||
| 118 | u64 id; | ||
| 119 | }; | 119 | }; |
| 120 | 120 | ||
| 121 | typedef union event_union { | 121 | typedef union event_union { |
| @@ -124,11 +124,37 @@ typedef union event_union { | |||
| 124 | struct mmap_event mmap; | 124 | struct mmap_event mmap; |
| 125 | struct comm_event comm; | 125 | struct comm_event comm; |
| 126 | struct fork_event fork; | 126 | struct fork_event fork; |
| 127 | struct period_event period; | ||
| 128 | struct lost_event lost; | 127 | struct lost_event lost; |
| 129 | struct read_event read; | 128 | struct read_event read; |
| 130 | } event_t; | 129 | } event_t; |
| 131 | 130 | ||
| 131 | static int repsep_fprintf(FILE *fp, const char *fmt, ...) | ||
| 132 | { | ||
| 133 | int n; | ||
| 134 | va_list ap; | ||
| 135 | |||
| 136 | va_start(ap, fmt); | ||
| 137 | if (!field_sep) | ||
| 138 | n = vfprintf(fp, fmt, ap); | ||
| 139 | else { | ||
| 140 | char *bf = NULL; | ||
| 141 | n = vasprintf(&bf, fmt, ap); | ||
| 142 | if (n > 0) { | ||
| 143 | char *sep = bf; | ||
| 144 | while (1) { | ||
| 145 | sep = strchr(sep, *field_sep); | ||
| 146 | if (sep == NULL) | ||
| 147 | break; | ||
| 148 | *sep = '.'; | ||
| 149 | } | ||
| 150 | } | ||
| 151 | fputs(bf, fp); | ||
| 152 | free(bf); | ||
| 153 | } | ||
| 154 | va_end(ap); | ||
| 155 | return n; | ||
| 156 | } | ||
| 157 | |||
| 132 | static LIST_HEAD(dsos); | 158 | static LIST_HEAD(dsos); |
| 133 | static struct dso *kernel_dso; | 159 | static struct dso *kernel_dso; |
| 134 | static struct dso *vdso; | 160 | static struct dso *vdso; |
| @@ -230,7 +256,7 @@ static int strcommon(const char *pathname) | |||
| 230 | { | 256 | { |
| 231 | int n = 0; | 257 | int n = 0; |
| 232 | 258 | ||
| 233 | while (pathname[n] == cwd[n] && n < cwdlen) | 259 | while (n < cwdlen && pathname[n] == cwd[n]) |
| 234 | ++n; | 260 | ++n; |
| 235 | 261 | ||
| 236 | return n; | 262 | return n; |
| @@ -360,12 +386,28 @@ static struct thread *thread__new(pid_t pid) | |||
| 360 | return self; | 386 | return self; |
| 361 | } | 387 | } |
| 362 | 388 | ||
| 389 | static unsigned int dsos__col_width, | ||
| 390 | comms__col_width, | ||
| 391 | threads__col_width; | ||
| 392 | |||
| 363 | static int thread__set_comm(struct thread *self, const char *comm) | 393 | static int thread__set_comm(struct thread *self, const char *comm) |
| 364 | { | 394 | { |
| 365 | if (self->comm) | 395 | if (self->comm) |
| 366 | free(self->comm); | 396 | free(self->comm); |
| 367 | self->comm = strdup(comm); | 397 | self->comm = strdup(comm); |
| 368 | return self->comm ? 0 : -ENOMEM; | 398 | if (!self->comm) |
| 399 | return -ENOMEM; | ||
| 400 | |||
| 401 | if (!col_width_list_str && !field_sep && | ||
| 402 | (!comm_list || strlist__has_entry(comm_list, comm))) { | ||
| 403 | unsigned int slen = strlen(comm); | ||
| 404 | if (slen > comms__col_width) { | ||
| 405 | comms__col_width = slen; | ||
| 406 | threads__col_width = slen + 6; | ||
| 407 | } | ||
| 408 | } | ||
| 409 | |||
| 410 | return 0; | ||
| 369 | } | 411 | } |
| 370 | 412 | ||
| 371 | static size_t thread__fprintf(struct thread *self, FILE *fp) | 413 | static size_t thread__fprintf(struct thread *self, FILE *fp) |
| @@ -536,7 +578,9 @@ struct sort_entry { | |||
| 536 | 578 | ||
| 537 | int64_t (*cmp)(struct hist_entry *, struct hist_entry *); | 579 | int64_t (*cmp)(struct hist_entry *, struct hist_entry *); |
| 538 | int64_t (*collapse)(struct hist_entry *, struct hist_entry *); | 580 | int64_t (*collapse)(struct hist_entry *, struct hist_entry *); |
| 539 | size_t (*print)(FILE *fp, struct hist_entry *); | 581 | size_t (*print)(FILE *fp, struct hist_entry *, unsigned int width); |
| 582 | unsigned int *width; | ||
| 583 | bool elide; | ||
| 540 | }; | 584 | }; |
| 541 | 585 | ||
| 542 | static int64_t cmp_null(void *l, void *r) | 586 | static int64_t cmp_null(void *l, void *r) |
| @@ -558,15 +602,17 @@ sort__thread_cmp(struct hist_entry *left, struct hist_entry *right) | |||
| 558 | } | 602 | } |
| 559 | 603 | ||
| 560 | static size_t | 604 | static size_t |
| 561 | sort__thread_print(FILE *fp, struct hist_entry *self) | 605 | sort__thread_print(FILE *fp, struct hist_entry *self, unsigned int width) |
| 562 | { | 606 | { |
| 563 | return fprintf(fp, "%16s:%5d", self->thread->comm ?: "", self->thread->pid); | 607 | return repsep_fprintf(fp, "%*s:%5d", width - 6, |
| 608 | self->thread->comm ?: "", self->thread->pid); | ||
| 564 | } | 609 | } |
| 565 | 610 | ||
| 566 | static struct sort_entry sort_thread = { | 611 | static struct sort_entry sort_thread = { |
| 567 | .header = " Command: Pid", | 612 | .header = "Command: Pid", |
| 568 | .cmp = sort__thread_cmp, | 613 | .cmp = sort__thread_cmp, |
| 569 | .print = sort__thread_print, | 614 | .print = sort__thread_print, |
| 615 | .width = &threads__col_width, | ||
| 570 | }; | 616 | }; |
| 571 | 617 | ||
| 572 | /* --sort comm */ | 618 | /* --sort comm */ |
| @@ -590,16 +636,17 @@ sort__comm_collapse(struct hist_entry *left, struct hist_entry *right) | |||
| 590 | } | 636 | } |
| 591 | 637 | ||
| 592 | static size_t | 638 | static size_t |
| 593 | sort__comm_print(FILE *fp, struct hist_entry *self) | 639 | sort__comm_print(FILE *fp, struct hist_entry *self, unsigned int width) |
| 594 | { | 640 | { |
| 595 | return fprintf(fp, "%16s", self->thread->comm); | 641 | return repsep_fprintf(fp, "%*s", width, self->thread->comm); |
| 596 | } | 642 | } |
| 597 | 643 | ||
| 598 | static struct sort_entry sort_comm = { | 644 | static struct sort_entry sort_comm = { |
| 599 | .header = " Command", | 645 | .header = "Command", |
| 600 | .cmp = sort__comm_cmp, | 646 | .cmp = sort__comm_cmp, |
| 601 | .collapse = sort__comm_collapse, | 647 | .collapse = sort__comm_collapse, |
| 602 | .print = sort__comm_print, | 648 | .print = sort__comm_print, |
| 649 | .width = &comms__col_width, | ||
| 603 | }; | 650 | }; |
| 604 | 651 | ||
| 605 | /* --sort dso */ | 652 | /* --sort dso */ |
| @@ -617,18 +664,19 @@ sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) | |||
| 617 | } | 664 | } |
| 618 | 665 | ||
| 619 | static size_t | 666 | static size_t |
| 620 | sort__dso_print(FILE *fp, struct hist_entry *self) | 667 | sort__dso_print(FILE *fp, struct hist_entry *self, unsigned int width) |
| 621 | { | 668 | { |
| 622 | if (self->dso) | 669 | if (self->dso) |
| 623 | return fprintf(fp, "%-25s", self->dso->name); | 670 | return repsep_fprintf(fp, "%-*s", width, self->dso->name); |
| 624 | 671 | ||
| 625 | return fprintf(fp, "%016llx ", (u64)self->ip); | 672 | return repsep_fprintf(fp, "%*llx", width, (u64)self->ip); |
| 626 | } | 673 | } |
| 627 | 674 | ||
| 628 | static struct sort_entry sort_dso = { | 675 | static struct sort_entry sort_dso = { |
| 629 | .header = "Shared Object ", | 676 | .header = "Shared Object", |
| 630 | .cmp = sort__dso_cmp, | 677 | .cmp = sort__dso_cmp, |
| 631 | .print = sort__dso_print, | 678 | .print = sort__dso_print, |
| 679 | .width = &dsos__col_width, | ||
| 632 | }; | 680 | }; |
| 633 | 681 | ||
| 634 | /* --sort symbol */ | 682 | /* --sort symbol */ |
| @@ -648,22 +696,23 @@ sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) | |||
| 648 | } | 696 | } |
| 649 | 697 | ||
| 650 | static size_t | 698 | static size_t |
| 651 | sort__sym_print(FILE *fp, struct hist_entry *self) | 699 | sort__sym_print(FILE *fp, struct hist_entry *self, unsigned int width __used) |
| 652 | { | 700 | { |
| 653 | size_t ret = 0; | 701 | size_t ret = 0; |
| 654 | 702 | ||
| 655 | if (verbose) | 703 | if (verbose) |
| 656 | ret += fprintf(fp, "%#018llx ", (u64)self->ip); | 704 | ret += repsep_fprintf(fp, "%#018llx %c ", (u64)self->ip, |
| 705 | dso__symtab_origin(self->dso)); | ||
| 657 | 706 | ||
| 707 | ret += repsep_fprintf(fp, "[%c] ", self->level); | ||
| 658 | if (self->sym) { | 708 | if (self->sym) { |
| 659 | ret += fprintf(fp, "[%c] %s", | 709 | ret += repsep_fprintf(fp, "%s", self->sym->name); |
| 660 | self->dso == kernel_dso ? 'k' : | ||
| 661 | self->dso == hypervisor_dso ? 'h' : '.', self->sym->name); | ||
| 662 | 710 | ||
| 663 | if (self->sym->module) | 711 | if (self->sym->module) |
| 664 | ret += fprintf(fp, "\t[%s]", self->sym->module->name); | 712 | ret += repsep_fprintf(fp, "\t[%s]", |
| 713 | self->sym->module->name); | ||
| 665 | } else { | 714 | } else { |
| 666 | ret += fprintf(fp, "%#016llx", (u64)self->ip); | 715 | ret += repsep_fprintf(fp, "%#016llx", (u64)self->ip); |
| 667 | } | 716 | } |
| 668 | 717 | ||
| 669 | return ret; | 718 | return ret; |
| @@ -690,19 +739,19 @@ sort__parent_cmp(struct hist_entry *left, struct hist_entry *right) | |||
| 690 | } | 739 | } |
| 691 | 740 | ||
| 692 | static size_t | 741 | static size_t |
| 693 | sort__parent_print(FILE *fp, struct hist_entry *self) | 742 | sort__parent_print(FILE *fp, struct hist_entry *self, unsigned int width) |
| 694 | { | 743 | { |
| 695 | size_t ret = 0; | 744 | return repsep_fprintf(fp, "%-*s", width, |
| 696 | 745 | self->parent ? self->parent->name : "[other]"); | |
| 697 | ret += fprintf(fp, "%-20s", self->parent ? self->parent->name : "[other]"); | ||
| 698 | |||
| 699 | return ret; | ||
| 700 | } | 746 | } |
| 701 | 747 | ||
| 748 | static unsigned int parent_symbol__col_width; | ||
| 749 | |||
| 702 | static struct sort_entry sort_parent = { | 750 | static struct sort_entry sort_parent = { |
| 703 | .header = "Parent symbol ", | 751 | .header = "Parent symbol", |
| 704 | .cmp = sort__parent_cmp, | 752 | .cmp = sort__parent_cmp, |
| 705 | .print = sort__parent_print, | 753 | .print = sort__parent_print, |
| 754 | .width = &parent_symbol__col_width, | ||
| 706 | }; | 755 | }; |
| 707 | 756 | ||
| 708 | static int sort__need_collapse = 0; | 757 | static int sort__need_collapse = 0; |
| @@ -843,6 +892,21 @@ ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, int depth, | |||
| 843 | return ret; | 892 | return ret; |
| 844 | } | 893 | } |
| 845 | 894 | ||
| 895 | static struct symbol *rem_sq_bracket; | ||
| 896 | static struct callchain_list rem_hits; | ||
| 897 | |||
| 898 | static void init_rem_hits(void) | ||
| 899 | { | ||
| 900 | rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6); | ||
| 901 | if (!rem_sq_bracket) { | ||
| 902 | fprintf(stderr, "Not enough memory to display remaining hits\n"); | ||
| 903 | return; | ||
| 904 | } | ||
| 905 | |||
| 906 | strcpy(rem_sq_bracket->name, "[...]"); | ||
| 907 | rem_hits.sym = rem_sq_bracket; | ||
| 908 | } | ||
| 909 | |||
| 846 | static size_t | 910 | static size_t |
| 847 | callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | 911 | callchain__fprintf_graph(FILE *fp, struct callchain_node *self, |
| 848 | u64 total_samples, int depth, int depth_mask) | 912 | u64 total_samples, int depth, int depth_mask) |
| @@ -852,25 +916,34 @@ callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | |||
| 852 | struct callchain_list *chain; | 916 | struct callchain_list *chain; |
| 853 | int new_depth_mask = depth_mask; | 917 | int new_depth_mask = depth_mask; |
| 854 | u64 new_total; | 918 | u64 new_total; |
| 919 | u64 remaining; | ||
| 855 | size_t ret = 0; | 920 | size_t ret = 0; |
| 856 | int i; | 921 | int i; |
| 857 | 922 | ||
| 858 | if (callchain_param.mode == CHAIN_GRAPH_REL) | 923 | if (callchain_param.mode == CHAIN_GRAPH_REL) |
| 859 | new_total = self->cumul_hit; | 924 | new_total = self->children_hit; |
| 860 | else | 925 | else |
| 861 | new_total = total_samples; | 926 | new_total = total_samples; |
| 862 | 927 | ||
| 928 | remaining = new_total; | ||
| 929 | |||
| 863 | node = rb_first(&self->rb_root); | 930 | node = rb_first(&self->rb_root); |
| 864 | while (node) { | 931 | while (node) { |
| 932 | u64 cumul; | ||
| 933 | |||
| 865 | child = rb_entry(node, struct callchain_node, rb_node); | 934 | child = rb_entry(node, struct callchain_node, rb_node); |
| 935 | cumul = cumul_hits(child); | ||
| 936 | remaining -= cumul; | ||
| 866 | 937 | ||
| 867 | /* | 938 | /* |
| 868 | * The depth mask manages the output of pipes that show | 939 | * The depth mask manages the output of pipes that show |
| 869 | * the depth. We don't want to keep the pipes of the current | 940 | * the depth. We don't want to keep the pipes of the current |
| 870 | * level for the last child of this depth | 941 | * level for the last child of this depth. |
| 942 | * Except if we have remaining filtered hits. They will | ||
| 943 | * supersede the last child | ||
| 871 | */ | 944 | */ |
| 872 | next = rb_next(node); | 945 | next = rb_next(node); |
| 873 | if (!next) | 946 | if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining)) |
| 874 | new_depth_mask &= ~(1 << (depth - 1)); | 947 | new_depth_mask &= ~(1 << (depth - 1)); |
| 875 | 948 | ||
| 876 | /* | 949 | /* |
| @@ -885,7 +958,7 @@ callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | |||
| 885 | ret += ipchain__fprintf_graph(fp, chain, depth, | 958 | ret += ipchain__fprintf_graph(fp, chain, depth, |
| 886 | new_depth_mask, i++, | 959 | new_depth_mask, i++, |
| 887 | new_total, | 960 | new_total, |
| 888 | child->cumul_hit); | 961 | cumul); |
| 889 | } | 962 | } |
| 890 | ret += callchain__fprintf_graph(fp, child, new_total, | 963 | ret += callchain__fprintf_graph(fp, child, new_total, |
| 891 | depth + 1, | 964 | depth + 1, |
| @@ -893,6 +966,19 @@ callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | |||
| 893 | node = next; | 966 | node = next; |
| 894 | } | 967 | } |
| 895 | 968 | ||
| 969 | if (callchain_param.mode == CHAIN_GRAPH_REL && | ||
| 970 | remaining && remaining != new_total) { | ||
| 971 | |||
| 972 | if (!rem_sq_bracket) | ||
| 973 | return ret; | ||
| 974 | |||
| 975 | new_depth_mask &= ~(1 << (depth - 1)); | ||
| 976 | |||
| 977 | ret += ipchain__fprintf_graph(fp, &rem_hits, depth, | ||
| 978 | new_depth_mask, 0, new_total, | ||
| 979 | remaining); | ||
| 980 | } | ||
| 981 | |||
| 896 | return ret; | 982 | return ret; |
| 897 | } | 983 | } |
| 898 | 984 | ||
| @@ -967,17 +1053,25 @@ hist_entry__fprintf(FILE *fp, struct hist_entry *self, u64 total_samples) | |||
| 967 | return 0; | 1053 | return 0; |
| 968 | 1054 | ||
| 969 | if (total_samples) | 1055 | if (total_samples) |
| 970 | ret = percent_color_fprintf(fp, " %6.2f%%", | 1056 | ret = percent_color_fprintf(fp, |
| 971 | (self->count * 100.0) / total_samples); | 1057 | field_sep ? "%.2f" : " %6.2f%%", |
| 1058 | (self->count * 100.0) / total_samples); | ||
| 972 | else | 1059 | else |
| 973 | ret = fprintf(fp, "%12Ld ", self->count); | 1060 | ret = fprintf(fp, field_sep ? "%lld" : "%12lld ", self->count); |
| 1061 | |||
| 1062 | if (show_nr_samples) { | ||
| 1063 | if (field_sep) | ||
| 1064 | fprintf(fp, "%c%lld", *field_sep, self->count); | ||
| 1065 | else | ||
| 1066 | fprintf(fp, "%11lld", self->count); | ||
| 1067 | } | ||
| 974 | 1068 | ||
| 975 | list_for_each_entry(se, &hist_entry__sort_list, list) { | 1069 | list_for_each_entry(se, &hist_entry__sort_list, list) { |
| 976 | if (exclude_other && (se == &sort_parent)) | 1070 | if (se->elide) |
| 977 | continue; | 1071 | continue; |
| 978 | 1072 | ||
| 979 | fprintf(fp, " "); | 1073 | fprintf(fp, "%s", field_sep ?: " "); |
| 980 | ret += se->print(fp, self); | 1074 | ret += se->print(fp, self, se->width ? *se->width : 0); |
| 981 | } | 1075 | } |
| 982 | 1076 | ||
| 983 | ret += fprintf(fp, "\n"); | 1077 | ret += fprintf(fp, "\n"); |
| @@ -992,6 +1086,18 @@ hist_entry__fprintf(FILE *fp, struct hist_entry *self, u64 total_samples) | |||
| 992 | * | 1086 | * |
| 993 | */ | 1087 | */ |
| 994 | 1088 | ||
| 1089 | static void dso__calc_col_width(struct dso *self) | ||
| 1090 | { | ||
| 1091 | if (!col_width_list_str && !field_sep && | ||
| 1092 | (!dso_list || strlist__has_entry(dso_list, self->name))) { | ||
| 1093 | unsigned int slen = strlen(self->name); | ||
| 1094 | if (slen > dsos__col_width) | ||
| 1095 | dsos__col_width = slen; | ||
| 1096 | } | ||
| 1097 | |||
| 1098 | self->slen_calculated = 1; | ||
| 1099 | } | ||
| 1100 | |||
| 995 | static struct symbol * | 1101 | static struct symbol * |
| 996 | resolve_symbol(struct thread *thread, struct map **mapp, | 1102 | resolve_symbol(struct thread *thread, struct map **mapp, |
| 997 | struct dso **dsop, u64 *ipp) | 1103 | struct dso **dsop, u64 *ipp) |
| @@ -1011,6 +1117,14 @@ resolve_symbol(struct thread *thread, struct map **mapp, | |||
| 1011 | 1117 | ||
| 1012 | map = thread__find_map(thread, ip); | 1118 | map = thread__find_map(thread, ip); |
| 1013 | if (map != NULL) { | 1119 | if (map != NULL) { |
| 1120 | /* | ||
| 1121 | * We have to do this here as we may have a dso | ||
| 1122 | * with no symbol hit that has a name longer than | ||
| 1123 | * the ones with symbols sampled. | ||
| 1124 | */ | ||
| 1125 | if (!sort_dso.elide && !map->dso->slen_calculated) | ||
| 1126 | dso__calc_col_width(map->dso); | ||
| 1127 | |||
| 1014 | if (mapp) | 1128 | if (mapp) |
| 1015 | *mapp = map; | 1129 | *mapp = map; |
| 1016 | got_map: | 1130 | got_map: |
| @@ -1282,35 +1396,69 @@ static size_t output__fprintf(FILE *fp, u64 total_samples) | |||
| 1282 | struct sort_entry *se; | 1396 | struct sort_entry *se; |
| 1283 | struct rb_node *nd; | 1397 | struct rb_node *nd; |
| 1284 | size_t ret = 0; | 1398 | size_t ret = 0; |
| 1399 | unsigned int width; | ||
| 1400 | char *col_width = col_width_list_str; | ||
| 1285 | 1401 | ||
| 1286 | fprintf(fp, "\n"); | 1402 | init_rem_hits(); |
| 1287 | fprintf(fp, "#\n"); | 1403 | |
| 1288 | fprintf(fp, "# (%Ld samples)\n", (u64)total_samples); | 1404 | fprintf(fp, "# Samples: %Ld\n", (u64)total_samples); |
| 1289 | fprintf(fp, "#\n"); | 1405 | fprintf(fp, "#\n"); |
| 1290 | 1406 | ||
| 1291 | fprintf(fp, "# Overhead"); | 1407 | fprintf(fp, "# Overhead"); |
| 1408 | if (show_nr_samples) { | ||
| 1409 | if (field_sep) | ||
| 1410 | fprintf(fp, "%cSamples", *field_sep); | ||
| 1411 | else | ||
| 1412 | fputs(" Samples ", fp); | ||
| 1413 | } | ||
| 1292 | list_for_each_entry(se, &hist_entry__sort_list, list) { | 1414 | list_for_each_entry(se, &hist_entry__sort_list, list) { |
| 1293 | if (exclude_other && (se == &sort_parent)) | 1415 | if (se->elide) |
| 1294 | continue; | 1416 | continue; |
| 1295 | fprintf(fp, " %s", se->header); | 1417 | if (field_sep) { |
| 1418 | fprintf(fp, "%c%s", *field_sep, se->header); | ||
| 1419 | continue; | ||
| 1420 | } | ||
| 1421 | width = strlen(se->header); | ||
| 1422 | if (se->width) { | ||
| 1423 | if (col_width_list_str) { | ||
| 1424 | if (col_width) { | ||
| 1425 | *se->width = atoi(col_width); | ||
| 1426 | col_width = strchr(col_width, ','); | ||
| 1427 | if (col_width) | ||
| 1428 | ++col_width; | ||
| 1429 | } | ||
| 1430 | } | ||
| 1431 | width = *se->width = max(*se->width, width); | ||
| 1432 | } | ||
| 1433 | fprintf(fp, " %*s", width, se->header); | ||
| 1296 | } | 1434 | } |
| 1297 | fprintf(fp, "\n"); | 1435 | fprintf(fp, "\n"); |
| 1298 | 1436 | ||
| 1437 | if (field_sep) | ||
| 1438 | goto print_entries; | ||
| 1439 | |||
| 1299 | fprintf(fp, "# ........"); | 1440 | fprintf(fp, "# ........"); |
| 1441 | if (show_nr_samples) | ||
| 1442 | fprintf(fp, " .........."); | ||
| 1300 | list_for_each_entry(se, &hist_entry__sort_list, list) { | 1443 | list_for_each_entry(se, &hist_entry__sort_list, list) { |
| 1301 | unsigned int i; | 1444 | unsigned int i; |
| 1302 | 1445 | ||
| 1303 | if (exclude_other && (se == &sort_parent)) | 1446 | if (se->elide) |
| 1304 | continue; | 1447 | continue; |
| 1305 | 1448 | ||
| 1306 | fprintf(fp, " "); | 1449 | fprintf(fp, " "); |
| 1307 | for (i = 0; i < strlen(se->header); i++) | 1450 | if (se->width) |
| 1451 | width = *se->width; | ||
| 1452 | else | ||
| 1453 | width = strlen(se->header); | ||
| 1454 | for (i = 0; i < width; i++) | ||
| 1308 | fprintf(fp, "."); | 1455 | fprintf(fp, "."); |
| 1309 | } | 1456 | } |
| 1310 | fprintf(fp, "\n"); | 1457 | fprintf(fp, "\n"); |
| 1311 | 1458 | ||
| 1312 | fprintf(fp, "#\n"); | 1459 | fprintf(fp, "#\n"); |
| 1313 | 1460 | ||
| 1461 | print_entries: | ||
| 1314 | for (nd = rb_first(&output_hists); nd; nd = rb_next(nd)) { | 1462 | for (nd = rb_first(&output_hists); nd; nd = rb_next(nd)) { |
| 1315 | pos = rb_entry(nd, struct hist_entry, rb_node); | 1463 | pos = rb_entry(nd, struct hist_entry, rb_node); |
| 1316 | ret += hist_entry__fprintf(fp, pos, total_samples); | 1464 | ret += hist_entry__fprintf(fp, pos, total_samples); |
| @@ -1319,11 +1467,13 @@ static size_t output__fprintf(FILE *fp, u64 total_samples) | |||
| 1319 | if (sort_order == default_sort_order && | 1467 | if (sort_order == default_sort_order && |
| 1320 | parent_pattern == default_parent_pattern) { | 1468 | parent_pattern == default_parent_pattern) { |
| 1321 | fprintf(fp, "#\n"); | 1469 | fprintf(fp, "#\n"); |
| 1322 | fprintf(fp, "# (For more details, try: perf report --sort comm,dso,symbol)\n"); | 1470 | fprintf(fp, "# (For a higher level overview, try: perf report --sort comm,dso)\n"); |
| 1323 | fprintf(fp, "#\n"); | 1471 | fprintf(fp, "#\n"); |
| 1324 | } | 1472 | } |
| 1325 | fprintf(fp, "\n"); | 1473 | fprintf(fp, "\n"); |
| 1326 | 1474 | ||
| 1475 | free(rem_sq_bracket); | ||
| 1476 | |||
| 1327 | return ret; | 1477 | return ret; |
| 1328 | } | 1478 | } |
| 1329 | 1479 | ||
| @@ -1377,11 +1527,11 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head) | |||
| 1377 | more_data += sizeof(u64); | 1527 | more_data += sizeof(u64); |
| 1378 | } | 1528 | } |
| 1379 | 1529 | ||
| 1380 | dprintf("%p [%p]: PERF_EVENT_SAMPLE (IP, %d): %d: %p period: %Ld\n", | 1530 | dprintf("%p [%p]: PERF_EVENT_SAMPLE (IP, %d): %d/%d: %p period: %Ld\n", |
| 1381 | (void *)(offset + head), | 1531 | (void *)(offset + head), |
| 1382 | (void *)(long)(event->header.size), | 1532 | (void *)(long)(event->header.size), |
| 1383 | event->header.misc, | 1533 | event->header.misc, |
| 1384 | event->ip.pid, | 1534 | event->ip.pid, event->ip.tid, |
| 1385 | (void *)(long)ip, | 1535 | (void *)(long)ip, |
| 1386 | (long long)period); | 1536 | (long long)period); |
| 1387 | 1537 | ||
| @@ -1441,10 +1591,11 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head) | |||
| 1441 | if (show & show_mask) { | 1591 | if (show & show_mask) { |
| 1442 | struct symbol *sym = resolve_symbol(thread, &map, &dso, &ip); | 1592 | struct symbol *sym = resolve_symbol(thread, &map, &dso, &ip); |
| 1443 | 1593 | ||
| 1444 | if (dso_list && dso && dso->name && !strlist__has_entry(dso_list, dso->name)) | 1594 | if (dso_list && (!dso || !dso->name || |
| 1595 | !strlist__has_entry(dso_list, dso->name))) | ||
| 1445 | return 0; | 1596 | return 0; |
| 1446 | 1597 | ||
| 1447 | if (sym_list && sym && !strlist__has_entry(sym_list, sym->name)) | 1598 | if (sym_list && (!sym || !strlist__has_entry(sym_list, sym->name))) |
| 1448 | return 0; | 1599 | return 0; |
| 1449 | 1600 | ||
| 1450 | if (hist_entry__add(thread, map, dso, sym, ip, chain, level, period)) { | 1601 | if (hist_entry__add(thread, map, dso, sym, ip, chain, level, period)) { |
| @@ -1463,10 +1614,11 @@ process_mmap_event(event_t *event, unsigned long offset, unsigned long head) | |||
| 1463 | struct thread *thread = threads__findnew(event->mmap.pid); | 1614 | struct thread *thread = threads__findnew(event->mmap.pid); |
| 1464 | struct map *map = map__new(&event->mmap); | 1615 | struct map *map = map__new(&event->mmap); |
| 1465 | 1616 | ||
| 1466 | dprintf("%p [%p]: PERF_EVENT_MMAP %d: [%p(%p) @ %p]: %s\n", | 1617 | dprintf("%p [%p]: PERF_EVENT_MMAP %d/%d: [%p(%p) @ %p]: %s\n", |
| 1467 | (void *)(offset + head), | 1618 | (void *)(offset + head), |
| 1468 | (void *)(long)(event->header.size), | 1619 | (void *)(long)(event->header.size), |
| 1469 | event->mmap.pid, | 1620 | event->mmap.pid, |
| 1621 | event->mmap.tid, | ||
| 1470 | (void *)(long)event->mmap.start, | 1622 | (void *)(long)event->mmap.start, |
| 1471 | (void *)(long)event->mmap.len, | 1623 | (void *)(long)event->mmap.len, |
| 1472 | (void *)(long)event->mmap.pgoff, | 1624 | (void *)(long)event->mmap.pgoff, |
| @@ -1504,15 +1656,27 @@ process_comm_event(event_t *event, unsigned long offset, unsigned long head) | |||
| 1504 | } | 1656 | } |
| 1505 | 1657 | ||
| 1506 | static int | 1658 | static int |
| 1507 | process_fork_event(event_t *event, unsigned long offset, unsigned long head) | 1659 | process_task_event(event_t *event, unsigned long offset, unsigned long head) |
| 1508 | { | 1660 | { |
| 1509 | struct thread *thread = threads__findnew(event->fork.pid); | 1661 | struct thread *thread = threads__findnew(event->fork.pid); |
| 1510 | struct thread *parent = threads__findnew(event->fork.ppid); | 1662 | struct thread *parent = threads__findnew(event->fork.ppid); |
| 1511 | 1663 | ||
| 1512 | dprintf("%p [%p]: PERF_EVENT_FORK: %d:%d\n", | 1664 | dprintf("%p [%p]: PERF_EVENT_%s: (%d:%d):(%d:%d)\n", |
| 1513 | (void *)(offset + head), | 1665 | (void *)(offset + head), |
| 1514 | (void *)(long)(event->header.size), | 1666 | (void *)(long)(event->header.size), |
| 1515 | event->fork.pid, event->fork.ppid); | 1667 | event->header.type == PERF_EVENT_FORK ? "FORK" : "EXIT", |
| 1668 | event->fork.pid, event->fork.tid, | ||
| 1669 | event->fork.ppid, event->fork.ptid); | ||
| 1670 | |||
| 1671 | /* | ||
| 1672 | * A thread clone will have the same PID for both | ||
| 1673 | * parent and child. | ||
| 1674 | */ | ||
| 1675 | if (thread == parent) | ||
| 1676 | return 0; | ||
| 1677 | |||
| 1678 | if (event->header.type == PERF_EVENT_EXIT) | ||
| 1679 | return 0; | ||
| 1516 | 1680 | ||
| 1517 | if (!thread || !parent || thread__fork(thread, parent)) { | 1681 | if (!thread || !parent || thread__fork(thread, parent)) { |
| 1518 | dprintf("problem processing PERF_EVENT_FORK, skipping event.\n"); | 1682 | dprintf("problem processing PERF_EVENT_FORK, skipping event.\n"); |
| @@ -1524,19 +1688,6 @@ process_fork_event(event_t *event, unsigned long offset, unsigned long head) | |||
| 1524 | } | 1688 | } |
| 1525 | 1689 | ||
| 1526 | static int | 1690 | static int |
| 1527 | process_period_event(event_t *event, unsigned long offset, unsigned long head) | ||
| 1528 | { | ||
| 1529 | dprintf("%p [%p]: PERF_EVENT_PERIOD: time:%Ld, id:%Ld: period:%Ld\n", | ||
| 1530 | (void *)(offset + head), | ||
| 1531 | (void *)(long)(event->header.size), | ||
| 1532 | event->period.time, | ||
| 1533 | event->period.id, | ||
| 1534 | event->period.sample_period); | ||
| 1535 | |||
| 1536 | return 0; | ||
| 1537 | } | ||
| 1538 | |||
| 1539 | static int | ||
| 1540 | process_lost_event(event_t *event, unsigned long offset, unsigned long head) | 1691 | process_lost_event(event_t *event, unsigned long offset, unsigned long head) |
| 1541 | { | 1692 | { |
| 1542 | dprintf("%p [%p]: PERF_EVENT_LOST: id:%Ld: lost:%Ld\n", | 1693 | dprintf("%p [%p]: PERF_EVENT_LOST: id:%Ld: lost:%Ld\n", |
| @@ -1586,14 +1737,37 @@ static void trace_event(event_t *event) | |||
| 1586 | dprintf(".\n"); | 1737 | dprintf(".\n"); |
| 1587 | } | 1738 | } |
| 1588 | 1739 | ||
| 1740 | static struct perf_header *header; | ||
| 1741 | |||
| 1742 | static struct perf_counter_attr *perf_header__find_attr(u64 id) | ||
| 1743 | { | ||
| 1744 | int i; | ||
| 1745 | |||
| 1746 | for (i = 0; i < header->attrs; i++) { | ||
| 1747 | struct perf_header_attr *attr = header->attr[i]; | ||
| 1748 | int j; | ||
| 1749 | |||
| 1750 | for (j = 0; j < attr->ids; j++) { | ||
| 1751 | if (attr->id[j] == id) | ||
| 1752 | return &attr->attr; | ||
| 1753 | } | ||
| 1754 | } | ||
| 1755 | |||
| 1756 | return NULL; | ||
| 1757 | } | ||
| 1758 | |||
| 1589 | static int | 1759 | static int |
| 1590 | process_read_event(event_t *event, unsigned long offset, unsigned long head) | 1760 | process_read_event(event_t *event, unsigned long offset, unsigned long head) |
| 1591 | { | 1761 | { |
| 1592 | dprintf("%p [%p]: PERF_EVENT_READ: %d %d %Lu\n", | 1762 | struct perf_counter_attr *attr = perf_header__find_attr(event->read.id); |
| 1763 | |||
| 1764 | dprintf("%p [%p]: PERF_EVENT_READ: %d %d %s %Lu\n", | ||
| 1593 | (void *)(offset + head), | 1765 | (void *)(offset + head), |
| 1594 | (void *)(long)(event->header.size), | 1766 | (void *)(long)(event->header.size), |
| 1595 | event->read.pid, | 1767 | event->read.pid, |
| 1596 | event->read.tid, | 1768 | event->read.tid, |
| 1769 | attr ? __event_name(attr->type, attr->config) | ||
| 1770 | : "FAIL", | ||
| 1597 | event->read.value); | 1771 | event->read.value); |
| 1598 | 1772 | ||
| 1599 | return 0; | 1773 | return 0; |
| @@ -1615,10 +1789,8 @@ process_event(event_t *event, unsigned long offset, unsigned long head) | |||
| 1615 | return process_comm_event(event, offset, head); | 1789 | return process_comm_event(event, offset, head); |
| 1616 | 1790 | ||
| 1617 | case PERF_EVENT_FORK: | 1791 | case PERF_EVENT_FORK: |
| 1618 | return process_fork_event(event, offset, head); | 1792 | case PERF_EVENT_EXIT: |
| 1619 | 1793 | return process_task_event(event, offset, head); | |
| 1620 | case PERF_EVENT_PERIOD: | ||
| 1621 | return process_period_event(event, offset, head); | ||
| 1622 | 1794 | ||
| 1623 | case PERF_EVENT_LOST: | 1795 | case PERF_EVENT_LOST: |
| 1624 | return process_lost_event(event, offset, head); | 1796 | return process_lost_event(event, offset, head); |
| @@ -1641,8 +1813,6 @@ process_event(event_t *event, unsigned long offset, unsigned long head) | |||
| 1641 | return 0; | 1813 | return 0; |
| 1642 | } | 1814 | } |
| 1643 | 1815 | ||
| 1644 | static struct perf_header *header; | ||
| 1645 | |||
| 1646 | static u64 perf_header__sample_type(void) | 1816 | static u64 perf_header__sample_type(void) |
| 1647 | { | 1817 | { |
| 1648 | u64 sample_type = 0; | 1818 | u64 sample_type = 0; |
| @@ -1687,6 +1857,11 @@ static int __cmd_report(void) | |||
| 1687 | exit(-1); | 1857 | exit(-1); |
| 1688 | } | 1858 | } |
| 1689 | 1859 | ||
| 1860 | if (!force && (stat.st_uid != geteuid())) { | ||
| 1861 | fprintf(stderr, "file: %s not owned by current user\n", input_name); | ||
| 1862 | exit(-1); | ||
| 1863 | } | ||
| 1864 | |||
| 1690 | if (!stat.st_size) { | 1865 | if (!stat.st_size) { |
| 1691 | fprintf(stderr, "zero-sized file, nothing to do!\n"); | 1866 | fprintf(stderr, "zero-sized file, nothing to do!\n"); |
| 1692 | exit(0); | 1867 | exit(0); |
| @@ -1710,6 +1885,13 @@ static int __cmd_report(void) | |||
| 1710 | " -g?\n"); | 1885 | " -g?\n"); |
| 1711 | exit(-1); | 1886 | exit(-1); |
| 1712 | } | 1887 | } |
| 1888 | } else if (callchain_param.mode != CHAIN_NONE && !callchain) { | ||
| 1889 | callchain = 1; | ||
| 1890 | if (register_callchain_param(&callchain_param) < 0) { | ||
| 1891 | fprintf(stderr, "Can't register callchain" | ||
| 1892 | " params\n"); | ||
| 1893 | exit(-1); | ||
| 1894 | } | ||
| 1713 | } | 1895 | } |
| 1714 | 1896 | ||
| 1715 | if (load_kernel() < 0) { | 1897 | if (load_kernel() < 0) { |
| @@ -1848,6 +2030,13 @@ parse_callchain_opt(const struct option *opt __used, const char *arg, | |||
| 1848 | else if (!strncmp(tok, "fractal", strlen(arg))) | 2030 | else if (!strncmp(tok, "fractal", strlen(arg))) |
| 1849 | callchain_param.mode = CHAIN_GRAPH_REL; | 2031 | callchain_param.mode = CHAIN_GRAPH_REL; |
| 1850 | 2032 | ||
| 2033 | else if (!strncmp(tok, "none", strlen(arg))) { | ||
| 2034 | callchain_param.mode = CHAIN_NONE; | ||
| 2035 | callchain = 0; | ||
| 2036 | |||
| 2037 | return 0; | ||
| 2038 | } | ||
| 2039 | |||
| 1851 | else | 2040 | else |
| 1852 | return -1; | 2041 | return -1; |
| 1853 | 2042 | ||
| @@ -1881,8 +2070,11 @@ static const struct option options[] = { | |||
| 1881 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, | 2070 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, |
| 1882 | "dump raw trace in ASCII"), | 2071 | "dump raw trace in ASCII"), |
| 1883 | OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"), | 2072 | OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"), |
| 2073 | OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), | ||
| 1884 | OPT_BOOLEAN('m', "modules", &modules, | 2074 | OPT_BOOLEAN('m', "modules", &modules, |
| 1885 | "load module symbols - WARNING: use only with -k and LIVE kernel"), | 2075 | "load module symbols - WARNING: use only with -k and LIVE kernel"), |
| 2076 | OPT_BOOLEAN('n', "show-nr-samples", &show_nr_samples, | ||
| 2077 | "Show a column with the number of samples"), | ||
| 1886 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", | 2078 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", |
| 1887 | "sort by key(s): pid, comm, dso, symbol, parent"), | 2079 | "sort by key(s): pid, comm, dso, symbol, parent"), |
| 1888 | OPT_BOOLEAN('P', "full-paths", &full_paths, | 2080 | OPT_BOOLEAN('P', "full-paths", &full_paths, |
| @@ -1891,15 +2083,21 @@ static const struct option options[] = { | |||
| 1891 | "regex filter to identify parent, see: '--sort parent'"), | 2083 | "regex filter to identify parent, see: '--sort parent'"), |
| 1892 | OPT_BOOLEAN('x', "exclude-other", &exclude_other, | 2084 | OPT_BOOLEAN('x', "exclude-other", &exclude_other, |
| 1893 | "Only display entries with parent-match"), | 2085 | "Only display entries with parent-match"), |
| 1894 | OPT_CALLBACK_DEFAULT('c', "callchain", NULL, "output_type,min_percent", | 2086 | OPT_CALLBACK_DEFAULT('g', "call-graph", NULL, "output_type,min_percent", |
| 1895 | "Display callchains using output_type and min percent threshold. " | 2087 | "Display callchains using output_type and min percent threshold. " |
| 1896 | "Default: flat,0", &parse_callchain_opt, callchain_default_opt), | 2088 | "Default: fractal,0.5", &parse_callchain_opt, callchain_default_opt), |
| 1897 | OPT_STRING('d', "dsos", &dso_list_str, "dso[,dso...]", | 2089 | OPT_STRING('d', "dsos", &dso_list_str, "dso[,dso...]", |
| 1898 | "only consider symbols in these dsos"), | 2090 | "only consider symbols in these dsos"), |
| 1899 | OPT_STRING('C', "comms", &comm_list_str, "comm[,comm...]", | 2091 | OPT_STRING('C', "comms", &comm_list_str, "comm[,comm...]", |
| 1900 | "only consider symbols in these comms"), | 2092 | "only consider symbols in these comms"), |
| 1901 | OPT_STRING('S', "symbols", &sym_list_str, "symbol[,symbol...]", | 2093 | OPT_STRING('S', "symbols", &sym_list_str, "symbol[,symbol...]", |
| 1902 | "only consider these symbols"), | 2094 | "only consider these symbols"), |
| 2095 | OPT_STRING('w', "column-widths", &col_width_list_str, | ||
| 2096 | "width[,width...]", | ||
| 2097 | "don't try to adjust column width, use these fixed values"), | ||
| 2098 | OPT_STRING('t', "field-separator", &field_sep, "separator", | ||
| 2099 | "separator for columns, no spaces will be added between " | ||
| 2100 | "columns '.' is reserved."), | ||
| 1903 | OPT_END() | 2101 | OPT_END() |
| 1904 | }; | 2102 | }; |
| 1905 | 2103 | ||
| @@ -1919,7 +2117,8 @@ static void setup_sorting(void) | |||
| 1919 | } | 2117 | } |
| 1920 | 2118 | ||
| 1921 | static void setup_list(struct strlist **list, const char *list_str, | 2119 | static void setup_list(struct strlist **list, const char *list_str, |
| 1922 | const char *list_name) | 2120 | struct sort_entry *se, const char *list_name, |
| 2121 | FILE *fp) | ||
| 1923 | { | 2122 | { |
| 1924 | if (list_str) { | 2123 | if (list_str) { |
| 1925 | *list = strlist__new(true, list_str); | 2124 | *list = strlist__new(true, list_str); |
| @@ -1928,6 +2127,11 @@ static void setup_list(struct strlist **list, const char *list_str, | |||
| 1928 | list_name); | 2127 | list_name); |
| 1929 | exit(129); | 2128 | exit(129); |
| 1930 | } | 2129 | } |
| 2130 | if (strlist__nr_entries(*list) == 1) { | ||
| 2131 | fprintf(fp, "# %s: %s\n", list_name, | ||
| 2132 | strlist__entry(*list, 0)->s); | ||
| 2133 | se->elide = true; | ||
| 2134 | } | ||
| 1931 | } | 2135 | } |
| 1932 | } | 2136 | } |
| 1933 | 2137 | ||
| @@ -1941,9 +2145,10 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) | |||
| 1941 | 2145 | ||
| 1942 | setup_sorting(); | 2146 | setup_sorting(); |
| 1943 | 2147 | ||
| 1944 | if (parent_pattern != default_parent_pattern) | 2148 | if (parent_pattern != default_parent_pattern) { |
| 1945 | sort_dimension__add("parent"); | 2149 | sort_dimension__add("parent"); |
| 1946 | else | 2150 | sort_parent.elide = 1; |
| 2151 | } else | ||
| 1947 | exclude_other = 0; | 2152 | exclude_other = 0; |
| 1948 | 2153 | ||
| 1949 | /* | 2154 | /* |
| @@ -1952,11 +2157,17 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) | |||
| 1952 | if (argc) | 2157 | if (argc) |
| 1953 | usage_with_options(report_usage, options); | 2158 | usage_with_options(report_usage, options); |
| 1954 | 2159 | ||
| 1955 | setup_list(&dso_list, dso_list_str, "dso"); | ||
| 1956 | setup_list(&comm_list, comm_list_str, "comm"); | ||
| 1957 | setup_list(&sym_list, sym_list_str, "symbol"); | ||
| 1958 | |||
| 1959 | setup_pager(); | 2160 | setup_pager(); |
| 1960 | 2161 | ||
| 2162 | setup_list(&dso_list, dso_list_str, &sort_dso, "dso", stdout); | ||
| 2163 | setup_list(&comm_list, comm_list_str, &sort_comm, "comm", stdout); | ||
| 2164 | setup_list(&sym_list, sym_list_str, &sort_sym, "symbol", stdout); | ||
| 2165 | |||
| 2166 | if (field_sep && *field_sep == '.') { | ||
| 2167 | fputs("'.' is the only non valid --field-separator argument\n", | ||
| 2168 | stderr); | ||
| 2169 | exit(129); | ||
| 2170 | } | ||
| 2171 | |||
| 1961 | return __cmd_report(); | 2172 | return __cmd_report(); |
| 1962 | } | 2173 | } |
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 27921a8ce1a9..b4b06c7903e1 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c | |||
| @@ -496,7 +496,7 @@ static const struct option options[] = { | |||
| 496 | "stat events on existing pid"), | 496 | "stat events on existing pid"), |
| 497 | OPT_BOOLEAN('a', "all-cpus", &system_wide, | 497 | OPT_BOOLEAN('a', "all-cpus", &system_wide, |
| 498 | "system-wide collection from all CPUs"), | 498 | "system-wide collection from all CPUs"), |
| 499 | OPT_BOOLEAN('S', "scale", &scale, | 499 | OPT_BOOLEAN('c', "scale", &scale, |
| 500 | "scale/normalize counters"), | 500 | "scale/normalize counters"), |
| 501 | OPT_BOOLEAN('v', "verbose", &verbose, | 501 | OPT_BOOLEAN('v', "verbose", &verbose, |
| 502 | "be more verbose (show counter open errors, etc)"), | 502 | "be more verbose (show counter open errors, etc)"), |
| @@ -511,7 +511,8 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used) | |||
| 511 | { | 511 | { |
| 512 | int status; | 512 | int status; |
| 513 | 513 | ||
| 514 | argc = parse_options(argc, argv, options, stat_usage, 0); | 514 | argc = parse_options(argc, argv, options, stat_usage, |
| 515 | PARSE_OPT_STOP_AT_NON_OPTION); | ||
| 515 | if (!argc) | 516 | if (!argc) |
| 516 | usage_with_options(stat_usage, options); | 517 | usage_with_options(stat_usage, options); |
| 517 | if (run_count <= 0 || run_count > MAX_RUN) | 518 | if (run_count <= 0 || run_count > MAX_RUN) |
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 95d5c0ae375a..7de28ce9ca26 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
| @@ -31,6 +31,8 @@ | |||
| 31 | #include <fcntl.h> | 31 | #include <fcntl.h> |
| 32 | 32 | ||
| 33 | #include <stdio.h> | 33 | #include <stdio.h> |
| 34 | #include <termios.h> | ||
| 35 | #include <unistd.h> | ||
| 34 | 36 | ||
| 35 | #include <errno.h> | 37 | #include <errno.h> |
| 36 | #include <time.h> | 38 | #include <time.h> |
| @@ -54,10 +56,11 @@ static int system_wide = 0; | |||
| 54 | 56 | ||
| 55 | static int default_interval = 100000; | 57 | static int default_interval = 100000; |
| 56 | 58 | ||
| 57 | static u64 count_filter = 5; | 59 | static int count_filter = 5; |
| 58 | static int print_entries = 15; | 60 | static int print_entries = 15; |
| 59 | 61 | ||
| 60 | static int target_pid = -1; | 62 | static int target_pid = -1; |
| 63 | static int inherit = 0; | ||
| 61 | static int profile_cpu = -1; | 64 | static int profile_cpu = -1; |
| 62 | static int nr_cpus = 0; | 65 | static int nr_cpus = 0; |
| 63 | static unsigned int realtime_prio = 0; | 66 | static unsigned int realtime_prio = 0; |
| @@ -68,15 +71,28 @@ static int freq = 0; | |||
| 68 | static int verbose = 0; | 71 | static int verbose = 0; |
| 69 | static char *vmlinux = NULL; | 72 | static char *vmlinux = NULL; |
| 70 | 73 | ||
| 71 | static char *sym_filter; | ||
| 72 | static unsigned long filter_start; | ||
| 73 | static unsigned long filter_end; | ||
| 74 | |||
| 75 | static int delay_secs = 2; | 74 | static int delay_secs = 2; |
| 76 | static int zero; | 75 | static int zero; |
| 77 | static int dump_symtab; | 76 | static int dump_symtab; |
| 78 | 77 | ||
| 79 | /* | 78 | /* |
| 79 | * Source | ||
| 80 | */ | ||
| 81 | |||
| 82 | struct source_line { | ||
| 83 | u64 eip; | ||
| 84 | unsigned long count[MAX_COUNTERS]; | ||
| 85 | char *line; | ||
| 86 | struct source_line *next; | ||
| 87 | }; | ||
| 88 | |||
| 89 | static char *sym_filter = NULL; | ||
| 90 | struct sym_entry *sym_filter_entry = NULL; | ||
| 91 | static int sym_pcnt_filter = 5; | ||
| 92 | static int sym_counter = 0; | ||
| 93 | static int display_weighted = -1; | ||
| 94 | |||
| 95 | /* | ||
| 80 | * Symbols | 96 | * Symbols |
| 81 | */ | 97 | */ |
| 82 | 98 | ||
| @@ -90,9 +106,237 @@ struct sym_entry { | |||
| 90 | unsigned long snap_count; | 106 | unsigned long snap_count; |
| 91 | double weight; | 107 | double weight; |
| 92 | int skip; | 108 | int skip; |
| 109 | struct source_line *source; | ||
| 110 | struct source_line *lines; | ||
| 111 | struct source_line **lines_tail; | ||
| 112 | pthread_mutex_t source_lock; | ||
| 93 | }; | 113 | }; |
| 94 | 114 | ||
| 95 | struct sym_entry *sym_filter_entry; | 115 | /* |
| 116 | * Source functions | ||
| 117 | */ | ||
| 118 | |||
| 119 | static void parse_source(struct sym_entry *syme) | ||
| 120 | { | ||
| 121 | struct symbol *sym; | ||
| 122 | struct module *module; | ||
| 123 | struct section *section = NULL; | ||
| 124 | FILE *file; | ||
| 125 | char command[PATH_MAX*2], *path = vmlinux; | ||
| 126 | u64 start, end, len; | ||
| 127 | |||
| 128 | if (!syme) | ||
| 129 | return; | ||
| 130 | |||
| 131 | if (syme->lines) { | ||
| 132 | pthread_mutex_lock(&syme->source_lock); | ||
| 133 | goto out_assign; | ||
| 134 | } | ||
| 135 | |||
| 136 | sym = (struct symbol *)(syme + 1); | ||
| 137 | module = sym->module; | ||
| 138 | |||
| 139 | if (module) | ||
| 140 | path = module->path; | ||
| 141 | if (!path) | ||
| 142 | return; | ||
| 143 | |||
| 144 | start = sym->obj_start; | ||
| 145 | if (!start) | ||
| 146 | start = sym->start; | ||
| 147 | |||
| 148 | if (module) { | ||
| 149 | section = module->sections->find_section(module->sections, ".text"); | ||
| 150 | if (section) | ||
| 151 | start -= section->vma; | ||
| 152 | } | ||
| 153 | |||
| 154 | end = start + sym->end - sym->start + 1; | ||
| 155 | len = sym->end - sym->start; | ||
| 156 | |||
| 157 | sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s", start, end, path); | ||
| 158 | |||
| 159 | file = popen(command, "r"); | ||
| 160 | if (!file) | ||
| 161 | return; | ||
| 162 | |||
| 163 | pthread_mutex_lock(&syme->source_lock); | ||
| 164 | syme->lines_tail = &syme->lines; | ||
| 165 | while (!feof(file)) { | ||
| 166 | struct source_line *src; | ||
| 167 | size_t dummy = 0; | ||
| 168 | char *c; | ||
| 169 | |||
| 170 | src = malloc(sizeof(struct source_line)); | ||
| 171 | assert(src != NULL); | ||
| 172 | memset(src, 0, sizeof(struct source_line)); | ||
| 173 | |||
| 174 | if (getline(&src->line, &dummy, file) < 0) | ||
| 175 | break; | ||
| 176 | if (!src->line) | ||
| 177 | break; | ||
| 178 | |||
| 179 | c = strchr(src->line, '\n'); | ||
| 180 | if (c) | ||
| 181 | *c = 0; | ||
| 182 | |||
| 183 | src->next = NULL; | ||
| 184 | *syme->lines_tail = src; | ||
| 185 | syme->lines_tail = &src->next; | ||
| 186 | |||
| 187 | if (strlen(src->line)>8 && src->line[8] == ':') { | ||
| 188 | src->eip = strtoull(src->line, NULL, 16); | ||
| 189 | if (section) | ||
| 190 | src->eip += section->vma; | ||
| 191 | } | ||
| 192 | if (strlen(src->line)>8 && src->line[16] == ':') { | ||
| 193 | src->eip = strtoull(src->line, NULL, 16); | ||
| 194 | if (section) | ||
| 195 | src->eip += section->vma; | ||
| 196 | } | ||
| 197 | } | ||
| 198 | pclose(file); | ||
| 199 | out_assign: | ||
| 200 | sym_filter_entry = syme; | ||
| 201 | pthread_mutex_unlock(&syme->source_lock); | ||
| 202 | } | ||
| 203 | |||
| 204 | static void __zero_source_counters(struct sym_entry *syme) | ||
| 205 | { | ||
| 206 | int i; | ||
| 207 | struct source_line *line; | ||
| 208 | |||
| 209 | line = syme->lines; | ||
| 210 | while (line) { | ||
| 211 | for (i = 0; i < nr_counters; i++) | ||
| 212 | line->count[i] = 0; | ||
| 213 | line = line->next; | ||
| 214 | } | ||
| 215 | } | ||
| 216 | |||
| 217 | static void record_precise_ip(struct sym_entry *syme, int counter, u64 ip) | ||
| 218 | { | ||
| 219 | struct source_line *line; | ||
| 220 | |||
| 221 | if (syme != sym_filter_entry) | ||
| 222 | return; | ||
| 223 | |||
| 224 | if (pthread_mutex_trylock(&syme->source_lock)) | ||
| 225 | return; | ||
| 226 | |||
| 227 | if (!syme->source) | ||
| 228 | goto out_unlock; | ||
| 229 | |||
| 230 | for (line = syme->lines; line; line = line->next) { | ||
| 231 | if (line->eip == ip) { | ||
| 232 | line->count[counter]++; | ||
| 233 | break; | ||
| 234 | } | ||
| 235 | if (line->eip > ip) | ||
| 236 | break; | ||
| 237 | } | ||
| 238 | out_unlock: | ||
| 239 | pthread_mutex_unlock(&syme->source_lock); | ||
| 240 | } | ||
| 241 | |||
| 242 | static void lookup_sym_source(struct sym_entry *syme) | ||
| 243 | { | ||
| 244 | struct symbol *symbol = (struct symbol *)(syme + 1); | ||
| 245 | struct source_line *line; | ||
| 246 | char pattern[PATH_MAX]; | ||
| 247 | char *idx; | ||
| 248 | |||
| 249 | sprintf(pattern, "<%s>:", symbol->name); | ||
| 250 | |||
| 251 | if (symbol->module) { | ||
| 252 | idx = strstr(pattern, "\t"); | ||
| 253 | if (idx) | ||
| 254 | *idx = 0; | ||
| 255 | } | ||
| 256 | |||
| 257 | pthread_mutex_lock(&syme->source_lock); | ||
| 258 | for (line = syme->lines; line; line = line->next) { | ||
| 259 | if (strstr(line->line, pattern)) { | ||
| 260 | syme->source = line; | ||
| 261 | break; | ||
| 262 | } | ||
| 263 | } | ||
| 264 | pthread_mutex_unlock(&syme->source_lock); | ||
| 265 | } | ||
| 266 | |||
| 267 | static void show_lines(struct source_line *queue, int count, int total) | ||
| 268 | { | ||
| 269 | int i; | ||
| 270 | struct source_line *line; | ||
| 271 | |||
| 272 | line = queue; | ||
| 273 | for (i = 0; i < count; i++) { | ||
| 274 | float pcnt = 100.0*(float)line->count[sym_counter]/(float)total; | ||
| 275 | |||
| 276 | printf("%8li %4.1f%%\t%s\n", line->count[sym_counter], pcnt, line->line); | ||
| 277 | line = line->next; | ||
| 278 | } | ||
| 279 | } | ||
| 280 | |||
| 281 | #define TRACE_COUNT 3 | ||
| 282 | |||
| 283 | static void show_details(struct sym_entry *syme) | ||
| 284 | { | ||
| 285 | struct symbol *symbol; | ||
| 286 | struct source_line *line; | ||
| 287 | struct source_line *line_queue = NULL; | ||
| 288 | int displayed = 0; | ||
| 289 | int line_queue_count = 0, total = 0, more = 0; | ||
| 290 | |||
| 291 | if (!syme) | ||
| 292 | return; | ||
| 293 | |||
| 294 | if (!syme->source) | ||
| 295 | lookup_sym_source(syme); | ||
| 296 | |||
| 297 | if (!syme->source) | ||
| 298 | return; | ||
| 299 | |||
| 300 | symbol = (struct symbol *)(syme + 1); | ||
| 301 | printf("Showing %s for %s\n", event_name(sym_counter), symbol->name); | ||
| 302 | printf(" Events Pcnt (>=%d%%)\n", sym_pcnt_filter); | ||
| 303 | |||
| 304 | pthread_mutex_lock(&syme->source_lock); | ||
| 305 | line = syme->source; | ||
| 306 | while (line) { | ||
| 307 | total += line->count[sym_counter]; | ||
| 308 | line = line->next; | ||
| 309 | } | ||
| 310 | |||
| 311 | line = syme->source; | ||
| 312 | while (line) { | ||
| 313 | float pcnt = 0.0; | ||
| 314 | |||
| 315 | if (!line_queue_count) | ||
| 316 | line_queue = line; | ||
| 317 | line_queue_count++; | ||
| 318 | |||
| 319 | if (line->count[sym_counter]) | ||
| 320 | pcnt = 100.0 * line->count[sym_counter] / (float)total; | ||
| 321 | if (pcnt >= (float)sym_pcnt_filter) { | ||
| 322 | if (displayed <= print_entries) | ||
| 323 | show_lines(line_queue, line_queue_count, total); | ||
| 324 | else more++; | ||
| 325 | displayed += line_queue_count; | ||
| 326 | line_queue_count = 0; | ||
| 327 | line_queue = NULL; | ||
| 328 | } else if (line_queue_count > TRACE_COUNT) { | ||
| 329 | line_queue = line_queue->next; | ||
| 330 | line_queue_count--; | ||
| 331 | } | ||
| 332 | |||
| 333 | line->count[sym_counter] = zero ? 0 : line->count[sym_counter] * 7 / 8; | ||
| 334 | line = line->next; | ||
| 335 | } | ||
| 336 | pthread_mutex_unlock(&syme->source_lock); | ||
| 337 | if (more) | ||
| 338 | printf("%d lines not displayed, maybe increase display entries [e]\n", more); | ||
| 339 | } | ||
| 96 | 340 | ||
| 97 | struct dso *kernel_dso; | 341 | struct dso *kernel_dso; |
| 98 | 342 | ||
| @@ -111,6 +355,9 @@ static double sym_weight(const struct sym_entry *sym) | |||
| 111 | double weight = sym->snap_count; | 355 | double weight = sym->snap_count; |
| 112 | int counter; | 356 | int counter; |
| 113 | 357 | ||
| 358 | if (!display_weighted) | ||
| 359 | return weight; | ||
| 360 | |||
| 114 | for (counter = 1; counter < nr_counters-1; counter++) | 361 | for (counter = 1; counter < nr_counters-1; counter++) |
| 115 | weight *= sym->count[counter]; | 362 | weight *= sym->count[counter]; |
| 116 | 363 | ||
| @@ -158,7 +405,7 @@ static void rb_insert_active_sym(struct rb_root *tree, struct sym_entry *se) | |||
| 158 | static void print_sym_table(void) | 405 | static void print_sym_table(void) |
| 159 | { | 406 | { |
| 160 | int printed = 0, j; | 407 | int printed = 0, j; |
| 161 | int counter; | 408 | int counter, snap = !display_weighted ? sym_counter : 0; |
| 162 | float samples_per_sec = samples/delay_secs; | 409 | float samples_per_sec = samples/delay_secs; |
| 163 | float ksamples_per_sec = (samples-userspace_samples)/delay_secs; | 410 | float ksamples_per_sec = (samples-userspace_samples)/delay_secs; |
| 164 | float sum_ksamples = 0.0; | 411 | float sum_ksamples = 0.0; |
| @@ -174,7 +421,7 @@ static void print_sym_table(void) | |||
| 174 | pthread_mutex_unlock(&active_symbols_lock); | 421 | pthread_mutex_unlock(&active_symbols_lock); |
| 175 | 422 | ||
| 176 | list_for_each_entry_safe_from(syme, n, &active_symbols, node) { | 423 | list_for_each_entry_safe_from(syme, n, &active_symbols, node) { |
| 177 | syme->snap_count = syme->count[0]; | 424 | syme->snap_count = syme->count[snap]; |
| 178 | if (syme->snap_count != 0) { | 425 | if (syme->snap_count != 0) { |
| 179 | syme->weight = sym_weight(syme); | 426 | syme->weight = sym_weight(syme); |
| 180 | rb_insert_active_sym(&tmp, syme); | 427 | rb_insert_active_sym(&tmp, syme); |
| @@ -194,7 +441,7 @@ static void print_sym_table(void) | |||
| 194 | samples_per_sec, | 441 | samples_per_sec, |
| 195 | 100.0 - (100.0*((samples_per_sec-ksamples_per_sec)/samples_per_sec))); | 442 | 100.0 - (100.0*((samples_per_sec-ksamples_per_sec)/samples_per_sec))); |
| 196 | 443 | ||
| 197 | if (nr_counters == 1) { | 444 | if (nr_counters == 1 || !display_weighted) { |
| 198 | printf("%Ld", (u64)attrs[0].sample_period); | 445 | printf("%Ld", (u64)attrs[0].sample_period); |
| 199 | if (freq) | 446 | if (freq) |
| 200 | printf("Hz "); | 447 | printf("Hz "); |
| @@ -202,7 +449,9 @@ static void print_sym_table(void) | |||
| 202 | printf(" "); | 449 | printf(" "); |
| 203 | } | 450 | } |
| 204 | 451 | ||
| 205 | for (counter = 0; counter < nr_counters; counter++) { | 452 | if (!display_weighted) |
| 453 | printf("%s", event_name(sym_counter)); | ||
| 454 | else for (counter = 0; counter < nr_counters; counter++) { | ||
| 206 | if (counter) | 455 | if (counter) |
| 207 | printf("/"); | 456 | printf("/"); |
| 208 | 457 | ||
| @@ -227,6 +476,11 @@ static void print_sym_table(void) | |||
| 227 | 476 | ||
| 228 | printf("------------------------------------------------------------------------------\n\n"); | 477 | printf("------------------------------------------------------------------------------\n\n"); |
| 229 | 478 | ||
| 479 | if (sym_filter_entry) { | ||
| 480 | show_details(sym_filter_entry); | ||
| 481 | return; | ||
| 482 | } | ||
| 483 | |||
| 230 | if (nr_counters == 1) | 484 | if (nr_counters == 1) |
| 231 | printf(" samples pcnt"); | 485 | printf(" samples pcnt"); |
| 232 | else | 486 | else |
| @@ -241,13 +495,13 @@ static void print_sym_table(void) | |||
| 241 | struct symbol *sym = (struct symbol *)(syme + 1); | 495 | struct symbol *sym = (struct symbol *)(syme + 1); |
| 242 | double pcnt; | 496 | double pcnt; |
| 243 | 497 | ||
| 244 | if (++printed > print_entries || syme->snap_count < count_filter) | 498 | if (++printed > print_entries || (int)syme->snap_count < count_filter) |
| 245 | continue; | 499 | continue; |
| 246 | 500 | ||
| 247 | pcnt = 100.0 - (100.0 * ((sum_ksamples - syme->snap_count) / | 501 | pcnt = 100.0 - (100.0 * ((sum_ksamples - syme->snap_count) / |
| 248 | sum_ksamples)); | 502 | sum_ksamples)); |
| 249 | 503 | ||
| 250 | if (nr_counters == 1) | 504 | if (nr_counters == 1 || !display_weighted) |
| 251 | printf("%20.2f - ", syme->weight); | 505 | printf("%20.2f - ", syme->weight); |
| 252 | else | 506 | else |
| 253 | printf("%9.1f %10ld - ", syme->weight, syme->snap_count); | 507 | printf("%9.1f %10ld - ", syme->weight, syme->snap_count); |
| @@ -260,19 +514,250 @@ static void print_sym_table(void) | |||
| 260 | } | 514 | } |
| 261 | } | 515 | } |
| 262 | 516 | ||
| 517 | static void prompt_integer(int *target, const char *msg) | ||
| 518 | { | ||
| 519 | char *buf = malloc(0), *p; | ||
| 520 | size_t dummy = 0; | ||
| 521 | int tmp; | ||
| 522 | |||
| 523 | fprintf(stdout, "\n%s: ", msg); | ||
| 524 | if (getline(&buf, &dummy, stdin) < 0) | ||
| 525 | return; | ||
| 526 | |||
| 527 | p = strchr(buf, '\n'); | ||
| 528 | if (p) | ||
| 529 | *p = 0; | ||
| 530 | |||
| 531 | p = buf; | ||
| 532 | while(*p) { | ||
| 533 | if (!isdigit(*p)) | ||
| 534 | goto out_free; | ||
| 535 | p++; | ||
| 536 | } | ||
| 537 | tmp = strtoul(buf, NULL, 10); | ||
| 538 | *target = tmp; | ||
| 539 | out_free: | ||
| 540 | free(buf); | ||
| 541 | } | ||
| 542 | |||
| 543 | static void prompt_percent(int *target, const char *msg) | ||
| 544 | { | ||
| 545 | int tmp = 0; | ||
| 546 | |||
| 547 | prompt_integer(&tmp, msg); | ||
| 548 | if (tmp >= 0 && tmp <= 100) | ||
| 549 | *target = tmp; | ||
| 550 | } | ||
| 551 | |||
| 552 | static void prompt_symbol(struct sym_entry **target, const char *msg) | ||
| 553 | { | ||
| 554 | char *buf = malloc(0), *p; | ||
| 555 | struct sym_entry *syme = *target, *n, *found = NULL; | ||
| 556 | size_t dummy = 0; | ||
| 557 | |||
| 558 | /* zero counters of active symbol */ | ||
| 559 | if (syme) { | ||
| 560 | pthread_mutex_lock(&syme->source_lock); | ||
| 561 | __zero_source_counters(syme); | ||
| 562 | *target = NULL; | ||
| 563 | pthread_mutex_unlock(&syme->source_lock); | ||
| 564 | } | ||
| 565 | |||
| 566 | fprintf(stdout, "\n%s: ", msg); | ||
| 567 | if (getline(&buf, &dummy, stdin) < 0) | ||
| 568 | goto out_free; | ||
| 569 | |||
| 570 | p = strchr(buf, '\n'); | ||
| 571 | if (p) | ||
| 572 | *p = 0; | ||
| 573 | |||
| 574 | pthread_mutex_lock(&active_symbols_lock); | ||
| 575 | syme = list_entry(active_symbols.next, struct sym_entry, node); | ||
| 576 | pthread_mutex_unlock(&active_symbols_lock); | ||
| 577 | |||
| 578 | list_for_each_entry_safe_from(syme, n, &active_symbols, node) { | ||
| 579 | struct symbol *sym = (struct symbol *)(syme + 1); | ||
| 580 | |||
| 581 | if (!strcmp(buf, sym->name)) { | ||
| 582 | found = syme; | ||
| 583 | break; | ||
| 584 | } | ||
| 585 | } | ||
| 586 | |||
| 587 | if (!found) { | ||
| 588 | fprintf(stderr, "Sorry, %s is not active.\n", sym_filter); | ||
| 589 | sleep(1); | ||
| 590 | return; | ||
| 591 | } else | ||
| 592 | parse_source(found); | ||
| 593 | |||
| 594 | out_free: | ||
| 595 | free(buf); | ||
| 596 | } | ||
| 597 | |||
| 598 | static void print_mapped_keys(void) | ||
| 599 | { | ||
| 600 | char *name = NULL; | ||
| 601 | |||
| 602 | if (sym_filter_entry) { | ||
| 603 | struct symbol *sym = (struct symbol *)(sym_filter_entry+1); | ||
| 604 | name = sym->name; | ||
| 605 | } | ||
| 606 | |||
| 607 | fprintf(stdout, "\nMapped keys:\n"); | ||
| 608 | fprintf(stdout, "\t[d] display refresh delay. \t(%d)\n", delay_secs); | ||
| 609 | fprintf(stdout, "\t[e] display entries (lines). \t(%d)\n", print_entries); | ||
| 610 | |||
| 611 | if (nr_counters > 1) | ||
| 612 | fprintf(stdout, "\t[E] active event counter. \t(%s)\n", event_name(sym_counter)); | ||
| 613 | |||
| 614 | fprintf(stdout, "\t[f] profile display filter (count). \t(%d)\n", count_filter); | ||
| 615 | |||
| 616 | if (vmlinux) { | ||
| 617 | fprintf(stdout, "\t[F] annotate display filter (percent). \t(%d%%)\n", sym_pcnt_filter); | ||
| 618 | fprintf(stdout, "\t[s] annotate symbol. \t(%s)\n", name?: "NULL"); | ||
| 619 | fprintf(stdout, "\t[S] stop annotation.\n"); | ||
| 620 | } | ||
| 621 | |||
| 622 | if (nr_counters > 1) | ||
| 623 | fprintf(stdout, "\t[w] toggle display weighted/count[E]r. \t(%d)\n", display_weighted ? 1 : 0); | ||
| 624 | |||
| 625 | fprintf(stdout, "\t[z] toggle sample zeroing. \t(%d)\n", zero ? 1 : 0); | ||
| 626 | fprintf(stdout, "\t[qQ] quit.\n"); | ||
| 627 | } | ||
| 628 | |||
| 629 | static int key_mapped(int c) | ||
| 630 | { | ||
| 631 | switch (c) { | ||
| 632 | case 'd': | ||
| 633 | case 'e': | ||
| 634 | case 'f': | ||
| 635 | case 'z': | ||
| 636 | case 'q': | ||
| 637 | case 'Q': | ||
| 638 | return 1; | ||
| 639 | case 'E': | ||
| 640 | case 'w': | ||
| 641 | return nr_counters > 1 ? 1 : 0; | ||
| 642 | case 'F': | ||
| 643 | case 's': | ||
| 644 | case 'S': | ||
| 645 | return vmlinux ? 1 : 0; | ||
| 646 | } | ||
| 647 | |||
| 648 | return 0; | ||
| 649 | } | ||
| 650 | |||
| 651 | static void handle_keypress(int c) | ||
| 652 | { | ||
| 653 | if (!key_mapped(c)) { | ||
| 654 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; | ||
| 655 | struct termios tc, save; | ||
| 656 | |||
| 657 | print_mapped_keys(); | ||
| 658 | fprintf(stdout, "\nEnter selection, or unmapped key to continue: "); | ||
| 659 | fflush(stdout); | ||
| 660 | |||
| 661 | tcgetattr(0, &save); | ||
| 662 | tc = save; | ||
| 663 | tc.c_lflag &= ~(ICANON | ECHO); | ||
| 664 | tc.c_cc[VMIN] = 0; | ||
| 665 | tc.c_cc[VTIME] = 0; | ||
| 666 | tcsetattr(0, TCSANOW, &tc); | ||
| 667 | |||
| 668 | poll(&stdin_poll, 1, -1); | ||
| 669 | c = getc(stdin); | ||
| 670 | |||
| 671 | tcsetattr(0, TCSAFLUSH, &save); | ||
| 672 | if (!key_mapped(c)) | ||
| 673 | return; | ||
| 674 | } | ||
| 675 | |||
| 676 | switch (c) { | ||
| 677 | case 'd': | ||
| 678 | prompt_integer(&delay_secs, "Enter display delay"); | ||
| 679 | break; | ||
| 680 | case 'e': | ||
| 681 | prompt_integer(&print_entries, "Enter display entries (lines)"); | ||
| 682 | break; | ||
| 683 | case 'E': | ||
| 684 | if (nr_counters > 1) { | ||
| 685 | int i; | ||
| 686 | |||
| 687 | fprintf(stderr, "\nAvailable events:"); | ||
| 688 | for (i = 0; i < nr_counters; i++) | ||
| 689 | fprintf(stderr, "\n\t%d %s", i, event_name(i)); | ||
| 690 | |||
| 691 | prompt_integer(&sym_counter, "Enter details event counter"); | ||
| 692 | |||
| 693 | if (sym_counter >= nr_counters) { | ||
| 694 | fprintf(stderr, "Sorry, no such event, using %s.\n", event_name(0)); | ||
| 695 | sym_counter = 0; | ||
| 696 | sleep(1); | ||
| 697 | } | ||
| 698 | } else sym_counter = 0; | ||
| 699 | break; | ||
| 700 | case 'f': | ||
| 701 | prompt_integer(&count_filter, "Enter display event count filter"); | ||
| 702 | break; | ||
| 703 | case 'F': | ||
| 704 | prompt_percent(&sym_pcnt_filter, "Enter details display event filter (percent)"); | ||
| 705 | break; | ||
| 706 | case 'q': | ||
| 707 | case 'Q': | ||
| 708 | printf("exiting.\n"); | ||
| 709 | exit(0); | ||
| 710 | case 's': | ||
| 711 | prompt_symbol(&sym_filter_entry, "Enter details symbol"); | ||
| 712 | break; | ||
| 713 | case 'S': | ||
| 714 | if (!sym_filter_entry) | ||
| 715 | break; | ||
| 716 | else { | ||
| 717 | struct sym_entry *syme = sym_filter_entry; | ||
| 718 | |||
| 719 | pthread_mutex_lock(&syme->source_lock); | ||
| 720 | sym_filter_entry = NULL; | ||
| 721 | __zero_source_counters(syme); | ||
| 722 | pthread_mutex_unlock(&syme->source_lock); | ||
| 723 | } | ||
| 724 | break; | ||
| 725 | case 'w': | ||
| 726 | display_weighted = ~display_weighted; | ||
| 727 | break; | ||
| 728 | case 'z': | ||
| 729 | zero = ~zero; | ||
| 730 | break; | ||
| 731 | } | ||
| 732 | } | ||
| 733 | |||
| 263 | static void *display_thread(void *arg __used) | 734 | static void *display_thread(void *arg __used) |
| 264 | { | 735 | { |
| 265 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; | 736 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; |
| 266 | int delay_msecs = delay_secs * 1000; | 737 | struct termios tc, save; |
| 738 | int delay_msecs, c; | ||
| 739 | |||
| 740 | tcgetattr(0, &save); | ||
| 741 | tc = save; | ||
| 742 | tc.c_lflag &= ~(ICANON | ECHO); | ||
| 743 | tc.c_cc[VMIN] = 0; | ||
| 744 | tc.c_cc[VTIME] = 0; | ||
| 267 | 745 | ||
| 268 | printf("PerfTop refresh period: %d seconds\n", delay_secs); | 746 | repeat: |
| 747 | delay_msecs = delay_secs * 1000; | ||
| 748 | tcsetattr(0, TCSANOW, &tc); | ||
| 749 | /* trash return*/ | ||
| 750 | getc(stdin); | ||
| 269 | 751 | ||
| 270 | do { | 752 | do { |
| 271 | print_sym_table(); | 753 | print_sym_table(); |
| 272 | } while (!poll(&stdin_poll, 1, delay_msecs) == 1); | 754 | } while (!poll(&stdin_poll, 1, delay_msecs) == 1); |
| 273 | 755 | ||
| 274 | printf("key pressed - exiting.\n"); | 756 | c = getc(stdin); |
| 275 | exit(0); | 757 | tcsetattr(0, TCSAFLUSH, &save); |
| 758 | |||
| 759 | handle_keypress(c); | ||
| 760 | goto repeat; | ||
| 276 | 761 | ||
| 277 | return NULL; | 762 | return NULL; |
| 278 | } | 763 | } |
| @@ -284,6 +769,7 @@ static const char *skip_symbols[] = { | |||
| 284 | "enter_idle", | 769 | "enter_idle", |
| 285 | "exit_idle", | 770 | "exit_idle", |
| 286 | "mwait_idle", | 771 | "mwait_idle", |
| 772 | "mwait_idle_with_hints", | ||
| 287 | "ppc64_runlatch_off", | 773 | "ppc64_runlatch_off", |
| 288 | "pseries_dedicated_idle_sleep", | 774 | "pseries_dedicated_idle_sleep", |
| 289 | NULL | 775 | NULL |
| @@ -291,7 +777,6 @@ static const char *skip_symbols[] = { | |||
| 291 | 777 | ||
| 292 | static int symbol_filter(struct dso *self, struct symbol *sym) | 778 | static int symbol_filter(struct dso *self, struct symbol *sym) |
| 293 | { | 779 | { |
| 294 | static int filter_match; | ||
| 295 | struct sym_entry *syme; | 780 | struct sym_entry *syme; |
| 296 | const char *name = sym->name; | 781 | const char *name = sym->name; |
| 297 | int i; | 782 | int i; |
| @@ -313,6 +798,10 @@ static int symbol_filter(struct dso *self, struct symbol *sym) | |||
| 313 | return 1; | 798 | return 1; |
| 314 | 799 | ||
| 315 | syme = dso__sym_priv(self, sym); | 800 | syme = dso__sym_priv(self, sym); |
| 801 | pthread_mutex_init(&syme->source_lock, NULL); | ||
| 802 | if (!sym_filter_entry && sym_filter && !strcmp(name, sym_filter)) | ||
| 803 | sym_filter_entry = syme; | ||
| 804 | |||
| 316 | for (i = 0; skip_symbols[i]; i++) { | 805 | for (i = 0; skip_symbols[i]; i++) { |
| 317 | if (!strcmp(skip_symbols[i], name)) { | 806 | if (!strcmp(skip_symbols[i], name)) { |
| 318 | syme->skip = 1; | 807 | syme->skip = 1; |
| @@ -320,29 +809,6 @@ static int symbol_filter(struct dso *self, struct symbol *sym) | |||
| 320 | } | 809 | } |
| 321 | } | 810 | } |
| 322 | 811 | ||
| 323 | if (filter_match == 1) { | ||
| 324 | filter_end = sym->start; | ||
| 325 | filter_match = -1; | ||
| 326 | if (filter_end - filter_start > 10000) { | ||
| 327 | fprintf(stderr, | ||
| 328 | "hm, too large filter symbol <%s> - skipping.\n", | ||
| 329 | sym_filter); | ||
| 330 | fprintf(stderr, "symbol filter start: %016lx\n", | ||
| 331 | filter_start); | ||
| 332 | fprintf(stderr, " end: %016lx\n", | ||
| 333 | filter_end); | ||
| 334 | filter_end = filter_start = 0; | ||
| 335 | sym_filter = NULL; | ||
| 336 | sleep(1); | ||
| 337 | } | ||
| 338 | } | ||
| 339 | |||
| 340 | if (filter_match == 0 && sym_filter && !strcmp(name, sym_filter)) { | ||
| 341 | filter_match = 1; | ||
| 342 | filter_start = sym->start; | ||
| 343 | } | ||
| 344 | |||
| 345 | |||
| 346 | return 0; | 812 | return 0; |
| 347 | } | 813 | } |
| 348 | 814 | ||
| @@ -378,8 +844,6 @@ out_delete_dso: | |||
| 378 | return -1; | 844 | return -1; |
| 379 | } | 845 | } |
| 380 | 846 | ||
| 381 | #define TRACE_COUNT 3 | ||
| 382 | |||
| 383 | /* | 847 | /* |
| 384 | * Binary search in the histogram table and record the hit: | 848 | * Binary search in the histogram table and record the hit: |
| 385 | */ | 849 | */ |
| @@ -392,6 +856,7 @@ static void record_ip(u64 ip, int counter) | |||
| 392 | 856 | ||
| 393 | if (!syme->skip) { | 857 | if (!syme->skip) { |
| 394 | syme->count[counter]++; | 858 | syme->count[counter]++; |
| 859 | record_precise_ip(syme, counter, ip); | ||
| 395 | pthread_mutex_lock(&active_symbols_lock); | 860 | pthread_mutex_lock(&active_symbols_lock); |
| 396 | if (list_empty(&syme->node) || !syme->node.next) | 861 | if (list_empty(&syme->node) || !syme->node.next) |
| 397 | __list_insert_active_sym(syme); | 862 | __list_insert_active_sym(syme); |
| @@ -549,7 +1014,7 @@ int group_fd; | |||
| 549 | static void start_counter(int i, int counter) | 1014 | static void start_counter(int i, int counter) |
| 550 | { | 1015 | { |
| 551 | struct perf_counter_attr *attr; | 1016 | struct perf_counter_attr *attr; |
| 552 | unsigned int cpu; | 1017 | int cpu; |
| 553 | 1018 | ||
| 554 | cpu = profile_cpu; | 1019 | cpu = profile_cpu; |
| 555 | if (target_pid == -1 && profile_cpu == -1) | 1020 | if (target_pid == -1 && profile_cpu == -1) |
| @@ -559,6 +1024,7 @@ static void start_counter(int i, int counter) | |||
| 559 | 1024 | ||
| 560 | attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID; | 1025 | attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID; |
| 561 | attr->freq = freq; | 1026 | attr->freq = freq; |
| 1027 | attr->inherit = (cpu < 0) && inherit; | ||
| 562 | 1028 | ||
| 563 | try_again: | 1029 | try_again: |
| 564 | fd[i][counter] = sys_perf_counter_open(attr, target_pid, cpu, group_fd, 0); | 1030 | fd[i][counter] = sys_perf_counter_open(attr, target_pid, cpu, group_fd, 0); |
| @@ -685,8 +1151,10 @@ static const struct option options[] = { | |||
| 685 | "only display functions with more events than this"), | 1151 | "only display functions with more events than this"), |
| 686 | OPT_BOOLEAN('g', "group", &group, | 1152 | OPT_BOOLEAN('g', "group", &group, |
| 687 | "put the counters into a counter group"), | 1153 | "put the counters into a counter group"), |
| 688 | OPT_STRING('s', "sym-filter", &sym_filter, "pattern", | 1154 | OPT_BOOLEAN('i', "inherit", &inherit, |
| 689 | "only display symbols matchig this pattern"), | 1155 | "child tasks inherit counters"), |
| 1156 | OPT_STRING('s', "sym-annotate", &sym_filter, "symbol name", | ||
| 1157 | "symbol to annotate - requires -k option"), | ||
| 690 | OPT_BOOLEAN('z', "zero", &zero, | 1158 | OPT_BOOLEAN('z', "zero", &zero, |
| 691 | "zero history across updates"), | 1159 | "zero history across updates"), |
| 692 | OPT_INTEGER('F', "freq", &freq, | 1160 | OPT_INTEGER('F', "freq", &freq, |
| @@ -729,6 +1197,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
| 729 | delay_secs = 1; | 1197 | delay_secs = 1; |
| 730 | 1198 | ||
| 731 | parse_symbols(); | 1199 | parse_symbols(); |
| 1200 | parse_source(sym_filter_entry); | ||
| 732 | 1201 | ||
| 733 | /* | 1202 | /* |
| 734 | * Fill in the ones not specifically initialized via -c: | 1203 | * Fill in the ones not specifically initialized via -c: |
diff --git a/tools/perf/perf.c b/tools/perf/perf.c index c5656784c61d..31982ad064b4 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c | |||
| @@ -12,6 +12,8 @@ | |||
| 12 | #include "util/cache.h" | 12 | #include "util/cache.h" |
| 13 | #include "util/quote.h" | 13 | #include "util/quote.h" |
| 14 | #include "util/run-command.h" | 14 | #include "util/run-command.h" |
| 15 | #include "util/parse-events.h" | ||
| 16 | #include "util/string.h" | ||
| 15 | 17 | ||
| 16 | const char perf_usage_string[] = | 18 | const char perf_usage_string[] = |
| 17 | "perf [--version] [--help] COMMAND [ARGS]"; | 19 | "perf [--version] [--help] COMMAND [ARGS]"; |
| @@ -25,6 +27,8 @@ struct pager_config { | |||
| 25 | int val; | 27 | int val; |
| 26 | }; | 28 | }; |
| 27 | 29 | ||
| 30 | static char debugfs_mntpt[MAXPATHLEN]; | ||
| 31 | |||
| 28 | static int pager_command_config(const char *var, const char *value, void *data) | 32 | static int pager_command_config(const char *var, const char *value, void *data) |
| 29 | { | 33 | { |
| 30 | struct pager_config *c = data; | 34 | struct pager_config *c = data; |
| @@ -56,6 +60,15 @@ static void commit_pager_choice(void) { | |||
| 56 | } | 60 | } |
| 57 | } | 61 | } |
| 58 | 62 | ||
| 63 | static void set_debugfs_path(void) | ||
| 64 | { | ||
| 65 | char *path; | ||
| 66 | |||
| 67 | path = getenv(PERF_DEBUGFS_ENVIRONMENT); | ||
| 68 | snprintf(debugfs_path, MAXPATHLEN, "%s/%s", path ?: debugfs_mntpt, | ||
| 69 | "tracing/events"); | ||
| 70 | } | ||
| 71 | |||
| 59 | static int handle_options(const char*** argv, int* argc, int* envchanged) | 72 | static int handle_options(const char*** argv, int* argc, int* envchanged) |
| 60 | { | 73 | { |
| 61 | int handled = 0; | 74 | int handled = 0; |
| @@ -122,6 +135,22 @@ static int handle_options(const char*** argv, int* argc, int* envchanged) | |||
| 122 | setenv(PERF_WORK_TREE_ENVIRONMENT, cmd + 12, 1); | 135 | setenv(PERF_WORK_TREE_ENVIRONMENT, cmd + 12, 1); |
| 123 | if (envchanged) | 136 | if (envchanged) |
| 124 | *envchanged = 1; | 137 | *envchanged = 1; |
| 138 | } else if (!strcmp(cmd, "--debugfs-dir")) { | ||
| 139 | if (*argc < 2) { | ||
| 140 | fprintf(stderr, "No directory given for --debugfs-dir.\n"); | ||
| 141 | usage(perf_usage_string); | ||
| 142 | } | ||
| 143 | strncpy(debugfs_mntpt, (*argv)[1], MAXPATHLEN); | ||
| 144 | debugfs_mntpt[MAXPATHLEN - 1] = '\0'; | ||
| 145 | if (envchanged) | ||
| 146 | *envchanged = 1; | ||
| 147 | (*argv)++; | ||
| 148 | (*argc)--; | ||
| 149 | } else if (!prefixcmp(cmd, "--debugfs-dir=")) { | ||
| 150 | strncpy(debugfs_mntpt, cmd + 14, MAXPATHLEN); | ||
| 151 | debugfs_mntpt[MAXPATHLEN - 1] = '\0'; | ||
| 152 | if (envchanged) | ||
| 153 | *envchanged = 1; | ||
| 125 | } else { | 154 | } else { |
| 126 | fprintf(stderr, "Unknown option: %s\n", cmd); | 155 | fprintf(stderr, "Unknown option: %s\n", cmd); |
| 127 | usage(perf_usage_string); | 156 | usage(perf_usage_string); |
| @@ -228,6 +257,7 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv) | |||
| 228 | if (use_pager == -1 && p->option & USE_PAGER) | 257 | if (use_pager == -1 && p->option & USE_PAGER) |
| 229 | use_pager = 1; | 258 | use_pager = 1; |
| 230 | commit_pager_choice(); | 259 | commit_pager_choice(); |
| 260 | set_debugfs_path(); | ||
| 231 | 261 | ||
| 232 | status = p->fn(argc, argv, prefix); | 262 | status = p->fn(argc, argv, prefix); |
| 233 | if (status) | 263 | if (status) |
| @@ -346,6 +376,49 @@ static int run_argv(int *argcp, const char ***argv) | |||
| 346 | return done_alias; | 376 | return done_alias; |
| 347 | } | 377 | } |
| 348 | 378 | ||
| 379 | /* mini /proc/mounts parser: searching for "^blah /mount/point debugfs" */ | ||
| 380 | static void get_debugfs_mntpt(void) | ||
| 381 | { | ||
| 382 | FILE *file; | ||
| 383 | char fs_type[100]; | ||
| 384 | char debugfs[MAXPATHLEN]; | ||
| 385 | |||
| 386 | /* | ||
| 387 | * try the standard location | ||
| 388 | */ | ||
| 389 | if (valid_debugfs_mount("/sys/kernel/debug/") == 0) { | ||
| 390 | strcpy(debugfs_mntpt, "/sys/kernel/debug/"); | ||
| 391 | return; | ||
| 392 | } | ||
| 393 | |||
| 394 | /* | ||
| 395 | * try the sane location | ||
| 396 | */ | ||
| 397 | if (valid_debugfs_mount("/debug/") == 0) { | ||
| 398 | strcpy(debugfs_mntpt, "/debug/"); | ||
| 399 | return; | ||
| 400 | } | ||
| 401 | |||
| 402 | /* | ||
| 403 | * give up and parse /proc/mounts | ||
| 404 | */ | ||
| 405 | file = fopen("/proc/mounts", "r"); | ||
| 406 | if (file == NULL) | ||
| 407 | return; | ||
| 408 | |||
| 409 | while (fscanf(file, "%*s %" | ||
| 410 | STR(MAXPATHLEN) | ||
| 411 | "s %99s %*s %*d %*d\n", | ||
| 412 | debugfs, fs_type) == 2) { | ||
| 413 | if (strcmp(fs_type, "debugfs") == 0) | ||
| 414 | break; | ||
| 415 | } | ||
| 416 | fclose(file); | ||
| 417 | if (strcmp(fs_type, "debugfs") == 0) { | ||
| 418 | strncpy(debugfs_mntpt, debugfs, MAXPATHLEN); | ||
| 419 | debugfs_mntpt[MAXPATHLEN - 1] = '\0'; | ||
| 420 | } | ||
| 421 | } | ||
| 349 | 422 | ||
| 350 | int main(int argc, const char **argv) | 423 | int main(int argc, const char **argv) |
| 351 | { | 424 | { |
| @@ -354,7 +427,8 @@ int main(int argc, const char **argv) | |||
| 354 | cmd = perf_extract_argv0_path(argv[0]); | 427 | cmd = perf_extract_argv0_path(argv[0]); |
| 355 | if (!cmd) | 428 | if (!cmd) |
| 356 | cmd = "perf-help"; | 429 | cmd = "perf-help"; |
| 357 | 430 | /* get debugfs mount point from /proc/mounts */ | |
| 431 | get_debugfs_mntpt(); | ||
| 358 | /* | 432 | /* |
| 359 | * "perf-xxxx" is the same as "perf xxxx", but we obviously: | 433 | * "perf-xxxx" is the same as "perf xxxx", but we obviously: |
| 360 | * | 434 | * |
| @@ -377,6 +451,7 @@ int main(int argc, const char **argv) | |||
| 377 | argc--; | 451 | argc--; |
| 378 | handle_options(&argv, &argc, NULL); | 452 | handle_options(&argv, &argc, NULL); |
| 379 | commit_pager_choice(); | 453 | commit_pager_choice(); |
| 454 | set_debugfs_path(); | ||
| 380 | if (argc > 0) { | 455 | if (argc > 0) { |
| 381 | if (!prefixcmp(argv[0], "--")) | 456 | if (!prefixcmp(argv[0], "--")) |
| 382 | argv[0] += 2; | 457 | argv[0] += 2; |
diff --git a/tools/perf/perf.h b/tools/perf/perf.h index 63e67cc5487b..e5148e2b6134 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h | |||
| @@ -1,7 +1,13 @@ | |||
| 1 | #ifndef _PERF_PERF_H | 1 | #ifndef _PERF_PERF_H |
| 2 | #define _PERF_PERF_H | 2 | #define _PERF_PERF_H |
| 3 | 3 | ||
| 4 | #if defined(__x86_64__) || defined(__i386__) | 4 | #if defined(__i386__) |
| 5 | #include "../../arch/x86/include/asm/unistd.h" | ||
| 6 | #define rmb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory") | ||
| 7 | #define cpu_relax() asm volatile("rep; nop" ::: "memory"); | ||
| 8 | #endif | ||
| 9 | |||
| 10 | #if defined(__x86_64__) | ||
| 5 | #include "../../arch/x86/include/asm/unistd.h" | 11 | #include "../../arch/x86/include/asm/unistd.h" |
| 6 | #define rmb() asm volatile("lfence" ::: "memory") | 12 | #define rmb() asm volatile("lfence" ::: "memory") |
| 7 | #define cpu_relax() asm volatile("rep; nop" ::: "memory"); | 13 | #define cpu_relax() asm volatile("rep; nop" ::: "memory"); |
diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h index 161d5f413e28..4b50c412b9c5 100644 --- a/tools/perf/util/cache.h +++ b/tools/perf/util/cache.h | |||
| @@ -18,6 +18,7 @@ | |||
| 18 | #define PERFATTRIBUTES_FILE ".perfattributes" | 18 | #define PERFATTRIBUTES_FILE ".perfattributes" |
| 19 | #define INFOATTRIBUTES_FILE "info/attributes" | 19 | #define INFOATTRIBUTES_FILE "info/attributes" |
| 20 | #define ATTRIBUTE_MACRO_PREFIX "[attr]" | 20 | #define ATTRIBUTE_MACRO_PREFIX "[attr]" |
| 21 | #define PERF_DEBUGFS_ENVIRONMENT "PERF_DEBUGFS_DIR" | ||
| 21 | 22 | ||
| 22 | typedef int (*config_fn_t)(const char *, const char *, void *); | 23 | typedef int (*config_fn_t)(const char *, const char *, void *); |
| 23 | extern int perf_default_config(const char *, const char *, void *); | 24 | extern int perf_default_config(const char *, const char *, void *); |
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 9d3c8141b8c1..011473411642 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | #include <stdio.h> | 13 | #include <stdio.h> |
| 14 | #include <stdbool.h> | 14 | #include <stdbool.h> |
| 15 | #include <errno.h> | 15 | #include <errno.h> |
| 16 | #include <math.h> | ||
| 16 | 17 | ||
| 17 | #include "callchain.h" | 18 | #include "callchain.h" |
| 18 | 19 | ||
| @@ -26,10 +27,14 @@ rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, | |||
| 26 | struct rb_node **p = &root->rb_node; | 27 | struct rb_node **p = &root->rb_node; |
| 27 | struct rb_node *parent = NULL; | 28 | struct rb_node *parent = NULL; |
| 28 | struct callchain_node *rnode; | 29 | struct callchain_node *rnode; |
| 30 | u64 chain_cumul = cumul_hits(chain); | ||
| 29 | 31 | ||
| 30 | while (*p) { | 32 | while (*p) { |
| 33 | u64 rnode_cumul; | ||
| 34 | |||
| 31 | parent = *p; | 35 | parent = *p; |
| 32 | rnode = rb_entry(parent, struct callchain_node, rb_node); | 36 | rnode = rb_entry(parent, struct callchain_node, rb_node); |
| 37 | rnode_cumul = cumul_hits(rnode); | ||
| 33 | 38 | ||
| 34 | switch (mode) { | 39 | switch (mode) { |
| 35 | case CHAIN_FLAT: | 40 | case CHAIN_FLAT: |
| @@ -40,7 +45,7 @@ rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, | |||
| 40 | break; | 45 | break; |
| 41 | case CHAIN_GRAPH_ABS: /* Falldown */ | 46 | case CHAIN_GRAPH_ABS: /* Falldown */ |
| 42 | case CHAIN_GRAPH_REL: | 47 | case CHAIN_GRAPH_REL: |
| 43 | if (rnode->cumul_hit < chain->cumul_hit) | 48 | if (rnode_cumul < chain_cumul) |
| 44 | p = &(*p)->rb_left; | 49 | p = &(*p)->rb_left; |
| 45 | else | 50 | else |
| 46 | p = &(*p)->rb_right; | 51 | p = &(*p)->rb_right; |
| @@ -87,7 +92,7 @@ static void __sort_chain_graph_abs(struct callchain_node *node, | |||
| 87 | 92 | ||
| 88 | chain_for_each_child(child, node) { | 93 | chain_for_each_child(child, node) { |
| 89 | __sort_chain_graph_abs(child, min_hit); | 94 | __sort_chain_graph_abs(child, min_hit); |
| 90 | if (child->cumul_hit >= min_hit) | 95 | if (cumul_hits(child) >= min_hit) |
| 91 | rb_insert_callchain(&node->rb_root, child, | 96 | rb_insert_callchain(&node->rb_root, child, |
| 92 | CHAIN_GRAPH_ABS); | 97 | CHAIN_GRAPH_ABS); |
| 93 | } | 98 | } |
| @@ -108,11 +113,11 @@ static void __sort_chain_graph_rel(struct callchain_node *node, | |||
| 108 | u64 min_hit; | 113 | u64 min_hit; |
| 109 | 114 | ||
| 110 | node->rb_root = RB_ROOT; | 115 | node->rb_root = RB_ROOT; |
| 111 | min_hit = node->cumul_hit * min_percent / 100.0; | 116 | min_hit = ceil(node->children_hit * min_percent); |
| 112 | 117 | ||
| 113 | chain_for_each_child(child, node) { | 118 | chain_for_each_child(child, node) { |
| 114 | __sort_chain_graph_rel(child, min_percent); | 119 | __sort_chain_graph_rel(child, min_percent); |
| 115 | if (child->cumul_hit >= min_hit) | 120 | if (cumul_hits(child) >= min_hit) |
| 116 | rb_insert_callchain(&node->rb_root, child, | 121 | rb_insert_callchain(&node->rb_root, child, |
| 117 | CHAIN_GRAPH_REL); | 122 | CHAIN_GRAPH_REL); |
| 118 | } | 123 | } |
| @@ -122,7 +127,7 @@ static void | |||
| 122 | sort_chain_graph_rel(struct rb_root *rb_root, struct callchain_node *chain_root, | 127 | sort_chain_graph_rel(struct rb_root *rb_root, struct callchain_node *chain_root, |
| 123 | u64 min_hit __used, struct callchain_param *param) | 128 | u64 min_hit __used, struct callchain_param *param) |
| 124 | { | 129 | { |
| 125 | __sort_chain_graph_rel(chain_root, param->min_percent); | 130 | __sort_chain_graph_rel(chain_root, param->min_percent / 100.0); |
| 126 | rb_root->rb_node = chain_root->rb_root.rb_node; | 131 | rb_root->rb_node = chain_root->rb_root.rb_node; |
| 127 | } | 132 | } |
| 128 | 133 | ||
| @@ -211,7 +216,8 @@ add_child(struct callchain_node *parent, struct ip_callchain *chain, | |||
| 211 | new = create_child(parent, false); | 216 | new = create_child(parent, false); |
| 212 | fill_node(new, chain, start, syms); | 217 | fill_node(new, chain, start, syms); |
| 213 | 218 | ||
| 214 | new->cumul_hit = new->hit = 1; | 219 | new->children_hit = 0; |
| 220 | new->hit = 1; | ||
| 215 | } | 221 | } |
| 216 | 222 | ||
| 217 | /* | 223 | /* |
| @@ -241,7 +247,8 @@ split_add_child(struct callchain_node *parent, struct ip_callchain *chain, | |||
| 241 | 247 | ||
| 242 | /* split the hits */ | 248 | /* split the hits */ |
| 243 | new->hit = parent->hit; | 249 | new->hit = parent->hit; |
| 244 | new->cumul_hit = parent->cumul_hit; | 250 | new->children_hit = parent->children_hit; |
| 251 | parent->children_hit = cumul_hits(new); | ||
| 245 | new->val_nr = parent->val_nr - idx_local; | 252 | new->val_nr = parent->val_nr - idx_local; |
| 246 | parent->val_nr = idx_local; | 253 | parent->val_nr = idx_local; |
| 247 | 254 | ||
| @@ -249,6 +256,7 @@ split_add_child(struct callchain_node *parent, struct ip_callchain *chain, | |||
| 249 | if (idx_total < chain->nr) { | 256 | if (idx_total < chain->nr) { |
| 250 | parent->hit = 0; | 257 | parent->hit = 0; |
| 251 | add_child(parent, chain, idx_total, syms); | 258 | add_child(parent, chain, idx_total, syms); |
| 259 | parent->children_hit++; | ||
| 252 | } else { | 260 | } else { |
| 253 | parent->hit = 1; | 261 | parent->hit = 1; |
| 254 | } | 262 | } |
| @@ -269,13 +277,13 @@ __append_chain_children(struct callchain_node *root, struct ip_callchain *chain, | |||
| 269 | unsigned int ret = __append_chain(rnode, chain, start, syms); | 277 | unsigned int ret = __append_chain(rnode, chain, start, syms); |
| 270 | 278 | ||
| 271 | if (!ret) | 279 | if (!ret) |
| 272 | goto cumul; | 280 | goto inc_children_hit; |
| 273 | } | 281 | } |
| 274 | /* nothing in children, add to the current node */ | 282 | /* nothing in children, add to the current node */ |
| 275 | add_child(root, chain, start, syms); | 283 | add_child(root, chain, start, syms); |
| 276 | 284 | ||
| 277 | cumul: | 285 | inc_children_hit: |
| 278 | root->cumul_hit++; | 286 | root->children_hit++; |
| 279 | } | 287 | } |
| 280 | 288 | ||
| 281 | static int | 289 | static int |
| @@ -317,8 +325,6 @@ __append_chain(struct callchain_node *root, struct ip_callchain *chain, | |||
| 317 | /* we match 100% of the path, increment the hit */ | 325 | /* we match 100% of the path, increment the hit */ |
| 318 | if (i - start == root->val_nr && i == chain->nr) { | 326 | if (i - start == root->val_nr && i == chain->nr) { |
| 319 | root->hit++; | 327 | root->hit++; |
| 320 | root->cumul_hit++; | ||
| 321 | |||
| 322 | return 0; | 328 | return 0; |
| 323 | } | 329 | } |
| 324 | 330 | ||
| @@ -331,5 +337,7 @@ __append_chain(struct callchain_node *root, struct ip_callchain *chain, | |||
| 331 | void append_chain(struct callchain_node *root, struct ip_callchain *chain, | 337 | void append_chain(struct callchain_node *root, struct ip_callchain *chain, |
| 332 | struct symbol **syms) | 338 | struct symbol **syms) |
| 333 | { | 339 | { |
| 340 | if (!chain->nr) | ||
| 341 | return; | ||
| 334 | __append_chain_children(root, chain, syms, 0); | 342 | __append_chain_children(root, chain, syms, 0); |
| 335 | } | 343 | } |
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index 7812122bea1d..a926ae4f5a16 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include "symbol.h" | 7 | #include "symbol.h" |
| 8 | 8 | ||
| 9 | enum chain_mode { | 9 | enum chain_mode { |
| 10 | CHAIN_NONE, | ||
| 10 | CHAIN_FLAT, | 11 | CHAIN_FLAT, |
| 11 | CHAIN_GRAPH_ABS, | 12 | CHAIN_GRAPH_ABS, |
| 12 | CHAIN_GRAPH_REL | 13 | CHAIN_GRAPH_REL |
| @@ -21,7 +22,7 @@ struct callchain_node { | |||
| 21 | struct rb_root rb_root; /* sorted tree of children */ | 22 | struct rb_root rb_root; /* sorted tree of children */ |
| 22 | unsigned int val_nr; | 23 | unsigned int val_nr; |
| 23 | u64 hit; | 24 | u64 hit; |
| 24 | u64 cumul_hit; /* hit + hits of children */ | 25 | u64 children_hit; |
| 25 | }; | 26 | }; |
| 26 | 27 | ||
| 27 | struct callchain_param; | 28 | struct callchain_param; |
| @@ -48,6 +49,11 @@ static inline void callchain_init(struct callchain_node *node) | |||
| 48 | INIT_LIST_HEAD(&node->val); | 49 | INIT_LIST_HEAD(&node->val); |
| 49 | } | 50 | } |
| 50 | 51 | ||
| 52 | static inline u64 cumul_hits(struct callchain_node *node) | ||
| 53 | { | ||
| 54 | return node->hit + node->children_hit; | ||
| 55 | } | ||
| 56 | |||
| 51 | int register_callchain_param(struct callchain_param *param); | 57 | int register_callchain_param(struct callchain_param *param); |
| 52 | void append_chain(struct callchain_node *root, struct ip_callchain *chain, | 58 | void append_chain(struct callchain_node *root, struct ip_callchain *chain, |
| 53 | struct symbol **syms); | 59 | struct symbol **syms); |
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 450384b3bbe5..b92a457ca32e 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
| @@ -185,6 +185,8 @@ static void do_read(int fd, void *buf, size_t size) | |||
| 185 | 185 | ||
| 186 | if (ret < 0) | 186 | if (ret < 0) |
| 187 | die("failed to read"); | 187 | die("failed to read"); |
| 188 | if (ret == 0) | ||
| 189 | die("failed to read: missing data"); | ||
| 188 | 190 | ||
| 189 | size -= ret; | 191 | size -= ret; |
| 190 | buf += ret; | 192 | buf += ret; |
| @@ -213,9 +215,10 @@ struct perf_header *perf_header__read(int fd) | |||
| 213 | 215 | ||
| 214 | for (i = 0; i < nr_attrs; i++) { | 216 | for (i = 0; i < nr_attrs; i++) { |
| 215 | struct perf_header_attr *attr; | 217 | struct perf_header_attr *attr; |
| 216 | off_t tmp = lseek(fd, 0, SEEK_CUR); | 218 | off_t tmp; |
| 217 | 219 | ||
| 218 | do_read(fd, &f_attr, sizeof(f_attr)); | 220 | do_read(fd, &f_attr, sizeof(f_attr)); |
| 221 | tmp = lseek(fd, 0, SEEK_CUR); | ||
| 219 | 222 | ||
| 220 | attr = perf_header_attr__new(&f_attr.attr); | 223 | attr = perf_header_attr__new(&f_attr.attr); |
| 221 | 224 | ||
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index b5ef53ad4c7a..bf280449fcfd 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h | |||
| @@ -16,7 +16,7 @@ struct perf_header { | |||
| 16 | int frozen; | 16 | int frozen; |
| 17 | int attrs, size; | 17 | int attrs, size; |
| 18 | struct perf_header_attr **attr; | 18 | struct perf_header_attr **attr; |
| 19 | off_t attr_offset; | 19 | s64 attr_offset; |
| 20 | u64 data_offset; | 20 | u64 data_offset; |
| 21 | u64 data_size; | 21 | u64 data_size; |
| 22 | }; | 22 | }; |
diff --git a/tools/perf/util/include/linux/kernel.h b/tools/perf/util/include/linux/kernel.h index 99c1b3d1edd9..a6b87390cb52 100644 --- a/tools/perf/util/include/linux/kernel.h +++ b/tools/perf/util/include/linux/kernel.h | |||
| @@ -18,4 +18,12 @@ | |||
| 18 | (type *)((char *)__mptr - offsetof(type, member)); }) | 18 | (type *)((char *)__mptr - offsetof(type, member)); }) |
| 19 | #endif | 19 | #endif |
| 20 | 20 | ||
| 21 | #ifndef max | ||
| 22 | #define max(x, y) ({ \ | ||
| 23 | typeof(x) _max1 = (x); \ | ||
| 24 | typeof(y) _max2 = (y); \ | ||
| 25 | (void) (&_max1 == &_max2); \ | ||
| 26 | _max1 > _max2 ? _max1 : _max2; }) | ||
| 27 | #endif | ||
| 28 | |||
| 21 | #endif | 29 | #endif |
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 5184959e0615..044178408783 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #include "parse-events.h" | 5 | #include "parse-events.h" |
| 6 | #include "exec_cmd.h" | 6 | #include "exec_cmd.h" |
| 7 | #include "string.h" | 7 | #include "string.h" |
| 8 | #include "cache.h" | ||
| 8 | 9 | ||
| 9 | extern char *strcasestr(const char *haystack, const char *needle); | 10 | extern char *strcasestr(const char *haystack, const char *needle); |
| 10 | 11 | ||
| @@ -19,6 +20,8 @@ struct event_symbol { | |||
| 19 | char *alias; | 20 | char *alias; |
| 20 | }; | 21 | }; |
| 21 | 22 | ||
| 23 | char debugfs_path[MAXPATHLEN]; | ||
| 24 | |||
| 22 | #define CHW(x) .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_##x | 25 | #define CHW(x) .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_##x |
| 23 | #define CSW(x) .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_##x | 26 | #define CSW(x) .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_##x |
| 24 | 27 | ||
| @@ -71,8 +74,8 @@ static char *sw_event_names[] = { | |||
| 71 | #define MAX_ALIASES 8 | 74 | #define MAX_ALIASES 8 |
| 72 | 75 | ||
| 73 | static char *hw_cache[][MAX_ALIASES] = { | 76 | static char *hw_cache[][MAX_ALIASES] = { |
| 74 | { "L1-d$", "l1-d", "l1d", "L1-data", }, | 77 | { "L1-dcache", "l1-d", "l1d", "L1-data", }, |
| 75 | { "L1-i$", "l1-i", "l1i", "L1-instruction", }, | 78 | { "L1-icache", "l1-i", "l1i", "L1-instruction", }, |
| 76 | { "LLC", "L2" }, | 79 | { "LLC", "L2" }, |
| 77 | { "dTLB", "d-tlb", "Data-TLB", }, | 80 | { "dTLB", "d-tlb", "Data-TLB", }, |
| 78 | { "iTLB", "i-tlb", "Instruction-TLB", }, | 81 | { "iTLB", "i-tlb", "Instruction-TLB", }, |
| @@ -110,6 +113,104 @@ static unsigned long hw_cache_stat[C(MAX)] = { | |||
| 110 | [C(BPU)] = (CACHE_READ), | 113 | [C(BPU)] = (CACHE_READ), |
| 111 | }; | 114 | }; |
| 112 | 115 | ||
| 116 | #define for_each_subsystem(sys_dir, sys_dirent, sys_next, file, st) \ | ||
| 117 | while (!readdir_r(sys_dir, &sys_dirent, &sys_next) && sys_next) \ | ||
| 118 | if (snprintf(file, MAXPATHLEN, "%s/%s", debugfs_path, \ | ||
| 119 | sys_dirent.d_name) && \ | ||
| 120 | (!stat(file, &st)) && (S_ISDIR(st.st_mode)) && \ | ||
| 121 | (strcmp(sys_dirent.d_name, ".")) && \ | ||
| 122 | (strcmp(sys_dirent.d_name, ".."))) | ||
| 123 | |||
| 124 | static int tp_event_has_id(struct dirent *sys_dir, struct dirent *evt_dir) | ||
| 125 | { | ||
| 126 | char evt_path[MAXPATHLEN]; | ||
| 127 | int fd; | ||
| 128 | |||
| 129 | snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", debugfs_path, | ||
| 130 | sys_dir->d_name, evt_dir->d_name); | ||
| 131 | fd = open(evt_path, O_RDONLY); | ||
| 132 | if (fd < 0) | ||
| 133 | return -EINVAL; | ||
| 134 | close(fd); | ||
| 135 | |||
| 136 | return 0; | ||
| 137 | } | ||
| 138 | |||
| 139 | #define for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next, file, st) \ | ||
| 140 | while (!readdir_r(evt_dir, &evt_dirent, &evt_next) && evt_next) \ | ||
| 141 | if (snprintf(file, MAXPATHLEN, "%s/%s/%s", debugfs_path, \ | ||
| 142 | sys_dirent.d_name, evt_dirent.d_name) && \ | ||
| 143 | (!stat(file, &st)) && (S_ISDIR(st.st_mode)) && \ | ||
| 144 | (strcmp(evt_dirent.d_name, ".")) && \ | ||
| 145 | (strcmp(evt_dirent.d_name, "..")) && \ | ||
| 146 | (!tp_event_has_id(&sys_dirent, &evt_dirent))) | ||
| 147 | |||
| 148 | #define MAX_EVENT_LENGTH 30 | ||
| 149 | |||
| 150 | int valid_debugfs_mount(const char *debugfs) | ||
| 151 | { | ||
| 152 | struct statfs st_fs; | ||
| 153 | |||
| 154 | if (statfs(debugfs, &st_fs) < 0) | ||
| 155 | return -ENOENT; | ||
| 156 | else if (st_fs.f_type != (long) DEBUGFS_MAGIC) | ||
| 157 | return -ENOENT; | ||
| 158 | return 0; | ||
| 159 | } | ||
| 160 | |||
| 161 | static char *tracepoint_id_to_name(u64 config) | ||
| 162 | { | ||
| 163 | static char tracepoint_name[2 * MAX_EVENT_LENGTH]; | ||
| 164 | DIR *sys_dir, *evt_dir; | ||
| 165 | struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent; | ||
| 166 | struct stat st; | ||
| 167 | char id_buf[4]; | ||
| 168 | int fd; | ||
| 169 | u64 id; | ||
| 170 | char evt_path[MAXPATHLEN]; | ||
| 171 | |||
| 172 | if (valid_debugfs_mount(debugfs_path)) | ||
| 173 | return "unkown"; | ||
| 174 | |||
| 175 | sys_dir = opendir(debugfs_path); | ||
| 176 | if (!sys_dir) | ||
| 177 | goto cleanup; | ||
| 178 | |||
| 179 | for_each_subsystem(sys_dir, sys_dirent, sys_next, evt_path, st) { | ||
| 180 | evt_dir = opendir(evt_path); | ||
| 181 | if (!evt_dir) | ||
| 182 | goto cleanup; | ||
| 183 | for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next, | ||
| 184 | evt_path, st) { | ||
| 185 | snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", | ||
| 186 | debugfs_path, sys_dirent.d_name, | ||
| 187 | evt_dirent.d_name); | ||
| 188 | fd = open(evt_path, O_RDONLY); | ||
| 189 | if (fd < 0) | ||
| 190 | continue; | ||
| 191 | if (read(fd, id_buf, sizeof(id_buf)) < 0) { | ||
| 192 | close(fd); | ||
| 193 | continue; | ||
| 194 | } | ||
| 195 | close(fd); | ||
| 196 | id = atoll(id_buf); | ||
| 197 | if (id == config) { | ||
| 198 | closedir(evt_dir); | ||
| 199 | closedir(sys_dir); | ||
| 200 | snprintf(tracepoint_name, 2 * MAX_EVENT_LENGTH, | ||
| 201 | "%s:%s", sys_dirent.d_name, | ||
| 202 | evt_dirent.d_name); | ||
| 203 | return tracepoint_name; | ||
| 204 | } | ||
| 205 | } | ||
| 206 | closedir(evt_dir); | ||
| 207 | } | ||
| 208 | |||
| 209 | cleanup: | ||
| 210 | closedir(sys_dir); | ||
| 211 | return "unkown"; | ||
| 212 | } | ||
| 213 | |||
| 113 | static int is_cache_op_valid(u8 cache_type, u8 cache_op) | 214 | static int is_cache_op_valid(u8 cache_type, u8 cache_op) |
| 114 | { | 215 | { |
| 115 | if (hw_cache_stat[cache_type] & COP(cache_op)) | 216 | if (hw_cache_stat[cache_type] & COP(cache_op)) |
| @@ -138,9 +239,15 @@ char *event_name(int counter) | |||
| 138 | { | 239 | { |
| 139 | u64 config = attrs[counter].config; | 240 | u64 config = attrs[counter].config; |
| 140 | int type = attrs[counter].type; | 241 | int type = attrs[counter].type; |
| 242 | |||
| 243 | return __event_name(type, config); | ||
| 244 | } | ||
| 245 | |||
| 246 | char *__event_name(int type, u64 config) | ||
| 247 | { | ||
| 141 | static char buf[32]; | 248 | static char buf[32]; |
| 142 | 249 | ||
| 143 | if (attrs[counter].type == PERF_TYPE_RAW) { | 250 | if (type == PERF_TYPE_RAW) { |
| 144 | sprintf(buf, "raw 0x%llx", config); | 251 | sprintf(buf, "raw 0x%llx", config); |
| 145 | return buf; | 252 | return buf; |
| 146 | } | 253 | } |
| @@ -177,6 +284,9 @@ char *event_name(int counter) | |||
| 177 | return sw_event_names[config]; | 284 | return sw_event_names[config]; |
| 178 | return "unknown-software"; | 285 | return "unknown-software"; |
| 179 | 286 | ||
| 287 | case PERF_TYPE_TRACEPOINT: | ||
| 288 | return tracepoint_id_to_name(config); | ||
| 289 | |||
| 180 | default: | 290 | default: |
| 181 | break; | 291 | break; |
| 182 | } | 292 | } |
| @@ -265,6 +375,63 @@ parse_generic_hw_event(const char **str, struct perf_counter_attr *attr) | |||
| 265 | return 1; | 375 | return 1; |
| 266 | } | 376 | } |
| 267 | 377 | ||
| 378 | static int parse_tracepoint_event(const char **strp, | ||
| 379 | struct perf_counter_attr *attr) | ||
| 380 | { | ||
| 381 | const char *evt_name; | ||
| 382 | char *flags; | ||
| 383 | char sys_name[MAX_EVENT_LENGTH]; | ||
| 384 | char id_buf[4]; | ||
| 385 | int fd; | ||
| 386 | unsigned int sys_length, evt_length; | ||
| 387 | u64 id; | ||
| 388 | char evt_path[MAXPATHLEN]; | ||
| 389 | |||
| 390 | if (valid_debugfs_mount(debugfs_path)) | ||
| 391 | return 0; | ||
| 392 | |||
| 393 | evt_name = strchr(*strp, ':'); | ||
| 394 | if (!evt_name) | ||
| 395 | return 0; | ||
| 396 | |||
| 397 | sys_length = evt_name - *strp; | ||
| 398 | if (sys_length >= MAX_EVENT_LENGTH) | ||
| 399 | return 0; | ||
| 400 | |||
| 401 | strncpy(sys_name, *strp, sys_length); | ||
| 402 | sys_name[sys_length] = '\0'; | ||
| 403 | evt_name = evt_name + 1; | ||
| 404 | |||
| 405 | flags = strchr(evt_name, ':'); | ||
| 406 | if (flags) { | ||
| 407 | *flags = '\0'; | ||
| 408 | flags++; | ||
| 409 | if (!strncmp(flags, "record", strlen(flags))) | ||
| 410 | attr->sample_type |= PERF_SAMPLE_RAW; | ||
| 411 | } | ||
| 412 | |||
| 413 | evt_length = strlen(evt_name); | ||
| 414 | if (evt_length >= MAX_EVENT_LENGTH) | ||
| 415 | return 0; | ||
| 416 | |||
| 417 | snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", debugfs_path, | ||
| 418 | sys_name, evt_name); | ||
| 419 | fd = open(evt_path, O_RDONLY); | ||
| 420 | if (fd < 0) | ||
| 421 | return 0; | ||
| 422 | |||
| 423 | if (read(fd, id_buf, sizeof(id_buf)) < 0) { | ||
| 424 | close(fd); | ||
| 425 | return 0; | ||
| 426 | } | ||
| 427 | close(fd); | ||
| 428 | id = atoll(id_buf); | ||
| 429 | attr->config = id; | ||
| 430 | attr->type = PERF_TYPE_TRACEPOINT; | ||
| 431 | *strp = evt_name + evt_length; | ||
| 432 | return 1; | ||
| 433 | } | ||
| 434 | |||
| 268 | static int check_events(const char *str, unsigned int i) | 435 | static int check_events(const char *str, unsigned int i) |
| 269 | { | 436 | { |
| 270 | int n; | 437 | int n; |
| @@ -374,7 +541,8 @@ parse_event_modifier(const char **strp, struct perf_counter_attr *attr) | |||
| 374 | */ | 541 | */ |
| 375 | static int parse_event_symbols(const char **str, struct perf_counter_attr *attr) | 542 | static int parse_event_symbols(const char **str, struct perf_counter_attr *attr) |
| 376 | { | 543 | { |
| 377 | if (!(parse_raw_event(str, attr) || | 544 | if (!(parse_tracepoint_event(str, attr) || |
| 545 | parse_raw_event(str, attr) || | ||
| 378 | parse_numeric_event(str, attr) || | 546 | parse_numeric_event(str, attr) || |
| 379 | parse_symbolic_event(str, attr) || | 547 | parse_symbolic_event(str, attr) || |
| 380 | parse_generic_hw_event(str, attr))) | 548 | parse_generic_hw_event(str, attr))) |
| @@ -423,6 +591,42 @@ static const char * const event_type_descriptors[] = { | |||
| 423 | }; | 591 | }; |
| 424 | 592 | ||
| 425 | /* | 593 | /* |
| 594 | * Print the events from <debugfs_mount_point>/tracing/events | ||
| 595 | */ | ||
| 596 | |||
| 597 | static void print_tracepoint_events(void) | ||
| 598 | { | ||
| 599 | DIR *sys_dir, *evt_dir; | ||
| 600 | struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent; | ||
| 601 | struct stat st; | ||
| 602 | char evt_path[MAXPATHLEN]; | ||
| 603 | |||
| 604 | if (valid_debugfs_mount(debugfs_path)) | ||
| 605 | return; | ||
| 606 | |||
| 607 | sys_dir = opendir(debugfs_path); | ||
| 608 | if (!sys_dir) | ||
| 609 | goto cleanup; | ||
| 610 | |||
| 611 | for_each_subsystem(sys_dir, sys_dirent, sys_next, evt_path, st) { | ||
| 612 | evt_dir = opendir(evt_path); | ||
| 613 | if (!evt_dir) | ||
| 614 | goto cleanup; | ||
| 615 | for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next, | ||
| 616 | evt_path, st) { | ||
| 617 | snprintf(evt_path, MAXPATHLEN, "%s:%s", | ||
| 618 | sys_dirent.d_name, evt_dirent.d_name); | ||
| 619 | fprintf(stderr, " %-40s [%s]\n", evt_path, | ||
| 620 | event_type_descriptors[PERF_TYPE_TRACEPOINT+1]); | ||
| 621 | } | ||
| 622 | closedir(evt_dir); | ||
| 623 | } | ||
| 624 | |||
| 625 | cleanup: | ||
| 626 | closedir(sys_dir); | ||
| 627 | } | ||
| 628 | |||
| 629 | /* | ||
| 426 | * Print the help text for the event symbols: | 630 | * Print the help text for the event symbols: |
| 427 | */ | 631 | */ |
| 428 | void print_events(void) | 632 | void print_events(void) |
| @@ -436,7 +640,7 @@ void print_events(void) | |||
| 436 | 640 | ||
| 437 | for (i = 0; i < ARRAY_SIZE(event_symbols); i++, syms++) { | 641 | for (i = 0; i < ARRAY_SIZE(event_symbols); i++, syms++) { |
| 438 | type = syms->type + 1; | 642 | type = syms->type + 1; |
| 439 | if (type > ARRAY_SIZE(event_type_descriptors)) | 643 | if (type >= ARRAY_SIZE(event_type_descriptors)) |
| 440 | type = 0; | 644 | type = 0; |
| 441 | 645 | ||
| 442 | if (type != prev_type) | 646 | if (type != prev_type) |
| @@ -472,5 +676,7 @@ void print_events(void) | |||
| 472 | "rNNN"); | 676 | "rNNN"); |
| 473 | fprintf(stderr, "\n"); | 677 | fprintf(stderr, "\n"); |
| 474 | 678 | ||
| 679 | print_tracepoint_events(); | ||
| 680 | |||
| 475 | exit(129); | 681 | exit(129); |
| 476 | } | 682 | } |
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index e3d552908e60..192a962e3a0f 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h | |||
| @@ -3,11 +3,14 @@ | |||
| 3 | * Parse symbolic events/counts passed in as options: | 3 | * Parse symbolic events/counts passed in as options: |
| 4 | */ | 4 | */ |
| 5 | 5 | ||
| 6 | struct option; | ||
| 7 | |||
| 6 | extern int nr_counters; | 8 | extern int nr_counters; |
| 7 | 9 | ||
| 8 | extern struct perf_counter_attr attrs[MAX_COUNTERS]; | 10 | extern struct perf_counter_attr attrs[MAX_COUNTERS]; |
| 9 | 11 | ||
| 10 | extern char *event_name(int ctr); | 12 | extern char *event_name(int ctr); |
| 13 | extern char *__event_name(int type, u64 config); | ||
| 11 | 14 | ||
| 12 | extern int parse_events(const struct option *opt, const char *str, int unset); | 15 | extern int parse_events(const struct option *opt, const char *str, int unset); |
| 13 | 16 | ||
| @@ -15,3 +18,6 @@ extern int parse_events(const struct option *opt, const char *str, int unset); | |||
| 15 | 18 | ||
| 16 | extern void print_events(void); | 19 | extern void print_events(void); |
| 17 | 20 | ||
| 21 | extern char debugfs_path[]; | ||
| 22 | extern int valid_debugfs_mount(const char *debugfs); | ||
| 23 | |||
diff --git a/tools/perf/util/quote.c b/tools/perf/util/quote.c index c6e5dc0dc82f..2726fe40eb5d 100644 --- a/tools/perf/util/quote.c +++ b/tools/perf/util/quote.c | |||
| @@ -318,7 +318,7 @@ char *quote_path_relative(const char *in, int len, | |||
| 318 | strbuf_addch(out, '"'); | 318 | strbuf_addch(out, '"'); |
| 319 | if (prefix) { | 319 | if (prefix) { |
| 320 | int off = 0; | 320 | int off = 0; |
| 321 | while (prefix[off] && off < len && prefix[off] == in[off]) | 321 | while (off < len && prefix[off] && prefix[off] == in[off]) |
| 322 | if (prefix[off] == '/') { | 322 | if (prefix[off] == '/') { |
| 323 | prefix += off + 1; | 323 | prefix += off + 1; |
| 324 | in += off + 1; | 324 | in += off + 1; |
diff --git a/tools/perf/util/string.h b/tools/perf/util/string.h index 3dca2f654cd0..bf39dfadfd24 100644 --- a/tools/perf/util/string.h +++ b/tools/perf/util/string.h | |||
| @@ -5,4 +5,7 @@ | |||
| 5 | 5 | ||
| 6 | int hex2u64(const char *ptr, u64 *val); | 6 | int hex2u64(const char *ptr, u64 *val); |
| 7 | 7 | ||
| 8 | #define _STR(x) #x | ||
| 9 | #define STR(x) _STR(x) | ||
| 10 | |||
| 8 | #endif | 11 | #endif |
diff --git a/tools/perf/util/strlist.c b/tools/perf/util/strlist.c index 025a78edfffe..7ad38171dc2b 100644 --- a/tools/perf/util/strlist.c +++ b/tools/perf/util/strlist.c | |||
| @@ -64,6 +64,7 @@ int strlist__add(struct strlist *self, const char *new_entry) | |||
| 64 | 64 | ||
| 65 | rb_link_node(&sn->rb_node, parent, p); | 65 | rb_link_node(&sn->rb_node, parent, p); |
| 66 | rb_insert_color(&sn->rb_node, &self->entries); | 66 | rb_insert_color(&sn->rb_node, &self->entries); |
| 67 | ++self->nr_entries; | ||
| 67 | 68 | ||
| 68 | return 0; | 69 | return 0; |
| 69 | } | 70 | } |
| @@ -155,8 +156,9 @@ struct strlist *strlist__new(bool dupstr, const char *slist) | |||
| 155 | struct strlist *self = malloc(sizeof(*self)); | 156 | struct strlist *self = malloc(sizeof(*self)); |
| 156 | 157 | ||
| 157 | if (self != NULL) { | 158 | if (self != NULL) { |
| 158 | self->entries = RB_ROOT; | 159 | self->entries = RB_ROOT; |
| 159 | self->dupstr = dupstr; | 160 | self->dupstr = dupstr; |
| 161 | self->nr_entries = 0; | ||
| 160 | if (slist && strlist__parse_list(self, slist) != 0) | 162 | if (slist && strlist__parse_list(self, slist) != 0) |
| 161 | goto out_error; | 163 | goto out_error; |
| 162 | } | 164 | } |
| @@ -182,3 +184,17 @@ void strlist__delete(struct strlist *self) | |||
| 182 | free(self); | 184 | free(self); |
| 183 | } | 185 | } |
| 184 | } | 186 | } |
| 187 | |||
| 188 | struct str_node *strlist__entry(const struct strlist *self, unsigned int idx) | ||
| 189 | { | ||
| 190 | struct rb_node *nd; | ||
| 191 | |||
| 192 | for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { | ||
| 193 | struct str_node *pos = rb_entry(nd, struct str_node, rb_node); | ||
| 194 | |||
| 195 | if (!idx--) | ||
| 196 | return pos; | ||
| 197 | } | ||
| 198 | |||
| 199 | return NULL; | ||
| 200 | } | ||
diff --git a/tools/perf/util/strlist.h b/tools/perf/util/strlist.h index 2fdcfee87586..921818e44a54 100644 --- a/tools/perf/util/strlist.h +++ b/tools/perf/util/strlist.h | |||
| @@ -11,7 +11,8 @@ struct str_node { | |||
| 11 | 11 | ||
| 12 | struct strlist { | 12 | struct strlist { |
| 13 | struct rb_root entries; | 13 | struct rb_root entries; |
| 14 | bool dupstr; | 14 | unsigned int nr_entries; |
| 15 | bool dupstr; | ||
| 15 | }; | 16 | }; |
| 16 | 17 | ||
| 17 | struct strlist *strlist__new(bool dupstr, const char *slist); | 18 | struct strlist *strlist__new(bool dupstr, const char *slist); |
| @@ -21,11 +22,17 @@ void strlist__remove(struct strlist *self, struct str_node *sn); | |||
| 21 | int strlist__load(struct strlist *self, const char *filename); | 22 | int strlist__load(struct strlist *self, const char *filename); |
| 22 | int strlist__add(struct strlist *self, const char *str); | 23 | int strlist__add(struct strlist *self, const char *str); |
| 23 | 24 | ||
| 25 | struct str_node *strlist__entry(const struct strlist *self, unsigned int idx); | ||
| 24 | bool strlist__has_entry(struct strlist *self, const char *entry); | 26 | bool strlist__has_entry(struct strlist *self, const char *entry); |
| 25 | 27 | ||
| 26 | static inline bool strlist__empty(const struct strlist *self) | 28 | static inline bool strlist__empty(const struct strlist *self) |
| 27 | { | 29 | { |
| 28 | return rb_first(&self->entries) == NULL; | 30 | return self->nr_entries == 0; |
| 31 | } | ||
| 32 | |||
| 33 | static inline unsigned int strlist__nr_entries(const struct strlist *self) | ||
| 34 | { | ||
| 35 | return self->nr_entries; | ||
| 29 | } | 36 | } |
| 30 | 37 | ||
| 31 | int strlist__parse_list(struct strlist *self, const char *s); | 38 | int strlist__parse_list(struct strlist *self, const char *s); |
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 4683b67b5ee4..5c0f42e6b33b 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
| @@ -9,6 +9,16 @@ | |||
| 9 | 9 | ||
| 10 | const char *sym_hist_filter; | 10 | const char *sym_hist_filter; |
| 11 | 11 | ||
| 12 | enum dso_origin { | ||
| 13 | DSO__ORIG_KERNEL = 0, | ||
| 14 | DSO__ORIG_JAVA_JIT, | ||
| 15 | DSO__ORIG_FEDORA, | ||
| 16 | DSO__ORIG_UBUNTU, | ||
| 17 | DSO__ORIG_BUILDID, | ||
| 18 | DSO__ORIG_DSO, | ||
| 19 | DSO__ORIG_NOT_FOUND, | ||
| 20 | }; | ||
| 21 | |||
| 12 | static struct symbol *symbol__new(u64 start, u64 len, | 22 | static struct symbol *symbol__new(u64 start, u64 len, |
| 13 | const char *name, unsigned int priv_size, | 23 | const char *name, unsigned int priv_size, |
| 14 | u64 obj_start, int verbose) | 24 | u64 obj_start, int verbose) |
| @@ -65,6 +75,8 @@ struct dso *dso__new(const char *name, unsigned int sym_priv_size) | |||
| 65 | self->syms = RB_ROOT; | 75 | self->syms = RB_ROOT; |
| 66 | self->sym_priv_size = sym_priv_size; | 76 | self->sym_priv_size = sym_priv_size; |
| 67 | self->find_symbol = dso__find_symbol; | 77 | self->find_symbol = dso__find_symbol; |
| 78 | self->slen_calculated = 0; | ||
| 79 | self->origin = DSO__ORIG_NOT_FOUND; | ||
| 68 | } | 80 | } |
| 69 | 81 | ||
| 70 | return self; | 82 | return self; |
| @@ -373,36 +385,61 @@ static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, | |||
| 373 | idx < nr_entries; \ | 385 | idx < nr_entries; \ |
| 374 | ++idx, pos = gelf_getrela(reldata, idx, &pos_mem)) | 386 | ++idx, pos = gelf_getrela(reldata, idx, &pos_mem)) |
| 375 | 387 | ||
| 376 | static int dso__synthesize_plt_symbols(struct dso *self, Elf *elf, | 388 | /* |
| 377 | GElf_Ehdr *ehdr, Elf_Scn *scn_dynsym, | 389 | * We need to check if we have a .dynsym, so that we can handle the |
| 378 | GElf_Shdr *shdr_dynsym, | 390 | * .plt, synthesizing its symbols, that aren't on the symtabs (be it |
| 379 | size_t dynsym_idx, int verbose) | 391 | * .dynsym or .symtab). |
| 392 | * And always look at the original dso, not at debuginfo packages, that | ||
| 393 | * have the PLT data stripped out (shdr_rel_plt.sh_type == SHT_NOBITS). | ||
| 394 | */ | ||
| 395 | static int dso__synthesize_plt_symbols(struct dso *self, int verbose) | ||
| 380 | { | 396 | { |
| 381 | uint32_t nr_rel_entries, idx; | 397 | uint32_t nr_rel_entries, idx; |
| 382 | GElf_Sym sym; | 398 | GElf_Sym sym; |
| 383 | u64 plt_offset; | 399 | u64 plt_offset; |
| 384 | GElf_Shdr shdr_plt; | 400 | GElf_Shdr shdr_plt; |
| 385 | struct symbol *f; | 401 | struct symbol *f; |
| 386 | GElf_Shdr shdr_rel_plt; | 402 | GElf_Shdr shdr_rel_plt, shdr_dynsym; |
| 387 | Elf_Data *reldata, *syms, *symstrs; | 403 | Elf_Data *reldata, *syms, *symstrs; |
| 388 | Elf_Scn *scn_plt_rel, *scn_symstrs; | 404 | Elf_Scn *scn_plt_rel, *scn_symstrs, *scn_dynsym; |
| 405 | size_t dynsym_idx; | ||
| 406 | GElf_Ehdr ehdr; | ||
| 389 | char sympltname[1024]; | 407 | char sympltname[1024]; |
| 390 | int nr = 0, symidx; | 408 | Elf *elf; |
| 409 | int nr = 0, symidx, fd, err = 0; | ||
| 410 | |||
| 411 | fd = open(self->name, O_RDONLY); | ||
| 412 | if (fd < 0) | ||
| 413 | goto out; | ||
| 391 | 414 | ||
| 392 | scn_plt_rel = elf_section_by_name(elf, ehdr, &shdr_rel_plt, | 415 | elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); |
| 416 | if (elf == NULL) | ||
| 417 | goto out_close; | ||
| 418 | |||
| 419 | if (gelf_getehdr(elf, &ehdr) == NULL) | ||
| 420 | goto out_elf_end; | ||
| 421 | |||
| 422 | scn_dynsym = elf_section_by_name(elf, &ehdr, &shdr_dynsym, | ||
| 423 | ".dynsym", &dynsym_idx); | ||
| 424 | if (scn_dynsym == NULL) | ||
| 425 | goto out_elf_end; | ||
| 426 | |||
| 427 | scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt, | ||
| 393 | ".rela.plt", NULL); | 428 | ".rela.plt", NULL); |
| 394 | if (scn_plt_rel == NULL) { | 429 | if (scn_plt_rel == NULL) { |
| 395 | scn_plt_rel = elf_section_by_name(elf, ehdr, &shdr_rel_plt, | 430 | scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt, |
| 396 | ".rel.plt", NULL); | 431 | ".rel.plt", NULL); |
| 397 | if (scn_plt_rel == NULL) | 432 | if (scn_plt_rel == NULL) |
| 398 | return 0; | 433 | goto out_elf_end; |
| 399 | } | 434 | } |
| 400 | 435 | ||
| 436 | err = -1; | ||
| 437 | |||
| 401 | if (shdr_rel_plt.sh_link != dynsym_idx) | 438 | if (shdr_rel_plt.sh_link != dynsym_idx) |
| 402 | return 0; | 439 | goto out_elf_end; |
| 403 | 440 | ||
| 404 | if (elf_section_by_name(elf, ehdr, &shdr_plt, ".plt", NULL) == NULL) | 441 | if (elf_section_by_name(elf, &ehdr, &shdr_plt, ".plt", NULL) == NULL) |
| 405 | return 0; | 442 | goto out_elf_end; |
| 406 | 443 | ||
| 407 | /* | 444 | /* |
| 408 | * Fetch the relocation section to find the indexes to the GOT | 445 | * Fetch the relocation section to find the indexes to the GOT |
| @@ -410,19 +447,19 @@ static int dso__synthesize_plt_symbols(struct dso *self, Elf *elf, | |||
| 410 | */ | 447 | */ |
| 411 | reldata = elf_getdata(scn_plt_rel, NULL); | 448 | reldata = elf_getdata(scn_plt_rel, NULL); |
| 412 | if (reldata == NULL) | 449 | if (reldata == NULL) |
| 413 | return -1; | 450 | goto out_elf_end; |
| 414 | 451 | ||
| 415 | syms = elf_getdata(scn_dynsym, NULL); | 452 | syms = elf_getdata(scn_dynsym, NULL); |
| 416 | if (syms == NULL) | 453 | if (syms == NULL) |
| 417 | return -1; | 454 | goto out_elf_end; |
| 418 | 455 | ||
| 419 | scn_symstrs = elf_getscn(elf, shdr_dynsym->sh_link); | 456 | scn_symstrs = elf_getscn(elf, shdr_dynsym.sh_link); |
| 420 | if (scn_symstrs == NULL) | 457 | if (scn_symstrs == NULL) |
| 421 | return -1; | 458 | goto out_elf_end; |
| 422 | 459 | ||
| 423 | symstrs = elf_getdata(scn_symstrs, NULL); | 460 | symstrs = elf_getdata(scn_symstrs, NULL); |
| 424 | if (symstrs == NULL) | 461 | if (symstrs == NULL) |
| 425 | return -1; | 462 | goto out_elf_end; |
| 426 | 463 | ||
| 427 | nr_rel_entries = shdr_rel_plt.sh_size / shdr_rel_plt.sh_entsize; | 464 | nr_rel_entries = shdr_rel_plt.sh_size / shdr_rel_plt.sh_entsize; |
| 428 | plt_offset = shdr_plt.sh_offset; | 465 | plt_offset = shdr_plt.sh_offset; |
| @@ -441,7 +478,7 @@ static int dso__synthesize_plt_symbols(struct dso *self, Elf *elf, | |||
| 441 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, | 478 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, |
| 442 | sympltname, self->sym_priv_size, 0, verbose); | 479 | sympltname, self->sym_priv_size, 0, verbose); |
| 443 | if (!f) | 480 | if (!f) |
| 444 | return -1; | 481 | goto out_elf_end; |
| 445 | 482 | ||
| 446 | dso__insert_symbol(self, f); | 483 | dso__insert_symbol(self, f); |
| 447 | ++nr; | 484 | ++nr; |
| @@ -459,19 +496,25 @@ static int dso__synthesize_plt_symbols(struct dso *self, Elf *elf, | |||
| 459 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, | 496 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, |
| 460 | sympltname, self->sym_priv_size, 0, verbose); | 497 | sympltname, self->sym_priv_size, 0, verbose); |
| 461 | if (!f) | 498 | if (!f) |
| 462 | return -1; | 499 | goto out_elf_end; |
| 463 | 500 | ||
| 464 | dso__insert_symbol(self, f); | 501 | dso__insert_symbol(self, f); |
| 465 | ++nr; | 502 | ++nr; |
| 466 | } | 503 | } |
| 467 | } else { | ||
| 468 | /* | ||
| 469 | * TODO: There are still one more shdr_rel_plt.sh_type | ||
| 470 | * I have to investigate, but probably should be ignored. | ||
| 471 | */ | ||
| 472 | } | 504 | } |
| 473 | 505 | ||
| 474 | return nr; | 506 | err = 0; |
| 507 | out_elf_end: | ||
| 508 | elf_end(elf); | ||
| 509 | out_close: | ||
| 510 | close(fd); | ||
| 511 | |||
| 512 | if (err == 0) | ||
| 513 | return nr; | ||
| 514 | out: | ||
| 515 | fprintf(stderr, "%s: problems reading %s PLT info.\n", | ||
| 516 | __func__, self->name); | ||
| 517 | return 0; | ||
| 475 | } | 518 | } |
| 476 | 519 | ||
| 477 | static int dso__load_sym(struct dso *self, int fd, const char *name, | 520 | static int dso__load_sym(struct dso *self, int fd, const char *name, |
| @@ -485,10 +528,9 @@ static int dso__load_sym(struct dso *self, int fd, const char *name, | |||
| 485 | GElf_Shdr shdr; | 528 | GElf_Shdr shdr; |
| 486 | Elf_Data *syms; | 529 | Elf_Data *syms; |
| 487 | GElf_Sym sym; | 530 | GElf_Sym sym; |
| 488 | Elf_Scn *sec, *sec_dynsym, *sec_strndx; | 531 | Elf_Scn *sec, *sec_strndx; |
| 489 | Elf *elf; | 532 | Elf *elf; |
| 490 | size_t dynsym_idx; | 533 | int nr = 0, kernel = !strcmp("[kernel]", self->name); |
| 491 | int nr = 0; | ||
| 492 | 534 | ||
| 493 | elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); | 535 | elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); |
| 494 | if (elf == NULL) { | 536 | if (elf == NULL) { |
| @@ -504,32 +546,11 @@ static int dso__load_sym(struct dso *self, int fd, const char *name, | |||
| 504 | goto out_elf_end; | 546 | goto out_elf_end; |
| 505 | } | 547 | } |
| 506 | 548 | ||
| 507 | /* | ||
| 508 | * We need to check if we have a .dynsym, so that we can handle the | ||
| 509 | * .plt, synthesizing its symbols, that aren't on the symtabs (be it | ||
| 510 | * .dynsym or .symtab) | ||
| 511 | */ | ||
| 512 | sec_dynsym = elf_section_by_name(elf, &ehdr, &shdr, | ||
| 513 | ".dynsym", &dynsym_idx); | ||
| 514 | if (sec_dynsym != NULL) { | ||
| 515 | nr = dso__synthesize_plt_symbols(self, elf, &ehdr, | ||
| 516 | sec_dynsym, &shdr, | ||
| 517 | dynsym_idx, verbose); | ||
| 518 | if (nr < 0) | ||
| 519 | goto out_elf_end; | ||
| 520 | } | ||
| 521 | |||
| 522 | /* | ||
| 523 | * But if we have a full .symtab (that is a superset of .dynsym) we | ||
| 524 | * should add the symbols not in the .dynsyn | ||
| 525 | */ | ||
| 526 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".symtab", NULL); | 549 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".symtab", NULL); |
| 527 | if (sec == NULL) { | 550 | if (sec == NULL) { |
| 528 | if (sec_dynsym == NULL) | 551 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".dynsym", NULL); |
| 552 | if (sec == NULL) | ||
| 529 | goto out_elf_end; | 553 | goto out_elf_end; |
| 530 | |||
| 531 | sec = sec_dynsym; | ||
| 532 | gelf_getshdr(sec, &shdr); | ||
| 533 | } | 554 | } |
| 534 | 555 | ||
| 535 | syms = elf_getdata(sec, NULL); | 556 | syms = elf_getdata(sec, NULL); |
| @@ -549,18 +570,23 @@ static int dso__load_sym(struct dso *self, int fd, const char *name, | |||
| 549 | goto out_elf_end; | 570 | goto out_elf_end; |
| 550 | 571 | ||
| 551 | secstrs = elf_getdata(sec_strndx, NULL); | 572 | secstrs = elf_getdata(sec_strndx, NULL); |
| 552 | if (symstrs == NULL) | 573 | if (secstrs == NULL) |
| 553 | goto out_elf_end; | 574 | goto out_elf_end; |
| 554 | 575 | ||
| 555 | nr_syms = shdr.sh_size / shdr.sh_entsize; | 576 | nr_syms = shdr.sh_size / shdr.sh_entsize; |
| 556 | 577 | ||
| 557 | memset(&sym, 0, sizeof(sym)); | 578 | memset(&sym, 0, sizeof(sym)); |
| 558 | self->adjust_symbols = (ehdr.e_type == ET_EXEC || | 579 | if (!kernel) { |
| 580 | self->adjust_symbols = (ehdr.e_type == ET_EXEC || | ||
| 559 | elf_section_by_name(elf, &ehdr, &shdr, | 581 | elf_section_by_name(elf, &ehdr, &shdr, |
| 560 | ".gnu.prelink_undo", | 582 | ".gnu.prelink_undo", |
| 561 | NULL) != NULL); | 583 | NULL) != NULL); |
| 584 | } else self->adjust_symbols = 0; | ||
| 585 | |||
| 562 | elf_symtab__for_each_symbol(syms, nr_syms, index, sym) { | 586 | elf_symtab__for_each_symbol(syms, nr_syms, index, sym) { |
| 563 | struct symbol *f; | 587 | struct symbol *f; |
| 588 | const char *name; | ||
| 589 | char *demangled; | ||
| 564 | u64 obj_start; | 590 | u64 obj_start; |
| 565 | struct section *section = NULL; | 591 | struct section *section = NULL; |
| 566 | int is_label = elf_sym__is_label(&sym); | 592 | int is_label = elf_sym__is_label(&sym); |
| @@ -599,10 +625,19 @@ static int dso__load_sym(struct dso *self, int fd, const char *name, | |||
| 599 | goto out_elf_end; | 625 | goto out_elf_end; |
| 600 | } | 626 | } |
| 601 | } | 627 | } |
| 628 | /* | ||
| 629 | * We need to figure out if the object was created from C++ sources | ||
| 630 | * DWARF DW_compile_unit has this, but we don't always have access | ||
| 631 | * to it... | ||
| 632 | */ | ||
| 633 | name = elf_sym__name(&sym, symstrs); | ||
| 634 | demangled = bfd_demangle(NULL, name, DMGL_PARAMS | DMGL_ANSI); | ||
| 635 | if (demangled != NULL) | ||
| 636 | name = demangled; | ||
| 602 | 637 | ||
| 603 | f = symbol__new(sym.st_value, sym.st_size, | 638 | f = symbol__new(sym.st_value, sym.st_size, name, |
| 604 | elf_sym__name(&sym, symstrs), | ||
| 605 | self->sym_priv_size, obj_start, verbose); | 639 | self->sym_priv_size, obj_start, verbose); |
| 640 | free(demangled); | ||
| 606 | if (!f) | 641 | if (!f) |
| 607 | goto out_elf_end; | 642 | goto out_elf_end; |
| 608 | 643 | ||
| @@ -622,11 +657,85 @@ out_close: | |||
| 622 | return err; | 657 | return err; |
| 623 | } | 658 | } |
| 624 | 659 | ||
| 660 | #define BUILD_ID_SIZE 128 | ||
| 661 | |||
| 662 | static char *dso__read_build_id(struct dso *self, int verbose) | ||
| 663 | { | ||
| 664 | int i; | ||
| 665 | GElf_Ehdr ehdr; | ||
| 666 | GElf_Shdr shdr; | ||
| 667 | Elf_Data *build_id_data; | ||
| 668 | Elf_Scn *sec; | ||
| 669 | char *build_id = NULL, *bid; | ||
| 670 | unsigned char *raw; | ||
| 671 | Elf *elf; | ||
| 672 | int fd = open(self->name, O_RDONLY); | ||
| 673 | |||
| 674 | if (fd < 0) | ||
| 675 | goto out; | ||
| 676 | |||
| 677 | elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); | ||
| 678 | if (elf == NULL) { | ||
| 679 | if (verbose) | ||
| 680 | fprintf(stderr, "%s: cannot read %s ELF file.\n", | ||
| 681 | __func__, self->name); | ||
| 682 | goto out_close; | ||
| 683 | } | ||
| 684 | |||
| 685 | if (gelf_getehdr(elf, &ehdr) == NULL) { | ||
| 686 | if (verbose) | ||
| 687 | fprintf(stderr, "%s: cannot get elf header.\n", __func__); | ||
| 688 | goto out_elf_end; | ||
| 689 | } | ||
| 690 | |||
| 691 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".note.gnu.build-id", NULL); | ||
| 692 | if (sec == NULL) | ||
| 693 | goto out_elf_end; | ||
| 694 | |||
| 695 | build_id_data = elf_getdata(sec, NULL); | ||
| 696 | if (build_id_data == NULL) | ||
| 697 | goto out_elf_end; | ||
| 698 | build_id = malloc(BUILD_ID_SIZE); | ||
| 699 | if (build_id == NULL) | ||
| 700 | goto out_elf_end; | ||
| 701 | raw = build_id_data->d_buf + 16; | ||
| 702 | bid = build_id; | ||
| 703 | |||
| 704 | for (i = 0; i < 20; ++i) { | ||
| 705 | sprintf(bid, "%02x", *raw); | ||
| 706 | ++raw; | ||
| 707 | bid += 2; | ||
| 708 | } | ||
| 709 | if (verbose >= 2) | ||
| 710 | printf("%s(%s): %s\n", __func__, self->name, build_id); | ||
| 711 | out_elf_end: | ||
| 712 | elf_end(elf); | ||
| 713 | out_close: | ||
| 714 | close(fd); | ||
| 715 | out: | ||
| 716 | return build_id; | ||
| 717 | } | ||
| 718 | |||
| 719 | char dso__symtab_origin(const struct dso *self) | ||
| 720 | { | ||
| 721 | static const char origin[] = { | ||
| 722 | [DSO__ORIG_KERNEL] = 'k', | ||
| 723 | [DSO__ORIG_JAVA_JIT] = 'j', | ||
| 724 | [DSO__ORIG_FEDORA] = 'f', | ||
| 725 | [DSO__ORIG_UBUNTU] = 'u', | ||
| 726 | [DSO__ORIG_BUILDID] = 'b', | ||
| 727 | [DSO__ORIG_DSO] = 'd', | ||
| 728 | }; | ||
| 729 | |||
| 730 | if (self == NULL || self->origin == DSO__ORIG_NOT_FOUND) | ||
| 731 | return '!'; | ||
| 732 | return origin[self->origin]; | ||
| 733 | } | ||
| 734 | |||
| 625 | int dso__load(struct dso *self, symbol_filter_t filter, int verbose) | 735 | int dso__load(struct dso *self, symbol_filter_t filter, int verbose) |
| 626 | { | 736 | { |
| 627 | int size = strlen(self->name) + sizeof("/usr/lib/debug%s.debug"); | 737 | int size = PATH_MAX; |
| 628 | char *name = malloc(size); | 738 | char *name = malloc(size), *build_id = NULL; |
| 629 | int variant = 0; | ||
| 630 | int ret = -1; | 739 | int ret = -1; |
| 631 | int fd; | 740 | int fd; |
| 632 | 741 | ||
| @@ -635,26 +744,43 @@ int dso__load(struct dso *self, symbol_filter_t filter, int verbose) | |||
| 635 | 744 | ||
| 636 | self->adjust_symbols = 0; | 745 | self->adjust_symbols = 0; |
| 637 | 746 | ||
| 638 | if (strncmp(self->name, "/tmp/perf-", 10) == 0) | 747 | if (strncmp(self->name, "/tmp/perf-", 10) == 0) { |
| 639 | return dso__load_perf_map(self, filter, verbose); | 748 | ret = dso__load_perf_map(self, filter, verbose); |
| 749 | self->origin = ret > 0 ? DSO__ORIG_JAVA_JIT : | ||
| 750 | DSO__ORIG_NOT_FOUND; | ||
| 751 | return ret; | ||
| 752 | } | ||
| 753 | |||
| 754 | self->origin = DSO__ORIG_FEDORA - 1; | ||
| 640 | 755 | ||
| 641 | more: | 756 | more: |
| 642 | do { | 757 | do { |
| 643 | switch (variant) { | 758 | self->origin++; |
| 644 | case 0: /* Fedora */ | 759 | switch (self->origin) { |
| 760 | case DSO__ORIG_FEDORA: | ||
| 645 | snprintf(name, size, "/usr/lib/debug%s.debug", self->name); | 761 | snprintf(name, size, "/usr/lib/debug%s.debug", self->name); |
| 646 | break; | 762 | break; |
| 647 | case 1: /* Ubuntu */ | 763 | case DSO__ORIG_UBUNTU: |
| 648 | snprintf(name, size, "/usr/lib/debug%s", self->name); | 764 | snprintf(name, size, "/usr/lib/debug%s", self->name); |
| 649 | break; | 765 | break; |
| 650 | case 2: /* Sane people */ | 766 | case DSO__ORIG_BUILDID: |
| 767 | build_id = dso__read_build_id(self, verbose); | ||
| 768 | if (build_id != NULL) { | ||
| 769 | snprintf(name, size, | ||
| 770 | "/usr/lib/debug/.build-id/%.2s/%s.debug", | ||
| 771 | build_id, build_id + 2); | ||
| 772 | free(build_id); | ||
| 773 | break; | ||
| 774 | } | ||
| 775 | self->origin++; | ||
| 776 | /* Fall thru */ | ||
| 777 | case DSO__ORIG_DSO: | ||
| 651 | snprintf(name, size, "%s", self->name); | 778 | snprintf(name, size, "%s", self->name); |
| 652 | break; | 779 | break; |
| 653 | 780 | ||
| 654 | default: | 781 | default: |
| 655 | goto out; | 782 | goto out; |
| 656 | } | 783 | } |
| 657 | variant++; | ||
| 658 | 784 | ||
| 659 | fd = open(name, O_RDONLY); | 785 | fd = open(name, O_RDONLY); |
| 660 | } while (fd < 0); | 786 | } while (fd < 0); |
| @@ -668,8 +794,15 @@ more: | |||
| 668 | if (!ret) | 794 | if (!ret) |
| 669 | goto more; | 795 | goto more; |
| 670 | 796 | ||
| 797 | if (ret > 0) { | ||
| 798 | int nr_plt = dso__synthesize_plt_symbols(self, verbose); | ||
| 799 | if (nr_plt > 0) | ||
| 800 | ret += nr_plt; | ||
| 801 | } | ||
| 671 | out: | 802 | out: |
| 672 | free(name); | 803 | free(name); |
| 804 | if (ret < 0 && strstr(self->name, " (deleted)") != NULL) | ||
| 805 | return 0; | ||
| 673 | return ret; | 806 | return ret; |
| 674 | } | 807 | } |
| 675 | 808 | ||
| @@ -785,6 +918,9 @@ int dso__load_kernel(struct dso *self, const char *vmlinux, | |||
| 785 | if (err <= 0) | 918 | if (err <= 0) |
| 786 | err = dso__load_kallsyms(self, filter, verbose); | 919 | err = dso__load_kallsyms(self, filter, verbose); |
| 787 | 920 | ||
| 921 | if (err > 0) | ||
| 922 | self->origin = DSO__ORIG_KERNEL; | ||
| 923 | |||
| 788 | return err; | 924 | return err; |
| 789 | } | 925 | } |
| 790 | 926 | ||
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 7918cffb23cd..b53bf0125c1b 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
| @@ -7,6 +7,30 @@ | |||
| 7 | #include <linux/rbtree.h> | 7 | #include <linux/rbtree.h> |
| 8 | #include "module.h" | 8 | #include "module.h" |
| 9 | 9 | ||
| 10 | #ifdef HAVE_CPLUS_DEMANGLE | ||
| 11 | extern char *cplus_demangle(const char *, int); | ||
| 12 | |||
| 13 | static inline char *bfd_demangle(void __used *v, const char *c, int i) | ||
| 14 | { | ||
| 15 | return cplus_demangle(c, i); | ||
| 16 | } | ||
| 17 | #else | ||
| 18 | #ifdef NO_DEMANGLE | ||
| 19 | static inline char *bfd_demangle(void __used *v, const char __used *c, | ||
| 20 | int __used i) | ||
| 21 | { | ||
| 22 | return NULL; | ||
| 23 | } | ||
| 24 | #else | ||
| 25 | #include <bfd.h> | ||
| 26 | #endif | ||
| 27 | #endif | ||
| 28 | |||
| 29 | #ifndef DMGL_PARAMS | ||
| 30 | #define DMGL_PARAMS (1 << 0) /* Include function args */ | ||
| 31 | #define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */ | ||
| 32 | #endif | ||
| 33 | |||
| 10 | struct symbol { | 34 | struct symbol { |
| 11 | struct rb_node rb_node; | 35 | struct rb_node rb_node; |
| 12 | u64 start; | 36 | u64 start; |
| @@ -25,6 +49,8 @@ struct dso { | |||
| 25 | struct symbol *(*find_symbol)(struct dso *, u64 ip); | 49 | struct symbol *(*find_symbol)(struct dso *, u64 ip); |
| 26 | unsigned int sym_priv_size; | 50 | unsigned int sym_priv_size; |
| 27 | unsigned char adjust_symbols; | 51 | unsigned char adjust_symbols; |
| 52 | unsigned char slen_calculated; | ||
| 53 | unsigned char origin; | ||
| 28 | char name[0]; | 54 | char name[0]; |
| 29 | }; | 55 | }; |
| 30 | 56 | ||
| @@ -48,6 +74,7 @@ int dso__load_modules(struct dso *self, symbol_filter_t filter, int verbose); | |||
| 48 | int dso__load(struct dso *self, symbol_filter_t filter, int verbose); | 74 | int dso__load(struct dso *self, symbol_filter_t filter, int verbose); |
| 49 | 75 | ||
| 50 | size_t dso__fprintf(struct dso *self, FILE *fp); | 76 | size_t dso__fprintf(struct dso *self, FILE *fp); |
| 77 | char dso__symtab_origin(const struct dso *self); | ||
| 51 | 78 | ||
| 52 | void symbol__init(void); | 79 | void symbol__init(void); |
| 53 | #endif /* _PERF_SYMBOL_ */ | 80 | #endif /* _PERF_SYMBOL_ */ |
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index b4be6071c105..68fe157d72fb 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h | |||
| @@ -50,6 +50,7 @@ | |||
| 50 | #include <unistd.h> | 50 | #include <unistd.h> |
| 51 | #include <stdio.h> | 51 | #include <stdio.h> |
| 52 | #include <sys/stat.h> | 52 | #include <sys/stat.h> |
| 53 | #include <sys/statfs.h> | ||
| 53 | #include <fcntl.h> | 54 | #include <fcntl.h> |
| 54 | #include <stddef.h> | 55 | #include <stddef.h> |
| 55 | #include <stdlib.h> | 56 | #include <stdlib.h> |
| @@ -80,6 +81,7 @@ | |||
| 80 | #include <netdb.h> | 81 | #include <netdb.h> |
| 81 | #include <pwd.h> | 82 | #include <pwd.h> |
| 82 | #include <inttypes.h> | 83 | #include <inttypes.h> |
| 84 | #include "../../../include/linux/magic.h" | ||
| 83 | 85 | ||
| 84 | #ifndef NO_ICONV | 86 | #ifndef NO_ICONV |
| 85 | #include <iconv.h> | 87 | #include <iconv.h> |
