aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorGrant Likely <grant.likely@secretlab.ca>2010-12-30 00:20:30 -0500
committerGrant Likely <grant.likely@secretlab.ca>2010-12-30 00:21:47 -0500
commitd392da5207352f09030e95d9ea335a4225667ec0 (patch)
tree7d6cd1932afcad0a5619a5c504a6d93ca318187c /tools
parente39d5ef678045d61812c1401f04fe8edb14d6359 (diff)
parent387c31c7e5c9805b0aef8833d1731a5fe7bdea14 (diff)
Merge v2.6.37-rc8 into powerpc/next
Diffstat (limited to 'tools')
-rw-r--r--tools/perf/Documentation/perf-annotate.txt11
-rw-r--r--tools/perf/Documentation/perf-list.txt17
-rw-r--r--tools/perf/Documentation/perf-probe.txt18
-rw-r--r--tools/perf/Documentation/perf-record.txt4
-rw-r--r--tools/perf/Documentation/perf-report.txt7
-rw-r--r--tools/perf/Documentation/perf-trace.txt57
-rw-r--r--tools/perf/Makefile87
-rw-r--r--tools/perf/builtin-annotate.c28
-rw-r--r--tools/perf/builtin-buildid-list.c3
-rw-r--r--tools/perf/builtin-probe.c83
-rw-r--r--tools/perf/builtin-record.c39
-rw-r--r--tools/perf/builtin-report.c45
-rw-r--r--tools/perf/builtin-timechart.c4
-rw-r--r--tools/perf/builtin-top.c12
-rw-r--r--tools/perf/builtin-trace.c235
-rw-r--r--tools/perf/feature-tests.mak13
-rw-r--r--tools/perf/perf.h12
-rw-r--r--tools/perf/scripts/perl/bin/failed-syscalls-record2
-rw-r--r--tools/perf/scripts/perl/bin/failed-syscalls-report2
-rw-r--r--tools/perf/scripts/perl/bin/rw-by-file-record2
-rw-r--r--tools/perf/scripts/perl/bin/rw-by-file-report2
-rw-r--r--tools/perf/scripts/perl/bin/rw-by-pid-record2
-rw-r--r--tools/perf/scripts/perl/bin/rw-by-pid-report2
-rw-r--r--tools/perf/scripts/perl/bin/rwtop-record2
-rw-r--r--tools/perf/scripts/perl/bin/rwtop-report2
-rw-r--r--tools/perf/scripts/perl/bin/wakeup-latency-record2
-rw-r--r--tools/perf/scripts/perl/bin/wakeup-latency-report2
-rw-r--r--tools/perf/scripts/perl/bin/workqueue-stats-record2
-rw-r--r--tools/perf/scripts/perl/bin/workqueue-stats-report2
-rw-r--r--tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py58
-rw-r--r--tools/perf/scripts/python/bin/failed-syscalls-by-pid-record2
-rw-r--r--tools/perf/scripts/python/bin/failed-syscalls-by-pid-report2
-rw-r--r--tools/perf/scripts/python/bin/futex-contention-record2
-rw-r--r--tools/perf/scripts/python/bin/futex-contention-report4
-rw-r--r--tools/perf/scripts/python/bin/netdev-times-record8
-rw-r--r--tools/perf/scripts/python/bin/netdev-times-report5
-rw-r--r--tools/perf/scripts/python/bin/sched-migration-record2
-rw-r--r--tools/perf/scripts/python/bin/sched-migration-report2
-rw-r--r--tools/perf/scripts/python/bin/sctop-record2
-rw-r--r--tools/perf/scripts/python/bin/sctop-report2
-rw-r--r--tools/perf/scripts/python/bin/syscall-counts-by-pid-record2
-rw-r--r--tools/perf/scripts/python/bin/syscall-counts-by-pid-report2
-rw-r--r--tools/perf/scripts/python/bin/syscall-counts-record2
-rw-r--r--tools/perf/scripts/python/bin/syscall-counts-report2
-rw-r--r--tools/perf/scripts/python/failed-syscalls-by-pid.py21
-rw-r--r--tools/perf/scripts/python/futex-contention.py50
-rw-r--r--tools/perf/scripts/python/netdev-times.py464
-rw-r--r--tools/perf/scripts/python/sctop.py9
-rw-r--r--tools/perf/scripts/python/syscall-counts-by-pid.py21
-rw-r--r--tools/perf/scripts/python/syscall-counts.py5
-rw-r--r--tools/perf/util/cache.h2
-rw-r--r--tools/perf/util/callchain.c98
-rw-r--r--tools/perf/util/callchain.h26
-rw-r--r--tools/perf/util/debug.c6
-rw-r--r--tools/perf/util/debug.h11
-rw-r--r--tools/perf/util/header.c21
-rw-r--r--tools/perf/util/hist.c20
-rw-r--r--tools/perf/util/hist.h3
-rw-r--r--tools/perf/util/include/linux/list.h8
-rw-r--r--tools/perf/util/include/linux/types.h12
-rw-r--r--tools/perf/util/map.h10
-rw-r--r--tools/perf/util/newt.c1568
-rw-r--r--tools/perf/util/path.c3
-rw-r--r--tools/perf/util/probe-event.c210
-rw-r--r--tools/perf/util/probe-event.h16
-rw-r--r--tools/perf/util/probe-finder.c703
-rw-r--r--tools/perf/util/probe-finder.h31
-rw-r--r--tools/perf/util/pstack.h2
-rw-r--r--tools/perf/util/sort.c6
-rw-r--r--tools/perf/util/sort.h2
-rw-r--r--tools/perf/util/string.c2
-rw-r--r--tools/perf/util/symbol.c128
-rw-r--r--tools/perf/util/symbol.h7
-rw-r--r--tools/perf/util/trace-event-scripting.c4
-rw-r--r--tools/perf/util/ui/browser.c337
-rw-r--r--tools/perf/util/ui/browser.h51
-rw-r--r--tools/perf/util/ui/browsers/annotate.c237
-rw-r--r--tools/perf/util/ui/browsers/hists.c1013
-rw-r--r--tools/perf/util/ui/browsers/map.c155
-rw-r--r--tools/perf/util/ui/browsers/map.h6
-rw-r--r--tools/perf/util/ui/helpline.c69
-rw-r--r--tools/perf/util/ui/helpline.h11
-rw-r--r--tools/perf/util/ui/libslang.h27
-rw-r--r--tools/perf/util/ui/progress.c60
-rw-r--r--tools/perf/util/ui/progress.h11
-rw-r--r--tools/perf/util/ui/setup.c42
-rw-r--r--tools/perf/util/ui/util.c113
-rw-r--r--tools/perf/util/ui/util.h10
-rw-r--r--tools/perf/util/util.h13
89 files changed, 4366 insertions, 2054 deletions
diff --git a/tools/perf/Documentation/perf-annotate.txt b/tools/perf/Documentation/perf-annotate.txt
index 5164a655c39f..b2c63309a651 100644
--- a/tools/perf/Documentation/perf-annotate.txt
+++ b/tools/perf/Documentation/perf-annotate.txt
@@ -8,7 +8,7 @@ perf-annotate - Read perf.data (created by perf record) and display annotated co
8SYNOPSIS 8SYNOPSIS
9-------- 9--------
10[verse] 10[verse]
11'perf annotate' [-i <file> | --input=file] symbol_name 11'perf annotate' [-i <file> | --input=file] [symbol_name]
12 12
13DESCRIPTION 13DESCRIPTION
14----------- 14-----------
@@ -24,6 +24,13 @@ OPTIONS
24--input=:: 24--input=::
25 Input file name. (default: perf.data) 25 Input file name. (default: perf.data)
26 26
27--stdio:: Use the stdio interface.
28
29--tui:: Use the TUI interface Use of --tui requires a tty, if one is not
30 present, as when piping to other commands, the stdio interface is
31 used. This interfaces starts by centering on the line with more
32 samples, TAB/UNTAB cycles thru the lines with more samples.
33
27SEE ALSO 34SEE ALSO
28-------- 35--------
29linkperf:perf-record[1] 36linkperf:perf-record[1], linkperf:perf-report[1]
diff --git a/tools/perf/Documentation/perf-list.txt b/tools/perf/Documentation/perf-list.txt
index 43e3dd284b90..399751befeed 100644
--- a/tools/perf/Documentation/perf-list.txt
+++ b/tools/perf/Documentation/perf-list.txt
@@ -15,6 +15,23 @@ DESCRIPTION
15This command displays the symbolic event types which can be selected in the 15This command displays the symbolic event types which can be selected in the
16various perf commands with the -e option. 16various perf commands with the -e option.
17 17
18EVENT MODIFIERS
19---------------
20
21Events can optionally have a modifer by appending a colon and one or
22more modifiers. Modifiers allow the user to restrict when events are
23counted with 'u' for user-space, 'k' for kernel, 'h' for hypervisor.
24
25The 'p' modifier can be used for specifying how precise the instruction
26address should be. The 'p' modifier is currently only implemented for
27Intel PEBS and can be specified multiple times:
28 0 - SAMPLE_IP can have arbitrary skid
29 1 - SAMPLE_IP must have constant skid
30 2 - SAMPLE_IP requested to have 0 skid
31 3 - SAMPLE_IP must have 0 skid
32
33The PEBS implementation now supports up to 2.
34
18RAW HARDWARE EVENT DESCRIPTOR 35RAW HARDWARE EVENT DESCRIPTOR
19----------------------------- 36-----------------------------
20Even when an event is not available in a symbolic form within perf right now, 37Even when an event is not available in a symbolic form within perf right now,
diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt
index 27d52dae5a43..62de1b7f4e76 100644
--- a/tools/perf/Documentation/perf-probe.txt
+++ b/tools/perf/Documentation/perf-probe.txt
@@ -16,7 +16,9 @@ or
16or 16or
17'perf probe' --list 17'perf probe' --list
18or 18or
19'perf probe' --line='FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]' 19'perf probe' [options] --line='FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]'
20or
21'perf probe' [options] --vars='PROBEPOINT'
20 22
21DESCRIPTION 23DESCRIPTION
22----------- 24-----------
@@ -31,6 +33,11 @@ OPTIONS
31--vmlinux=PATH:: 33--vmlinux=PATH::
32 Specify vmlinux path which has debuginfo (Dwarf binary). 34 Specify vmlinux path which has debuginfo (Dwarf binary).
33 35
36-m::
37--module=MODNAME::
38 Specify module name in which perf-probe searches probe points
39 or lines.
40
34-s:: 41-s::
35--source=PATH:: 42--source=PATH::
36 Specify path to kernel source. 43 Specify path to kernel source.
@@ -57,6 +64,15 @@ OPTIONS
57 Show source code lines which can be probed. This needs an argument 64 Show source code lines which can be probed. This needs an argument
58 which specifies a range of the source code. (see LINE SYNTAX for detail) 65 which specifies a range of the source code. (see LINE SYNTAX for detail)
59 66
67-V::
68--vars=::
69 Show available local variables at given probe point. The argument
70 syntax is same as PROBE SYNTAX, but NO ARGs.
71
72--externs::
73 (Only for --vars) Show external defined variables in addition to local
74 variables.
75
60-f:: 76-f::
61--force:: 77--force::
62 Forcibly add events with existing name. 78 Forcibly add events with existing name.
diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt
index 3ee27dccfde9..a91f9f9e6e5c 100644
--- a/tools/perf/Documentation/perf-record.txt
+++ b/tools/perf/Documentation/perf-record.txt
@@ -83,6 +83,10 @@ OPTIONS
83--call-graph:: 83--call-graph::
84 Do call-graph (stack chain/backtrace) recording. 84 Do call-graph (stack chain/backtrace) recording.
85 85
86-q::
87--quiet::
88 Don't print any message, useful for scripting.
89
86-v:: 90-v::
87--verbose:: 91--verbose::
88 Be more verbose (show counter open errors, etc). 92 Be more verbose (show counter open errors, etc).
diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt
index abfabe9147a4..12052c9ed0ba 100644
--- a/tools/perf/Documentation/perf-report.txt
+++ b/tools/perf/Documentation/perf-report.txt
@@ -65,6 +65,13 @@ OPTIONS
65 the tree is considered as a new profiled object. + 65 the tree is considered as a new profiled object. +
66 Default: fractal,0.5. 66 Default: fractal,0.5.
67 67
68--stdio:: Use the stdio interface.
69
70--tui:: Use the TUI interface, that is integrated with annotate and allows
71 zooming into DSOs or threads, among other features. Use of --tui
72 requires a tty, if one is not present, as when piping to other
73 commands, the stdio interface is used.
74
68SEE ALSO 75SEE ALSO
69-------- 76--------
70linkperf:perf-stat[1] 77linkperf:perf-stat[1]
diff --git a/tools/perf/Documentation/perf-trace.txt b/tools/perf/Documentation/perf-trace.txt
index 122ec9dc4853..26aff6bf9e50 100644
--- a/tools/perf/Documentation/perf-trace.txt
+++ b/tools/perf/Documentation/perf-trace.txt
@@ -8,7 +8,11 @@ perf-trace - Read perf.data (created by perf record) and display trace output
8SYNOPSIS 8SYNOPSIS
9-------- 9--------
10[verse] 10[verse]
11'perf trace' {record <script> | report <script> [args] } 11'perf trace' [<options>]
12'perf trace' [<options>] record <script> [<record-options>] <command>
13'perf trace' [<options>] report <script> [script-args]
14'perf trace' [<options>] <script> <required-script-args> [<record-options>] <command>
15'perf trace' [<options>] <top-script> [script-args]
12 16
13DESCRIPTION 17DESCRIPTION
14----------- 18-----------
@@ -24,23 +28,53 @@ There are several variants of perf trace:
24 available via 'perf trace -l'). The following variants allow you to 28 available via 'perf trace -l'). The following variants allow you to
25 record and run those scripts: 29 record and run those scripts:
26 30
27 'perf trace record <script>' to record the events required for 'perf 31 'perf trace record <script> <command>' to record the events required
28 trace report'. <script> is the name displayed in the output of 32 for 'perf trace report'. <script> is the name displayed in the
29 'perf trace --list' i.e. the actual script name minus any language 33 output of 'perf trace --list' i.e. the actual script name minus any
30 extension. 34 language extension. If <command> is not specified, the events are
35 recorded using the -a (system-wide) 'perf record' option.
31 36
32 'perf trace report <script>' to run and display the results of 37 'perf trace report <script> [args]' to run and display the results
33 <script>. <script> is the name displayed in the output of 'perf 38 of <script>. <script> is the name displayed in the output of 'perf
34 trace --list' i.e. the actual script name minus any language 39 trace --list' i.e. the actual script name minus any language
35 extension. The perf.data output from a previous run of 'perf trace 40 extension. The perf.data output from a previous run of 'perf trace
36 record <script>' is used and should be present for this command to 41 record <script>' is used and should be present for this command to
37 succeed. 42 succeed. [args] refers to the (mainly optional) args expected by
43 the script.
44
45 'perf trace <script> <required-script-args> <command>' to both
46 record the events required for <script> and to run the <script>
47 using 'live-mode' i.e. without writing anything to disk. <script>
48 is the name displayed in the output of 'perf trace --list' i.e. the
49 actual script name minus any language extension. If <command> is
50 not specified, the events are recorded using the -a (system-wide)
51 'perf record' option. If <script> has any required args, they
52 should be specified before <command>. This mode doesn't allow for
53 optional script args to be specified; if optional script args are
54 desired, they can be specified using separate 'perf trace record'
55 and 'perf trace report' commands, with the stdout of the record step
56 piped to the stdin of the report script, using the '-o -' and '-i -'
57 options of the corresponding commands.
58
59 'perf trace <top-script>' to both record the events required for
60 <top-script> and to run the <top-script> using 'live-mode'
61 i.e. without writing anything to disk. <top-script> is the name
62 displayed in the output of 'perf trace --list' i.e. the actual
63 script name minus any language extension; a <top-script> is defined
64 as any script name ending with the string 'top'.
65
66 [<record-options>] can be passed to the record steps of 'perf trace
67 record' and 'live-mode' variants; this isn't possible however for
68 <top-script> 'live-mode' or 'perf trace report' variants.
38 69
39 See the 'SEE ALSO' section for links to language-specific 70 See the 'SEE ALSO' section for links to language-specific
40 information on how to write and run your own trace scripts. 71 information on how to write and run your own trace scripts.
41 72
42OPTIONS 73OPTIONS
43------- 74-------
75<command>...::
76 Any command you can specify in a shell.
77
44-D:: 78-D::
45--dump-raw-trace=:: 79--dump-raw-trace=::
46 Display verbose dump of the trace data. 80 Display verbose dump of the trace data.
@@ -64,6 +98,13 @@ OPTIONS
64 Generate perf-trace.[ext] starter script for given language, 98 Generate perf-trace.[ext] starter script for given language,
65 using current perf.data. 99 using current perf.data.
66 100
101-a::
102 Force system-wide collection. Scripts run without a <command>
103 normally use -a by default, while scripts run with a <command>
104 normally don't - this option allows the latter to be run in
105 system-wide mode.
106
107
67SEE ALSO 108SEE ALSO
68-------- 109--------
69linkperf:perf-record[1], linkperf:perf-trace-perl[1], 110linkperf:perf-record[1], linkperf:perf-trace-perl[1],
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index 26f626d45a9e..d1db0f676a4b 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -5,6 +5,12 @@ endif
5# The default target of this Makefile is... 5# The default target of this Makefile is...
6all:: 6all::
7 7
8ifneq ($(OUTPUT),)
9# check that the output directory actually exists
10OUTDIR := $(shell cd $(OUTPUT) && /bin/pwd)
11$(if $(OUTDIR),, $(error output directory "$(OUTPUT)" does not exist))
12endif
13
8# Define V=1 to have a more verbose compile. 14# Define V=1 to have a more verbose compile.
9# Define V=2 to have an even more verbose compile. 15# Define V=2 to have an even more verbose compile.
10# 16#
@@ -157,11 +163,6 @@ all::
157# 163#
158# Define NO_DWARF if you do not want debug-info analysis feature at all. 164# Define NO_DWARF if you do not want debug-info analysis feature at all.
159 165
160$(shell sh -c 'mkdir -p $(OUTPUT)scripts/python/Perf-Trace-Util/' 2> /dev/null)
161$(shell sh -c 'mkdir -p $(OUTPUT)scripts/perl/Perf-Trace-Util/' 2> /dev/null)
162$(shell sh -c 'mkdir -p $(OUTPUT)util/scripting-engines/' 2> /dev/null)
163$(shell sh -c 'mkdir $(OUTPUT)bench' 2> /dev/null)
164
165$(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE 166$(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE
166 @$(SHELL_PATH) util/PERF-VERSION-GEN $(OUTPUT) 167 @$(SHELL_PATH) util/PERF-VERSION-GEN $(OUTPUT)
167-include $(OUTPUT)PERF-VERSION-FILE 168-include $(OUTPUT)PERF-VERSION-FILE
@@ -187,8 +188,6 @@ ifeq ($(ARCH),x86_64)
187 ARCH := x86 188 ARCH := x86
188endif 189endif
189 190
190$(shell sh -c 'mkdir -p $(OUTPUT)arch/$(ARCH)/util/' 2> /dev/null)
191
192# CFLAGS and LDFLAGS are for the users to override from the command line. 191# CFLAGS and LDFLAGS are for the users to override from the command line.
193 192
194# 193#
@@ -269,6 +268,7 @@ export prefix bindir sharedir sysconfdir
269CC = $(CROSS_COMPILE)gcc 268CC = $(CROSS_COMPILE)gcc
270AR = $(CROSS_COMPILE)ar 269AR = $(CROSS_COMPILE)ar
271RM = rm -f 270RM = rm -f
271MKDIR = mkdir
272TAR = tar 272TAR = tar
273FIND = find 273FIND = find
274INSTALL = install 274INSTALL = install
@@ -313,6 +313,9 @@ TEST_PROGRAMS =
313 313
314SCRIPT_SH += perf-archive.sh 314SCRIPT_SH += perf-archive.sh
315 315
316grep-libs = $(filter -l%,$(1))
317strip-libs = $(filter-out -l%,$(1))
318
316# 319#
317# No Perl scripts right now: 320# No Perl scripts right now:
318# 321#
@@ -568,21 +571,37 @@ else
568 # Fedora has /usr/include/slang/slang.h, but ubuntu /usr/include/slang.h 571 # Fedora has /usr/include/slang/slang.h, but ubuntu /usr/include/slang.h
569 BASIC_CFLAGS += -I/usr/include/slang 572 BASIC_CFLAGS += -I/usr/include/slang
570 EXTLIBS += -lnewt -lslang 573 EXTLIBS += -lnewt -lslang
571 LIB_OBJS += $(OUTPUT)util/newt.o 574 LIB_OBJS += $(OUTPUT)util/ui/setup.o
575 LIB_OBJS += $(OUTPUT)util/ui/browser.o
576 LIB_OBJS += $(OUTPUT)util/ui/browsers/annotate.o
577 LIB_OBJS += $(OUTPUT)util/ui/browsers/hists.o
578 LIB_OBJS += $(OUTPUT)util/ui/browsers/map.o
579 LIB_OBJS += $(OUTPUT)util/ui/helpline.o
580 LIB_OBJS += $(OUTPUT)util/ui/progress.o
581 LIB_OBJS += $(OUTPUT)util/ui/util.o
582 LIB_H += util/ui/browser.h
583 LIB_H += util/ui/browsers/map.h
584 LIB_H += util/ui/helpline.h
585 LIB_H += util/ui/libslang.h
586 LIB_H += util/ui/progress.h
587 LIB_H += util/ui/util.h
572 endif 588 endif
573endif 589endif
574 590
575ifdef NO_LIBPERL 591ifdef NO_LIBPERL
576 BASIC_CFLAGS += -DNO_LIBPERL 592 BASIC_CFLAGS += -DNO_LIBPERL
577else 593else
578 PERL_EMBED_LDOPTS = `perl -MExtUtils::Embed -e ldopts 2>/dev/null` 594 PERL_EMBED_LDOPTS = $(shell perl -MExtUtils::Embed -e ldopts 2>/dev/null)
595 PERL_EMBED_LDFLAGS = $(call strip-libs,$(PERL_EMBED_LDOPTS))
596 PERL_EMBED_LIBADD = $(call grep-libs,$(PERL_EMBED_LDOPTS))
579 PERL_EMBED_CCOPTS = `perl -MExtUtils::Embed -e ccopts 2>/dev/null` 597 PERL_EMBED_CCOPTS = `perl -MExtUtils::Embed -e ccopts 2>/dev/null`
580 FLAGS_PERL_EMBED=$(PERL_EMBED_CCOPTS) $(PERL_EMBED_LDOPTS) 598 FLAGS_PERL_EMBED=$(PERL_EMBED_CCOPTS) $(PERL_EMBED_LDOPTS)
581 599
582 ifneq ($(call try-cc,$(SOURCE_PERL_EMBED),$(FLAGS_PERL_EMBED)),y) 600 ifneq ($(call try-cc,$(SOURCE_PERL_EMBED),$(FLAGS_PERL_EMBED)),y)
583 BASIC_CFLAGS += -DNO_LIBPERL 601 BASIC_CFLAGS += -DNO_LIBPERL
584 else 602 else
585 ALL_LDFLAGS += $(PERL_EMBED_LDOPTS) 603 ALL_LDFLAGS += $(PERL_EMBED_LDFLAGS)
604 EXTLIBS += $(PERL_EMBED_LIBADD)
586 LIB_OBJS += $(OUTPUT)util/scripting-engines/trace-event-perl.o 605 LIB_OBJS += $(OUTPUT)util/scripting-engines/trace-event-perl.o
587 LIB_OBJS += $(OUTPUT)scripts/perl/Perf-Trace-Util/Context.o 606 LIB_OBJS += $(OUTPUT)scripts/perl/Perf-Trace-Util/Context.o
588 endif 607 endif
@@ -591,13 +610,16 @@ endif
591ifdef NO_LIBPYTHON 610ifdef NO_LIBPYTHON
592 BASIC_CFLAGS += -DNO_LIBPYTHON 611 BASIC_CFLAGS += -DNO_LIBPYTHON
593else 612else
594 PYTHON_EMBED_LDOPTS = `python-config --ldflags 2>/dev/null` 613 PYTHON_EMBED_LDOPTS = $(shell python-config --ldflags 2>/dev/null)
614 PYTHON_EMBED_LDFLAGS = $(call strip-libs,$(PYTHON_EMBED_LDOPTS))
615 PYTHON_EMBED_LIBADD = $(call grep-libs,$(PYTHON_EMBED_LDOPTS))
595 PYTHON_EMBED_CCOPTS = `python-config --cflags 2>/dev/null` 616 PYTHON_EMBED_CCOPTS = `python-config --cflags 2>/dev/null`
596 FLAGS_PYTHON_EMBED=$(PYTHON_EMBED_CCOPTS) $(PYTHON_EMBED_LDOPTS) 617 FLAGS_PYTHON_EMBED=$(PYTHON_EMBED_CCOPTS) $(PYTHON_EMBED_LDOPTS)
597 ifneq ($(call try-cc,$(SOURCE_PYTHON_EMBED),$(FLAGS_PYTHON_EMBED)),y) 618 ifneq ($(call try-cc,$(SOURCE_PYTHON_EMBED),$(FLAGS_PYTHON_EMBED)),y)
598 BASIC_CFLAGS += -DNO_LIBPYTHON 619 BASIC_CFLAGS += -DNO_LIBPYTHON
599 else 620 else
600 ALL_LDFLAGS += $(PYTHON_EMBED_LDOPTS) 621 ALL_LDFLAGS += $(PYTHON_EMBED_LDFLAGS)
622 EXTLIBS += $(PYTHON_EMBED_LIBADD)
601 LIB_OBJS += $(OUTPUT)util/scripting-engines/trace-event-python.o 623 LIB_OBJS += $(OUTPUT)util/scripting-engines/trace-event-python.o
602 LIB_OBJS += $(OUTPUT)scripts/python/Perf-Trace-Util/Context.o 624 LIB_OBJS += $(OUTPUT)scripts/python/Perf-Trace-Util/Context.o
603 endif 625 endif
@@ -640,6 +662,15 @@ else
640 endif 662 endif
641endif 663endif
642 664
665
666ifdef NO_STRLCPY
667 BASIC_CFLAGS += -DNO_STRLCPY
668else
669 ifneq ($(call try-cc,$(SOURCE_STRLCPY),),y)
670 BASIC_CFLAGS += -DNO_STRLCPY
671 endif
672endif
673
643ifndef CC_LD_DYNPATH 674ifndef CC_LD_DYNPATH
644 ifdef NO_R_TO_GCC_LINKER 675 ifdef NO_R_TO_GCC_LINKER
645 # Some gcc does not accept and pass -R to the linker to specify 676 # Some gcc does not accept and pass -R to the linker to specify
@@ -826,6 +857,7 @@ ifndef V
826 QUIET_CC = @echo ' ' CC $@; 857 QUIET_CC = @echo ' ' CC $@;
827 QUIET_AR = @echo ' ' AR $@; 858 QUIET_AR = @echo ' ' AR $@;
828 QUIET_LINK = @echo ' ' LINK $@; 859 QUIET_LINK = @echo ' ' LINK $@;
860 QUIET_MKDIR = @echo ' ' MKDIR $@;
829 QUIET_BUILT_IN = @echo ' ' BUILTIN $@; 861 QUIET_BUILT_IN = @echo ' ' BUILTIN $@;
830 QUIET_GEN = @echo ' ' GEN $@; 862 QUIET_GEN = @echo ' ' GEN $@;
831 QUIET_SUBDIR0 = +@subdir= 863 QUIET_SUBDIR0 = +@subdir=
@@ -896,8 +928,8 @@ $(OUTPUT)perf.o: perf.c $(OUTPUT)common-cmds.h $(OUTPUT)PERF-CFLAGS
896 $(ALL_CFLAGS) -c $(filter %.c,$^) -o $@ 928 $(ALL_CFLAGS) -c $(filter %.c,$^) -o $@
897 929
898$(OUTPUT)perf$X: $(OUTPUT)perf.o $(BUILTIN_OBJS) $(PERFLIBS) 930$(OUTPUT)perf$X: $(OUTPUT)perf.o $(BUILTIN_OBJS) $(PERFLIBS)
899 $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(OUTPUT)perf.o \ 931 $(QUIET_LINK)$(CC) $(ALL_CFLAGS) $(ALL_LDFLAGS) $(OUTPUT)perf.o \
900 $(BUILTIN_OBJS) $(ALL_LDFLAGS) $(LIBS) 932 $(BUILTIN_OBJS) $(LIBS) -o $@
901 933
902$(OUTPUT)builtin-help.o: builtin-help.c $(OUTPUT)common-cmds.h $(OUTPUT)PERF-CFLAGS 934$(OUTPUT)builtin-help.o: builtin-help.c $(OUTPUT)common-cmds.h $(OUTPUT)PERF-CFLAGS
903 $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \ 935 $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \
@@ -923,15 +955,15 @@ $(OUTPUT)common-cmds.h: $(wildcard Documentation/perf-*.txt)
923 $(QUIET_GEN). util/generate-cmdlist.sh > $@+ && mv $@+ $@ 955 $(QUIET_GEN). util/generate-cmdlist.sh > $@+ && mv $@+ $@
924 956
925$(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh 957$(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
926 $(QUIET_GEN)$(RM) $@ $@+ && \ 958 $(QUIET_GEN)$(RM) $(OUTPUT)$@ $(OUTPUT)$@+ && \
927 sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \ 959 sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
928 -e 's|@SHELL_PATH@|$(SHELL_PATH_SQ)|' \ 960 -e 's|@SHELL_PATH@|$(SHELL_PATH_SQ)|' \
929 -e 's|@@PERL@@|$(PERL_PATH_SQ)|g' \ 961 -e 's|@@PERL@@|$(PERL_PATH_SQ)|g' \
930 -e 's/@@PERF_VERSION@@/$(PERF_VERSION)/g' \ 962 -e 's/@@PERF_VERSION@@/$(PERF_VERSION)/g' \
931 -e 's/@@NO_CURL@@/$(NO_CURL)/g' \ 963 -e 's/@@NO_CURL@@/$(NO_CURL)/g' \
932 $@.sh >$@+ && \ 964 $@.sh > $(OUTPUT)$@+ && \
933 chmod +x $@+ && \ 965 chmod +x $(OUTPUT)$@+ && \
934 mv $@+ $(OUTPUT)$@ 966 mv $(OUTPUT)$@+ $(OUTPUT)$@
935 967
936configure: configure.ac 968configure: configure.ac
937 $(QUIET_GEN)$(RM) $@ $<+ && \ 969 $(QUIET_GEN)$(RM) $@ $<+ && \
@@ -966,7 +998,16 @@ $(OUTPUT)builtin-init-db.o: builtin-init-db.c $(OUTPUT)PERF-CFLAGS
966$(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS 998$(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS
967 $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< 999 $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $<
968 1000
969$(OUTPUT)util/newt.o: util/newt.c $(OUTPUT)PERF-CFLAGS 1001$(OUTPUT)util/ui/browser.o: util/ui/browser.c $(OUTPUT)PERF-CFLAGS
1002 $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $<
1003
1004$(OUTPUT)util/ui/browsers/annotate.o: util/ui/browsers/annotate.c $(OUTPUT)PERF-CFLAGS
1005 $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $<
1006
1007$(OUTPUT)util/ui/browsers/hists.o: util/ui/browsers/hists.c $(OUTPUT)PERF-CFLAGS
1008 $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $<
1009
1010$(OUTPUT)util/ui/browsers/map.o: util/ui/browsers/map.c $(OUTPUT)PERF-CFLAGS
970 $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< 1011 $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $<
971 1012
972$(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS 1013$(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS
@@ -991,6 +1032,14 @@ $(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H)
991$(patsubst perf-%$X,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h) 1032$(patsubst perf-%$X,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h)
992builtin-revert.o wt-status.o: wt-status.h 1033builtin-revert.o wt-status.o: wt-status.h
993 1034
1035# we compile into subdirectories. if the target directory is not the source directory, they might not exists. So
1036# we depend the various files onto their directories.
1037DIRECTORY_DEPS = $(LIB_OBJS) $(BUILTIN_OBJS) $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)common-cmds.h
1038$(DIRECTORY_DEPS): | $(sort $(dir $(DIRECTORY_DEPS)))
1039# In the second step, we make a rule to actually create these directories
1040$(sort $(dir $(DIRECTORY_DEPS))):
1041 $(QUIET_MKDIR)$(MKDIR) -p $@ 2>/dev/null
1042
994$(LIB_FILE): $(LIB_OBJS) 1043$(LIB_FILE): $(LIB_OBJS)
995 $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIB_OBJS) 1044 $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIB_OBJS)
996 1045
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index fd20670ce986..6d5604d8df95 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -28,7 +28,7 @@
28 28
29static char const *input_name = "perf.data"; 29static char const *input_name = "perf.data";
30 30
31static bool force; 31static bool force, use_tui, use_stdio;
32 32
33static bool full_paths; 33static bool full_paths;
34 34
@@ -285,7 +285,7 @@ static int hist_entry__tty_annotate(struct hist_entry *he)
285 LIST_HEAD(head); 285 LIST_HEAD(head);
286 struct objdump_line *pos, *n; 286 struct objdump_line *pos, *n;
287 287
288 if (hist_entry__annotate(he, &head) < 0) 288 if (hist_entry__annotate(he, &head, 0) < 0)
289 return -1; 289 return -1;
290 290
291 if (full_paths) 291 if (full_paths)
@@ -321,7 +321,7 @@ static int hist_entry__tty_annotate(struct hist_entry *he)
321 321
322static void hists__find_annotations(struct hists *self) 322static void hists__find_annotations(struct hists *self)
323{ 323{
324 struct rb_node *first = rb_first(&self->entries), *nd = first; 324 struct rb_node *nd = rb_first(&self->entries), *next;
325 int key = KEY_RIGHT; 325 int key = KEY_RIGHT;
326 326
327 while (nd) { 327 while (nd) {
@@ -343,20 +343,19 @@ find_next:
343 343
344 if (use_browser > 0) { 344 if (use_browser > 0) {
345 key = hist_entry__tui_annotate(he); 345 key = hist_entry__tui_annotate(he);
346 if (is_exit_key(key))
347 break;
348 switch (key) { 346 switch (key) {
349 case KEY_RIGHT: 347 case KEY_RIGHT:
350 case '\t': 348 next = rb_next(nd);
351 nd = rb_next(nd);
352 break; 349 break;
353 case KEY_LEFT: 350 case KEY_LEFT:
354 if (nd == first) 351 next = rb_prev(nd);
355 continue;
356 nd = rb_prev(nd);
357 default:
358 break; 352 break;
353 default:
354 return;
359 } 355 }
356
357 if (next != NULL)
358 nd = next;
360 } else { 359 } else {
361 hist_entry__tty_annotate(he); 360 hist_entry__tty_annotate(he);
362 nd = rb_next(nd); 361 nd = rb_next(nd);
@@ -428,6 +427,8 @@ static const struct option options[] = {
428 "be more verbose (show symbol address, etc)"), 427 "be more verbose (show symbol address, etc)"),
429 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, 428 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
430 "dump raw trace in ASCII"), 429 "dump raw trace in ASCII"),
430 OPT_BOOLEAN(0, "tui", &use_tui, "Use the TUI interface"),
431 OPT_BOOLEAN(0, "stdio", &use_stdio, "Use the stdio interface"),
431 OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, 432 OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
432 "file", "vmlinux pathname"), 433 "file", "vmlinux pathname"),
433 OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules, 434 OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules,
@@ -443,6 +444,11 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __used)
443{ 444{
444 argc = parse_options(argc, argv, options, annotate_usage, 0); 445 argc = parse_options(argc, argv, options, annotate_usage, 0);
445 446
447 if (use_stdio)
448 use_browser = 0;
449 else if (use_tui)
450 use_browser = 1;
451
446 setup_browser(); 452 setup_browser();
447 453
448 symbol_conf.priv_size = sizeof(struct sym_priv); 454 symbol_conf.priv_size = sizeof(struct sym_priv);
diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c
index 44a47e13bd67..c49837de7d3f 100644
--- a/tools/perf/builtin-buildid-list.c
+++ b/tools/perf/builtin-buildid-list.c
@@ -36,7 +36,6 @@ static const struct option options[] = {
36 36
37static int __cmd_buildid_list(void) 37static int __cmd_buildid_list(void)
38{ 38{
39 int err = -1;
40 struct perf_session *session; 39 struct perf_session *session;
41 40
42 session = perf_session__new(input_name, O_RDONLY, force, false); 41 session = perf_session__new(input_name, O_RDONLY, force, false);
@@ -49,7 +48,7 @@ static int __cmd_buildid_list(void)
49 perf_session__fprintf_dsos_buildid(session, stdout, with_hits); 48 perf_session__fprintf_dsos_buildid(session, stdout, with_hits);
50 49
51 perf_session__delete(session); 50 perf_session__delete(session);
52 return err; 51 return 0;
53} 52}
54 53
55int cmd_buildid_list(int argc, const char **argv, const char *prefix __used) 54int cmd_buildid_list(int argc, const char **argv, const char *prefix __used)
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index 199d5e19554f..add163c9f0e7 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -50,14 +50,17 @@ static struct {
50 bool list_events; 50 bool list_events;
51 bool force_add; 51 bool force_add;
52 bool show_lines; 52 bool show_lines;
53 bool show_vars;
54 bool show_ext_vars;
55 bool mod_events;
53 int nevents; 56 int nevents;
54 struct perf_probe_event events[MAX_PROBES]; 57 struct perf_probe_event events[MAX_PROBES];
55 struct strlist *dellist; 58 struct strlist *dellist;
56 struct line_range line_range; 59 struct line_range line_range;
60 const char *target_module;
57 int max_probe_points; 61 int max_probe_points;
58} params; 62} params;
59 63
60
61/* Parse an event definition. Note that any error must die. */ 64/* Parse an event definition. Note that any error must die. */
62static int parse_probe_event(const char *str) 65static int parse_probe_event(const char *str)
63{ 66{
@@ -92,6 +95,7 @@ static int parse_probe_event_argv(int argc, const char **argv)
92 len = 0; 95 len = 0;
93 for (i = 0; i < argc; i++) 96 for (i = 0; i < argc; i++)
94 len += sprintf(&buf[len], "%s ", argv[i]); 97 len += sprintf(&buf[len], "%s ", argv[i]);
98 params.mod_events = true;
95 ret = parse_probe_event(buf); 99 ret = parse_probe_event(buf);
96 free(buf); 100 free(buf);
97 return ret; 101 return ret;
@@ -100,9 +104,10 @@ static int parse_probe_event_argv(int argc, const char **argv)
100static int opt_add_probe_event(const struct option *opt __used, 104static int opt_add_probe_event(const struct option *opt __used,
101 const char *str, int unset __used) 105 const char *str, int unset __used)
102{ 106{
103 if (str) 107 if (str) {
108 params.mod_events = true;
104 return parse_probe_event(str); 109 return parse_probe_event(str);
105 else 110 } else
106 return 0; 111 return 0;
107} 112}
108 113
@@ -110,6 +115,7 @@ static int opt_del_probe_event(const struct option *opt __used,
110 const char *str, int unset __used) 115 const char *str, int unset __used)
111{ 116{
112 if (str) { 117 if (str) {
118 params.mod_events = true;
113 if (!params.dellist) 119 if (!params.dellist)
114 params.dellist = strlist__new(true, NULL); 120 params.dellist = strlist__new(true, NULL);
115 strlist__add(params.dellist, str); 121 strlist__add(params.dellist, str);
@@ -130,6 +136,25 @@ static int opt_show_lines(const struct option *opt __used,
130 136
131 return ret; 137 return ret;
132} 138}
139
140static int opt_show_vars(const struct option *opt __used,
141 const char *str, int unset __used)
142{
143 struct perf_probe_event *pev = &params.events[params.nevents];
144 int ret;
145
146 if (!str)
147 return 0;
148
149 ret = parse_probe_event(str);
150 if (!ret && pev->nargs != 0) {
151 pr_err(" Error: '--vars' doesn't accept arguments.\n");
152 return -EINVAL;
153 }
154 params.show_vars = true;
155
156 return ret;
157}
133#endif 158#endif
134 159
135static const char * const probe_usage[] = { 160static const char * const probe_usage[] = {
@@ -138,7 +163,8 @@ static const char * const probe_usage[] = {
138 "perf probe [<options>] --del '[GROUP:]EVENT' ...", 163 "perf probe [<options>] --del '[GROUP:]EVENT' ...",
139 "perf probe --list", 164 "perf probe --list",
140#ifdef DWARF_SUPPORT 165#ifdef DWARF_SUPPORT
141 "perf probe --line 'LINEDESC'", 166 "perf probe [<options>] --line 'LINEDESC'",
167 "perf probe [<options>] --vars 'PROBEPOINT'",
142#endif 168#endif
143 NULL 169 NULL
144}; 170};
@@ -180,10 +206,17 @@ static const struct option options[] = {
180 OPT_CALLBACK('L', "line", NULL, 206 OPT_CALLBACK('L', "line", NULL,
181 "FUNC[:RLN[+NUM|-RLN2]]|SRC:ALN[+NUM|-ALN2]", 207 "FUNC[:RLN[+NUM|-RLN2]]|SRC:ALN[+NUM|-ALN2]",
182 "Show source code lines.", opt_show_lines), 208 "Show source code lines.", opt_show_lines),
209 OPT_CALLBACK('V', "vars", NULL,
210 "FUNC[@SRC][+OFF|%return|:RL|;PT]|SRC:AL|SRC;PT",
211 "Show accessible variables on PROBEDEF", opt_show_vars),
212 OPT_BOOLEAN('\0', "externs", &params.show_ext_vars,
213 "Show external variables too (with --vars only)"),
183 OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, 214 OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
184 "file", "vmlinux pathname"), 215 "file", "vmlinux pathname"),
185 OPT_STRING('s', "source", &symbol_conf.source_prefix, 216 OPT_STRING('s', "source", &symbol_conf.source_prefix,
186 "directory", "path to kernel source"), 217 "directory", "path to kernel source"),
218 OPT_STRING('m', "module", &params.target_module,
219 "modname", "target module name"),
187#endif 220#endif
188 OPT__DRY_RUN(&probe_event_dry_run), 221 OPT__DRY_RUN(&probe_event_dry_run),
189 OPT_INTEGER('\0', "max-probes", &params.max_probe_points, 222 OPT_INTEGER('\0', "max-probes", &params.max_probe_points,
@@ -216,8 +249,13 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
216 !params.show_lines)) 249 !params.show_lines))
217 usage_with_options(probe_usage, options); 250 usage_with_options(probe_usage, options);
218 251
252 /*
253 * Only consider the user's kernel image path if given.
254 */
255 symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL);
256
219 if (params.list_events) { 257 if (params.list_events) {
220 if (params.nevents != 0 || params.dellist) { 258 if (params.mod_events) {
221 pr_err(" Error: Don't use --list with --add/--del.\n"); 259 pr_err(" Error: Don't use --list with --add/--del.\n");
222 usage_with_options(probe_usage, options); 260 usage_with_options(probe_usage, options);
223 } 261 }
@@ -225,6 +263,10 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
225 pr_err(" Error: Don't use --list with --line.\n"); 263 pr_err(" Error: Don't use --list with --line.\n");
226 usage_with_options(probe_usage, options); 264 usage_with_options(probe_usage, options);
227 } 265 }
266 if (params.show_vars) {
267 pr_err(" Error: Don't use --list with --vars.\n");
268 usage_with_options(probe_usage, options);
269 }
228 ret = show_perf_probe_events(); 270 ret = show_perf_probe_events();
229 if (ret < 0) 271 if (ret < 0)
230 pr_err(" Error: Failed to show event list. (%d)\n", 272 pr_err(" Error: Failed to show event list. (%d)\n",
@@ -234,17 +276,35 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
234 276
235#ifdef DWARF_SUPPORT 277#ifdef DWARF_SUPPORT
236 if (params.show_lines) { 278 if (params.show_lines) {
237 if (params.nevents != 0 || params.dellist) { 279 if (params.mod_events) {
238 pr_warning(" Error: Don't use --line with" 280 pr_err(" Error: Don't use --line with"
239 " --add/--del.\n"); 281 " --add/--del.\n");
282 usage_with_options(probe_usage, options);
283 }
284 if (params.show_vars) {
285 pr_err(" Error: Don't use --line with --vars.\n");
240 usage_with_options(probe_usage, options); 286 usage_with_options(probe_usage, options);
241 } 287 }
242 288
243 ret = show_line_range(&params.line_range); 289 ret = show_line_range(&params.line_range, params.target_module);
244 if (ret < 0) 290 if (ret < 0)
245 pr_err(" Error: Failed to show lines. (%d)\n", ret); 291 pr_err(" Error: Failed to show lines. (%d)\n", ret);
246 return ret; 292 return ret;
247 } 293 }
294 if (params.show_vars) {
295 if (params.mod_events) {
296 pr_err(" Error: Don't use --vars with"
297 " --add/--del.\n");
298 usage_with_options(probe_usage, options);
299 }
300 ret = show_available_vars(params.events, params.nevents,
301 params.max_probe_points,
302 params.target_module,
303 params.show_ext_vars);
304 if (ret < 0)
305 pr_err(" Error: Failed to show vars. (%d)\n", ret);
306 return ret;
307 }
248#endif 308#endif
249 309
250 if (params.dellist) { 310 if (params.dellist) {
@@ -258,8 +318,9 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
258 318
259 if (params.nevents) { 319 if (params.nevents) {
260 ret = add_perf_probe_events(params.events, params.nevents, 320 ret = add_perf_probe_events(params.events, params.nevents,
261 params.force_add, 321 params.max_probe_points,
262 params.max_probe_points); 322 params.target_module,
323 params.force_add);
263 if (ret < 0) { 324 if (ret < 0) {
264 pr_err(" Error: Failed to add events. (%d)\n", ret); 325 pr_err(" Error: Failed to add events. (%d)\n", ret);
265 return ret; 326 return ret;
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index ff77b805de71..564491fa18b2 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -197,7 +197,7 @@ static void sig_atexit(void)
197 if (child_pid > 0) 197 if (child_pid > 0)
198 kill(child_pid, SIGTERM); 198 kill(child_pid, SIGTERM);
199 199
200 if (signr == -1) 200 if (signr == -1 || signr == SIGUSR1)
201 return; 201 return;
202 202
203 signal(signr, SIG_DFL); 203 signal(signr, SIG_DFL);
@@ -353,7 +353,7 @@ try_again:
353 } 353 }
354 354
355 if (read(fd[nr_cpu][counter][thread_index], &read_data, sizeof(read_data)) == -1) { 355 if (read(fd[nr_cpu][counter][thread_index], &read_data, sizeof(read_data)) == -1) {
356 perror("Unable to read perf file descriptor\n"); 356 perror("Unable to read perf file descriptor");
357 exit(-1); 357 exit(-1);
358 } 358 }
359 359
@@ -515,6 +515,7 @@ static int __cmd_record(int argc, const char **argv)
515 atexit(sig_atexit); 515 atexit(sig_atexit);
516 signal(SIGCHLD, sig_handler); 516 signal(SIGCHLD, sig_handler);
517 signal(SIGINT, sig_handler); 517 signal(SIGINT, sig_handler);
518 signal(SIGUSR1, sig_handler);
518 519
519 if (forks && (pipe(child_ready_pipe) < 0 || pipe(go_pipe) < 0)) { 520 if (forks && (pipe(child_ready_pipe) < 0 || pipe(go_pipe) < 0)) {
520 perror("failed to create pipes"); 521 perror("failed to create pipes");
@@ -606,6 +607,7 @@ static int __cmd_record(int argc, const char **argv)
606 execvp(argv[0], (char **)argv); 607 execvp(argv[0], (char **)argv);
607 608
608 perror(argv[0]); 609 perror(argv[0]);
610 kill(getppid(), SIGUSR1);
609 exit(-1); 611 exit(-1);
610 } 612 }
611 613
@@ -626,7 +628,7 @@ static int __cmd_record(int argc, const char **argv)
626 628
627 nr_cpus = read_cpu_map(cpu_list); 629 nr_cpus = read_cpu_map(cpu_list);
628 if (nr_cpus < 1) { 630 if (nr_cpus < 1) {
629 perror("failed to collect number of CPUs\n"); 631 perror("failed to collect number of CPUs");
630 return -1; 632 return -1;
631 } 633 }
632 634
@@ -697,17 +699,18 @@ static int __cmd_record(int argc, const char **argv)
697 if (err < 0) 699 if (err < 0)
698 err = event__synthesize_kernel_mmap(process_synthesized_event, 700 err = event__synthesize_kernel_mmap(process_synthesized_event,
699 session, machine, "_stext"); 701 session, machine, "_stext");
700 if (err < 0) { 702 if (err < 0)
701 pr_err("Couldn't record kernel reference relocation symbol.\n"); 703 pr_err("Couldn't record kernel reference relocation symbol\n"
702 return err; 704 "Symbol resolution may be skewed if relocation was used (e.g. kexec).\n"
703 } 705 "Check /proc/kallsyms permission or run as root.\n");
704 706
705 err = event__synthesize_modules(process_synthesized_event, 707 err = event__synthesize_modules(process_synthesized_event,
706 session, machine); 708 session, machine);
707 if (err < 0) { 709 if (err < 0)
708 pr_err("Couldn't record kernel reference relocation symbol.\n"); 710 pr_err("Couldn't record kernel module information.\n"
709 return err; 711 "Symbol resolution may be skewed if relocation was used (e.g. kexec).\n"
710 } 712 "Check /proc/modules permission or run as root.\n");
713
711 if (perf_guest) 714 if (perf_guest)
712 perf_session__process_machines(session, event__synthesize_guest_os); 715 perf_session__process_machines(session, event__synthesize_guest_os);
713 716
@@ -761,6 +764,9 @@ static int __cmd_record(int argc, const char **argv)
761 } 764 }
762 } 765 }
763 766
767 if (quiet || signr == SIGUSR1)
768 return 0;
769
764 fprintf(stderr, "[ perf record: Woken up %ld times to write data ]\n", waking); 770 fprintf(stderr, "[ perf record: Woken up %ld times to write data ]\n", waking);
765 771
766 /* 772 /*
@@ -787,7 +793,7 @@ static const char * const record_usage[] = {
787 793
788static bool force, append_file; 794static bool force, append_file;
789 795
790static const struct option options[] = { 796const struct option record_options[] = {
791 OPT_CALLBACK('e', "event", NULL, "event", 797 OPT_CALLBACK('e', "event", NULL, "event",
792 "event selector. use 'perf list' to list available events", 798 "event selector. use 'perf list' to list available events",
793 parse_events), 799 parse_events),
@@ -820,6 +826,7 @@ static const struct option options[] = {
820 "do call-graph (stack chain/backtrace) recording"), 826 "do call-graph (stack chain/backtrace) recording"),
821 OPT_INCR('v', "verbose", &verbose, 827 OPT_INCR('v', "verbose", &verbose,
822 "be more verbose (show counter open errors, etc)"), 828 "be more verbose (show counter open errors, etc)"),
829 OPT_BOOLEAN('q', "quiet", &quiet, "don't print any message"),
823 OPT_BOOLEAN('s', "stat", &inherit_stat, 830 OPT_BOOLEAN('s', "stat", &inherit_stat,
824 "per thread counts"), 831 "per thread counts"),
825 OPT_BOOLEAN('d', "data", &sample_address, 832 OPT_BOOLEAN('d', "data", &sample_address,
@@ -835,16 +842,16 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
835{ 842{
836 int i, j, err = -ENOMEM; 843 int i, j, err = -ENOMEM;
837 844
838 argc = parse_options(argc, argv, options, record_usage, 845 argc = parse_options(argc, argv, record_options, record_usage,
839 PARSE_OPT_STOP_AT_NON_OPTION); 846 PARSE_OPT_STOP_AT_NON_OPTION);
840 if (!argc && target_pid == -1 && target_tid == -1 && 847 if (!argc && target_pid == -1 && target_tid == -1 &&
841 !system_wide && !cpu_list) 848 !system_wide && !cpu_list)
842 usage_with_options(record_usage, options); 849 usage_with_options(record_usage, record_options);
843 850
844 if (force && append_file) { 851 if (force && append_file) {
845 fprintf(stderr, "Can't overwrite and append at the same time." 852 fprintf(stderr, "Can't overwrite and append at the same time."
846 " You need to choose between -f and -A"); 853 " You need to choose between -f and -A");
847 usage_with_options(record_usage, options); 854 usage_with_options(record_usage, record_options);
848 } else if (append_file) { 855 } else if (append_file) {
849 write_mode = WRITE_APPEND; 856 write_mode = WRITE_APPEND;
850 } else { 857 } else {
@@ -867,7 +874,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
867 if (thread_num <= 0) { 874 if (thread_num <= 0) {
868 fprintf(stderr, "Can't find all threads of pid %d\n", 875 fprintf(stderr, "Can't find all threads of pid %d\n",
869 target_pid); 876 target_pid);
870 usage_with_options(record_usage, options); 877 usage_with_options(record_usage, record_options);
871 } 878 }
872 } else { 879 } else {
873 all_tids=malloc(sizeof(pid_t)); 880 all_tids=malloc(sizeof(pid_t));
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 2f4b92925b26..5de405d45230 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -32,7 +32,7 @@
32 32
33static char const *input_name = "perf.data"; 33static char const *input_name = "perf.data";
34 34
35static bool force; 35static bool force, use_tui, use_stdio;
36static bool hide_unresolved; 36static bool hide_unresolved;
37static bool dont_use_callchains; 37static bool dont_use_callchains;
38 38
@@ -107,7 +107,8 @@ static int perf_session__add_hist_entry(struct perf_session *self,
107 goto out_free_syms; 107 goto out_free_syms;
108 err = 0; 108 err = 0;
109 if (symbol_conf.use_callchain) { 109 if (symbol_conf.use_callchain) {
110 err = append_chain(he->callchain, data->callchain, syms, data->period); 110 err = callchain_append(he->callchain, data->callchain, syms,
111 data->period);
111 if (err) 112 if (err)
112 goto out_free_syms; 113 goto out_free_syms;
113 } 114 }
@@ -348,7 +349,18 @@ static int __cmd_report(void)
348 hists__tty_browse_tree(&session->hists_tree, help); 349 hists__tty_browse_tree(&session->hists_tree, help);
349 350
350out_delete: 351out_delete:
351 perf_session__delete(session); 352 /*
353 * Speed up the exit process, for large files this can
354 * take quite a while.
355 *
356 * XXX Enable this when using valgrind or if we ever
357 * librarize this command.
358 *
359 * Also experiment with obstacks to see how much speed
360 * up we'll get here.
361 *
362 * perf_session__delete(session);
363 */
352 return ret; 364 return ret;
353} 365}
354 366
@@ -439,6 +451,8 @@ static const struct option options[] = {
439 "Show per-thread event counters"), 451 "Show per-thread event counters"),
440 OPT_STRING(0, "pretty", &pretty_printing_style, "key", 452 OPT_STRING(0, "pretty", &pretty_printing_style, "key",
441 "pretty printing style key: normal raw"), 453 "pretty printing style key: normal raw"),
454 OPT_BOOLEAN(0, "tui", &use_tui, "Use the TUI interface"),
455 OPT_BOOLEAN(0, "stdio", &use_stdio, "Use the stdio interface"),
442 OPT_STRING('s', "sort", &sort_order, "key[,key2...]", 456 OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
443 "sort by key(s): pid, comm, dso, symbol, parent"), 457 "sort by key(s): pid, comm, dso, symbol, parent"),
444 OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization, 458 OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization,
@@ -471,15 +485,38 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
471{ 485{
472 argc = parse_options(argc, argv, options, report_usage, 0); 486 argc = parse_options(argc, argv, options, report_usage, 0);
473 487
488 if (use_stdio)
489 use_browser = 0;
490 else if (use_tui)
491 use_browser = 1;
492
474 if (strcmp(input_name, "-") != 0) 493 if (strcmp(input_name, "-") != 0)
475 setup_browser(); 494 setup_browser();
495 else
496 use_browser = 0;
476 /* 497 /*
477 * Only in the newt browser we are doing integrated annotation, 498 * Only in the newt browser we are doing integrated annotation,
478 * so don't allocate extra space that won't be used in the stdio 499 * so don't allocate extra space that won't be used in the stdio
479 * implementation. 500 * implementation.
480 */ 501 */
481 if (use_browser > 0) 502 if (use_browser > 0) {
482 symbol_conf.priv_size = sizeof(struct sym_priv); 503 symbol_conf.priv_size = sizeof(struct sym_priv);
504 /*
505 * For searching by name on the "Browse map details".
506 * providing it only in verbose mode not to bloat too
507 * much struct symbol.
508 */
509 if (verbose) {
510 /*
511 * XXX: Need to provide a less kludgy way to ask for
512 * more space per symbol, the u32 is for the index on
513 * the ui browser.
514 * See symbol__browser_index.
515 */
516 symbol_conf.priv_size += sizeof(u32);
517 symbol_conf.sort_by_name = true;
518 }
519 }
483 520
484 if (symbol__init() < 0) 521 if (symbol__init() < 0)
485 return -1; 522 return -1;
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c
index 5161619d4714..9bcc38f0b706 100644
--- a/tools/perf/builtin-timechart.c
+++ b/tools/perf/builtin-timechart.c
@@ -455,8 +455,8 @@ static void sched_switch(int cpu, u64 timestamp, struct trace_entry *te)
455 if (p->current->state != TYPE_NONE) 455 if (p->current->state != TYPE_NONE)
456 pid_put_sample(sw->next_pid, p->current->state, cpu, p->current->state_since, timestamp); 456 pid_put_sample(sw->next_pid, p->current->state, cpu, p->current->state_since, timestamp);
457 457
458 p->current->state_since = timestamp; 458 p->current->state_since = timestamp;
459 p->current->state = TYPE_RUNNING; 459 p->current->state = TYPE_RUNNING;
460 } 460 }
461 461
462 if (prev_p->current) { 462 if (prev_p->current) {
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index b513e40974f4..dd625808c2a5 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -69,7 +69,6 @@ static int target_tid = -1;
69static pid_t *all_tids = NULL; 69static pid_t *all_tids = NULL;
70static int thread_num = 0; 70static int thread_num = 0;
71static bool inherit = false; 71static bool inherit = false;
72static int profile_cpu = -1;
73static int nr_cpus = 0; 72static int nr_cpus = 0;
74static int realtime_prio = 0; 73static int realtime_prio = 0;
75static bool group = false; 74static bool group = false;
@@ -558,13 +557,13 @@ static void print_sym_table(void)
558 else 557 else
559 printf(" (all"); 558 printf(" (all");
560 559
561 if (profile_cpu != -1) 560 if (cpu_list)
562 printf(", cpu: %d)\n", profile_cpu); 561 printf(", CPU%s: %s)\n", nr_cpus > 1 ? "s" : "", cpu_list);
563 else { 562 else {
564 if (target_tid != -1) 563 if (target_tid != -1)
565 printf(")\n"); 564 printf(")\n");
566 else 565 else
567 printf(", %d CPUs)\n", nr_cpus); 566 printf(", %d CPU%s)\n", nr_cpus, nr_cpus > 1 ? "s" : "");
568 } 567 }
569 568
570 printf("%-*.*s\n", win_width, win_width, graph_dotted_line); 569 printf("%-*.*s\n", win_width, win_width, graph_dotted_line);
@@ -1187,11 +1186,10 @@ int group_fd;
1187static void start_counter(int i, int counter) 1186static void start_counter(int i, int counter)
1188{ 1187{
1189 struct perf_event_attr *attr; 1188 struct perf_event_attr *attr;
1190 int cpu; 1189 int cpu = -1;
1191 int thread_index; 1190 int thread_index;
1192 1191
1193 cpu = profile_cpu; 1192 if (target_tid == -1)
1194 if (target_tid == -1 && profile_cpu == -1)
1195 cpu = cpumap[i]; 1193 cpu = cpumap[i];
1196 1194
1197 attr = attrs + counter; 1195 attr = attrs + counter;
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index 294da725a57d..86cfe3800e6b 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -1,19 +1,24 @@
1#include "builtin.h" 1#include "builtin.h"
2 2
3#include "util/util.h" 3#include "perf.h"
4#include "util/cache.h" 4#include "util/cache.h"
5#include "util/debug.h"
6#include "util/exec_cmd.h"
7#include "util/header.h"
8#include "util/parse-options.h"
9#include "util/session.h"
5#include "util/symbol.h" 10#include "util/symbol.h"
6#include "util/thread.h" 11#include "util/thread.h"
7#include "util/header.h"
8#include "util/exec_cmd.h"
9#include "util/trace-event.h" 12#include "util/trace-event.h"
10#include "util/session.h" 13#include "util/parse-options.h"
14#include "util/util.h"
11 15
12static char const *script_name; 16static char const *script_name;
13static char const *generate_script_lang; 17static char const *generate_script_lang;
14static bool debug_mode; 18static bool debug_mode;
15static u64 last_timestamp; 19static u64 last_timestamp;
16static u64 nr_unordered; 20static u64 nr_unordered;
21extern const struct option record_options[];
17 22
18static int default_start_script(const char *script __unused, 23static int default_start_script(const char *script __unused,
19 int argc __unused, 24 int argc __unused,
@@ -43,9 +48,6 @@ static struct scripting_ops *scripting_ops;
43 48
44static void setup_scripting(void) 49static void setup_scripting(void)
45{ 50{
46 /* make sure PERF_EXEC_PATH is set for scripts */
47 perf_set_argv_exec_path(perf_exec_path());
48
49 setup_perl_scripting(); 51 setup_perl_scripting();
50 setup_python_scripting(); 52 setup_python_scripting();
51 53
@@ -59,14 +61,6 @@ static int cleanup_scripting(void)
59 return scripting_ops->stop_script(); 61 return scripting_ops->stop_script();
60} 62}
61 63
62#include "util/parse-options.h"
63
64#include "perf.h"
65#include "util/debug.h"
66
67#include "util/trace-event.h"
68#include "util/exec_cmd.h"
69
70static char const *input_name = "perf.data"; 64static char const *input_name = "perf.data";
71 65
72static int process_sample_event(event_t *event, struct perf_session *session) 66static int process_sample_event(event_t *event, struct perf_session *session)
@@ -290,7 +284,7 @@ static int parse_scriptname(const struct option *opt __used,
290 script++; 284 script++;
291 } else { 285 } else {
292 script = str; 286 script = str;
293 ext = strchr(script, '.'); 287 ext = strrchr(script, '.');
294 if (!ext) { 288 if (!ext) {
295 fprintf(stderr, "invalid script extension"); 289 fprintf(stderr, "invalid script extension");
296 return -1; 290 return -1;
@@ -336,7 +330,7 @@ static struct script_desc *script_desc__new(const char *name)
336{ 330{
337 struct script_desc *s = zalloc(sizeof(*s)); 331 struct script_desc *s = zalloc(sizeof(*s));
338 332
339 if (s != NULL) 333 if (s != NULL && name)
340 s->name = strdup(name); 334 s->name = strdup(name);
341 335
342 return s; 336 return s;
@@ -345,6 +339,8 @@ static struct script_desc *script_desc__new(const char *name)
345static void script_desc__delete(struct script_desc *s) 339static void script_desc__delete(struct script_desc *s)
346{ 340{
347 free(s->name); 341 free(s->name);
342 free(s->half_liner);
343 free(s->args);
348 free(s); 344 free(s);
349} 345}
350 346
@@ -545,8 +541,40 @@ static char *get_script_path(const char *script_root, const char *suffix)
545 return path; 541 return path;
546} 542}
547 543
544static bool is_top_script(const char *script_path)
545{
546 return ends_with((char *)script_path, "top") == NULL ? false : true;
547}
548
549static int has_required_arg(char *script_path)
550{
551 struct script_desc *desc;
552 int n_args = 0;
553 char *p;
554
555 desc = script_desc__new(NULL);
556
557 if (read_script_info(desc, script_path))
558 goto out;
559
560 if (!desc->args)
561 goto out;
562
563 for (p = desc->args; *p; p++)
564 if (*p == '<')
565 n_args++;
566out:
567 script_desc__delete(desc);
568
569 return n_args;
570}
571
548static const char * const trace_usage[] = { 572static const char * const trace_usage[] = {
549 "perf trace [<options>] <command>", 573 "perf trace [<options>]",
574 "perf trace [<options>] record <script> [<record-options>] <command>",
575 "perf trace [<options>] report <script> [script-args]",
576 "perf trace [<options>] <script> [<record-options>] <command>",
577 "perf trace [<options>] <top-script> [script-args]",
550 NULL 578 NULL
551}; 579};
552 580
@@ -572,47 +600,81 @@ static const struct option options[] = {
572 OPT_END() 600 OPT_END()
573}; 601};
574 602
603static bool have_cmd(int argc, const char **argv)
604{
605 char **__argv = malloc(sizeof(const char *) * argc);
606
607 if (!__argv)
608 die("malloc");
609 memcpy(__argv, argv, sizeof(const char *) * argc);
610 argc = parse_options(argc, (const char **)__argv, record_options,
611 NULL, PARSE_OPT_STOP_AT_NON_OPTION);
612 free(__argv);
613
614 return argc != 0;
615}
616
575int cmd_trace(int argc, const char **argv, const char *prefix __used) 617int cmd_trace(int argc, const char **argv, const char *prefix __used)
576{ 618{
619 char *rec_script_path = NULL;
620 char *rep_script_path = NULL;
577 struct perf_session *session; 621 struct perf_session *session;
578 const char *suffix = NULL; 622 char *script_path = NULL;
579 const char **__argv; 623 const char **__argv;
580 char *script_path; 624 bool system_wide;
581 int i, err; 625 int i, j, err;
582 626
583 if (argc >= 2 && strncmp(argv[1], "rec", strlen("rec")) == 0) { 627 setup_scripting();
584 if (argc < 3) { 628
585 fprintf(stderr, 629 argc = parse_options(argc, argv, options, trace_usage,
586 "Please specify a record script\n"); 630 PARSE_OPT_STOP_AT_NON_OPTION);
587 return -1; 631
588 } 632 if (argc > 1 && !strncmp(argv[0], "rec", strlen("rec"))) {
589 suffix = RECORD_SUFFIX; 633 rec_script_path = get_script_path(argv[1], RECORD_SUFFIX);
634 if (!rec_script_path)
635 return cmd_record(argc, argv, NULL);
590 } 636 }
591 637
592 if (argc >= 2 && strncmp(argv[1], "rep", strlen("rep")) == 0) { 638 if (argc > 1 && !strncmp(argv[0], "rep", strlen("rep"))) {
593 if (argc < 3) { 639 rep_script_path = get_script_path(argv[1], REPORT_SUFFIX);
640 if (!rep_script_path) {
594 fprintf(stderr, 641 fprintf(stderr,
595 "Please specify a report script\n"); 642 "Please specify a valid report script"
643 "(see 'perf trace -l' for listing)\n");
596 return -1; 644 return -1;
597 } 645 }
598 suffix = REPORT_SUFFIX;
599 } 646 }
600 647
601 if (!suffix && argc >= 2 && strncmp(argv[1], "-", strlen("-")) != 0) { 648 /* make sure PERF_EXEC_PATH is set for scripts */
602 char *record_script_path, *report_script_path; 649 perf_set_argv_exec_path(perf_exec_path());
650
651 if (argc && !script_name && !rec_script_path && !rep_script_path) {
603 int live_pipe[2]; 652 int live_pipe[2];
653 int rep_args;
604 pid_t pid; 654 pid_t pid;
605 655
606 record_script_path = get_script_path(argv[1], RECORD_SUFFIX); 656 rec_script_path = get_script_path(argv[0], RECORD_SUFFIX);
607 if (!record_script_path) { 657 rep_script_path = get_script_path(argv[0], REPORT_SUFFIX);
608 fprintf(stderr, "record script not found\n"); 658
609 return -1; 659 if (!rec_script_path && !rep_script_path) {
660 fprintf(stderr, " Couldn't find script %s\n\n See perf"
661 " trace -l for available scripts.\n", argv[0]);
662 usage_with_options(trace_usage, options);
610 } 663 }
611 664
612 report_script_path = get_script_path(argv[1], REPORT_SUFFIX); 665 if (is_top_script(argv[0])) {
613 if (!report_script_path) { 666 rep_args = argc - 1;
614 fprintf(stderr, "report script not found\n"); 667 } else {
615 return -1; 668 int rec_args;
669
670 rep_args = has_required_arg(rep_script_path);
671 rec_args = (argc - 1) - rep_args;
672 if (rec_args < 0) {
673 fprintf(stderr, " %s script requires options."
674 "\n\n See perf trace -l for available "
675 "scripts and options.\n", argv[0]);
676 usage_with_options(trace_usage, options);
677 }
616 } 678 }
617 679
618 if (pipe(live_pipe) < 0) { 680 if (pipe(live_pipe) < 0) {
@@ -627,59 +689,84 @@ int cmd_trace(int argc, const char **argv, const char *prefix __used)
627 } 689 }
628 690
629 if (!pid) { 691 if (!pid) {
692 system_wide = true;
693 j = 0;
694
630 dup2(live_pipe[1], 1); 695 dup2(live_pipe[1], 1);
631 close(live_pipe[0]); 696 close(live_pipe[0]);
632 697
633 __argv = malloc(5 * sizeof(const char *)); 698 if (!is_top_script(argv[0]))
634 __argv[0] = "/bin/sh"; 699 system_wide = !have_cmd(argc - rep_args,
635 __argv[1] = record_script_path; 700 &argv[rep_args]);
636 __argv[2] = "-o"; 701
637 __argv[3] = "-"; 702 __argv = malloc((argc + 6) * sizeof(const char *));
638 __argv[4] = NULL; 703 if (!__argv)
704 die("malloc");
705
706 __argv[j++] = "/bin/sh";
707 __argv[j++] = rec_script_path;
708 if (system_wide)
709 __argv[j++] = "-a";
710 __argv[j++] = "-q";
711 __argv[j++] = "-o";
712 __argv[j++] = "-";
713 for (i = rep_args + 1; i < argc; i++)
714 __argv[j++] = argv[i];
715 __argv[j++] = NULL;
639 716
640 execvp("/bin/sh", (char **)__argv); 717 execvp("/bin/sh", (char **)__argv);
718 free(__argv);
641 exit(-1); 719 exit(-1);
642 } 720 }
643 721
644 dup2(live_pipe[0], 0); 722 dup2(live_pipe[0], 0);
645 close(live_pipe[1]); 723 close(live_pipe[1]);
646 724
647 __argv = malloc((argc + 3) * sizeof(const char *)); 725 __argv = malloc((argc + 4) * sizeof(const char *));
648 __argv[0] = "/bin/sh"; 726 if (!__argv)
649 __argv[1] = report_script_path; 727 die("malloc");
650 for (i = 2; i < argc; i++) 728 j = 0;
651 __argv[i] = argv[i]; 729 __argv[j++] = "/bin/sh";
652 __argv[i++] = "-i"; 730 __argv[j++] = rep_script_path;
653 __argv[i++] = "-"; 731 for (i = 1; i < rep_args + 1; i++)
654 __argv[i++] = NULL; 732 __argv[j++] = argv[i];
733 __argv[j++] = "-i";
734 __argv[j++] = "-";
735 __argv[j++] = NULL;
655 736
656 execvp("/bin/sh", (char **)__argv); 737 execvp("/bin/sh", (char **)__argv);
738 free(__argv);
657 exit(-1); 739 exit(-1);
658 } 740 }
659 741
660 if (suffix) { 742 if (rec_script_path)
661 script_path = get_script_path(argv[2], suffix); 743 script_path = rec_script_path;
662 if (!script_path) { 744 if (rep_script_path)
663 fprintf(stderr, "script not found\n"); 745 script_path = rep_script_path;
664 return -1; 746
665 } 747 if (script_path) {
666 748 system_wide = false;
667 __argv = malloc((argc + 1) * sizeof(const char *)); 749 j = 0;
668 __argv[0] = "/bin/sh"; 750
669 __argv[1] = script_path; 751 if (rec_script_path)
670 for (i = 3; i < argc; i++) 752 system_wide = !have_cmd(argc - 1, &argv[1]);
671 __argv[i - 1] = argv[i]; 753
672 __argv[argc - 1] = NULL; 754 __argv = malloc((argc + 2) * sizeof(const char *));
755 if (!__argv)
756 die("malloc");
757 __argv[j++] = "/bin/sh";
758 __argv[j++] = script_path;
759 if (system_wide)
760 __argv[j++] = "-a";
761 for (i = 2; i < argc; i++)
762 __argv[j++] = argv[i];
763 __argv[j++] = NULL;
673 764
674 execvp("/bin/sh", (char **)__argv); 765 execvp("/bin/sh", (char **)__argv);
766 free(__argv);
675 exit(-1); 767 exit(-1);
676 } 768 }
677 769
678 setup_scripting();
679
680 argc = parse_options(argc, argv, options, trace_usage,
681 PARSE_OPT_STOP_AT_NON_OPTION);
682
683 if (symbol__init() < 0) 770 if (symbol__init() < 0)
684 return -1; 771 return -1;
685 if (!script_name) 772 if (!script_name)
diff --git a/tools/perf/feature-tests.mak b/tools/perf/feature-tests.mak
index ddb68e601f0e..b253db634f04 100644
--- a/tools/perf/feature-tests.mak
+++ b/tools/perf/feature-tests.mak
@@ -110,10 +110,21 @@ int main(void)
110} 110}
111endef 111endef
112 112
113define SOURCE_STRLCPY
114#include <stdlib.h>
115extern size_t strlcpy(char *dest, const char *src, size_t size);
116
117int main(void)
118{
119 strlcpy(NULL, NULL, 0);
120 return 0;
121}
122endef
123
113# try-cc 124# try-cc
114# Usage: option = $(call try-cc, source-to-build, cc-options) 125# Usage: option = $(call try-cc, source-to-build, cc-options)
115try-cc = $(shell sh -c \ 126try-cc = $(shell sh -c \
116 'TMP="$(TMPOUT).$$$$"; \ 127 'TMP="$(OUTPUT)$(TMPOUT).$$$$"; \
117 echo "$(1)" | \ 128 echo "$(1)" | \
118 $(CC) -x c - $(2) -o "$$TMP" > /dev/null 2>&1 && echo y; \ 129 $(CC) -x c - $(2) -o "$$TMP" > /dev/null 2>&1 && echo y; \
119 rm -f "$$TMP"') 130 rm -f "$$TMP"')
diff --git a/tools/perf/perf.h b/tools/perf/perf.h
index ef7aa0a0c526..95aaf565c704 100644
--- a/tools/perf/perf.h
+++ b/tools/perf/perf.h
@@ -73,6 +73,18 @@ void get_term_dimensions(struct winsize *ws);
73#define cpu_relax() asm volatile("":::"memory") 73#define cpu_relax() asm volatile("":::"memory")
74#endif 74#endif
75 75
76#ifdef __mips__
77#include "../../arch/mips/include/asm/unistd.h"
78#define rmb() asm volatile( \
79 ".set mips2\n\t" \
80 "sync\n\t" \
81 ".set mips0" \
82 : /* no output */ \
83 : /* no input */ \
84 : "memory")
85#define cpu_relax() asm volatile("" ::: "memory")
86#endif
87
76#include <time.h> 88#include <time.h>
77#include <unistd.h> 89#include <unistd.h>
78#include <sys/types.h> 90#include <sys/types.h>
diff --git a/tools/perf/scripts/perl/bin/failed-syscalls-record b/tools/perf/scripts/perl/bin/failed-syscalls-record
index eb5846bcb565..8104895a7b67 100644
--- a/tools/perf/scripts/perl/bin/failed-syscalls-record
+++ b/tools/perf/scripts/perl/bin/failed-syscalls-record
@@ -1,2 +1,2 @@
1#!/bin/bash 1#!/bin/bash
2perf record -a -e raw_syscalls:sys_exit $@ 2perf record -e raw_syscalls:sys_exit $@
diff --git a/tools/perf/scripts/perl/bin/failed-syscalls-report b/tools/perf/scripts/perl/bin/failed-syscalls-report
index e3a5e55d54ff..4028d92dc4ae 100644
--- a/tools/perf/scripts/perl/bin/failed-syscalls-report
+++ b/tools/perf/scripts/perl/bin/failed-syscalls-report
@@ -7,4 +7,4 @@ if [ $# -gt 0 ] ; then
7 shift 7 shift
8 fi 8 fi
9fi 9fi
10perf trace $@ -s ~/libexec/perf-core/scripts/perl/failed-syscalls.pl $comm 10perf trace $@ -s "$PERF_EXEC_PATH"/scripts/perl/failed-syscalls.pl $comm
diff --git a/tools/perf/scripts/perl/bin/rw-by-file-record b/tools/perf/scripts/perl/bin/rw-by-file-record
index 5bfaae5a6cba..33efc8673aae 100644
--- a/tools/perf/scripts/perl/bin/rw-by-file-record
+++ b/tools/perf/scripts/perl/bin/rw-by-file-record
@@ -1,3 +1,3 @@
1#!/bin/bash 1#!/bin/bash
2perf record -a -e syscalls:sys_enter_read -e syscalls:sys_enter_write $@ 2perf record -e syscalls:sys_enter_read -e syscalls:sys_enter_write $@
3 3
diff --git a/tools/perf/scripts/perl/bin/rw-by-file-report b/tools/perf/scripts/perl/bin/rw-by-file-report
index d83070b7eeb5..ba25f4d41fb0 100644
--- a/tools/perf/scripts/perl/bin/rw-by-file-report
+++ b/tools/perf/scripts/perl/bin/rw-by-file-report
@@ -7,7 +7,7 @@ if [ $# -lt 1 ] ; then
7fi 7fi
8comm=$1 8comm=$1
9shift 9shift
10perf trace $@ -s ~/libexec/perf-core/scripts/perl/rw-by-file.pl $comm 10perf trace $@ -s "$PERF_EXEC_PATH"/scripts/perl/rw-by-file.pl $comm
11 11
12 12
13 13
diff --git a/tools/perf/scripts/perl/bin/rw-by-pid-record b/tools/perf/scripts/perl/bin/rw-by-pid-record
index 6e0b2f7755ac..7cb9db230448 100644
--- a/tools/perf/scripts/perl/bin/rw-by-pid-record
+++ b/tools/perf/scripts/perl/bin/rw-by-pid-record
@@ -1,2 +1,2 @@
1#!/bin/bash 1#!/bin/bash
2perf record -a -e syscalls:sys_enter_read -e syscalls:sys_exit_read -e syscalls:sys_enter_write -e syscalls:sys_exit_write $@ 2perf record -e syscalls:sys_enter_read -e syscalls:sys_exit_read -e syscalls:sys_enter_write -e syscalls:sys_exit_write $@
diff --git a/tools/perf/scripts/perl/bin/rw-by-pid-report b/tools/perf/scripts/perl/bin/rw-by-pid-report
index 7ef46983f62f..641a3f5d085c 100644
--- a/tools/perf/scripts/perl/bin/rw-by-pid-report
+++ b/tools/perf/scripts/perl/bin/rw-by-pid-report
@@ -1,6 +1,6 @@
1#!/bin/bash 1#!/bin/bash
2# description: system-wide r/w activity 2# description: system-wide r/w activity
3perf trace $@ -s ~/libexec/perf-core/scripts/perl/rw-by-pid.pl 3perf trace $@ -s "$PERF_EXEC_PATH"/scripts/perl/rw-by-pid.pl
4 4
5 5
6 6
diff --git a/tools/perf/scripts/perl/bin/rwtop-record b/tools/perf/scripts/perl/bin/rwtop-record
index 6e0b2f7755ac..7cb9db230448 100644
--- a/tools/perf/scripts/perl/bin/rwtop-record
+++ b/tools/perf/scripts/perl/bin/rwtop-record
@@ -1,2 +1,2 @@
1#!/bin/bash 1#!/bin/bash
2perf record -a -e syscalls:sys_enter_read -e syscalls:sys_exit_read -e syscalls:sys_enter_write -e syscalls:sys_exit_write $@ 2perf record -e syscalls:sys_enter_read -e syscalls:sys_exit_read -e syscalls:sys_enter_write -e syscalls:sys_exit_write $@
diff --git a/tools/perf/scripts/perl/bin/rwtop-report b/tools/perf/scripts/perl/bin/rwtop-report
index 93e698cd3f38..4918dba77021 100644
--- a/tools/perf/scripts/perl/bin/rwtop-report
+++ b/tools/perf/scripts/perl/bin/rwtop-report
@@ -17,7 +17,7 @@ if [ "$n_args" -gt 0 ] ; then
17 interval=$1 17 interval=$1
18 shift 18 shift
19fi 19fi
20perf trace $@ -s ~/libexec/perf-core/scripts/perl/rwtop.pl $interval 20perf trace $@ -s "$PERF_EXEC_PATH"/scripts/perl/rwtop.pl $interval
21 21
22 22
23 23
diff --git a/tools/perf/scripts/perl/bin/wakeup-latency-record b/tools/perf/scripts/perl/bin/wakeup-latency-record
index 9f2acaaae9f0..464251a1bd7e 100644
--- a/tools/perf/scripts/perl/bin/wakeup-latency-record
+++ b/tools/perf/scripts/perl/bin/wakeup-latency-record
@@ -1,5 +1,5 @@
1#!/bin/bash 1#!/bin/bash
2perf record -a -e sched:sched_switch -e sched:sched_wakeup $@ 2perf record -e sched:sched_switch -e sched:sched_wakeup $@
3 3
4 4
5 5
diff --git a/tools/perf/scripts/perl/bin/wakeup-latency-report b/tools/perf/scripts/perl/bin/wakeup-latency-report
index a0d898f9ca1d..49052ebcb632 100644
--- a/tools/perf/scripts/perl/bin/wakeup-latency-report
+++ b/tools/perf/scripts/perl/bin/wakeup-latency-report
@@ -1,6 +1,6 @@
1#!/bin/bash 1#!/bin/bash
2# description: system-wide min/max/avg wakeup latency 2# description: system-wide min/max/avg wakeup latency
3perf trace $@ -s ~/libexec/perf-core/scripts/perl/wakeup-latency.pl 3perf trace $@ -s "$PERF_EXEC_PATH"/scripts/perl/wakeup-latency.pl
4 4
5 5
6 6
diff --git a/tools/perf/scripts/perl/bin/workqueue-stats-record b/tools/perf/scripts/perl/bin/workqueue-stats-record
index 85301f2471ff..8edda9078d5d 100644
--- a/tools/perf/scripts/perl/bin/workqueue-stats-record
+++ b/tools/perf/scripts/perl/bin/workqueue-stats-record
@@ -1,2 +1,2 @@
1#!/bin/bash 1#!/bin/bash
2perf record -a -e workqueue:workqueue_creation -e workqueue:workqueue_destruction -e workqueue:workqueue_execution -e workqueue:workqueue_insertion $@ 2perf record -e workqueue:workqueue_creation -e workqueue:workqueue_destruction -e workqueue:workqueue_execution -e workqueue:workqueue_insertion $@
diff --git a/tools/perf/scripts/perl/bin/workqueue-stats-report b/tools/perf/scripts/perl/bin/workqueue-stats-report
index 35081132ef97..df0c65f4ca93 100644
--- a/tools/perf/scripts/perl/bin/workqueue-stats-report
+++ b/tools/perf/scripts/perl/bin/workqueue-stats-report
@@ -1,6 +1,6 @@
1#!/bin/bash 1#!/bin/bash
2# description: workqueue stats (ins/exe/create/destroy) 2# description: workqueue stats (ins/exe/create/destroy)
3perf trace $@ -s ~/libexec/perf-core/scripts/perl/workqueue-stats.pl 3perf trace $@ -s "$PERF_EXEC_PATH"/scripts/perl/workqueue-stats.pl
4 4
5 5
6 6
diff --git a/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py
index 9689bc0acd9f..13cc02b5893a 100644
--- a/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py
+++ b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py
@@ -6,6 +6,14 @@
6# Public License ("GPL") version 2 as published by the Free Software 6# Public License ("GPL") version 2 as published by the Free Software
7# Foundation. 7# Foundation.
8 8
9import errno, os
10
11FUTEX_WAIT = 0
12FUTEX_WAKE = 1
13FUTEX_PRIVATE_FLAG = 128
14FUTEX_CLOCK_REALTIME = 256
15FUTEX_CMD_MASK = ~(FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME)
16
9NSECS_PER_SEC = 1000000000 17NSECS_PER_SEC = 1000000000
10 18
11def avg(total, n): 19def avg(total, n):
@@ -24,5 +32,55 @@ def nsecs_str(nsecs):
24 str = "%5u.%09u" % (nsecs_secs(nsecs), nsecs_nsecs(nsecs)), 32 str = "%5u.%09u" % (nsecs_secs(nsecs), nsecs_nsecs(nsecs)),
25 return str 33 return str
26 34
35def add_stats(dict, key, value):
36 if not dict.has_key(key):
37 dict[key] = (value, value, value, 1)
38 else:
39 min, max, avg, count = dict[key]
40 if value < min:
41 min = value
42 if value > max:
43 max = value
44 avg = (avg + value) / 2
45 dict[key] = (min, max, avg, count + 1)
46
27def clear_term(): 47def clear_term():
28 print("\x1b[H\x1b[2J") 48 print("\x1b[H\x1b[2J")
49
50audit_package_warned = False
51
52try:
53 import audit
54 machine_to_id = {
55 'x86_64': audit.MACH_86_64,
56 'alpha' : audit.MACH_ALPHA,
57 'ia64' : audit.MACH_IA64,
58 'ppc' : audit.MACH_PPC,
59 'ppc64' : audit.MACH_PPC64,
60 's390' : audit.MACH_S390,
61 's390x' : audit.MACH_S390X,
62 'i386' : audit.MACH_X86,
63 'i586' : audit.MACH_X86,
64 'i686' : audit.MACH_X86,
65 }
66 try:
67 machine_to_id['armeb'] = audit.MACH_ARMEB
68 except:
69 pass
70 machine_id = machine_to_id[os.uname()[4]]
71except:
72 if not audit_package_warned:
73 audit_package_warned = True
74 print "Install the audit-libs-python package to get syscall names"
75
76def syscall_name(id):
77 try:
78 return audit.audit_syscall_to_name(id, machine_id)
79 except:
80 return str(id)
81
82def strerror(nr):
83 try:
84 return errno.errorcode[abs(nr)]
85 except:
86 return "Unknown %d errno" % nr
diff --git a/tools/perf/scripts/python/bin/failed-syscalls-by-pid-record b/tools/perf/scripts/python/bin/failed-syscalls-by-pid-record
index eb5846bcb565..8104895a7b67 100644
--- a/tools/perf/scripts/python/bin/failed-syscalls-by-pid-record
+++ b/tools/perf/scripts/python/bin/failed-syscalls-by-pid-record
@@ -1,2 +1,2 @@
1#!/bin/bash 1#!/bin/bash
2perf record -a -e raw_syscalls:sys_exit $@ 2perf record -e raw_syscalls:sys_exit $@
diff --git a/tools/perf/scripts/python/bin/failed-syscalls-by-pid-report b/tools/perf/scripts/python/bin/failed-syscalls-by-pid-report
index 30293545fcc2..03587021463d 100644
--- a/tools/perf/scripts/python/bin/failed-syscalls-by-pid-report
+++ b/tools/perf/scripts/python/bin/failed-syscalls-by-pid-report
@@ -7,4 +7,4 @@ if [ $# -gt 0 ] ; then
7 shift 7 shift
8 fi 8 fi
9fi 9fi
10perf trace $@ -s ~/libexec/perf-core/scripts/python/failed-syscalls-by-pid.py $comm 10perf trace $@ -s "$PERF_EXEC_PATH"/scripts/python/failed-syscalls-by-pid.py $comm
diff --git a/tools/perf/scripts/python/bin/futex-contention-record b/tools/perf/scripts/python/bin/futex-contention-record
new file mode 100644
index 000000000000..b1495c9a9b20
--- /dev/null
+++ b/tools/perf/scripts/python/bin/futex-contention-record
@@ -0,0 +1,2 @@
1#!/bin/bash
2perf record -e syscalls:sys_enter_futex -e syscalls:sys_exit_futex $@
diff --git a/tools/perf/scripts/python/bin/futex-contention-report b/tools/perf/scripts/python/bin/futex-contention-report
new file mode 100644
index 000000000000..c8268138fb7e
--- /dev/null
+++ b/tools/perf/scripts/python/bin/futex-contention-report
@@ -0,0 +1,4 @@
1#!/bin/bash
2# description: futext contention measurement
3
4perf trace $@ -s "$PERF_EXEC_PATH"/scripts/python/futex-contention.py
diff --git a/tools/perf/scripts/python/bin/netdev-times-record b/tools/perf/scripts/python/bin/netdev-times-record
new file mode 100644
index 000000000000..558754b840a9
--- /dev/null
+++ b/tools/perf/scripts/python/bin/netdev-times-record
@@ -0,0 +1,8 @@
1#!/bin/bash
2perf record -e net:net_dev_xmit -e net:net_dev_queue \
3 -e net:netif_receive_skb -e net:netif_rx \
4 -e skb:consume_skb -e skb:kfree_skb \
5 -e skb:skb_copy_datagram_iovec -e napi:napi_poll \
6 -e irq:irq_handler_entry -e irq:irq_handler_exit \
7 -e irq:softirq_entry -e irq:softirq_exit \
8 -e irq:softirq_raise $@
diff --git a/tools/perf/scripts/python/bin/netdev-times-report b/tools/perf/scripts/python/bin/netdev-times-report
new file mode 100644
index 000000000000..4ad361b31249
--- /dev/null
+++ b/tools/perf/scripts/python/bin/netdev-times-report
@@ -0,0 +1,5 @@
1#!/bin/bash
2# description: display a process of packet and processing time
3# args: [tx] [rx] [dev=] [debug]
4
5perf trace -s "$PERF_EXEC_PATH"/scripts/python/netdev-times.py $@
diff --git a/tools/perf/scripts/python/bin/sched-migration-record b/tools/perf/scripts/python/bin/sched-migration-record
index 17a3e9bd9e8f..7493fddbe995 100644
--- a/tools/perf/scripts/python/bin/sched-migration-record
+++ b/tools/perf/scripts/python/bin/sched-migration-record
@@ -1,2 +1,2 @@
1#!/bin/bash 1#!/bin/bash
2perf record -m 16384 -a -e sched:sched_wakeup -e sched:sched_wakeup_new -e sched:sched_switch -e sched:sched_migrate_task $@ 2perf record -m 16384 -e sched:sched_wakeup -e sched:sched_wakeup_new -e sched:sched_switch -e sched:sched_migrate_task $@
diff --git a/tools/perf/scripts/python/bin/sched-migration-report b/tools/perf/scripts/python/bin/sched-migration-report
index 61d05f72e443..df1791f07c24 100644
--- a/tools/perf/scripts/python/bin/sched-migration-report
+++ b/tools/perf/scripts/python/bin/sched-migration-report
@@ -1,3 +1,3 @@
1#!/bin/bash 1#!/bin/bash
2# description: sched migration overview 2# description: sched migration overview
3perf trace $@ -s ~/libexec/perf-core/scripts/python/sched-migration.py 3perf trace $@ -s "$PERF_EXEC_PATH"/scripts/python/sched-migration.py
diff --git a/tools/perf/scripts/python/bin/sctop-record b/tools/perf/scripts/python/bin/sctop-record
index 1fc5998b721d..4efbfaa7f6a5 100644
--- a/tools/perf/scripts/python/bin/sctop-record
+++ b/tools/perf/scripts/python/bin/sctop-record
@@ -1,2 +1,2 @@
1#!/bin/bash 1#!/bin/bash
2perf record -a -e raw_syscalls:sys_enter $@ 2perf record -e raw_syscalls:sys_enter $@
diff --git a/tools/perf/scripts/python/bin/sctop-report b/tools/perf/scripts/python/bin/sctop-report
index b01c842ae7b4..36b409c05e50 100644
--- a/tools/perf/scripts/python/bin/sctop-report
+++ b/tools/perf/scripts/python/bin/sctop-report
@@ -21,4 +21,4 @@ elif [ "$n_args" -gt 0 ] ; then
21 interval=$1 21 interval=$1
22 shift 22 shift
23fi 23fi
24perf trace $@ -s ~/libexec/perf-core/scripts/python/sctop.py $comm $interval 24perf trace $@ -s "$PERF_EXEC_PATH"/scripts/python/sctop.py $comm $interval
diff --git a/tools/perf/scripts/python/bin/syscall-counts-by-pid-record b/tools/perf/scripts/python/bin/syscall-counts-by-pid-record
index 1fc5998b721d..4efbfaa7f6a5 100644
--- a/tools/perf/scripts/python/bin/syscall-counts-by-pid-record
+++ b/tools/perf/scripts/python/bin/syscall-counts-by-pid-record
@@ -1,2 +1,2 @@
1#!/bin/bash 1#!/bin/bash
2perf record -a -e raw_syscalls:sys_enter $@ 2perf record -e raw_syscalls:sys_enter $@
diff --git a/tools/perf/scripts/python/bin/syscall-counts-by-pid-report b/tools/perf/scripts/python/bin/syscall-counts-by-pid-report
index 9e9d8ddd72ce..4eb88c9fc83c 100644
--- a/tools/perf/scripts/python/bin/syscall-counts-by-pid-report
+++ b/tools/perf/scripts/python/bin/syscall-counts-by-pid-report
@@ -7,4 +7,4 @@ if [ $# -gt 0 ] ; then
7 shift 7 shift
8 fi 8 fi
9fi 9fi
10perf trace $@ -s ~/libexec/perf-core/scripts/python/syscall-counts-by-pid.py $comm 10perf trace $@ -s "$PERF_EXEC_PATH"/scripts/python/syscall-counts-by-pid.py $comm
diff --git a/tools/perf/scripts/python/bin/syscall-counts-record b/tools/perf/scripts/python/bin/syscall-counts-record
index 1fc5998b721d..4efbfaa7f6a5 100644
--- a/tools/perf/scripts/python/bin/syscall-counts-record
+++ b/tools/perf/scripts/python/bin/syscall-counts-record
@@ -1,2 +1,2 @@
1#!/bin/bash 1#!/bin/bash
2perf record -a -e raw_syscalls:sys_enter $@ 2perf record -e raw_syscalls:sys_enter $@
diff --git a/tools/perf/scripts/python/bin/syscall-counts-report b/tools/perf/scripts/python/bin/syscall-counts-report
index dc076b618796..cb2f9c5cf17e 100644
--- a/tools/perf/scripts/python/bin/syscall-counts-report
+++ b/tools/perf/scripts/python/bin/syscall-counts-report
@@ -7,4 +7,4 @@ if [ $# -gt 0 ] ; then
7 shift 7 shift
8 fi 8 fi
9fi 9fi
10perf trace $@ -s ~/libexec/perf-core/scripts/python/syscall-counts.py $comm 10perf trace $@ -s "$PERF_EXEC_PATH"/scripts/python/syscall-counts.py $comm
diff --git a/tools/perf/scripts/python/failed-syscalls-by-pid.py b/tools/perf/scripts/python/failed-syscalls-by-pid.py
index 0ca02278fe69..acd7848717b3 100644
--- a/tools/perf/scripts/python/failed-syscalls-by-pid.py
+++ b/tools/perf/scripts/python/failed-syscalls-by-pid.py
@@ -13,21 +13,26 @@ sys.path.append(os.environ['PERF_EXEC_PATH'] + \
13 13
14from perf_trace_context import * 14from perf_trace_context import *
15from Core import * 15from Core import *
16from Util import *
16 17
17usage = "perf trace -s syscall-counts-by-pid.py [comm]\n"; 18usage = "perf trace -s syscall-counts-by-pid.py [comm|pid]\n";
18 19
19for_comm = None 20for_comm = None
21for_pid = None
20 22
21if len(sys.argv) > 2: 23if len(sys.argv) > 2:
22 sys.exit(usage) 24 sys.exit(usage)
23 25
24if len(sys.argv) > 1: 26if len(sys.argv) > 1:
25 for_comm = sys.argv[1] 27 try:
28 for_pid = int(sys.argv[1])
29 except:
30 for_comm = sys.argv[1]
26 31
27syscalls = autodict() 32syscalls = autodict()
28 33
29def trace_begin(): 34def trace_begin():
30 pass 35 print "Press control+C to stop and show the summary"
31 36
32def trace_end(): 37def trace_end():
33 print_error_totals() 38 print_error_totals()
@@ -35,9 +40,9 @@ def trace_end():
35def raw_syscalls__sys_exit(event_name, context, common_cpu, 40def raw_syscalls__sys_exit(event_name, context, common_cpu,
36 common_secs, common_nsecs, common_pid, common_comm, 41 common_secs, common_nsecs, common_pid, common_comm,
37 id, ret): 42 id, ret):
38 if for_comm is not None: 43 if (for_comm and common_comm != for_comm) or \
39 if common_comm != for_comm: 44 (for_pid and common_pid != for_pid ):
40 return 45 return
41 46
42 if ret < 0: 47 if ret < 0:
43 try: 48 try:
@@ -62,7 +67,7 @@ def print_error_totals():
62 print "\n%s [%d]\n" % (comm, pid), 67 print "\n%s [%d]\n" % (comm, pid),
63 id_keys = syscalls[comm][pid].keys() 68 id_keys = syscalls[comm][pid].keys()
64 for id in id_keys: 69 for id in id_keys:
65 print " syscall: %-16d\n" % (id), 70 print " syscall: %-16s\n" % syscall_name(id),
66 ret_keys = syscalls[comm][pid][id].keys() 71 ret_keys = syscalls[comm][pid][id].keys()
67 for ret, val in sorted(syscalls[comm][pid][id].iteritems(), key = lambda(k, v): (v, k), reverse = True): 72 for ret, val in sorted(syscalls[comm][pid][id].iteritems(), key = lambda(k, v): (v, k), reverse = True):
68 print " err = %-20d %10d\n" % (ret, val), 73 print " err = %-20s %10d\n" % (strerror(ret), val),
diff --git a/tools/perf/scripts/python/futex-contention.py b/tools/perf/scripts/python/futex-contention.py
new file mode 100644
index 000000000000..11e70a388d41
--- /dev/null
+++ b/tools/perf/scripts/python/futex-contention.py
@@ -0,0 +1,50 @@
1# futex contention
2# (c) 2010, Arnaldo Carvalho de Melo <acme@redhat.com>
3# Licensed under the terms of the GNU GPL License version 2
4#
5# Translation of:
6#
7# http://sourceware.org/systemtap/wiki/WSFutexContention
8#
9# to perf python scripting.
10#
11# Measures futex contention
12
13import os, sys
14sys.path.append(os.environ['PERF_EXEC_PATH'] + '/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
15from Util import *
16
17process_names = {}
18thread_thislock = {}
19thread_blocktime = {}
20
21lock_waits = {} # long-lived stats on (tid,lock) blockage elapsed time
22process_names = {} # long-lived pid-to-execname mapping
23
24def syscalls__sys_enter_futex(event, ctxt, cpu, s, ns, tid, comm,
25 nr, uaddr, op, val, utime, uaddr2, val3):
26 cmd = op & FUTEX_CMD_MASK
27 if cmd != FUTEX_WAIT:
28 return # we don't care about originators of WAKE events
29
30 process_names[tid] = comm
31 thread_thislock[tid] = uaddr
32 thread_blocktime[tid] = nsecs(s, ns)
33
34def syscalls__sys_exit_futex(event, ctxt, cpu, s, ns, tid, comm,
35 nr, ret):
36 if thread_blocktime.has_key(tid):
37 elapsed = nsecs(s, ns) - thread_blocktime[tid]
38 add_stats(lock_waits, (tid, thread_thislock[tid]), elapsed)
39 del thread_blocktime[tid]
40 del thread_thislock[tid]
41
42def trace_begin():
43 print "Press control+C to stop and show the summary"
44
45def trace_end():
46 for (tid, lock) in lock_waits:
47 min, max, avg, count = lock_waits[tid, lock]
48 print "%s[%d] lock %x contended %d times, %d avg ns" % \
49 (process_names[tid], tid, lock, count, avg)
50
diff --git a/tools/perf/scripts/python/netdev-times.py b/tools/perf/scripts/python/netdev-times.py
new file mode 100644
index 000000000000..9aa0a32972e8
--- /dev/null
+++ b/tools/perf/scripts/python/netdev-times.py
@@ -0,0 +1,464 @@
1# Display a process of packets and processed time.
2# It helps us to investigate networking or network device.
3#
4# options
5# tx: show only tx chart
6# rx: show only rx chart
7# dev=: show only thing related to specified device
8# debug: work with debug mode. It shows buffer status.
9
10import os
11import sys
12
13sys.path.append(os.environ['PERF_EXEC_PATH'] + \
14 '/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
15
16from perf_trace_context import *
17from Core import *
18from Util import *
19
20all_event_list = []; # insert all tracepoint event related with this script
21irq_dic = {}; # key is cpu and value is a list which stacks irqs
22 # which raise NET_RX softirq
23net_rx_dic = {}; # key is cpu and value include time of NET_RX softirq-entry
24 # and a list which stacks receive
25receive_hunk_list = []; # a list which include a sequence of receive events
26rx_skb_list = []; # received packet list for matching
27 # skb_copy_datagram_iovec
28
29buffer_budget = 65536; # the budget of rx_skb_list, tx_queue_list and
30 # tx_xmit_list
31of_count_rx_skb_list = 0; # overflow count
32
33tx_queue_list = []; # list of packets which pass through dev_queue_xmit
34of_count_tx_queue_list = 0; # overflow count
35
36tx_xmit_list = []; # list of packets which pass through dev_hard_start_xmit
37of_count_tx_xmit_list = 0; # overflow count
38
39tx_free_list = []; # list of packets which is freed
40
41# options
42show_tx = 0;
43show_rx = 0;
44dev = 0; # store a name of device specified by option "dev="
45debug = 0;
46
47# indices of event_info tuple
48EINFO_IDX_NAME= 0
49EINFO_IDX_CONTEXT=1
50EINFO_IDX_CPU= 2
51EINFO_IDX_TIME= 3
52EINFO_IDX_PID= 4
53EINFO_IDX_COMM= 5
54
55# Calculate a time interval(msec) from src(nsec) to dst(nsec)
56def diff_msec(src, dst):
57 return (dst - src) / 1000000.0
58
59# Display a process of transmitting a packet
60def print_transmit(hunk):
61 if dev != 0 and hunk['dev'].find(dev) < 0:
62 return
63 print "%7s %5d %6d.%06dsec %12.3fmsec %12.3fmsec" % \
64 (hunk['dev'], hunk['len'],
65 nsecs_secs(hunk['queue_t']),
66 nsecs_nsecs(hunk['queue_t'])/1000,
67 diff_msec(hunk['queue_t'], hunk['xmit_t']),
68 diff_msec(hunk['xmit_t'], hunk['free_t']))
69
70# Format for displaying rx packet processing
71PF_IRQ_ENTRY= " irq_entry(+%.3fmsec irq=%d:%s)"
72PF_SOFT_ENTRY=" softirq_entry(+%.3fmsec)"
73PF_NAPI_POLL= " napi_poll_exit(+%.3fmsec %s)"
74PF_JOINT= " |"
75PF_WJOINT= " | |"
76PF_NET_RECV= " |---netif_receive_skb(+%.3fmsec skb=%x len=%d)"
77PF_NET_RX= " |---netif_rx(+%.3fmsec skb=%x)"
78PF_CPY_DGRAM= " | skb_copy_datagram_iovec(+%.3fmsec %d:%s)"
79PF_KFREE_SKB= " | kfree_skb(+%.3fmsec location=%x)"
80PF_CONS_SKB= " | consume_skb(+%.3fmsec)"
81
82# Display a process of received packets and interrputs associated with
83# a NET_RX softirq
84def print_receive(hunk):
85 show_hunk = 0
86 irq_list = hunk['irq_list']
87 cpu = irq_list[0]['cpu']
88 base_t = irq_list[0]['irq_ent_t']
89 # check if this hunk should be showed
90 if dev != 0:
91 for i in range(len(irq_list)):
92 if irq_list[i]['name'].find(dev) >= 0:
93 show_hunk = 1
94 break
95 else:
96 show_hunk = 1
97 if show_hunk == 0:
98 return
99
100 print "%d.%06dsec cpu=%d" % \
101 (nsecs_secs(base_t), nsecs_nsecs(base_t)/1000, cpu)
102 for i in range(len(irq_list)):
103 print PF_IRQ_ENTRY % \
104 (diff_msec(base_t, irq_list[i]['irq_ent_t']),
105 irq_list[i]['irq'], irq_list[i]['name'])
106 print PF_JOINT
107 irq_event_list = irq_list[i]['event_list']
108 for j in range(len(irq_event_list)):
109 irq_event = irq_event_list[j]
110 if irq_event['event'] == 'netif_rx':
111 print PF_NET_RX % \
112 (diff_msec(base_t, irq_event['time']),
113 irq_event['skbaddr'])
114 print PF_JOINT
115 print PF_SOFT_ENTRY % \
116 diff_msec(base_t, hunk['sirq_ent_t'])
117 print PF_JOINT
118 event_list = hunk['event_list']
119 for i in range(len(event_list)):
120 event = event_list[i]
121 if event['event_name'] == 'napi_poll':
122 print PF_NAPI_POLL % \
123 (diff_msec(base_t, event['event_t']), event['dev'])
124 if i == len(event_list) - 1:
125 print ""
126 else:
127 print PF_JOINT
128 else:
129 print PF_NET_RECV % \
130 (diff_msec(base_t, event['event_t']), event['skbaddr'],
131 event['len'])
132 if 'comm' in event.keys():
133 print PF_WJOINT
134 print PF_CPY_DGRAM % \
135 (diff_msec(base_t, event['comm_t']),
136 event['pid'], event['comm'])
137 elif 'handle' in event.keys():
138 print PF_WJOINT
139 if event['handle'] == "kfree_skb":
140 print PF_KFREE_SKB % \
141 (diff_msec(base_t,
142 event['comm_t']),
143 event['location'])
144 elif event['handle'] == "consume_skb":
145 print PF_CONS_SKB % \
146 diff_msec(base_t,
147 event['comm_t'])
148 print PF_JOINT
149
150def trace_begin():
151 global show_tx
152 global show_rx
153 global dev
154 global debug
155
156 for i in range(len(sys.argv)):
157 if i == 0:
158 continue
159 arg = sys.argv[i]
160 if arg == 'tx':
161 show_tx = 1
162 elif arg =='rx':
163 show_rx = 1
164 elif arg.find('dev=',0, 4) >= 0:
165 dev = arg[4:]
166 elif arg == 'debug':
167 debug = 1
168 if show_tx == 0 and show_rx == 0:
169 show_tx = 1
170 show_rx = 1
171
172def trace_end():
173 # order all events in time
174 all_event_list.sort(lambda a,b :cmp(a[EINFO_IDX_TIME],
175 b[EINFO_IDX_TIME]))
176 # process all events
177 for i in range(len(all_event_list)):
178 event_info = all_event_list[i]
179 name = event_info[EINFO_IDX_NAME]
180 if name == 'irq__softirq_exit':
181 handle_irq_softirq_exit(event_info)
182 elif name == 'irq__softirq_entry':
183 handle_irq_softirq_entry(event_info)
184 elif name == 'irq__softirq_raise':
185 handle_irq_softirq_raise(event_info)
186 elif name == 'irq__irq_handler_entry':
187 handle_irq_handler_entry(event_info)
188 elif name == 'irq__irq_handler_exit':
189 handle_irq_handler_exit(event_info)
190 elif name == 'napi__napi_poll':
191 handle_napi_poll(event_info)
192 elif name == 'net__netif_receive_skb':
193 handle_netif_receive_skb(event_info)
194 elif name == 'net__netif_rx':
195 handle_netif_rx(event_info)
196 elif name == 'skb__skb_copy_datagram_iovec':
197 handle_skb_copy_datagram_iovec(event_info)
198 elif name == 'net__net_dev_queue':
199 handle_net_dev_queue(event_info)
200 elif name == 'net__net_dev_xmit':
201 handle_net_dev_xmit(event_info)
202 elif name == 'skb__kfree_skb':
203 handle_kfree_skb(event_info)
204 elif name == 'skb__consume_skb':
205 handle_consume_skb(event_info)
206 # display receive hunks
207 if show_rx:
208 for i in range(len(receive_hunk_list)):
209 print_receive(receive_hunk_list[i])
210 # display transmit hunks
211 if show_tx:
212 print " dev len Qdisc " \
213 " netdevice free"
214 for i in range(len(tx_free_list)):
215 print_transmit(tx_free_list[i])
216 if debug:
217 print "debug buffer status"
218 print "----------------------------"
219 print "xmit Qdisc:remain:%d overflow:%d" % \
220 (len(tx_queue_list), of_count_tx_queue_list)
221 print "xmit netdevice:remain:%d overflow:%d" % \
222 (len(tx_xmit_list), of_count_tx_xmit_list)
223 print "receive:remain:%d overflow:%d" % \
224 (len(rx_skb_list), of_count_rx_skb_list)
225
226# called from perf, when it finds a correspoinding event
227def irq__softirq_entry(name, context, cpu, sec, nsec, pid, comm, vec):
228 if symbol_str("irq__softirq_entry", "vec", vec) != "NET_RX":
229 return
230 event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm, vec)
231 all_event_list.append(event_info)
232
233def irq__softirq_exit(name, context, cpu, sec, nsec, pid, comm, vec):
234 if symbol_str("irq__softirq_entry", "vec", vec) != "NET_RX":
235 return
236 event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm, vec)
237 all_event_list.append(event_info)
238
239def irq__softirq_raise(name, context, cpu, sec, nsec, pid, comm, vec):
240 if symbol_str("irq__softirq_entry", "vec", vec) != "NET_RX":
241 return
242 event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm, vec)
243 all_event_list.append(event_info)
244
245def irq__irq_handler_entry(name, context, cpu, sec, nsec, pid, comm,
246 irq, irq_name):
247 event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm,
248 irq, irq_name)
249 all_event_list.append(event_info)
250
251def irq__irq_handler_exit(name, context, cpu, sec, nsec, pid, comm, irq, ret):
252 event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm, irq, ret)
253 all_event_list.append(event_info)
254
255def napi__napi_poll(name, context, cpu, sec, nsec, pid, comm, napi, dev_name):
256 event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm,
257 napi, dev_name)
258 all_event_list.append(event_info)
259
260def net__netif_receive_skb(name, context, cpu, sec, nsec, pid, comm, skbaddr,
261 skblen, dev_name):
262 event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm,
263 skbaddr, skblen, dev_name)
264 all_event_list.append(event_info)
265
266def net__netif_rx(name, context, cpu, sec, nsec, pid, comm, skbaddr,
267 skblen, dev_name):
268 event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm,
269 skbaddr, skblen, dev_name)
270 all_event_list.append(event_info)
271
272def net__net_dev_queue(name, context, cpu, sec, nsec, pid, comm,
273 skbaddr, skblen, dev_name):
274 event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm,
275 skbaddr, skblen, dev_name)
276 all_event_list.append(event_info)
277
278def net__net_dev_xmit(name, context, cpu, sec, nsec, pid, comm,
279 skbaddr, skblen, rc, dev_name):
280 event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm,
281 skbaddr, skblen, rc ,dev_name)
282 all_event_list.append(event_info)
283
284def skb__kfree_skb(name, context, cpu, sec, nsec, pid, comm,
285 skbaddr, protocol, location):
286 event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm,
287 skbaddr, protocol, location)
288 all_event_list.append(event_info)
289
290def skb__consume_skb(name, context, cpu, sec, nsec, pid, comm, skbaddr):
291 event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm,
292 skbaddr)
293 all_event_list.append(event_info)
294
295def skb__skb_copy_datagram_iovec(name, context, cpu, sec, nsec, pid, comm,
296 skbaddr, skblen):
297 event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm,
298 skbaddr, skblen)
299 all_event_list.append(event_info)
300
301def handle_irq_handler_entry(event_info):
302 (name, context, cpu, time, pid, comm, irq, irq_name) = event_info
303 if cpu not in irq_dic.keys():
304 irq_dic[cpu] = []
305 irq_record = {'irq':irq, 'name':irq_name, 'cpu':cpu, 'irq_ent_t':time}
306 irq_dic[cpu].append(irq_record)
307
308def handle_irq_handler_exit(event_info):
309 (name, context, cpu, time, pid, comm, irq, ret) = event_info
310 if cpu not in irq_dic.keys():
311 return
312 irq_record = irq_dic[cpu].pop()
313 if irq != irq_record['irq']:
314 return
315 irq_record.update({'irq_ext_t':time})
316 # if an irq doesn't include NET_RX softirq, drop.
317 if 'event_list' in irq_record.keys():
318 irq_dic[cpu].append(irq_record)
319
320def handle_irq_softirq_raise(event_info):
321 (name, context, cpu, time, pid, comm, vec) = event_info
322 if cpu not in irq_dic.keys() \
323 or len(irq_dic[cpu]) == 0:
324 return
325 irq_record = irq_dic[cpu].pop()
326 if 'event_list' in irq_record.keys():
327 irq_event_list = irq_record['event_list']
328 else:
329 irq_event_list = []
330 irq_event_list.append({'time':time, 'event':'sirq_raise'})
331 irq_record.update({'event_list':irq_event_list})
332 irq_dic[cpu].append(irq_record)
333
334def handle_irq_softirq_entry(event_info):
335 (name, context, cpu, time, pid, comm, vec) = event_info
336 net_rx_dic[cpu] = {'sirq_ent_t':time, 'event_list':[]}
337
338def handle_irq_softirq_exit(event_info):
339 (name, context, cpu, time, pid, comm, vec) = event_info
340 irq_list = []
341 event_list = 0
342 if cpu in irq_dic.keys():
343 irq_list = irq_dic[cpu]
344 del irq_dic[cpu]
345 if cpu in net_rx_dic.keys():
346 sirq_ent_t = net_rx_dic[cpu]['sirq_ent_t']
347 event_list = net_rx_dic[cpu]['event_list']
348 del net_rx_dic[cpu]
349 if irq_list == [] or event_list == 0:
350 return
351 rec_data = {'sirq_ent_t':sirq_ent_t, 'sirq_ext_t':time,
352 'irq_list':irq_list, 'event_list':event_list}
353 # merge information realted to a NET_RX softirq
354 receive_hunk_list.append(rec_data)
355
356def handle_napi_poll(event_info):
357 (name, context, cpu, time, pid, comm, napi, dev_name) = event_info
358 if cpu in net_rx_dic.keys():
359 event_list = net_rx_dic[cpu]['event_list']
360 rec_data = {'event_name':'napi_poll',
361 'dev':dev_name, 'event_t':time}
362 event_list.append(rec_data)
363
364def handle_netif_rx(event_info):
365 (name, context, cpu, time, pid, comm,
366 skbaddr, skblen, dev_name) = event_info
367 if cpu not in irq_dic.keys() \
368 or len(irq_dic[cpu]) == 0:
369 return
370 irq_record = irq_dic[cpu].pop()
371 if 'event_list' in irq_record.keys():
372 irq_event_list = irq_record['event_list']
373 else:
374 irq_event_list = []
375 irq_event_list.append({'time':time, 'event':'netif_rx',
376 'skbaddr':skbaddr, 'skblen':skblen, 'dev_name':dev_name})
377 irq_record.update({'event_list':irq_event_list})
378 irq_dic[cpu].append(irq_record)
379
380def handle_netif_receive_skb(event_info):
381 global of_count_rx_skb_list
382
383 (name, context, cpu, time, pid, comm,
384 skbaddr, skblen, dev_name) = event_info
385 if cpu in net_rx_dic.keys():
386 rec_data = {'event_name':'netif_receive_skb',
387 'event_t':time, 'skbaddr':skbaddr, 'len':skblen}
388 event_list = net_rx_dic[cpu]['event_list']
389 event_list.append(rec_data)
390 rx_skb_list.insert(0, rec_data)
391 if len(rx_skb_list) > buffer_budget:
392 rx_skb_list.pop()
393 of_count_rx_skb_list += 1
394
395def handle_net_dev_queue(event_info):
396 global of_count_tx_queue_list
397
398 (name, context, cpu, time, pid, comm,
399 skbaddr, skblen, dev_name) = event_info
400 skb = {'dev':dev_name, 'skbaddr':skbaddr, 'len':skblen, 'queue_t':time}
401 tx_queue_list.insert(0, skb)
402 if len(tx_queue_list) > buffer_budget:
403 tx_queue_list.pop()
404 of_count_tx_queue_list += 1
405
406def handle_net_dev_xmit(event_info):
407 global of_count_tx_xmit_list
408
409 (name, context, cpu, time, pid, comm,
410 skbaddr, skblen, rc, dev_name) = event_info
411 if rc == 0: # NETDEV_TX_OK
412 for i in range(len(tx_queue_list)):
413 skb = tx_queue_list[i]
414 if skb['skbaddr'] == skbaddr:
415 skb['xmit_t'] = time
416 tx_xmit_list.insert(0, skb)
417 del tx_queue_list[i]
418 if len(tx_xmit_list) > buffer_budget:
419 tx_xmit_list.pop()
420 of_count_tx_xmit_list += 1
421 return
422
423def handle_kfree_skb(event_info):
424 (name, context, cpu, time, pid, comm,
425 skbaddr, protocol, location) = event_info
426 for i in range(len(tx_queue_list)):
427 skb = tx_queue_list[i]
428 if skb['skbaddr'] == skbaddr:
429 del tx_queue_list[i]
430 return
431 for i in range(len(tx_xmit_list)):
432 skb = tx_xmit_list[i]
433 if skb['skbaddr'] == skbaddr:
434 skb['free_t'] = time
435 tx_free_list.append(skb)
436 del tx_xmit_list[i]
437 return
438 for i in range(len(rx_skb_list)):
439 rec_data = rx_skb_list[i]
440 if rec_data['skbaddr'] == skbaddr:
441 rec_data.update({'handle':"kfree_skb",
442 'comm':comm, 'pid':pid, 'comm_t':time})
443 del rx_skb_list[i]
444 return
445
446def handle_consume_skb(event_info):
447 (name, context, cpu, time, pid, comm, skbaddr) = event_info
448 for i in range(len(tx_xmit_list)):
449 skb = tx_xmit_list[i]
450 if skb['skbaddr'] == skbaddr:
451 skb['free_t'] = time
452 tx_free_list.append(skb)
453 del tx_xmit_list[i]
454 return
455
456def handle_skb_copy_datagram_iovec(event_info):
457 (name, context, cpu, time, pid, comm, skbaddr, skblen) = event_info
458 for i in range(len(rx_skb_list)):
459 rec_data = rx_skb_list[i]
460 if skbaddr == rec_data['skbaddr']:
461 rec_data.update({'handle':"skb_copy_datagram_iovec",
462 'comm':comm, 'pid':pid, 'comm_t':time})
463 del rx_skb_list[i]
464 return
diff --git a/tools/perf/scripts/python/sctop.py b/tools/perf/scripts/python/sctop.py
index 6cafad40c296..7a6ec2c7d8ab 100644
--- a/tools/perf/scripts/python/sctop.py
+++ b/tools/perf/scripts/python/sctop.py
@@ -8,10 +8,7 @@
8# will be refreshed every [interval] seconds. The default interval is 8# will be refreshed every [interval] seconds. The default interval is
9# 3 seconds. 9# 3 seconds.
10 10
11import thread 11import os, sys, thread, time
12import time
13import os
14import sys
15 12
16sys.path.append(os.environ['PERF_EXEC_PATH'] + \ 13sys.path.append(os.environ['PERF_EXEC_PATH'] + \
17 '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') 14 '/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
@@ -20,7 +17,7 @@ from perf_trace_context import *
20from Core import * 17from Core import *
21from Util import * 18from Util import *
22 19
23usage = "perf trace -s syscall-counts.py [comm] [interval]\n"; 20usage = "perf trace -s sctop.py [comm] [interval]\n";
24 21
25for_comm = None 22for_comm = None
26default_interval = 3 23default_interval = 3
@@ -71,7 +68,7 @@ def print_syscall_totals(interval):
71 for id, val in sorted(syscalls.iteritems(), key = lambda(k, v): (v, k), \ 68 for id, val in sorted(syscalls.iteritems(), key = lambda(k, v): (v, k), \
72 reverse = True): 69 reverse = True):
73 try: 70 try:
74 print "%-40d %10d\n" % (id, val), 71 print "%-40s %10d\n" % (syscall_name(id), val),
75 except TypeError: 72 except TypeError:
76 pass 73 pass
77 syscalls.clear() 74 syscalls.clear()
diff --git a/tools/perf/scripts/python/syscall-counts-by-pid.py b/tools/perf/scripts/python/syscall-counts-by-pid.py
index af722d6a4b3f..d1ee3ec10cf2 100644
--- a/tools/perf/scripts/python/syscall-counts-by-pid.py
+++ b/tools/perf/scripts/python/syscall-counts-by-pid.py
@@ -5,29 +5,33 @@
5# Displays system-wide system call totals, broken down by syscall. 5# Displays system-wide system call totals, broken down by syscall.
6# If a [comm] arg is specified, only syscalls called by [comm] are displayed. 6# If a [comm] arg is specified, only syscalls called by [comm] are displayed.
7 7
8import os 8import os, sys
9import sys
10 9
11sys.path.append(os.environ['PERF_EXEC_PATH'] + \ 10sys.path.append(os.environ['PERF_EXEC_PATH'] + \
12 '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') 11 '/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
13 12
14from perf_trace_context import * 13from perf_trace_context import *
15from Core import * 14from Core import *
15from Util import syscall_name
16 16
17usage = "perf trace -s syscall-counts-by-pid.py [comm]\n"; 17usage = "perf trace -s syscall-counts-by-pid.py [comm]\n";
18 18
19for_comm = None 19for_comm = None
20for_pid = None
20 21
21if len(sys.argv) > 2: 22if len(sys.argv) > 2:
22 sys.exit(usage) 23 sys.exit(usage)
23 24
24if len(sys.argv) > 1: 25if len(sys.argv) > 1:
25 for_comm = sys.argv[1] 26 try:
27 for_pid = int(sys.argv[1])
28 except:
29 for_comm = sys.argv[1]
26 30
27syscalls = autodict() 31syscalls = autodict()
28 32
29def trace_begin(): 33def trace_begin():
30 pass 34 print "Press control+C to stop and show the summary"
31 35
32def trace_end(): 36def trace_end():
33 print_syscall_totals() 37 print_syscall_totals()
@@ -35,9 +39,10 @@ def trace_end():
35def raw_syscalls__sys_enter(event_name, context, common_cpu, 39def raw_syscalls__sys_enter(event_name, context, common_cpu,
36 common_secs, common_nsecs, common_pid, common_comm, 40 common_secs, common_nsecs, common_pid, common_comm,
37 id, args): 41 id, args):
38 if for_comm is not None: 42
39 if common_comm != for_comm: 43 if (for_comm and common_comm != for_comm) or \
40 return 44 (for_pid and common_pid != for_pid ):
45 return
41 try: 46 try:
42 syscalls[common_comm][common_pid][id] += 1 47 syscalls[common_comm][common_pid][id] += 1
43 except TypeError: 48 except TypeError:
@@ -61,4 +66,4 @@ def print_syscall_totals():
61 id_keys = syscalls[comm][pid].keys() 66 id_keys = syscalls[comm][pid].keys()
62 for id, val in sorted(syscalls[comm][pid].iteritems(), \ 67 for id, val in sorted(syscalls[comm][pid].iteritems(), \
63 key = lambda(k, v): (v, k), reverse = True): 68 key = lambda(k, v): (v, k), reverse = True):
64 print " %-38d %10d\n" % (id, val), 69 print " %-38s %10d\n" % (syscall_name(id), val),
diff --git a/tools/perf/scripts/python/syscall-counts.py b/tools/perf/scripts/python/syscall-counts.py
index f977e85ff049..ea183dc82d29 100644
--- a/tools/perf/scripts/python/syscall-counts.py
+++ b/tools/perf/scripts/python/syscall-counts.py
@@ -13,6 +13,7 @@ sys.path.append(os.environ['PERF_EXEC_PATH'] + \
13 13
14from perf_trace_context import * 14from perf_trace_context import *
15from Core import * 15from Core import *
16from Util import syscall_name
16 17
17usage = "perf trace -s syscall-counts.py [comm]\n"; 18usage = "perf trace -s syscall-counts.py [comm]\n";
18 19
@@ -27,7 +28,7 @@ if len(sys.argv) > 1:
27syscalls = autodict() 28syscalls = autodict()
28 29
29def trace_begin(): 30def trace_begin():
30 pass 31 print "Press control+C to stop and show the summary"
31 32
32def trace_end(): 33def trace_end():
33 print_syscall_totals() 34 print_syscall_totals()
@@ -55,4 +56,4 @@ def print_syscall_totals():
55 56
56 for id, val in sorted(syscalls.iteritems(), key = lambda(k, v): (v, k), \ 57 for id, val in sorted(syscalls.iteritems(), key = lambda(k, v): (v, k), \
57 reverse = True): 58 reverse = True):
58 print "%-40d %10d\n" % (id, val), 59 print "%-40s %10d\n" % (syscall_name(id), val),
diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h
index 27e9ebe4076e..a7729797fd96 100644
--- a/tools/perf/util/cache.h
+++ b/tools/perf/util/cache.h
@@ -82,6 +82,8 @@ extern char *perf_path(const char *fmt, ...) __attribute__((format (printf, 1, 2
82extern char *perf_pathdup(const char *fmt, ...) 82extern char *perf_pathdup(const char *fmt, ...)
83 __attribute__((format (printf, 1, 2))); 83 __attribute__((format (printf, 1, 2)));
84 84
85#ifdef NO_STRLCPY
85extern size_t strlcpy(char *dest, const char *src, size_t size); 86extern size_t strlcpy(char *dest, const char *src, size_t size);
87#endif
86 88
87#endif /* __PERF_CACHE_H */ 89#endif /* __PERF_CACHE_H */
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c
index f231f43424d2..e12d539417b2 100644
--- a/tools/perf/util/callchain.c
+++ b/tools/perf/util/callchain.c
@@ -28,6 +28,9 @@ bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event)
28#define chain_for_each_child(child, parent) \ 28#define chain_for_each_child(child, parent) \
29 list_for_each_entry(child, &parent->children, brothers) 29 list_for_each_entry(child, &parent->children, brothers)
30 30
31#define chain_for_each_child_safe(child, next, parent) \
32 list_for_each_entry_safe(child, next, &parent->children, brothers)
33
31static void 34static void
32rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, 35rb_insert_callchain(struct rb_root *root, struct callchain_node *chain,
33 enum chain_mode mode) 36 enum chain_mode mode)
@@ -86,10 +89,10 @@ __sort_chain_flat(struct rb_root *rb_root, struct callchain_node *node,
86 * sort them by hit 89 * sort them by hit
87 */ 90 */
88static void 91static void
89sort_chain_flat(struct rb_root *rb_root, struct callchain_node *node, 92sort_chain_flat(struct rb_root *rb_root, struct callchain_root *root,
90 u64 min_hit, struct callchain_param *param __used) 93 u64 min_hit, struct callchain_param *param __used)
91{ 94{
92 __sort_chain_flat(rb_root, node, min_hit); 95 __sort_chain_flat(rb_root, &root->node, min_hit);
93} 96}
94 97
95static void __sort_chain_graph_abs(struct callchain_node *node, 98static void __sort_chain_graph_abs(struct callchain_node *node,
@@ -108,11 +111,11 @@ static void __sort_chain_graph_abs(struct callchain_node *node,
108} 111}
109 112
110static void 113static void
111sort_chain_graph_abs(struct rb_root *rb_root, struct callchain_node *chain_root, 114sort_chain_graph_abs(struct rb_root *rb_root, struct callchain_root *chain_root,
112 u64 min_hit, struct callchain_param *param __used) 115 u64 min_hit, struct callchain_param *param __used)
113{ 116{
114 __sort_chain_graph_abs(chain_root, min_hit); 117 __sort_chain_graph_abs(&chain_root->node, min_hit);
115 rb_root->rb_node = chain_root->rb_root.rb_node; 118 rb_root->rb_node = chain_root->node.rb_root.rb_node;
116} 119}
117 120
118static void __sort_chain_graph_rel(struct callchain_node *node, 121static void __sort_chain_graph_rel(struct callchain_node *node,
@@ -133,11 +136,11 @@ static void __sort_chain_graph_rel(struct callchain_node *node,
133} 136}
134 137
135static void 138static void
136sort_chain_graph_rel(struct rb_root *rb_root, struct callchain_node *chain_root, 139sort_chain_graph_rel(struct rb_root *rb_root, struct callchain_root *chain_root,
137 u64 min_hit __used, struct callchain_param *param) 140 u64 min_hit __used, struct callchain_param *param)
138{ 141{
139 __sort_chain_graph_rel(chain_root, param->min_percent / 100.0); 142 __sort_chain_graph_rel(&chain_root->node, param->min_percent / 100.0);
140 rb_root->rb_node = chain_root->rb_root.rb_node; 143 rb_root->rb_node = chain_root->node.rb_root.rb_node;
141} 144}
142 145
143int register_callchain_param(struct callchain_param *param) 146int register_callchain_param(struct callchain_param *param)
@@ -284,19 +287,18 @@ split_add_child(struct callchain_node *parent, struct resolved_chain *chain,
284} 287}
285 288
286static int 289static int
287__append_chain(struct callchain_node *root, struct resolved_chain *chain, 290append_chain(struct callchain_node *root, struct resolved_chain *chain,
288 unsigned int start, u64 period); 291 unsigned int start, u64 period);
289 292
290static void 293static void
291__append_chain_children(struct callchain_node *root, 294append_chain_children(struct callchain_node *root, struct resolved_chain *chain,
292 struct resolved_chain *chain, 295 unsigned int start, u64 period)
293 unsigned int start, u64 period)
294{ 296{
295 struct callchain_node *rnode; 297 struct callchain_node *rnode;
296 298
297 /* lookup in childrens */ 299 /* lookup in childrens */
298 chain_for_each_child(rnode, root) { 300 chain_for_each_child(rnode, root) {
299 unsigned int ret = __append_chain(rnode, chain, start, period); 301 unsigned int ret = append_chain(rnode, chain, start, period);
300 302
301 if (!ret) 303 if (!ret)
302 goto inc_children_hit; 304 goto inc_children_hit;
@@ -309,8 +311,8 @@ inc_children_hit:
309} 311}
310 312
311static int 313static int
312__append_chain(struct callchain_node *root, struct resolved_chain *chain, 314append_chain(struct callchain_node *root, struct resolved_chain *chain,
313 unsigned int start, u64 period) 315 unsigned int start, u64 period)
314{ 316{
315 struct callchain_list *cnode; 317 struct callchain_list *cnode;
316 unsigned int i = start; 318 unsigned int i = start;
@@ -357,7 +359,7 @@ __append_chain(struct callchain_node *root, struct resolved_chain *chain,
357 } 359 }
358 360
359 /* We match the node and still have a part remaining */ 361 /* We match the node and still have a part remaining */
360 __append_chain_children(root, chain, i, period); 362 append_chain_children(root, chain, i, period);
361 363
362 return 0; 364 return 0;
363} 365}
@@ -380,8 +382,8 @@ static void filter_context(struct ip_callchain *old, struct resolved_chain *new,
380} 382}
381 383
382 384
383int append_chain(struct callchain_node *root, struct ip_callchain *chain, 385int callchain_append(struct callchain_root *root, struct ip_callchain *chain,
384 struct map_symbol *syms, u64 period) 386 struct map_symbol *syms, u64 period)
385{ 387{
386 struct resolved_chain *filtered; 388 struct resolved_chain *filtered;
387 389
@@ -398,9 +400,65 @@ int append_chain(struct callchain_node *root, struct ip_callchain *chain,
398 if (!filtered->nr) 400 if (!filtered->nr)
399 goto end; 401 goto end;
400 402
401 __append_chain_children(root, filtered, 0, period); 403 append_chain_children(&root->node, filtered, 0, period);
404
405 if (filtered->nr > root->max_depth)
406 root->max_depth = filtered->nr;
402end: 407end:
403 free(filtered); 408 free(filtered);
404 409
405 return 0; 410 return 0;
406} 411}
412
413static int
414merge_chain_branch(struct callchain_node *dst, struct callchain_node *src,
415 struct resolved_chain *chain)
416{
417 struct callchain_node *child, *next_child;
418 struct callchain_list *list, *next_list;
419 int old_pos = chain->nr;
420 int err = 0;
421
422 list_for_each_entry_safe(list, next_list, &src->val, list) {
423 chain->ips[chain->nr].ip = list->ip;
424 chain->ips[chain->nr].ms = list->ms;
425 chain->nr++;
426 list_del(&list->list);
427 free(list);
428 }
429
430 if (src->hit)
431 append_chain_children(dst, chain, 0, src->hit);
432
433 chain_for_each_child_safe(child, next_child, src) {
434 err = merge_chain_branch(dst, child, chain);
435 if (err)
436 break;
437
438 list_del(&child->brothers);
439 free(child);
440 }
441
442 chain->nr = old_pos;
443
444 return err;
445}
446
447int callchain_merge(struct callchain_root *dst, struct callchain_root *src)
448{
449 struct resolved_chain *chain;
450 int err;
451
452 chain = malloc(sizeof(*chain) +
453 src->max_depth * sizeof(struct resolved_ip));
454 if (!chain)
455 return -ENOMEM;
456
457 chain->nr = 0;
458
459 err = merge_chain_branch(&dst->node, &src->node, chain);
460
461 free(chain);
462
463 return err;
464}
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h
index 624a96c636fd..c15fb8c24ad2 100644
--- a/tools/perf/util/callchain.h
+++ b/tools/perf/util/callchain.h
@@ -26,9 +26,14 @@ struct callchain_node {
26 u64 children_hit; 26 u64 children_hit;
27}; 27};
28 28
29struct callchain_root {
30 u64 max_depth;
31 struct callchain_node node;
32};
33
29struct callchain_param; 34struct callchain_param;
30 35
31typedef void (*sort_chain_func_t)(struct rb_root *, struct callchain_node *, 36typedef void (*sort_chain_func_t)(struct rb_root *, struct callchain_root *,
32 u64, struct callchain_param *); 37 u64, struct callchain_param *);
33 38
34struct callchain_param { 39struct callchain_param {
@@ -44,14 +49,16 @@ struct callchain_list {
44 struct list_head list; 49 struct list_head list;
45}; 50};
46 51
47static inline void callchain_init(struct callchain_node *node) 52static inline void callchain_init(struct callchain_root *root)
48{ 53{
49 INIT_LIST_HEAD(&node->brothers); 54 INIT_LIST_HEAD(&root->node.brothers);
50 INIT_LIST_HEAD(&node->children); 55 INIT_LIST_HEAD(&root->node.children);
51 INIT_LIST_HEAD(&node->val); 56 INIT_LIST_HEAD(&root->node.val);
52 57
53 node->parent = NULL; 58 root->node.parent = NULL;
54 node->hit = 0; 59 root->node.hit = 0;
60 root->node.children_hit = 0;
61 root->max_depth = 0;
55} 62}
56 63
57static inline u64 cumul_hits(struct callchain_node *node) 64static inline u64 cumul_hits(struct callchain_node *node)
@@ -60,8 +67,9 @@ static inline u64 cumul_hits(struct callchain_node *node)
60} 67}
61 68
62int register_callchain_param(struct callchain_param *param); 69int register_callchain_param(struct callchain_param *param);
63int append_chain(struct callchain_node *root, struct ip_callchain *chain, 70int callchain_append(struct callchain_root *root, struct ip_callchain *chain,
64 struct map_symbol *syms, u64 period); 71 struct map_symbol *syms, u64 period);
72int callchain_merge(struct callchain_root *dst, struct callchain_root *src);
65 73
66bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event); 74bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event);
67#endif /* __PERF_CALLCHAIN_H */ 75#endif /* __PERF_CALLCHAIN_H */
diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c
index 318dab15d177..c8d81b00089d 100644
--- a/tools/perf/util/debug.c
+++ b/tools/perf/util/debug.c
@@ -12,8 +12,8 @@
12#include "debug.h" 12#include "debug.h"
13#include "util.h" 13#include "util.h"
14 14
15int verbose = 0; 15int verbose;
16bool dump_trace = false; 16bool dump_trace = false, quiet = false;
17 17
18int eprintf(int level, const char *fmt, ...) 18int eprintf(int level, const char *fmt, ...)
19{ 19{
@@ -23,7 +23,7 @@ int eprintf(int level, const char *fmt, ...)
23 if (verbose >= level) { 23 if (verbose >= level) {
24 va_start(args, fmt); 24 va_start(args, fmt);
25 if (use_browser > 0) 25 if (use_browser > 0)
26 ret = browser__show_help(fmt, args); 26 ret = ui_helpline__show_help(fmt, args);
27 else 27 else
28 ret = vfprintf(stderr, fmt, args); 28 ret = vfprintf(stderr, fmt, args);
29 va_end(args); 29 va_end(args);
diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h
index 047ac3324ebe..7b514082bbaf 100644
--- a/tools/perf/util/debug.h
+++ b/tools/perf/util/debug.h
@@ -6,7 +6,7 @@
6#include "event.h" 6#include "event.h"
7 7
8extern int verbose; 8extern int verbose;
9extern bool dump_trace; 9extern bool quiet, dump_trace;
10 10
11int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); 11int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
12void trace_event(event_t *event); 12void trace_event(event_t *event);
@@ -14,7 +14,7 @@ void trace_event(event_t *event);
14struct ui_progress; 14struct ui_progress;
15 15
16#ifdef NO_NEWT_SUPPORT 16#ifdef NO_NEWT_SUPPORT
17static inline int browser__show_help(const char *format __used, va_list ap __used) 17static inline int ui_helpline__show_help(const char *format __used, va_list ap __used)
18{ 18{
19 return 0; 19 return 0;
20} 20}
@@ -30,10 +30,9 @@ static inline void ui_progress__update(struct ui_progress *self __used,
30 30
31static inline void ui_progress__delete(struct ui_progress *self __used) {} 31static inline void ui_progress__delete(struct ui_progress *self __used) {}
32#else 32#else
33int browser__show_help(const char *format, va_list ap); 33extern char ui_helpline__last_msg[];
34struct ui_progress *ui_progress__new(const char *title, u64 total); 34int ui_helpline__show_help(const char *format, va_list ap);
35void ui_progress__update(struct ui_progress *self, u64 curr); 35#include "ui/progress.h"
36void ui_progress__delete(struct ui_progress *self);
37#endif 36#endif
38 37
39#endif /* __PERF_DEBUG_H */ 38#endif /* __PERF_DEBUG_H */
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index d7e67b167ea3..7cba0551a565 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -265,15 +265,16 @@ int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
265 const char *name, bool is_kallsyms) 265 const char *name, bool is_kallsyms)
266{ 266{
267 const size_t size = PATH_MAX; 267 const size_t size = PATH_MAX;
268 char *filename = malloc(size), 268 char *realname = realpath(name, NULL),
269 *filename = malloc(size),
269 *linkname = malloc(size), *targetname; 270 *linkname = malloc(size), *targetname;
270 int len, err = -1; 271 int len, err = -1;
271 272
272 if (filename == NULL || linkname == NULL) 273 if (realname == NULL || filename == NULL || linkname == NULL)
273 goto out_free; 274 goto out_free;
274 275
275 len = snprintf(filename, size, "%s%s%s", 276 len = snprintf(filename, size, "%s%s%s",
276 debugdir, is_kallsyms ? "/" : "", name); 277 debugdir, is_kallsyms ? "/" : "", realname);
277 if (mkdir_p(filename, 0755)) 278 if (mkdir_p(filename, 0755))
278 goto out_free; 279 goto out_free;
279 280
@@ -283,7 +284,7 @@ int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
283 if (is_kallsyms) { 284 if (is_kallsyms) {
284 if (copyfile("/proc/kallsyms", filename)) 285 if (copyfile("/proc/kallsyms", filename))
285 goto out_free; 286 goto out_free;
286 } else if (link(name, filename) && copyfile(name, filename)) 287 } else if (link(realname, filename) && copyfile(name, filename))
287 goto out_free; 288 goto out_free;
288 } 289 }
289 290
@@ -300,6 +301,7 @@ int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
300 if (symlink(targetname, linkname) == 0) 301 if (symlink(targetname, linkname) == 0)
301 err = 0; 302 err = 0;
302out_free: 303out_free:
304 free(realname);
303 free(filename); 305 free(filename);
304 free(linkname); 306 free(linkname);
305 return err; 307 return err;
@@ -946,11 +948,16 @@ perf_header__find_attr(u64 id, struct perf_header *header)
946 948
947 /* 949 /*
948 * We set id to -1 if the data file doesn't contain sample 950 * We set id to -1 if the data file doesn't contain sample
949 * ids. Check for this and avoid walking through the entire 951 * ids. This can happen when the data file contains one type
950 * list of ids which may be large. 952 * of event and in that case, the header can still store the
953 * event attribute information. Check for this and avoid
954 * walking through the entire list of ids which may be large.
951 */ 955 */
952 if (id == -1ULL) 956 if (id == -1ULL) {
957 if (header->attrs > 0)
958 return &header->attr[0]->attr;
953 return NULL; 959 return NULL;
960 }
954 961
955 for (i = 0; i < header->attrs; i++) { 962 for (i = 0; i < header->attrs; i++) {
956 struct perf_header_attr *attr = header->attr[i]; 963 struct perf_header_attr *attr = header->attr[i];
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index e7263d49bcf0..2022e8740994 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -87,7 +87,7 @@ static void hist_entry__add_cpumode_period(struct hist_entry *self,
87 87
88static struct hist_entry *hist_entry__new(struct hist_entry *template) 88static struct hist_entry *hist_entry__new(struct hist_entry *template)
89{ 89{
90 size_t callchain_size = symbol_conf.use_callchain ? sizeof(struct callchain_node) : 0; 90 size_t callchain_size = symbol_conf.use_callchain ? sizeof(struct callchain_root) : 0;
91 struct hist_entry *self = malloc(sizeof(*self) + callchain_size); 91 struct hist_entry *self = malloc(sizeof(*self) + callchain_size);
92 92
93 if (self != NULL) { 93 if (self != NULL) {
@@ -226,6 +226,8 @@ static bool collapse__insert_entry(struct rb_root *root, struct hist_entry *he)
226 226
227 if (!cmp) { 227 if (!cmp) {
228 iter->period += he->period; 228 iter->period += he->period;
229 if (symbol_conf.use_callchain)
230 callchain_merge(iter->callchain, he->callchain);
229 hist_entry__free(he); 231 hist_entry__free(he);
230 return false; 232 return false;
231 } 233 }
@@ -876,6 +878,9 @@ unsigned int hists__sort_list_width(struct hists *self)
876 if (!se->elide) 878 if (!se->elide)
877 ret += 2 + hists__col_len(self, se->se_width_idx); 879 ret += 2 + hists__col_len(self, se->se_width_idx);
878 880
881 if (verbose) /* Addr + origin */
882 ret += 3 + BITS_PER_LONG / 4;
883
879 return ret; 884 return ret;
880} 885}
881 886
@@ -980,9 +985,9 @@ int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip)
980 return 0; 985 return 0;
981} 986}
982 987
983static struct objdump_line *objdump_line__new(s64 offset, char *line) 988static struct objdump_line *objdump_line__new(s64 offset, char *line, size_t privsize)
984{ 989{
985 struct objdump_line *self = malloc(sizeof(*self)); 990 struct objdump_line *self = malloc(sizeof(*self) + privsize);
986 991
987 if (self != NULL) { 992 if (self != NULL) {
988 self->offset = offset; 993 self->offset = offset;
@@ -1014,7 +1019,7 @@ struct objdump_line *objdump__get_next_ip_line(struct list_head *head,
1014} 1019}
1015 1020
1016static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file, 1021static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file,
1017 struct list_head *head) 1022 struct list_head *head, size_t privsize)
1018{ 1023{
1019 struct symbol *sym = self->ms.sym; 1024 struct symbol *sym = self->ms.sym;
1020 struct objdump_line *objdump_line; 1025 struct objdump_line *objdump_line;
@@ -1065,7 +1070,7 @@ static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file,
1065 offset = -1; 1070 offset = -1;
1066 } 1071 }
1067 1072
1068 objdump_line = objdump_line__new(offset, line); 1073 objdump_line = objdump_line__new(offset, line, privsize);
1069 if (objdump_line == NULL) { 1074 if (objdump_line == NULL) {
1070 free(line); 1075 free(line);
1071 return -1; 1076 return -1;
@@ -1075,7 +1080,8 @@ static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file,
1075 return 0; 1080 return 0;
1076} 1081}
1077 1082
1078int hist_entry__annotate(struct hist_entry *self, struct list_head *head) 1083int hist_entry__annotate(struct hist_entry *self, struct list_head *head,
1084 size_t privsize)
1079{ 1085{
1080 struct symbol *sym = self->ms.sym; 1086 struct symbol *sym = self->ms.sym;
1081 struct map *map = self->ms.map; 1087 struct map *map = self->ms.map;
@@ -1140,7 +1146,7 @@ fallback:
1140 goto out_free_filename; 1146 goto out_free_filename;
1141 1147
1142 while (!feof(file)) 1148 while (!feof(file))
1143 if (hist_entry__parse_objdump_line(self, file, head) < 0) 1149 if (hist_entry__parse_objdump_line(self, file, head, privsize) < 0)
1144 break; 1150 break;
1145 1151
1146 pclose(file); 1152 pclose(file);
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h
index 65a48db46a29..587d375d3430 100644
--- a/tools/perf/util/hist.h
+++ b/tools/perf/util/hist.h
@@ -101,7 +101,8 @@ size_t hists__fprintf(struct hists *self, struct hists *pair,
101 bool show_displacement, FILE *fp); 101 bool show_displacement, FILE *fp);
102 102
103int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip); 103int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip);
104int hist_entry__annotate(struct hist_entry *self, struct list_head *head); 104int hist_entry__annotate(struct hist_entry *self, struct list_head *head,
105 size_t privsize);
105 106
106void hists__filter_by_dso(struct hists *self, const struct dso *dso); 107void hists__filter_by_dso(struct hists *self, const struct dso *dso);
107void hists__filter_by_thread(struct hists *self, const struct thread *thread); 108void hists__filter_by_thread(struct hists *self, const struct thread *thread);
diff --git a/tools/perf/util/include/linux/list.h b/tools/perf/util/include/linux/list.h
index dbe4b814382a..f5ca26e53fbb 100644
--- a/tools/perf/util/include/linux/list.h
+++ b/tools/perf/util/include/linux/list.h
@@ -15,4 +15,12 @@ static inline void list_del_range(struct list_head *begin,
15 begin->prev->next = end->next; 15 begin->prev->next = end->next;
16 end->next->prev = begin->prev; 16 end->next->prev = begin->prev;
17} 17}
18
19/**
20 * list_for_each_from - iterate over a list from one of its nodes
21 * @pos: the &struct list_head to use as a loop cursor, from where to start
22 * @head: the head for your list.
23 */
24#define list_for_each_from(pos, head) \
25 for (; prefetch(pos->next), pos != (head); pos = pos->next)
18#endif 26#endif
diff --git a/tools/perf/util/include/linux/types.h b/tools/perf/util/include/linux/types.h
index 196862a81a21..12de3b8112f9 100644
--- a/tools/perf/util/include/linux/types.h
+++ b/tools/perf/util/include/linux/types.h
@@ -6,4 +6,16 @@
6#define DECLARE_BITMAP(name,bits) \ 6#define DECLARE_BITMAP(name,bits) \
7 unsigned long name[BITS_TO_LONGS(bits)] 7 unsigned long name[BITS_TO_LONGS(bits)]
8 8
9struct list_head {
10 struct list_head *next, *prev;
11};
12
13struct hlist_head {
14 struct hlist_node *first;
15};
16
17struct hlist_node {
18 struct hlist_node *next, **pprev;
19};
20
9#endif 21#endif
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h
index 78575796d5f3..b397c0383728 100644
--- a/tools/perf/util/map.h
+++ b/tools/perf/util/map.h
@@ -215,6 +215,16 @@ struct symbol *map_groups__find_function_by_name(struct map_groups *self,
215 return map_groups__find_symbol_by_name(self, MAP__FUNCTION, name, mapp, filter); 215 return map_groups__find_symbol_by_name(self, MAP__FUNCTION, name, mapp, filter);
216} 216}
217 217
218static inline
219struct symbol *machine__find_kernel_function_by_name(struct machine *self,
220 const char *name,
221 struct map **mapp,
222 symbol_filter_t filter)
223{
224 return map_groups__find_function_by_name(&self->kmaps, name, mapp,
225 filter);
226}
227
218int map_groups__fixup_overlappings(struct map_groups *self, struct map *map, 228int map_groups__fixup_overlappings(struct map_groups *self, struct map *map,
219 int verbose, FILE *fp); 229 int verbose, FILE *fp);
220 230
diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c
deleted file mode 100644
index 91de99b58445..000000000000
--- a/tools/perf/util/newt.c
+++ /dev/null
@@ -1,1568 +0,0 @@
1#define _GNU_SOURCE
2#include <stdio.h>
3#undef _GNU_SOURCE
4/*
5 * slang versions <= 2.0.6 have a "#if HAVE_LONG_LONG" that breaks
6 * the build if it isn't defined. Use the equivalent one that glibc
7 * has on features.h.
8 */
9#include <features.h>
10#ifndef HAVE_LONG_LONG
11#define HAVE_LONG_LONG __GLIBC_HAVE_LONG_LONG
12#endif
13#include <slang.h>
14#include <signal.h>
15#include <stdlib.h>
16#include <newt.h>
17#include <sys/ttydefaults.h>
18
19#include "cache.h"
20#include "hist.h"
21#include "pstack.h"
22#include "session.h"
23#include "sort.h"
24#include "symbol.h"
25
26#if SLANG_VERSION < 20104
27#define slsmg_printf(msg, args...) SLsmg_printf((char *)msg, ##args)
28#define slsmg_write_nstring(msg, len) SLsmg_write_nstring((char *)msg, len)
29#define sltt_set_color(obj, name, fg, bg) SLtt_set_color(obj,(char *)name,\
30 (char *)fg, (char *)bg)
31#else
32#define slsmg_printf SLsmg_printf
33#define slsmg_write_nstring SLsmg_write_nstring
34#define sltt_set_color SLtt_set_color
35#endif
36
37struct ui_progress {
38 newtComponent form, scale;
39};
40
41struct ui_progress *ui_progress__new(const char *title, u64 total)
42{
43 struct ui_progress *self = malloc(sizeof(*self));
44
45 if (self != NULL) {
46 int cols;
47
48 if (use_browser <= 0)
49 return self;
50 newtGetScreenSize(&cols, NULL);
51 cols -= 4;
52 newtCenteredWindow(cols, 1, title);
53 self->form = newtForm(NULL, NULL, 0);
54 if (self->form == NULL)
55 goto out_free_self;
56 self->scale = newtScale(0, 0, cols, total);
57 if (self->scale == NULL)
58 goto out_free_form;
59 newtFormAddComponent(self->form, self->scale);
60 newtRefresh();
61 }
62
63 return self;
64
65out_free_form:
66 newtFormDestroy(self->form);
67out_free_self:
68 free(self);
69 return NULL;
70}
71
72void ui_progress__update(struct ui_progress *self, u64 curr)
73{
74 /*
75 * FIXME: We should have a per UI backend way of showing progress,
76 * stdio will just show a percentage as NN%, etc.
77 */
78 if (use_browser <= 0)
79 return;
80 newtScaleSet(self->scale, curr);
81 newtRefresh();
82}
83
84void ui_progress__delete(struct ui_progress *self)
85{
86 if (use_browser > 0) {
87 newtFormDestroy(self->form);
88 newtPopWindow();
89 }
90 free(self);
91}
92
93static void ui_helpline__pop(void)
94{
95 newtPopHelpLine();
96}
97
98static void ui_helpline__push(const char *msg)
99{
100 newtPushHelpLine(msg);
101}
102
103static void ui_helpline__vpush(const char *fmt, va_list ap)
104{
105 char *s;
106
107 if (vasprintf(&s, fmt, ap) < 0)
108 vfprintf(stderr, fmt, ap);
109 else {
110 ui_helpline__push(s);
111 free(s);
112 }
113}
114
115static void ui_helpline__fpush(const char *fmt, ...)
116{
117 va_list ap;
118
119 va_start(ap, fmt);
120 ui_helpline__vpush(fmt, ap);
121 va_end(ap);
122}
123
124static void ui_helpline__puts(const char *msg)
125{
126 ui_helpline__pop();
127 ui_helpline__push(msg);
128}
129
130static char browser__last_msg[1024];
131
132int browser__show_help(const char *format, va_list ap)
133{
134 int ret;
135 static int backlog;
136
137 ret = vsnprintf(browser__last_msg + backlog,
138 sizeof(browser__last_msg) - backlog, format, ap);
139 backlog += ret;
140
141 if (browser__last_msg[backlog - 1] == '\n') {
142 ui_helpline__puts(browser__last_msg);
143 newtRefresh();
144 backlog = 0;
145 }
146
147 return ret;
148}
149
150static void newt_form__set_exit_keys(newtComponent self)
151{
152 newtFormAddHotKey(self, NEWT_KEY_LEFT);
153 newtFormAddHotKey(self, NEWT_KEY_ESCAPE);
154 newtFormAddHotKey(self, 'Q');
155 newtFormAddHotKey(self, 'q');
156 newtFormAddHotKey(self, CTRL('c'));
157}
158
159static newtComponent newt_form__new(void)
160{
161 newtComponent self = newtForm(NULL, NULL, 0);
162 if (self)
163 newt_form__set_exit_keys(self);
164 return self;
165}
166
167static int popup_menu(int argc, char * const argv[])
168{
169 struct newtExitStruct es;
170 int i, rc = -1, max_len = 5;
171 newtComponent listbox, form = newt_form__new();
172
173 if (form == NULL)
174 return -1;
175
176 listbox = newtListbox(0, 0, argc, NEWT_FLAG_RETURNEXIT);
177 if (listbox == NULL)
178 goto out_destroy_form;
179
180 newtFormAddComponent(form, listbox);
181
182 for (i = 0; i < argc; ++i) {
183 int len = strlen(argv[i]);
184 if (len > max_len)
185 max_len = len;
186 if (newtListboxAddEntry(listbox, argv[i], (void *)(long)i))
187 goto out_destroy_form;
188 }
189
190 newtCenteredWindow(max_len, argc, NULL);
191 newtFormRun(form, &es);
192 rc = newtListboxGetCurrent(listbox) - NULL;
193 if (es.reason == NEWT_EXIT_HOTKEY)
194 rc = -1;
195 newtPopWindow();
196out_destroy_form:
197 newtFormDestroy(form);
198 return rc;
199}
200
201static int ui__help_window(const char *text)
202{
203 struct newtExitStruct es;
204 newtComponent tb, form = newt_form__new();
205 int rc = -1;
206 int max_len = 0, nr_lines = 0;
207 const char *t;
208
209 if (form == NULL)
210 return -1;
211
212 t = text;
213 while (1) {
214 const char *sep = strchr(t, '\n');
215 int len;
216
217 if (sep == NULL)
218 sep = strchr(t, '\0');
219 len = sep - t;
220 if (max_len < len)
221 max_len = len;
222 ++nr_lines;
223 if (*sep == '\0')
224 break;
225 t = sep + 1;
226 }
227
228 tb = newtTextbox(0, 0, max_len, nr_lines, 0);
229 if (tb == NULL)
230 goto out_destroy_form;
231
232 newtTextboxSetText(tb, text);
233 newtFormAddComponent(form, tb);
234 newtCenteredWindow(max_len, nr_lines, NULL);
235 newtFormRun(form, &es);
236 newtPopWindow();
237 rc = 0;
238out_destroy_form:
239 newtFormDestroy(form);
240 return rc;
241}
242
243static bool dialog_yesno(const char *msg)
244{
245 /* newtWinChoice should really be accepting const char pointers... */
246 char yes[] = "Yes", no[] = "No";
247 return newtWinChoice(NULL, yes, no, (char *)msg) == 1;
248}
249
250static void ui__error_window(const char *fmt, ...)
251{
252 va_list ap;
253
254 va_start(ap, fmt);
255 newtWinMessagev((char *)"Error", (char *)"Ok", (char *)fmt, ap);
256 va_end(ap);
257}
258
259#define HE_COLORSET_TOP 50
260#define HE_COLORSET_MEDIUM 51
261#define HE_COLORSET_NORMAL 52
262#define HE_COLORSET_SELECTED 53
263#define HE_COLORSET_CODE 54
264
265static int ui_browser__percent_color(double percent, bool current)
266{
267 if (current)
268 return HE_COLORSET_SELECTED;
269 if (percent >= MIN_RED)
270 return HE_COLORSET_TOP;
271 if (percent >= MIN_GREEN)
272 return HE_COLORSET_MEDIUM;
273 return HE_COLORSET_NORMAL;
274}
275
276struct ui_browser {
277 newtComponent form, sb;
278 u64 index, first_visible_entry_idx;
279 void *first_visible_entry, *entries;
280 u16 top, left, width, height;
281 void *priv;
282 unsigned int (*refresh_entries)(struct ui_browser *self);
283 void (*seek)(struct ui_browser *self,
284 off_t offset, int whence);
285 u32 nr_entries;
286};
287
288static void ui_browser__list_head_seek(struct ui_browser *self,
289 off_t offset, int whence)
290{
291 struct list_head *head = self->entries;
292 struct list_head *pos;
293
294 switch (whence) {
295 case SEEK_SET:
296 pos = head->next;
297 break;
298 case SEEK_CUR:
299 pos = self->first_visible_entry;
300 break;
301 case SEEK_END:
302 pos = head->prev;
303 break;
304 default:
305 return;
306 }
307
308 if (offset > 0) {
309 while (offset-- != 0)
310 pos = pos->next;
311 } else {
312 while (offset++ != 0)
313 pos = pos->prev;
314 }
315
316 self->first_visible_entry = pos;
317}
318
319static bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row)
320{
321 return (self->first_visible_entry_idx + row) == self->index;
322}
323
324static void ui_browser__refresh_dimensions(struct ui_browser *self)
325{
326 int cols, rows;
327 newtGetScreenSize(&cols, &rows);
328
329 if (self->width > cols - 4)
330 self->width = cols - 4;
331 self->height = rows - 5;
332 if (self->height > self->nr_entries)
333 self->height = self->nr_entries;
334 self->top = (rows - self->height) / 2;
335 self->left = (cols - self->width) / 2;
336}
337
338static void ui_browser__reset_index(struct ui_browser *self)
339{
340 self->index = self->first_visible_entry_idx = 0;
341 self->seek(self, 0, SEEK_SET);
342}
343
344static int ui_browser__show(struct ui_browser *self, const char *title)
345{
346 if (self->form != NULL) {
347 newtFormDestroy(self->form);
348 newtPopWindow();
349 }
350 ui_browser__refresh_dimensions(self);
351 newtCenteredWindow(self->width, self->height, title);
352 self->form = newt_form__new();
353 if (self->form == NULL)
354 return -1;
355
356 self->sb = newtVerticalScrollbar(self->width, 0, self->height,
357 HE_COLORSET_NORMAL,
358 HE_COLORSET_SELECTED);
359 if (self->sb == NULL)
360 return -1;
361
362 newtFormAddHotKey(self->form, NEWT_KEY_UP);
363 newtFormAddHotKey(self->form, NEWT_KEY_DOWN);
364 newtFormAddHotKey(self->form, NEWT_KEY_PGUP);
365 newtFormAddHotKey(self->form, NEWT_KEY_PGDN);
366 newtFormAddHotKey(self->form, NEWT_KEY_HOME);
367 newtFormAddHotKey(self->form, NEWT_KEY_END);
368 newtFormAddComponent(self->form, self->sb);
369 return 0;
370}
371
372static int objdump_line__show(struct objdump_line *self, struct list_head *head,
373 int width, struct hist_entry *he, int len,
374 bool current_entry)
375{
376 if (self->offset != -1) {
377 struct symbol *sym = he->ms.sym;
378 unsigned int hits = 0;
379 double percent = 0.0;
380 int color;
381 struct sym_priv *priv = symbol__priv(sym);
382 struct sym_ext *sym_ext = priv->ext;
383 struct sym_hist *h = priv->hist;
384 s64 offset = self->offset;
385 struct objdump_line *next = objdump__get_next_ip_line(head, self);
386
387 while (offset < (s64)len &&
388 (next == NULL || offset < next->offset)) {
389 if (sym_ext) {
390 percent += sym_ext[offset].percent;
391 } else
392 hits += h->ip[offset];
393
394 ++offset;
395 }
396
397 if (sym_ext == NULL && h->sum)
398 percent = 100.0 * hits / h->sum;
399
400 color = ui_browser__percent_color(percent, current_entry);
401 SLsmg_set_color(color);
402 slsmg_printf(" %7.2f ", percent);
403 if (!current_entry)
404 SLsmg_set_color(HE_COLORSET_CODE);
405 } else {
406 int color = ui_browser__percent_color(0, current_entry);
407 SLsmg_set_color(color);
408 slsmg_write_nstring(" ", 9);
409 }
410
411 SLsmg_write_char(':');
412 slsmg_write_nstring(" ", 8);
413 if (!*self->line)
414 slsmg_write_nstring(" ", width - 18);
415 else
416 slsmg_write_nstring(self->line, width - 18);
417
418 return 0;
419}
420
421static int ui_browser__refresh_entries(struct ui_browser *self)
422{
423 int row;
424
425 newtScrollbarSet(self->sb, self->index, self->nr_entries - 1);
426 row = self->refresh_entries(self);
427 SLsmg_set_color(HE_COLORSET_NORMAL);
428 SLsmg_fill_region(self->top + row, self->left,
429 self->height - row, self->width, ' ');
430
431 return 0;
432}
433
434static int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es)
435{
436 if (ui_browser__refresh_entries(self) < 0)
437 return -1;
438
439 while (1) {
440 off_t offset;
441
442 newtFormRun(self->form, es);
443
444 if (es->reason != NEWT_EXIT_HOTKEY)
445 break;
446 if (is_exit_key(es->u.key))
447 return es->u.key;
448 switch (es->u.key) {
449 case NEWT_KEY_DOWN:
450 if (self->index == self->nr_entries - 1)
451 break;
452 ++self->index;
453 if (self->index == self->first_visible_entry_idx + self->height) {
454 ++self->first_visible_entry_idx;
455 self->seek(self, +1, SEEK_CUR);
456 }
457 break;
458 case NEWT_KEY_UP:
459 if (self->index == 0)
460 break;
461 --self->index;
462 if (self->index < self->first_visible_entry_idx) {
463 --self->first_visible_entry_idx;
464 self->seek(self, -1, SEEK_CUR);
465 }
466 break;
467 case NEWT_KEY_PGDN:
468 case ' ':
469 if (self->first_visible_entry_idx + self->height > self->nr_entries - 1)
470 break;
471
472 offset = self->height;
473 if (self->index + offset > self->nr_entries - 1)
474 offset = self->nr_entries - 1 - self->index;
475 self->index += offset;
476 self->first_visible_entry_idx += offset;
477 self->seek(self, +offset, SEEK_CUR);
478 break;
479 case NEWT_KEY_PGUP:
480 if (self->first_visible_entry_idx == 0)
481 break;
482
483 if (self->first_visible_entry_idx < self->height)
484 offset = self->first_visible_entry_idx;
485 else
486 offset = self->height;
487
488 self->index -= offset;
489 self->first_visible_entry_idx -= offset;
490 self->seek(self, -offset, SEEK_CUR);
491 break;
492 case NEWT_KEY_HOME:
493 ui_browser__reset_index(self);
494 break;
495 case NEWT_KEY_END:
496 offset = self->height - 1;
497 if (offset >= self->nr_entries)
498 offset = self->nr_entries - 1;
499
500 self->index = self->nr_entries - 1;
501 self->first_visible_entry_idx = self->index - offset;
502 self->seek(self, -offset, SEEK_END);
503 break;
504 default:
505 return es->u.key;
506 }
507 if (ui_browser__refresh_entries(self) < 0)
508 return -1;
509 }
510 return 0;
511}
512
513static char *callchain_list__sym_name(struct callchain_list *self,
514 char *bf, size_t bfsize)
515{
516 if (self->ms.sym)
517 return self->ms.sym->name;
518
519 snprintf(bf, bfsize, "%#Lx", self->ip);
520 return bf;
521}
522
523static unsigned int hist_entry__annotate_browser_refresh(struct ui_browser *self)
524{
525 struct objdump_line *pos;
526 struct list_head *head = self->entries;
527 struct hist_entry *he = self->priv;
528 int row = 0;
529 int len = he->ms.sym->end - he->ms.sym->start;
530
531 if (self->first_visible_entry == NULL || self->first_visible_entry == self->entries)
532 self->first_visible_entry = head->next;
533
534 pos = list_entry(self->first_visible_entry, struct objdump_line, node);
535
536 list_for_each_entry_from(pos, head, node) {
537 bool current_entry = ui_browser__is_current_entry(self, row);
538 SLsmg_gotorc(self->top + row, self->left);
539 objdump_line__show(pos, head, self->width,
540 he, len, current_entry);
541 if (++row == self->height)
542 break;
543 }
544
545 return row;
546}
547
548int hist_entry__tui_annotate(struct hist_entry *self)
549{
550 struct ui_browser browser;
551 struct newtExitStruct es;
552 struct objdump_line *pos, *n;
553 LIST_HEAD(head);
554 int ret;
555
556 if (self->ms.sym == NULL)
557 return -1;
558
559 if (self->ms.map->dso->annotate_warned)
560 return -1;
561
562 if (hist_entry__annotate(self, &head) < 0) {
563 ui__error_window(browser__last_msg);
564 return -1;
565 }
566
567 ui_helpline__push("Press <- or ESC to exit");
568
569 memset(&browser, 0, sizeof(browser));
570 browser.entries = &head;
571 browser.refresh_entries = hist_entry__annotate_browser_refresh;
572 browser.seek = ui_browser__list_head_seek;
573 browser.priv = self;
574 list_for_each_entry(pos, &head, node) {
575 size_t line_len = strlen(pos->line);
576 if (browser.width < line_len)
577 browser.width = line_len;
578 ++browser.nr_entries;
579 }
580
581 browser.width += 18; /* Percentage */
582 ui_browser__show(&browser, self->ms.sym->name);
583 newtFormAddHotKey(browser.form, ' ');
584 ret = ui_browser__run(&browser, &es);
585 newtFormDestroy(browser.form);
586 newtPopWindow();
587 list_for_each_entry_safe(pos, n, &head, node) {
588 list_del(&pos->node);
589 objdump_line__free(pos);
590 }
591 ui_helpline__pop();
592 return ret;
593}
594
595struct hist_browser {
596 struct ui_browser b;
597 struct hists *hists;
598 struct hist_entry *he_selection;
599 struct map_symbol *selection;
600};
601
602static void hist_browser__reset(struct hist_browser *self);
603static int hist_browser__run(struct hist_browser *self, const char *title,
604 struct newtExitStruct *es);
605static unsigned int hist_browser__refresh_entries(struct ui_browser *self);
606static void ui_browser__hists_seek(struct ui_browser *self,
607 off_t offset, int whence);
608
609static struct hist_browser *hist_browser__new(struct hists *hists)
610{
611 struct hist_browser *self = zalloc(sizeof(*self));
612
613 if (self) {
614 self->hists = hists;
615 self->b.refresh_entries = hist_browser__refresh_entries;
616 self->b.seek = ui_browser__hists_seek;
617 }
618
619 return self;
620}
621
622static void hist_browser__delete(struct hist_browser *self)
623{
624 newtFormDestroy(self->b.form);
625 newtPopWindow();
626 free(self);
627}
628
629static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self)
630{
631 return self->he_selection;
632}
633
634static struct thread *hist_browser__selected_thread(struct hist_browser *self)
635{
636 return self->he_selection->thread;
637}
638
639static int hist_browser__title(char *bf, size_t size, const char *ev_name,
640 const struct dso *dso, const struct thread *thread)
641{
642 int printed = 0;
643
644 if (thread)
645 printed += snprintf(bf + printed, size - printed,
646 "Thread: %s(%d)",
647 (thread->comm_set ? thread->comm : ""),
648 thread->pid);
649 if (dso)
650 printed += snprintf(bf + printed, size - printed,
651 "%sDSO: %s", thread ? " " : "",
652 dso->short_name);
653 return printed ?: snprintf(bf, size, "Event: %s", ev_name);
654}
655
656int hists__browse(struct hists *self, const char *helpline, const char *ev_name)
657{
658 struct hist_browser *browser = hist_browser__new(self);
659 struct pstack *fstack;
660 const struct thread *thread_filter = NULL;
661 const struct dso *dso_filter = NULL;
662 struct newtExitStruct es;
663 char msg[160];
664 int key = -1;
665
666 if (browser == NULL)
667 return -1;
668
669 fstack = pstack__new(2);
670 if (fstack == NULL)
671 goto out;
672
673 ui_helpline__push(helpline);
674
675 hist_browser__title(msg, sizeof(msg), ev_name,
676 dso_filter, thread_filter);
677
678 while (1) {
679 const struct thread *thread;
680 const struct dso *dso;
681 char *options[16];
682 int nr_options = 0, choice = 0, i,
683 annotate = -2, zoom_dso = -2, zoom_thread = -2;
684
685 if (hist_browser__run(browser, msg, &es))
686 break;
687
688 thread = hist_browser__selected_thread(browser);
689 dso = browser->selection->map ? browser->selection->map->dso : NULL;
690
691 if (es.reason == NEWT_EXIT_HOTKEY) {
692 key = es.u.key;
693
694 switch (key) {
695 case NEWT_KEY_F1:
696 goto do_help;
697 case NEWT_KEY_TAB:
698 case NEWT_KEY_UNTAB:
699 /*
700 * Exit the browser, let hists__browser_tree
701 * go to the next or previous
702 */
703 goto out_free_stack;
704 default:;
705 }
706
707 key = toupper(key);
708 switch (key) {
709 case 'A':
710 if (browser->selection->map == NULL &&
711 browser->selection->map->dso->annotate_warned)
712 continue;
713 goto do_annotate;
714 case 'D':
715 goto zoom_dso;
716 case 'T':
717 goto zoom_thread;
718 case 'H':
719 case '?':
720do_help:
721 ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n"
722 "<- Zoom out\n"
723 "a Annotate current symbol\n"
724 "h/?/F1 Show this window\n"
725 "d Zoom into current DSO\n"
726 "t Zoom into current Thread\n"
727 "q/CTRL+C Exit browser");
728 continue;
729 default:;
730 }
731 if (is_exit_key(key)) {
732 if (key == NEWT_KEY_ESCAPE) {
733 if (dialog_yesno("Do you really want to exit?"))
734 break;
735 else
736 continue;
737 } else
738 break;
739 }
740
741 if (es.u.key == NEWT_KEY_LEFT) {
742 const void *top;
743
744 if (pstack__empty(fstack))
745 continue;
746 top = pstack__pop(fstack);
747 if (top == &dso_filter)
748 goto zoom_out_dso;
749 if (top == &thread_filter)
750 goto zoom_out_thread;
751 continue;
752 }
753 }
754
755 if (browser->selection->sym != NULL &&
756 !browser->selection->map->dso->annotate_warned &&
757 asprintf(&options[nr_options], "Annotate %s",
758 browser->selection->sym->name) > 0)
759 annotate = nr_options++;
760
761 if (thread != NULL &&
762 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
763 (thread_filter ? "out of" : "into"),
764 (thread->comm_set ? thread->comm : ""),
765 thread->pid) > 0)
766 zoom_thread = nr_options++;
767
768 if (dso != NULL &&
769 asprintf(&options[nr_options], "Zoom %s %s DSO",
770 (dso_filter ? "out of" : "into"),
771 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
772 zoom_dso = nr_options++;
773
774 options[nr_options++] = (char *)"Exit";
775
776 choice = popup_menu(nr_options, options);
777
778 for (i = 0; i < nr_options - 1; ++i)
779 free(options[i]);
780
781 if (choice == nr_options - 1)
782 break;
783
784 if (choice == -1)
785 continue;
786
787 if (choice == annotate) {
788 struct hist_entry *he;
789do_annotate:
790 if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) {
791 browser->selection->map->dso->annotate_warned = 1;
792 ui_helpline__puts("No vmlinux file found, can't "
793 "annotate with just a "
794 "kallsyms file");
795 continue;
796 }
797
798 he = hist_browser__selected_entry(browser);
799 if (he == NULL)
800 continue;
801
802 hist_entry__tui_annotate(he);
803 } else if (choice == zoom_dso) {
804zoom_dso:
805 if (dso_filter) {
806 pstack__remove(fstack, &dso_filter);
807zoom_out_dso:
808 ui_helpline__pop();
809 dso_filter = NULL;
810 } else {
811 if (dso == NULL)
812 continue;
813 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
814 dso->kernel ? "the Kernel" : dso->short_name);
815 dso_filter = dso;
816 pstack__push(fstack, &dso_filter);
817 }
818 hists__filter_by_dso(self, dso_filter);
819 hist_browser__title(msg, sizeof(msg), ev_name,
820 dso_filter, thread_filter);
821 hist_browser__reset(browser);
822 } else if (choice == zoom_thread) {
823zoom_thread:
824 if (thread_filter) {
825 pstack__remove(fstack, &thread_filter);
826zoom_out_thread:
827 ui_helpline__pop();
828 thread_filter = NULL;
829 } else {
830 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
831 thread->comm_set ? thread->comm : "",
832 thread->pid);
833 thread_filter = thread;
834 pstack__push(fstack, &thread_filter);
835 }
836 hists__filter_by_thread(self, thread_filter);
837 hist_browser__title(msg, sizeof(msg), ev_name,
838 dso_filter, thread_filter);
839 hist_browser__reset(browser);
840 }
841 }
842out_free_stack:
843 pstack__delete(fstack);
844out:
845 hist_browser__delete(browser);
846 return key;
847}
848
849int hists__tui_browse_tree(struct rb_root *self, const char *help)
850{
851 struct rb_node *first = rb_first(self), *nd = first, *next;
852 int key = 0;
853
854 while (nd) {
855 struct hists *hists = rb_entry(nd, struct hists, rb_node);
856 const char *ev_name = __event_name(hists->type, hists->config);
857
858 key = hists__browse(hists, help, ev_name);
859
860 if (is_exit_key(key))
861 break;
862
863 switch (key) {
864 case NEWT_KEY_TAB:
865 next = rb_next(nd);
866 if (next)
867 nd = next;
868 break;
869 case NEWT_KEY_UNTAB:
870 if (nd == first)
871 continue;
872 nd = rb_prev(nd);
873 default:
874 break;
875 }
876 }
877
878 return key;
879}
880
881static struct newtPercentTreeColors {
882 const char *topColorFg, *topColorBg;
883 const char *mediumColorFg, *mediumColorBg;
884 const char *normalColorFg, *normalColorBg;
885 const char *selColorFg, *selColorBg;
886 const char *codeColorFg, *codeColorBg;
887} defaultPercentTreeColors = {
888 "red", "lightgray",
889 "green", "lightgray",
890 "black", "lightgray",
891 "lightgray", "magenta",
892 "blue", "lightgray",
893};
894
895static void newt_suspend(void *d __used)
896{
897 newtSuspend();
898 raise(SIGTSTP);
899 newtResume();
900}
901
902void setup_browser(void)
903{
904 struct newtPercentTreeColors *c = &defaultPercentTreeColors;
905
906 if (!isatty(1) || !use_browser || dump_trace) {
907 use_browser = 0;
908 setup_pager();
909 return;
910 }
911
912 use_browser = 1;
913 newtInit();
914 newtCls();
915 newtSetSuspendCallback(newt_suspend, NULL);
916 ui_helpline__puts(" ");
917 sltt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg);
918 sltt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg);
919 sltt_set_color(HE_COLORSET_NORMAL, NULL, c->normalColorFg, c->normalColorBg);
920 sltt_set_color(HE_COLORSET_SELECTED, NULL, c->selColorFg, c->selColorBg);
921 sltt_set_color(HE_COLORSET_CODE, NULL, c->codeColorFg, c->codeColorBg);
922}
923
924void exit_browser(bool wait_for_ok)
925{
926 if (use_browser > 0) {
927 if (wait_for_ok) {
928 char title[] = "Fatal Error", ok[] = "Ok";
929 newtWinMessage(title, ok, browser__last_msg);
930 }
931 newtFinished();
932 }
933}
934
935static void hist_browser__refresh_dimensions(struct hist_browser *self)
936{
937 /* 3 == +/- toggle symbol before actual hist_entry rendering */
938 self->b.width = 3 + (hists__sort_list_width(self->hists) +
939 sizeof("[k]"));
940}
941
942static void hist_browser__reset(struct hist_browser *self)
943{
944 self->b.nr_entries = self->hists->nr_entries;
945 hist_browser__refresh_dimensions(self);
946 ui_browser__reset_index(&self->b);
947}
948
949static char tree__folded_sign(bool unfolded)
950{
951 return unfolded ? '-' : '+';
952}
953
954static char map_symbol__folded(const struct map_symbol *self)
955{
956 return self->has_children ? tree__folded_sign(self->unfolded) : ' ';
957}
958
959static char hist_entry__folded(const struct hist_entry *self)
960{
961 return map_symbol__folded(&self->ms);
962}
963
964static char callchain_list__folded(const struct callchain_list *self)
965{
966 return map_symbol__folded(&self->ms);
967}
968
969static bool map_symbol__toggle_fold(struct map_symbol *self)
970{
971 if (!self->has_children)
972 return false;
973
974 self->unfolded = !self->unfolded;
975 return true;
976}
977
978#define LEVEL_OFFSET_STEP 3
979
980static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self,
981 struct callchain_node *chain_node,
982 u64 total, int level,
983 unsigned short row,
984 off_t *row_offset,
985 bool *is_current_entry)
986{
987 struct rb_node *node;
988 int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
989 u64 new_total, remaining;
990
991 if (callchain_param.mode == CHAIN_GRAPH_REL)
992 new_total = chain_node->children_hit;
993 else
994 new_total = total;
995
996 remaining = new_total;
997 node = rb_first(&chain_node->rb_root);
998 while (node) {
999 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1000 struct rb_node *next = rb_next(node);
1001 u64 cumul = cumul_hits(child);
1002 struct callchain_list *chain;
1003 char folded_sign = ' ';
1004 int first = true;
1005 int extra_offset = 0;
1006
1007 remaining -= cumul;
1008
1009 list_for_each_entry(chain, &child->val, list) {
1010 char ipstr[BITS_PER_LONG / 4 + 1], *alloc_str;
1011 const char *str;
1012 int color;
1013 bool was_first = first;
1014
1015 if (first) {
1016 first = false;
1017 chain->ms.has_children = chain->list.next != &child->val ||
1018 rb_first(&child->rb_root) != NULL;
1019 } else {
1020 extra_offset = LEVEL_OFFSET_STEP;
1021 chain->ms.has_children = chain->list.next == &child->val &&
1022 rb_first(&child->rb_root) != NULL;
1023 }
1024
1025 folded_sign = callchain_list__folded(chain);
1026 if (*row_offset != 0) {
1027 --*row_offset;
1028 goto do_next;
1029 }
1030
1031 alloc_str = NULL;
1032 str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
1033 if (was_first) {
1034 double percent = cumul * 100.0 / new_total;
1035
1036 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
1037 str = "Not enough memory!";
1038 else
1039 str = alloc_str;
1040 }
1041
1042 color = HE_COLORSET_NORMAL;
1043 width = self->b.width - (offset + extra_offset + 2);
1044 if (ui_browser__is_current_entry(&self->b, row)) {
1045 self->selection = &chain->ms;
1046 color = HE_COLORSET_SELECTED;
1047 *is_current_entry = true;
1048 }
1049
1050 SLsmg_set_color(color);
1051 SLsmg_gotorc(self->b.top + row, self->b.left);
1052 slsmg_write_nstring(" ", offset + extra_offset);
1053 slsmg_printf("%c ", folded_sign);
1054 slsmg_write_nstring(str, width);
1055 free(alloc_str);
1056
1057 if (++row == self->b.height)
1058 goto out;
1059do_next:
1060 if (folded_sign == '+')
1061 break;
1062 }
1063
1064 if (folded_sign == '-') {
1065 const int new_level = level + (extra_offset ? 2 : 1);
1066 row += hist_browser__show_callchain_node_rb_tree(self, child, new_total,
1067 new_level, row, row_offset,
1068 is_current_entry);
1069 }
1070 if (row == self->b.height)
1071 goto out;
1072 node = next;
1073 }
1074out:
1075 return row - first_row;
1076}
1077
1078static int hist_browser__show_callchain_node(struct hist_browser *self,
1079 struct callchain_node *node,
1080 int level, unsigned short row,
1081 off_t *row_offset,
1082 bool *is_current_entry)
1083{
1084 struct callchain_list *chain;
1085 int first_row = row,
1086 offset = level * LEVEL_OFFSET_STEP,
1087 width = self->b.width - offset;
1088 char folded_sign = ' ';
1089
1090 list_for_each_entry(chain, &node->val, list) {
1091 char ipstr[BITS_PER_LONG / 4 + 1], *s;
1092 int color;
1093 /*
1094 * FIXME: This should be moved to somewhere else,
1095 * probably when the callchain is created, so as not to
1096 * traverse it all over again
1097 */
1098 chain->ms.has_children = rb_first(&node->rb_root) != NULL;
1099 folded_sign = callchain_list__folded(chain);
1100
1101 if (*row_offset != 0) {
1102 --*row_offset;
1103 continue;
1104 }
1105
1106 color = HE_COLORSET_NORMAL;
1107 if (ui_browser__is_current_entry(&self->b, row)) {
1108 self->selection = &chain->ms;
1109 color = HE_COLORSET_SELECTED;
1110 *is_current_entry = true;
1111 }
1112
1113 s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
1114 SLsmg_gotorc(self->b.top + row, self->b.left);
1115 SLsmg_set_color(color);
1116 slsmg_write_nstring(" ", offset);
1117 slsmg_printf("%c ", folded_sign);
1118 slsmg_write_nstring(s, width - 2);
1119
1120 if (++row == self->b.height)
1121 goto out;
1122 }
1123
1124 if (folded_sign == '-')
1125 row += hist_browser__show_callchain_node_rb_tree(self, node,
1126 self->hists->stats.total_period,
1127 level + 1, row,
1128 row_offset,
1129 is_current_entry);
1130out:
1131 return row - first_row;
1132}
1133
1134static int hist_browser__show_callchain(struct hist_browser *self,
1135 struct rb_root *chain,
1136 int level, unsigned short row,
1137 off_t *row_offset,
1138 bool *is_current_entry)
1139{
1140 struct rb_node *nd;
1141 int first_row = row;
1142
1143 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
1144 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
1145
1146 row += hist_browser__show_callchain_node(self, node, level,
1147 row, row_offset,
1148 is_current_entry);
1149 if (row == self->b.height)
1150 break;
1151 }
1152
1153 return row - first_row;
1154}
1155
1156static int hist_browser__show_entry(struct hist_browser *self,
1157 struct hist_entry *entry,
1158 unsigned short row)
1159{
1160 char s[256];
1161 double percent;
1162 int printed = 0;
1163 int color, width = self->b.width;
1164 char folded_sign = ' ';
1165 bool current_entry = ui_browser__is_current_entry(&self->b, row);
1166 off_t row_offset = entry->row_offset;
1167
1168 if (current_entry) {
1169 self->he_selection = entry;
1170 self->selection = &entry->ms;
1171 }
1172
1173 if (symbol_conf.use_callchain) {
1174 entry->ms.has_children = !RB_EMPTY_ROOT(&entry->sorted_chain);
1175 folded_sign = hist_entry__folded(entry);
1176 }
1177
1178 if (row_offset == 0) {
1179 hist_entry__snprintf(entry, s, sizeof(s), self->hists, NULL, false,
1180 0, false, self->hists->stats.total_period);
1181 percent = (entry->period * 100.0) / self->hists->stats.total_period;
1182
1183 color = HE_COLORSET_SELECTED;
1184 if (!current_entry) {
1185 if (percent >= MIN_RED)
1186 color = HE_COLORSET_TOP;
1187 else if (percent >= MIN_GREEN)
1188 color = HE_COLORSET_MEDIUM;
1189 else
1190 color = HE_COLORSET_NORMAL;
1191 }
1192
1193 SLsmg_set_color(color);
1194 SLsmg_gotorc(self->b.top + row, self->b.left);
1195 if (symbol_conf.use_callchain) {
1196 slsmg_printf("%c ", folded_sign);
1197 width -= 2;
1198 }
1199 slsmg_write_nstring(s, width);
1200 ++row;
1201 ++printed;
1202 } else
1203 --row_offset;
1204
1205 if (folded_sign == '-' && row != self->b.height) {
1206 printed += hist_browser__show_callchain(self, &entry->sorted_chain,
1207 1, row, &row_offset,
1208 &current_entry);
1209 if (current_entry)
1210 self->he_selection = entry;
1211 }
1212
1213 return printed;
1214}
1215
1216static unsigned int hist_browser__refresh_entries(struct ui_browser *self)
1217{
1218 unsigned row = 0;
1219 struct rb_node *nd;
1220 struct hist_browser *hb = container_of(self, struct hist_browser, b);
1221
1222 if (self->first_visible_entry == NULL)
1223 self->first_visible_entry = rb_first(&hb->hists->entries);
1224
1225 for (nd = self->first_visible_entry; nd; nd = rb_next(nd)) {
1226 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1227
1228 if (h->filtered)
1229 continue;
1230
1231 row += hist_browser__show_entry(hb, h, row);
1232 if (row == self->height)
1233 break;
1234 }
1235
1236 return row;
1237}
1238
1239static void callchain_node__init_have_children_rb_tree(struct callchain_node *self)
1240{
1241 struct rb_node *nd = rb_first(&self->rb_root);
1242
1243 for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
1244 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
1245 struct callchain_list *chain;
1246 int first = true;
1247
1248 list_for_each_entry(chain, &child->val, list) {
1249 if (first) {
1250 first = false;
1251 chain->ms.has_children = chain->list.next != &child->val ||
1252 rb_first(&child->rb_root) != NULL;
1253 } else
1254 chain->ms.has_children = chain->list.next == &child->val &&
1255 rb_first(&child->rb_root) != NULL;
1256 }
1257
1258 callchain_node__init_have_children_rb_tree(child);
1259 }
1260}
1261
1262static void callchain_node__init_have_children(struct callchain_node *self)
1263{
1264 struct callchain_list *chain;
1265
1266 list_for_each_entry(chain, &self->val, list)
1267 chain->ms.has_children = rb_first(&self->rb_root) != NULL;
1268
1269 callchain_node__init_have_children_rb_tree(self);
1270}
1271
1272static void callchain__init_have_children(struct rb_root *self)
1273{
1274 struct rb_node *nd;
1275
1276 for (nd = rb_first(self); nd; nd = rb_next(nd)) {
1277 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
1278 callchain_node__init_have_children(node);
1279 }
1280}
1281
1282static void hist_entry__init_have_children(struct hist_entry *self)
1283{
1284 if (!self->init_have_children) {
1285 callchain__init_have_children(&self->sorted_chain);
1286 self->init_have_children = true;
1287 }
1288}
1289
1290static struct rb_node *hists__filter_entries(struct rb_node *nd)
1291{
1292 while (nd != NULL) {
1293 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1294 if (!h->filtered)
1295 return nd;
1296
1297 nd = rb_next(nd);
1298 }
1299
1300 return NULL;
1301}
1302
1303static struct rb_node *hists__filter_prev_entries(struct rb_node *nd)
1304{
1305 while (nd != NULL) {
1306 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1307 if (!h->filtered)
1308 return nd;
1309
1310 nd = rb_prev(nd);
1311 }
1312
1313 return NULL;
1314}
1315
1316static void ui_browser__hists_seek(struct ui_browser *self,
1317 off_t offset, int whence)
1318{
1319 struct hist_entry *h;
1320 struct rb_node *nd;
1321 bool first = true;
1322
1323 switch (whence) {
1324 case SEEK_SET:
1325 nd = hists__filter_entries(rb_first(self->entries));
1326 break;
1327 case SEEK_CUR:
1328 nd = self->first_visible_entry;
1329 goto do_offset;
1330 case SEEK_END:
1331 nd = hists__filter_prev_entries(rb_last(self->entries));
1332 first = false;
1333 break;
1334 default:
1335 return;
1336 }
1337
1338 /*
1339 * Moves not relative to the first visible entry invalidates its
1340 * row_offset:
1341 */
1342 h = rb_entry(self->first_visible_entry, struct hist_entry, rb_node);
1343 h->row_offset = 0;
1344
1345 /*
1346 * Here we have to check if nd is expanded (+), if it is we can't go
1347 * the next top level hist_entry, instead we must compute an offset of
1348 * what _not_ to show and not change the first visible entry.
1349 *
1350 * This offset increments when we are going from top to bottom and
1351 * decreases when we're going from bottom to top.
1352 *
1353 * As we don't have backpointers to the top level in the callchains
1354 * structure, we need to always print the whole hist_entry callchain,
1355 * skipping the first ones that are before the first visible entry
1356 * and stop when we printed enough lines to fill the screen.
1357 */
1358do_offset:
1359 if (offset > 0) {
1360 do {
1361 h = rb_entry(nd, struct hist_entry, rb_node);
1362 if (h->ms.unfolded) {
1363 u16 remaining = h->nr_rows - h->row_offset;
1364 if (offset > remaining) {
1365 offset -= remaining;
1366 h->row_offset = 0;
1367 } else {
1368 h->row_offset += offset;
1369 offset = 0;
1370 self->first_visible_entry = nd;
1371 break;
1372 }
1373 }
1374 nd = hists__filter_entries(rb_next(nd));
1375 if (nd == NULL)
1376 break;
1377 --offset;
1378 self->first_visible_entry = nd;
1379 } while (offset != 0);
1380 } else if (offset < 0) {
1381 while (1) {
1382 h = rb_entry(nd, struct hist_entry, rb_node);
1383 if (h->ms.unfolded) {
1384 if (first) {
1385 if (-offset > h->row_offset) {
1386 offset += h->row_offset;
1387 h->row_offset = 0;
1388 } else {
1389 h->row_offset += offset;
1390 offset = 0;
1391 self->first_visible_entry = nd;
1392 break;
1393 }
1394 } else {
1395 if (-offset > h->nr_rows) {
1396 offset += h->nr_rows;
1397 h->row_offset = 0;
1398 } else {
1399 h->row_offset = h->nr_rows + offset;
1400 offset = 0;
1401 self->first_visible_entry = nd;
1402 break;
1403 }
1404 }
1405 }
1406
1407 nd = hists__filter_prev_entries(rb_prev(nd));
1408 if (nd == NULL)
1409 break;
1410 ++offset;
1411 self->first_visible_entry = nd;
1412 if (offset == 0) {
1413 /*
1414 * Last unfiltered hist_entry, check if it is
1415 * unfolded, if it is then we should have
1416 * row_offset at its last entry.
1417 */
1418 h = rb_entry(nd, struct hist_entry, rb_node);
1419 if (h->ms.unfolded)
1420 h->row_offset = h->nr_rows;
1421 break;
1422 }
1423 first = false;
1424 }
1425 } else {
1426 self->first_visible_entry = nd;
1427 h = rb_entry(nd, struct hist_entry, rb_node);
1428 h->row_offset = 0;
1429 }
1430}
1431
1432static int callchain_node__count_rows_rb_tree(struct callchain_node *self)
1433{
1434 int n = 0;
1435 struct rb_node *nd;
1436
1437 for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
1438 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
1439 struct callchain_list *chain;
1440 char folded_sign = ' '; /* No children */
1441
1442 list_for_each_entry(chain, &child->val, list) {
1443 ++n;
1444 /* We need this because we may not have children */
1445 folded_sign = callchain_list__folded(chain);
1446 if (folded_sign == '+')
1447 break;
1448 }
1449
1450 if (folded_sign == '-') /* Have children and they're unfolded */
1451 n += callchain_node__count_rows_rb_tree(child);
1452 }
1453
1454 return n;
1455}
1456
1457static int callchain_node__count_rows(struct callchain_node *node)
1458{
1459 struct callchain_list *chain;
1460 bool unfolded = false;
1461 int n = 0;
1462
1463 list_for_each_entry(chain, &node->val, list) {
1464 ++n;
1465 unfolded = chain->ms.unfolded;
1466 }
1467
1468 if (unfolded)
1469 n += callchain_node__count_rows_rb_tree(node);
1470
1471 return n;
1472}
1473
1474static int callchain__count_rows(struct rb_root *chain)
1475{
1476 struct rb_node *nd;
1477 int n = 0;
1478
1479 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
1480 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
1481 n += callchain_node__count_rows(node);
1482 }
1483
1484 return n;
1485}
1486
1487static bool hist_browser__toggle_fold(struct hist_browser *self)
1488{
1489 if (map_symbol__toggle_fold(self->selection)) {
1490 struct hist_entry *he = self->he_selection;
1491
1492 hist_entry__init_have_children(he);
1493 self->hists->nr_entries -= he->nr_rows;
1494
1495 if (he->ms.unfolded)
1496 he->nr_rows = callchain__count_rows(&he->sorted_chain);
1497 else
1498 he->nr_rows = 0;
1499 self->hists->nr_entries += he->nr_rows;
1500 self->b.nr_entries = self->hists->nr_entries;
1501
1502 return true;
1503 }
1504
1505 /* If it doesn't have children, no toggling performed */
1506 return false;
1507}
1508
1509static int hist_browser__run(struct hist_browser *self, const char *title,
1510 struct newtExitStruct *es)
1511{
1512 char str[256], unit;
1513 unsigned long nr_events = self->hists->stats.nr_events[PERF_RECORD_SAMPLE];
1514
1515 self->b.entries = &self->hists->entries;
1516 self->b.nr_entries = self->hists->nr_entries;
1517
1518 hist_browser__refresh_dimensions(self);
1519
1520 nr_events = convert_unit(nr_events, &unit);
1521 snprintf(str, sizeof(str), "Events: %lu%c ",
1522 nr_events, unit);
1523 newtDrawRootText(0, 0, str);
1524
1525 if (ui_browser__show(&self->b, title) < 0)
1526 return -1;
1527
1528 newtFormAddHotKey(self->b.form, 'A');
1529 newtFormAddHotKey(self->b.form, 'a');
1530 newtFormAddHotKey(self->b.form, '?');
1531 newtFormAddHotKey(self->b.form, 'h');
1532 newtFormAddHotKey(self->b.form, 'H');
1533 newtFormAddHotKey(self->b.form, 'd');
1534
1535 newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT);
1536 newtFormAddHotKey(self->b.form, NEWT_KEY_RIGHT);
1537 newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER);
1538
1539 while (1) {
1540 ui_browser__run(&self->b, es);
1541
1542 if (es->reason != NEWT_EXIT_HOTKEY)
1543 break;
1544 switch (es->u.key) {
1545 case 'd': { /* Debug */
1546 static int seq;
1547 struct hist_entry *h = rb_entry(self->b.first_visible_entry,
1548 struct hist_entry, rb_node);
1549 ui_helpline__pop();
1550 ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
1551 seq++, self->b.nr_entries,
1552 self->hists->nr_entries,
1553 self->b.height,
1554 self->b.index,
1555 self->b.first_visible_entry_idx,
1556 h->row_offset, h->nr_rows);
1557 }
1558 continue;
1559 case NEWT_KEY_ENTER:
1560 if (hist_browser__toggle_fold(self))
1561 break;
1562 /* fall thru */
1563 default:
1564 return 0;
1565 }
1566 }
1567 return 0;
1568}
diff --git a/tools/perf/util/path.c b/tools/perf/util/path.c
index 58a470d036dd..bd7497711424 100644
--- a/tools/perf/util/path.c
+++ b/tools/perf/util/path.c
@@ -22,6 +22,7 @@ static const char *get_perf_dir(void)
22 return "."; 22 return ".";
23} 23}
24 24
25#ifdef NO_STRLCPY
25size_t strlcpy(char *dest, const char *src, size_t size) 26size_t strlcpy(char *dest, const char *src, size_t size)
26{ 27{
27 size_t ret = strlen(src); 28 size_t ret = strlen(src);
@@ -33,7 +34,7 @@ size_t strlcpy(char *dest, const char *src, size_t size)
33 } 34 }
34 return ret; 35 return ret;
35} 36}
36 37#endif
37 38
38static char *get_pathname(void) 39static char *get_pathname(void)
39{ 40{
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 2e665cb84055..61191c6cbe7a 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -74,10 +74,9 @@ static int e_snprintf(char *str, size_t size, const char *format, ...)
74static char *synthesize_perf_probe_point(struct perf_probe_point *pp); 74static char *synthesize_perf_probe_point(struct perf_probe_point *pp);
75static struct machine machine; 75static struct machine machine;
76 76
77/* Initialize symbol maps and path of vmlinux */ 77/* Initialize symbol maps and path of vmlinux/modules */
78static int init_vmlinux(void) 78static int init_vmlinux(void)
79{ 79{
80 struct dso *kernel;
81 int ret; 80 int ret;
82 81
83 symbol_conf.sort_by_name = true; 82 symbol_conf.sort_by_name = true;
@@ -91,33 +90,70 @@ static int init_vmlinux(void)
91 goto out; 90 goto out;
92 } 91 }
93 92
94 ret = machine__init(&machine, "/", 0); 93 ret = machine__init(&machine, "", HOST_KERNEL_ID);
95 if (ret < 0) 94 if (ret < 0)
96 goto out; 95 goto out;
97 96
98 kernel = dso__new_kernel(symbol_conf.vmlinux_name); 97 if (machine__create_kernel_maps(&machine) < 0) {
99 if (kernel == NULL) 98 pr_debug("machine__create_kernel_maps ");
100 die("Failed to create kernel dso."); 99 goto out;
101 100 }
102 ret = __machine__create_kernel_maps(&machine, kernel);
103 if (ret < 0)
104 pr_debug("Failed to create kernel maps.\n");
105
106out: 101out:
107 if (ret < 0) 102 if (ret < 0)
108 pr_warning("Failed to init vmlinux path.\n"); 103 pr_warning("Failed to init vmlinux path.\n");
109 return ret; 104 return ret;
110} 105}
111 106
107static struct symbol *__find_kernel_function_by_name(const char *name,
108 struct map **mapp)
109{
110 return machine__find_kernel_function_by_name(&machine, name, mapp,
111 NULL);
112}
113
114const char *kernel_get_module_path(const char *module)
115{
116 struct dso *dso;
117 struct map *map;
118 const char *vmlinux_name;
119
120 if (module) {
121 list_for_each_entry(dso, &machine.kernel_dsos, node) {
122 if (strncmp(dso->short_name + 1, module,
123 dso->short_name_len - 2) == 0)
124 goto found;
125 }
126 pr_debug("Failed to find module %s.\n", module);
127 return NULL;
128 }
129
130 map = machine.vmlinux_maps[MAP__FUNCTION];
131 dso = map->dso;
132
133 vmlinux_name = symbol_conf.vmlinux_name;
134 if (vmlinux_name) {
135 if (dso__load_vmlinux(dso, map, vmlinux_name, NULL) <= 0)
136 return NULL;
137 } else {
138 if (dso__load_vmlinux_path(dso, map, NULL) <= 0) {
139 pr_debug("Failed to load kernel map.\n");
140 return NULL;
141 }
142 }
143found:
144 return dso->long_name;
145}
146
112#ifdef DWARF_SUPPORT 147#ifdef DWARF_SUPPORT
113static int open_vmlinux(void) 148static int open_vmlinux(const char *module)
114{ 149{
115 if (map__load(machine.vmlinux_maps[MAP__FUNCTION], NULL) < 0) { 150 const char *path = kernel_get_module_path(module);
116 pr_debug("Failed to load kernel map.\n"); 151 if (!path) {
117 return -EINVAL; 152 pr_err("Failed to find path of %s module", module ?: "kernel");
153 return -ENOENT;
118 } 154 }
119 pr_debug("Try to open %s\n", machine.vmlinux_maps[MAP__FUNCTION]->dso->long_name); 155 pr_debug("Try to open %s\n", path);
120 return open(machine.vmlinux_maps[MAP__FUNCTION]->dso->long_name, O_RDONLY); 156 return open(path, O_RDONLY);
121} 157}
122 158
123/* 159/*
@@ -125,20 +161,19 @@ static int open_vmlinux(void)
125 * Currently only handles kprobes. 161 * Currently only handles kprobes.
126 */ 162 */
127static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, 163static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
128 struct perf_probe_point *pp) 164 struct perf_probe_point *pp)
129{ 165{
130 struct symbol *sym; 166 struct symbol *sym;
131 int fd, ret = -ENOENT; 167 struct map *map;
168 u64 addr;
169 int ret = -ENOENT;
132 170
133 sym = map__find_symbol_by_name(machine.vmlinux_maps[MAP__FUNCTION], 171 sym = __find_kernel_function_by_name(tp->symbol, &map);
134 tp->symbol, NULL);
135 if (sym) { 172 if (sym) {
136 fd = open_vmlinux(); 173 addr = map->unmap_ip(map, sym->start + tp->offset);
137 if (fd >= 0) { 174 pr_debug("try to find %s+%ld@%llx\n", tp->symbol,
138 ret = find_perf_probe_point(fd, 175 tp->offset, addr);
139 sym->start + tp->offset, pp); 176 ret = find_perf_probe_point((unsigned long)addr, pp);
140 close(fd);
141 }
142 } 177 }
143 if (ret <= 0) { 178 if (ret <= 0) {
144 pr_debug("Failed to find corresponding probes from " 179 pr_debug("Failed to find corresponding probes from "
@@ -156,12 +191,12 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
156/* Try to find perf_probe_event with debuginfo */ 191/* Try to find perf_probe_event with debuginfo */
157static int try_to_find_probe_trace_events(struct perf_probe_event *pev, 192static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
158 struct probe_trace_event **tevs, 193 struct probe_trace_event **tevs,
159 int max_tevs) 194 int max_tevs, const char *module)
160{ 195{
161 bool need_dwarf = perf_probe_event_need_dwarf(pev); 196 bool need_dwarf = perf_probe_event_need_dwarf(pev);
162 int fd, ntevs; 197 int fd, ntevs;
163 198
164 fd = open_vmlinux(); 199 fd = open_vmlinux(module);
165 if (fd < 0) { 200 if (fd < 0) {
166 if (need_dwarf) { 201 if (need_dwarf) {
167 pr_warning("Failed to open debuginfo file.\n"); 202 pr_warning("Failed to open debuginfo file.\n");
@@ -300,7 +335,7 @@ error:
300 * Show line-range always requires debuginfo to find source file and 335 * Show line-range always requires debuginfo to find source file and
301 * line number. 336 * line number.
302 */ 337 */
303int show_line_range(struct line_range *lr) 338int show_line_range(struct line_range *lr, const char *module)
304{ 339{
305 int l = 1; 340 int l = 1;
306 struct line_node *ln; 341 struct line_node *ln;
@@ -313,7 +348,7 @@ int show_line_range(struct line_range *lr)
313 if (ret < 0) 348 if (ret < 0)
314 return ret; 349 return ret;
315 350
316 fd = open_vmlinux(); 351 fd = open_vmlinux(module);
317 if (fd < 0) { 352 if (fd < 0) {
318 pr_warning("Failed to open debuginfo file.\n"); 353 pr_warning("Failed to open debuginfo file.\n");
319 return fd; 354 return fd;
@@ -378,11 +413,84 @@ end:
378 return ret; 413 return ret;
379} 414}
380 415
416static int show_available_vars_at(int fd, struct perf_probe_event *pev,
417 int max_vls, bool externs)
418{
419 char *buf;
420 int ret, i;
421 struct str_node *node;
422 struct variable_list *vls = NULL, *vl;
423
424 buf = synthesize_perf_probe_point(&pev->point);
425 if (!buf)
426 return -EINVAL;
427 pr_debug("Searching variables at %s\n", buf);
428
429 ret = find_available_vars_at(fd, pev, &vls, max_vls, externs);
430 if (ret > 0) {
431 /* Some variables were found */
432 fprintf(stdout, "Available variables at %s\n", buf);
433 for (i = 0; i < ret; i++) {
434 vl = &vls[i];
435 /*
436 * A probe point might be converted to
437 * several trace points.
438 */
439 fprintf(stdout, "\t@<%s+%lu>\n", vl->point.symbol,
440 vl->point.offset);
441 free(vl->point.symbol);
442 if (vl->vars) {
443 strlist__for_each(node, vl->vars)
444 fprintf(stdout, "\t\t%s\n", node->s);
445 strlist__delete(vl->vars);
446 } else
447 fprintf(stdout, "(No variables)\n");
448 }
449 free(vls);
450 } else
451 pr_err("Failed to find variables at %s (%d)\n", buf, ret);
452
453 free(buf);
454 return ret;
455}
456
457/* Show available variables on given probe point */
458int show_available_vars(struct perf_probe_event *pevs, int npevs,
459 int max_vls, const char *module, bool externs)
460{
461 int i, fd, ret = 0;
462
463 ret = init_vmlinux();
464 if (ret < 0)
465 return ret;
466
467 fd = open_vmlinux(module);
468 if (fd < 0) {
469 pr_warning("Failed to open debuginfo file.\n");
470 return fd;
471 }
472
473 setup_pager();
474
475 for (i = 0; i < npevs && ret >= 0; i++)
476 ret = show_available_vars_at(fd, &pevs[i], max_vls, externs);
477
478 close(fd);
479 return ret;
480}
481
381#else /* !DWARF_SUPPORT */ 482#else /* !DWARF_SUPPORT */
382 483
383static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, 484static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
384 struct perf_probe_point *pp) 485 struct perf_probe_point *pp)
385{ 486{
487 struct symbol *sym;
488
489 sym = __find_kernel_function_by_name(tp->symbol, NULL);
490 if (!sym) {
491 pr_err("Failed to find symbol %s in kernel.\n", tp->symbol);
492 return -ENOENT;
493 }
386 pp->function = strdup(tp->symbol); 494 pp->function = strdup(tp->symbol);
387 if (pp->function == NULL) 495 if (pp->function == NULL)
388 return -ENOMEM; 496 return -ENOMEM;
@@ -394,7 +502,7 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
394 502
395static int try_to_find_probe_trace_events(struct perf_probe_event *pev, 503static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
396 struct probe_trace_event **tevs __unused, 504 struct probe_trace_event **tevs __unused,
397 int max_tevs __unused) 505 int max_tevs __unused, const char *mod __unused)
398{ 506{
399 if (perf_probe_event_need_dwarf(pev)) { 507 if (perf_probe_event_need_dwarf(pev)) {
400 pr_warning("Debuginfo-analysis is not supported.\n"); 508 pr_warning("Debuginfo-analysis is not supported.\n");
@@ -403,12 +511,19 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
403 return 0; 511 return 0;
404} 512}
405 513
406int show_line_range(struct line_range *lr __unused) 514int show_line_range(struct line_range *lr __unused, const char *module __unused)
407{ 515{
408 pr_warning("Debuginfo-analysis is not supported.\n"); 516 pr_warning("Debuginfo-analysis is not supported.\n");
409 return -ENOSYS; 517 return -ENOSYS;
410} 518}
411 519
520int show_available_vars(struct perf_probe_event *pevs __unused,
521 int npevs __unused, int max_vls __unused,
522 const char *module __unused, bool externs __unused)
523{
524 pr_warning("Debuginfo-analysis is not supported.\n");
525 return -ENOSYS;
526}
412#endif 527#endif
413 528
414int parse_line_range_desc(const char *arg, struct line_range *lr) 529int parse_line_range_desc(const char *arg, struct line_range *lr)
@@ -1087,7 +1202,7 @@ error:
1087} 1202}
1088 1203
1089static int convert_to_perf_probe_event(struct probe_trace_event *tev, 1204static int convert_to_perf_probe_event(struct probe_trace_event *tev,
1090 struct perf_probe_event *pev) 1205 struct perf_probe_event *pev)
1091{ 1206{
1092 char buf[64] = ""; 1207 char buf[64] = "";
1093 int i, ret; 1208 int i, ret;
@@ -1516,14 +1631,14 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
1516 1631
1517static int convert_to_probe_trace_events(struct perf_probe_event *pev, 1632static int convert_to_probe_trace_events(struct perf_probe_event *pev,
1518 struct probe_trace_event **tevs, 1633 struct probe_trace_event **tevs,
1519 int max_tevs) 1634 int max_tevs, const char *module)
1520{ 1635{
1521 struct symbol *sym; 1636 struct symbol *sym;
1522 int ret = 0, i; 1637 int ret = 0, i;
1523 struct probe_trace_event *tev; 1638 struct probe_trace_event *tev;
1524 1639
1525 /* Convert perf_probe_event with debuginfo */ 1640 /* Convert perf_probe_event with debuginfo */
1526 ret = try_to_find_probe_trace_events(pev, tevs, max_tevs); 1641 ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, module);
1527 if (ret != 0) 1642 if (ret != 0)
1528 return ret; 1643 return ret;
1529 1644
@@ -1539,6 +1654,7 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev,
1539 goto error; 1654 goto error;
1540 } 1655 }
1541 tev->point.offset = pev->point.offset; 1656 tev->point.offset = pev->point.offset;
1657 tev->point.retprobe = pev->point.retprobe;
1542 tev->nargs = pev->nargs; 1658 tev->nargs = pev->nargs;
1543 if (tev->nargs) { 1659 if (tev->nargs) {
1544 tev->args = zalloc(sizeof(struct probe_trace_arg) 1660 tev->args = zalloc(sizeof(struct probe_trace_arg)
@@ -1571,8 +1687,7 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev,
1571 } 1687 }
1572 1688
1573 /* Currently just checking function name from symbol map */ 1689 /* Currently just checking function name from symbol map */
1574 sym = map__find_symbol_by_name(machine.vmlinux_maps[MAP__FUNCTION], 1690 sym = __find_kernel_function_by_name(tev->point.symbol, NULL);
1575 tev->point.symbol, NULL);
1576 if (!sym) { 1691 if (!sym) {
1577 pr_warning("Kernel symbol \'%s\' not found.\n", 1692 pr_warning("Kernel symbol \'%s\' not found.\n",
1578 tev->point.symbol); 1693 tev->point.symbol);
@@ -1595,7 +1710,7 @@ struct __event_package {
1595}; 1710};
1596 1711
1597int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, 1712int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
1598 bool force_add, int max_tevs) 1713 int max_tevs, const char *module, bool force_add)
1599{ 1714{
1600 int i, j, ret; 1715 int i, j, ret;
1601 struct __event_package *pkgs; 1716 struct __event_package *pkgs;
@@ -1606,15 +1721,19 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
1606 1721
1607 /* Init vmlinux path */ 1722 /* Init vmlinux path */
1608 ret = init_vmlinux(); 1723 ret = init_vmlinux();
1609 if (ret < 0) 1724 if (ret < 0) {
1725 free(pkgs);
1610 return ret; 1726 return ret;
1727 }
1611 1728
1612 /* Loop 1: convert all events */ 1729 /* Loop 1: convert all events */
1613 for (i = 0; i < npevs; i++) { 1730 for (i = 0; i < npevs; i++) {
1614 pkgs[i].pev = &pevs[i]; 1731 pkgs[i].pev = &pevs[i];
1615 /* Convert with or without debuginfo */ 1732 /* Convert with or without debuginfo */
1616 ret = convert_to_probe_trace_events(pkgs[i].pev, 1733 ret = convert_to_probe_trace_events(pkgs[i].pev,
1617 &pkgs[i].tevs, max_tevs); 1734 &pkgs[i].tevs,
1735 max_tevs,
1736 module);
1618 if (ret < 0) 1737 if (ret < 0)
1619 goto end; 1738 goto end;
1620 pkgs[i].ntevs = ret; 1739 pkgs[i].ntevs = ret;
@@ -1625,10 +1744,13 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
1625 ret = __add_probe_trace_events(pkgs[i].pev, pkgs[i].tevs, 1744 ret = __add_probe_trace_events(pkgs[i].pev, pkgs[i].tevs,
1626 pkgs[i].ntevs, force_add); 1745 pkgs[i].ntevs, force_add);
1627end: 1746end:
1628 /* Loop 3: cleanup trace events */ 1747 /* Loop 3: cleanup and free trace events */
1629 for (i = 0; i < npevs; i++) 1748 for (i = 0; i < npevs; i++) {
1630 for (j = 0; j < pkgs[i].ntevs; j++) 1749 for (j = 0; j < pkgs[i].ntevs; j++)
1631 clear_probe_trace_event(&pkgs[i].tevs[j]); 1750 clear_probe_trace_event(&pkgs[i].tevs[j]);
1751 free(pkgs[i].tevs);
1752 }
1753 free(pkgs);
1632 1754
1633 return ret; 1755 return ret;
1634} 1756}
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index 5af39243a25b..5accbedfea37 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -90,6 +90,12 @@ struct line_range {
90 struct list_head line_list; /* Visible lines */ 90 struct list_head line_list; /* Visible lines */
91}; 91};
92 92
93/* List of variables */
94struct variable_list {
95 struct probe_trace_point point; /* Actual probepoint */
96 struct strlist *vars; /* Available variables */
97};
98
93/* Command string to events */ 99/* Command string to events */
94extern int parse_perf_probe_command(const char *cmd, 100extern int parse_perf_probe_command(const char *cmd,
95 struct perf_probe_event *pev); 101 struct perf_probe_event *pev);
@@ -109,12 +115,18 @@ extern void clear_perf_probe_event(struct perf_probe_event *pev);
109/* Command string to line-range */ 115/* Command string to line-range */
110extern int parse_line_range_desc(const char *cmd, struct line_range *lr); 116extern int parse_line_range_desc(const char *cmd, struct line_range *lr);
111 117
118/* Internal use: Return kernel/module path */
119extern const char *kernel_get_module_path(const char *module);
112 120
113extern int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, 121extern int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
114 bool force_add, int max_probe_points); 122 int max_probe_points, const char *module,
123 bool force_add);
115extern int del_perf_probe_events(struct strlist *dellist); 124extern int del_perf_probe_events(struct strlist *dellist);
116extern int show_perf_probe_events(void); 125extern int show_perf_probe_events(void);
117extern int show_line_range(struct line_range *lr); 126extern int show_line_range(struct line_range *lr, const char *module);
127extern int show_available_vars(struct perf_probe_event *pevs, int npevs,
128 int max_probe_points, const char *module,
129 bool externs);
118 130
119 131
120/* Maximum index number of event-name postfix */ 132/* Maximum index number of event-name postfix */
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index 840f1aabbb74..ddf4d4556321 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -33,7 +33,6 @@
33#include <ctype.h> 33#include <ctype.h>
34#include <dwarf-regs.h> 34#include <dwarf-regs.h>
35 35
36#include "string.h"
37#include "event.h" 36#include "event.h"
38#include "debug.h" 37#include "debug.h"
39#include "util.h" 38#include "util.h"
@@ -117,6 +116,126 @@ static void line_list__free(struct list_head *head)
117 } 116 }
118} 117}
119 118
119/* Dwarf FL wrappers */
120static char *debuginfo_path; /* Currently dummy */
121
122static const Dwfl_Callbacks offline_callbacks = {
123 .find_debuginfo = dwfl_standard_find_debuginfo,
124 .debuginfo_path = &debuginfo_path,
125
126 .section_address = dwfl_offline_section_address,
127
128 /* We use this table for core files too. */
129 .find_elf = dwfl_build_id_find_elf,
130};
131
132/* Get a Dwarf from offline image */
133static Dwarf *dwfl_init_offline_dwarf(int fd, Dwfl **dwflp, Dwarf_Addr *bias)
134{
135 Dwfl_Module *mod;
136 Dwarf *dbg = NULL;
137
138 if (!dwflp)
139 return NULL;
140
141 *dwflp = dwfl_begin(&offline_callbacks);
142 if (!*dwflp)
143 return NULL;
144
145 mod = dwfl_report_offline(*dwflp, "", "", fd);
146 if (!mod)
147 goto error;
148
149 dbg = dwfl_module_getdwarf(mod, bias);
150 if (!dbg) {
151error:
152 dwfl_end(*dwflp);
153 *dwflp = NULL;
154 }
155 return dbg;
156}
157
158#if _ELFUTILS_PREREQ(0, 148)
159/* This method is buggy if elfutils is older than 0.148 */
160static int __linux_kernel_find_elf(Dwfl_Module *mod,
161 void **userdata,
162 const char *module_name,
163 Dwarf_Addr base,
164 char **file_name, Elf **elfp)
165{
166 int fd;
167 const char *path = kernel_get_module_path(module_name);
168
169 pr_debug2("Use file %s for %s\n", path, module_name);
170 if (path) {
171 fd = open(path, O_RDONLY);
172 if (fd >= 0) {
173 *file_name = strdup(path);
174 return fd;
175 }
176 }
177 /* If failed, try to call standard method */
178 return dwfl_linux_kernel_find_elf(mod, userdata, module_name, base,
179 file_name, elfp);
180}
181
182static const Dwfl_Callbacks kernel_callbacks = {
183 .find_debuginfo = dwfl_standard_find_debuginfo,
184 .debuginfo_path = &debuginfo_path,
185
186 .find_elf = __linux_kernel_find_elf,
187 .section_address = dwfl_linux_kernel_module_section_address,
188};
189
190/* Get a Dwarf from live kernel image */
191static Dwarf *dwfl_init_live_kernel_dwarf(Dwarf_Addr addr, Dwfl **dwflp,
192 Dwarf_Addr *bias)
193{
194 Dwarf *dbg;
195
196 if (!dwflp)
197 return NULL;
198
199 *dwflp = dwfl_begin(&kernel_callbacks);
200 if (!*dwflp)
201 return NULL;
202
203 /* Load the kernel dwarves: Don't care the result here */
204 dwfl_linux_kernel_report_kernel(*dwflp);
205 dwfl_linux_kernel_report_modules(*dwflp);
206
207 dbg = dwfl_addrdwarf(*dwflp, addr, bias);
208 /* Here, check whether we could get a real dwarf */
209 if (!dbg) {
210 pr_debug("Failed to find kernel dwarf at %lx\n",
211 (unsigned long)addr);
212 dwfl_end(*dwflp);
213 *dwflp = NULL;
214 }
215 return dbg;
216}
217#else
218/* With older elfutils, this just support kernel module... */
219static Dwarf *dwfl_init_live_kernel_dwarf(Dwarf_Addr addr __used, Dwfl **dwflp,
220 Dwarf_Addr *bias)
221{
222 int fd;
223 const char *path = kernel_get_module_path("kernel");
224
225 if (!path) {
226 pr_err("Failed to find vmlinux path\n");
227 return NULL;
228 }
229
230 pr_debug2("Use file %s for debuginfo\n", path);
231 fd = open(path, O_RDONLY);
232 if (fd < 0)
233 return NULL;
234
235 return dwfl_init_offline_dwarf(fd, dwflp, bias);
236}
237#endif
238
120/* Dwarf wrappers */ 239/* Dwarf wrappers */
121 240
122/* Find the realpath of the target file. */ 241/* Find the realpath of the target file. */
@@ -161,26 +280,44 @@ static bool die_compare_name(Dwarf_Die *dw_die, const char *tname)
161 return name ? (strcmp(tname, name) == 0) : false; 280 return name ? (strcmp(tname, name) == 0) : false;
162} 281}
163 282
164/* Get type die, but skip qualifiers and typedef */ 283/* Get type die */
165static Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) 284static Dwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
166{ 285{
167 Dwarf_Attribute attr; 286 Dwarf_Attribute attr;
287
288 if (dwarf_attr_integrate(vr_die, DW_AT_type, &attr) &&
289 dwarf_formref_die(&attr, die_mem))
290 return die_mem;
291 else
292 return NULL;
293}
294
295/* Get a type die, but skip qualifiers */
296static Dwarf_Die *__die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
297{
168 int tag; 298 int tag;
169 299
170 do { 300 do {
171 if (dwarf_attr(vr_die, DW_AT_type, &attr) == NULL || 301 vr_die = die_get_type(vr_die, die_mem);
172 dwarf_formref_die(&attr, die_mem) == NULL) 302 if (!vr_die)
173 return NULL; 303 break;
174 304 tag = dwarf_tag(vr_die);
175 tag = dwarf_tag(die_mem);
176 vr_die = die_mem;
177 } while (tag == DW_TAG_const_type || 305 } while (tag == DW_TAG_const_type ||
178 tag == DW_TAG_restrict_type || 306 tag == DW_TAG_restrict_type ||
179 tag == DW_TAG_volatile_type || 307 tag == DW_TAG_volatile_type ||
180 tag == DW_TAG_shared_type || 308 tag == DW_TAG_shared_type);
181 tag == DW_TAG_typedef);
182 309
183 return die_mem; 310 return vr_die;
311}
312
313/* Get a type die, but skip qualifiers and typedef */
314static Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
315{
316 do {
317 vr_die = __die_get_real_type(vr_die, die_mem);
318 } while (vr_die && dwarf_tag(vr_die) == DW_TAG_typedef);
319
320 return vr_die;
184} 321}
185 322
186static bool die_is_signed_type(Dwarf_Die *tp_die) 323static bool die_is_signed_type(Dwarf_Die *tp_die)
@@ -321,25 +458,35 @@ static Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr,
321 return die_find_child(sp_die, __die_find_inline_cb, &addr, die_mem); 458 return die_find_child(sp_die, __die_find_inline_cb, &addr, die_mem);
322} 459}
323 460
461struct __find_variable_param {
462 const char *name;
463 Dwarf_Addr addr;
464};
465
324static int __die_find_variable_cb(Dwarf_Die *die_mem, void *data) 466static int __die_find_variable_cb(Dwarf_Die *die_mem, void *data)
325{ 467{
326 const char *name = data; 468 struct __find_variable_param *fvp = data;
327 int tag; 469 int tag;
328 470
329 tag = dwarf_tag(die_mem); 471 tag = dwarf_tag(die_mem);
330 if ((tag == DW_TAG_formal_parameter || 472 if ((tag == DW_TAG_formal_parameter ||
331 tag == DW_TAG_variable) && 473 tag == DW_TAG_variable) &&
332 die_compare_name(die_mem, name)) 474 die_compare_name(die_mem, fvp->name))
333 return DIE_FIND_CB_FOUND; 475 return DIE_FIND_CB_FOUND;
334 476
335 return DIE_FIND_CB_CONTINUE; 477 if (dwarf_haspc(die_mem, fvp->addr))
478 return DIE_FIND_CB_CONTINUE;
479 else
480 return DIE_FIND_CB_SIBLING;
336} 481}
337 482
338/* Find a variable called 'name' */ 483/* Find a variable called 'name' at given address */
339static Dwarf_Die *die_find_variable(Dwarf_Die *sp_die, const char *name, 484static Dwarf_Die *die_find_variable_at(Dwarf_Die *sp_die, const char *name,
340 Dwarf_Die *die_mem) 485 Dwarf_Addr addr, Dwarf_Die *die_mem)
341{ 486{
342 return die_find_child(sp_die, __die_find_variable_cb, (void *)name, 487 struct __find_variable_param fvp = { .name = name, .addr = addr};
488
489 return die_find_child(sp_die, __die_find_variable_cb, (void *)&fvp,
343 die_mem); 490 die_mem);
344} 491}
345 492
@@ -362,6 +509,60 @@ static Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name,
362 die_mem); 509 die_mem);
363} 510}
364 511
512/* Get the name of given variable DIE */
513static int die_get_typename(Dwarf_Die *vr_die, char *buf, int len)
514{
515 Dwarf_Die type;
516 int tag, ret, ret2;
517 const char *tmp = "";
518
519 if (__die_get_real_type(vr_die, &type) == NULL)
520 return -ENOENT;
521
522 tag = dwarf_tag(&type);
523 if (tag == DW_TAG_array_type || tag == DW_TAG_pointer_type)
524 tmp = "*";
525 else if (tag == DW_TAG_subroutine_type) {
526 /* Function pointer */
527 ret = snprintf(buf, len, "(function_type)");
528 return (ret >= len) ? -E2BIG : ret;
529 } else {
530 if (!dwarf_diename(&type))
531 return -ENOENT;
532 if (tag == DW_TAG_union_type)
533 tmp = "union ";
534 else if (tag == DW_TAG_structure_type)
535 tmp = "struct ";
536 /* Write a base name */
537 ret = snprintf(buf, len, "%s%s", tmp, dwarf_diename(&type));
538 return (ret >= len) ? -E2BIG : ret;
539 }
540 ret = die_get_typename(&type, buf, len);
541 if (ret > 0) {
542 ret2 = snprintf(buf + ret, len - ret, "%s", tmp);
543 ret = (ret2 >= len - ret) ? -E2BIG : ret2 + ret;
544 }
545 return ret;
546}
547
548/* Get the name and type of given variable DIE, stored as "type\tname" */
549static int die_get_varname(Dwarf_Die *vr_die, char *buf, int len)
550{
551 int ret, ret2;
552
553 ret = die_get_typename(vr_die, buf, len);
554 if (ret < 0) {
555 pr_debug("Failed to get type, make it unknown.\n");
556 ret = snprintf(buf, len, "(unknown_type)");
557 }
558 if (ret > 0) {
559 ret2 = snprintf(buf + ret, len - ret, "\t%s",
560 dwarf_diename(vr_die));
561 ret = (ret2 >= len - ret) ? -E2BIG : ret2 + ret;
562 }
563 return ret;
564}
565
365/* 566/*
366 * Probe finder related functions 567 * Probe finder related functions
367 */ 568 */
@@ -375,8 +576,13 @@ static struct probe_trace_arg_ref *alloc_trace_arg_ref(long offs)
375 return ref; 576 return ref;
376} 577}
377 578
378/* Show a location */ 579/*
379static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf) 580 * Convert a location into trace_arg.
581 * If tvar == NULL, this just checks variable can be converted.
582 */
583static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr,
584 Dwarf_Op *fb_ops,
585 struct probe_trace_arg *tvar)
380{ 586{
381 Dwarf_Attribute attr; 587 Dwarf_Attribute attr;
382 Dwarf_Op *op; 588 Dwarf_Op *op;
@@ -385,20 +591,23 @@ static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf)
385 Dwarf_Word offs = 0; 591 Dwarf_Word offs = 0;
386 bool ref = false; 592 bool ref = false;
387 const char *regs; 593 const char *regs;
388 struct probe_trace_arg *tvar = pf->tvar;
389 int ret; 594 int ret;
390 595
596 if (dwarf_attr(vr_die, DW_AT_external, &attr) != NULL)
597 goto static_var;
598
391 /* TODO: handle more than 1 exprs */ 599 /* TODO: handle more than 1 exprs */
392 if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL || 600 if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL ||
393 dwarf_getlocation_addr(&attr, pf->addr, &op, &nops, 1) <= 0 || 601 dwarf_getlocation_addr(&attr, addr, &op, &nops, 1) <= 0 ||
394 nops == 0) { 602 nops == 0) {
395 /* TODO: Support const_value */ 603 /* TODO: Support const_value */
396 pr_err("Failed to find the location of %s at this address.\n"
397 " Perhaps, it has been optimized out.\n", pf->pvar->var);
398 return -ENOENT; 604 return -ENOENT;
399 } 605 }
400 606
401 if (op->atom == DW_OP_addr) { 607 if (op->atom == DW_OP_addr) {
608static_var:
609 if (!tvar)
610 return 0;
402 /* Static variables on memory (not stack), make @varname */ 611 /* Static variables on memory (not stack), make @varname */
403 ret = strlen(dwarf_diename(vr_die)); 612 ret = strlen(dwarf_diename(vr_die));
404 tvar->value = zalloc(ret + 2); 613 tvar->value = zalloc(ret + 2);
@@ -413,14 +622,11 @@ static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf)
413 622
414 /* If this is based on frame buffer, set the offset */ 623 /* If this is based on frame buffer, set the offset */
415 if (op->atom == DW_OP_fbreg) { 624 if (op->atom == DW_OP_fbreg) {
416 if (pf->fb_ops == NULL) { 625 if (fb_ops == NULL)
417 pr_warning("The attribute of frame base is not "
418 "supported.\n");
419 return -ENOTSUP; 626 return -ENOTSUP;
420 }
421 ref = true; 627 ref = true;
422 offs = op->number; 628 offs = op->number;
423 op = &pf->fb_ops[0]; 629 op = &fb_ops[0];
424 } 630 }
425 631
426 if (op->atom >= DW_OP_breg0 && op->atom <= DW_OP_breg31) { 632 if (op->atom >= DW_OP_breg0 && op->atom <= DW_OP_breg31) {
@@ -436,13 +642,18 @@ static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf)
436 } else if (op->atom == DW_OP_regx) { 642 } else if (op->atom == DW_OP_regx) {
437 regn = op->number; 643 regn = op->number;
438 } else { 644 } else {
439 pr_warning("DW_OP %x is not supported.\n", op->atom); 645 pr_debug("DW_OP %x is not supported.\n", op->atom);
440 return -ENOTSUP; 646 return -ENOTSUP;
441 } 647 }
442 648
649 if (!tvar)
650 return 0;
651
443 regs = get_arch_regstr(regn); 652 regs = get_arch_regstr(regn);
444 if (!regs) { 653 if (!regs) {
445 pr_warning("Mapping for DWARF register number %u missing on this architecture.", regn); 654 /* This should be a bug in DWARF or this tool */
655 pr_warning("Mapping for DWARF register number %u "
656 "missing on this architecture.", regn);
446 return -ERANGE; 657 return -ERANGE;
447 } 658 }
448 659
@@ -667,8 +878,14 @@ static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)
667 pr_debug("Converting variable %s into trace event.\n", 878 pr_debug("Converting variable %s into trace event.\n",
668 dwarf_diename(vr_die)); 879 dwarf_diename(vr_die));
669 880
670 ret = convert_variable_location(vr_die, pf); 881 ret = convert_variable_location(vr_die, pf->addr, pf->fb_ops,
671 if (ret == 0 && pf->pvar->field) { 882 pf->tvar);
883 if (ret == -ENOENT)
884 pr_err("Failed to find the location of %s at this address.\n"
885 " Perhaps, it has been optimized out.\n", pf->pvar->var);
886 else if (ret == -ENOTSUP)
887 pr_err("Sorry, we don't support this variable location yet.\n");
888 else if (pf->pvar->field) {
672 ret = convert_variable_fields(vr_die, pf->pvar->var, 889 ret = convert_variable_fields(vr_die, pf->pvar->var,
673 pf->pvar->field, &pf->tvar->ref, 890 pf->pvar->field, &pf->tvar->ref,
674 &die_mem); 891 &die_mem);
@@ -687,6 +904,25 @@ static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf)
687 char buf[32], *ptr; 904 char buf[32], *ptr;
688 int ret, nscopes; 905 int ret, nscopes;
689 906
907 if (!is_c_varname(pf->pvar->var)) {
908 /* Copy raw parameters */
909 pf->tvar->value = strdup(pf->pvar->var);
910 if (pf->tvar->value == NULL)
911 return -ENOMEM;
912 if (pf->pvar->type) {
913 pf->tvar->type = strdup(pf->pvar->type);
914 if (pf->tvar->type == NULL)
915 return -ENOMEM;
916 }
917 if (pf->pvar->name) {
918 pf->tvar->name = strdup(pf->pvar->name);
919 if (pf->tvar->name == NULL)
920 return -ENOMEM;
921 } else
922 pf->tvar->name = NULL;
923 return 0;
924 }
925
690 if (pf->pvar->name) 926 if (pf->pvar->name)
691 pf->tvar->name = strdup(pf->pvar->name); 927 pf->tvar->name = strdup(pf->pvar->name);
692 else { 928 else {
@@ -701,68 +937,42 @@ static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf)
701 if (pf->tvar->name == NULL) 937 if (pf->tvar->name == NULL)
702 return -ENOMEM; 938 return -ENOMEM;
703 939
704 if (!is_c_varname(pf->pvar->var)) {
705 /* Copy raw parameters */
706 pf->tvar->value = strdup(pf->pvar->var);
707 if (pf->tvar->value == NULL)
708 return -ENOMEM;
709 else
710 return 0;
711 }
712
713 pr_debug("Searching '%s' variable in context.\n", 940 pr_debug("Searching '%s' variable in context.\n",
714 pf->pvar->var); 941 pf->pvar->var);
715 /* Search child die for local variables and parameters. */ 942 /* Search child die for local variables and parameters. */
716 if (die_find_variable(sp_die, pf->pvar->var, &vr_die)) 943 if (die_find_variable_at(sp_die, pf->pvar->var, pf->addr, &vr_die))
717 ret = convert_variable(&vr_die, pf); 944 ret = convert_variable(&vr_die, pf);
718 else { 945 else {
719 /* Search upper class */ 946 /* Search upper class */
720 nscopes = dwarf_getscopes_die(sp_die, &scopes); 947 nscopes = dwarf_getscopes_die(sp_die, &scopes);
721 if (nscopes > 0) { 948 while (nscopes-- > 1) {
722 ret = dwarf_getscopevar(scopes, nscopes, pf->pvar->var, 949 pr_debug("Searching variables in %s\n",
723 0, NULL, 0, 0, &vr_die); 950 dwarf_diename(&scopes[nscopes]));
724 if (ret >= 0) 951 /* We should check this scope, so give dummy address */
952 if (die_find_variable_at(&scopes[nscopes],
953 pf->pvar->var, 0,
954 &vr_die)) {
725 ret = convert_variable(&vr_die, pf); 955 ret = convert_variable(&vr_die, pf);
726 else 956 goto found;
727 ret = -ENOENT; 957 }
958 }
959 if (scopes)
728 free(scopes); 960 free(scopes);
729 } else 961 ret = -ENOENT;
730 ret = -ENOENT;
731 } 962 }
963found:
732 if (ret < 0) 964 if (ret < 0)
733 pr_warning("Failed to find '%s' in this function.\n", 965 pr_warning("Failed to find '%s' in this function.\n",
734 pf->pvar->var); 966 pf->pvar->var);
735 return ret; 967 return ret;
736} 968}
737 969
738/* Show a probe point to output buffer */ 970/* Convert subprogram DIE to trace point */
739static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) 971static int convert_to_trace_point(Dwarf_Die *sp_die, Dwarf_Addr paddr,
972 bool retprobe, struct probe_trace_point *tp)
740{ 973{
741 struct probe_trace_event *tev;
742 Dwarf_Addr eaddr; 974 Dwarf_Addr eaddr;
743 Dwarf_Die die_mem;
744 const char *name; 975 const char *name;
745 int ret, i;
746 Dwarf_Attribute fb_attr;
747 size_t nops;
748
749 if (pf->ntevs == pf->max_tevs) {
750 pr_warning("Too many( > %d) probe point found.\n",
751 pf->max_tevs);
752 return -ERANGE;
753 }
754 tev = &pf->tevs[pf->ntevs++];
755
756 /* If no real subprogram, find a real one */
757 if (!sp_die || dwarf_tag(sp_die) != DW_TAG_subprogram) {
758 sp_die = die_find_real_subprogram(&pf->cu_die,
759 pf->addr, &die_mem);
760 if (!sp_die) {
761 pr_warning("Failed to find probe point in any "
762 "functions.\n");
763 return -ENOENT;
764 }
765 }
766 976
767 /* Copy the name of probe point */ 977 /* Copy the name of probe point */
768 name = dwarf_diename(sp_die); 978 name = dwarf_diename(sp_die);
@@ -772,16 +982,45 @@ static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)
772 dwarf_diename(sp_die)); 982 dwarf_diename(sp_die));
773 return -ENOENT; 983 return -ENOENT;
774 } 984 }
775 tev->point.symbol = strdup(name); 985 tp->symbol = strdup(name);
776 if (tev->point.symbol == NULL) 986 if (tp->symbol == NULL)
777 return -ENOMEM; 987 return -ENOMEM;
778 tev->point.offset = (unsigned long)(pf->addr - eaddr); 988 tp->offset = (unsigned long)(paddr - eaddr);
779 } else 989 } else
780 /* This function has no name. */ 990 /* This function has no name. */
781 tev->point.offset = (unsigned long)pf->addr; 991 tp->offset = (unsigned long)paddr;
782 992
783 pr_debug("Probe point found: %s+%lu\n", tev->point.symbol, 993 /* Return probe must be on the head of a subprogram */
784 tev->point.offset); 994 if (retprobe) {
995 if (eaddr != paddr) {
996 pr_warning("Return probe must be on the head of"
997 " a real function\n");
998 return -EINVAL;
999 }
1000 tp->retprobe = true;
1001 }
1002
1003 return 0;
1004}
1005
1006/* Call probe_finder callback with real subprogram DIE */
1007static int call_probe_finder(Dwarf_Die *sp_die, struct probe_finder *pf)
1008{
1009 Dwarf_Die die_mem;
1010 Dwarf_Attribute fb_attr;
1011 size_t nops;
1012 int ret;
1013
1014 /* If no real subprogram, find a real one */
1015 if (!sp_die || dwarf_tag(sp_die) != DW_TAG_subprogram) {
1016 sp_die = die_find_real_subprogram(&pf->cu_die,
1017 pf->addr, &die_mem);
1018 if (!sp_die) {
1019 pr_warning("Failed to find probe point in any "
1020 "functions.\n");
1021 return -ENOENT;
1022 }
1023 }
785 1024
786 /* Get the frame base attribute/ops */ 1025 /* Get the frame base attribute/ops */
787 dwarf_attr(sp_die, DW_AT_frame_base, &fb_attr); 1026 dwarf_attr(sp_die, DW_AT_frame_base, &fb_attr);
@@ -801,22 +1040,13 @@ static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)
801#endif 1040#endif
802 } 1041 }
803 1042
804 /* Find each argument */ 1043 /* Call finder's callback handler */
805 tev->nargs = pf->pev->nargs; 1044 ret = pf->callback(sp_die, pf);
806 tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs);
807 if (tev->args == NULL)
808 return -ENOMEM;
809 for (i = 0; i < pf->pev->nargs; i++) {
810 pf->pvar = &pf->pev->args[i];
811 pf->tvar = &tev->args[i];
812 ret = find_variable(sp_die, pf);
813 if (ret != 0)
814 return ret;
815 }
816 1045
817 /* *pf->fb_ops will be cached in libdw. Don't free it. */ 1046 /* *pf->fb_ops will be cached in libdw. Don't free it. */
818 pf->fb_ops = NULL; 1047 pf->fb_ops = NULL;
819 return 0; 1048
1049 return ret;
820} 1050}
821 1051
822/* Find probe point from its line number */ 1052/* Find probe point from its line number */
@@ -852,7 +1082,7 @@ static int find_probe_point_by_line(struct probe_finder *pf)
852 (int)i, lineno, (uintmax_t)addr); 1082 (int)i, lineno, (uintmax_t)addr);
853 pf->addr = addr; 1083 pf->addr = addr;
854 1084
855 ret = convert_probe_point(NULL, pf); 1085 ret = call_probe_finder(NULL, pf);
856 /* Continuing, because target line might be inlined. */ 1086 /* Continuing, because target line might be inlined. */
857 } 1087 }
858 return ret; 1088 return ret;
@@ -965,7 +1195,7 @@ static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf)
965 (int)i, lineno, (unsigned long long)addr); 1195 (int)i, lineno, (unsigned long long)addr);
966 pf->addr = addr; 1196 pf->addr = addr;
967 1197
968 ret = convert_probe_point(sp_die, pf); 1198 ret = call_probe_finder(sp_die, pf);
969 /* Continuing, because target line might be inlined. */ 1199 /* Continuing, because target line might be inlined. */
970 } 1200 }
971 /* TODO: deallocate lines, but how? */ 1201 /* TODO: deallocate lines, but how? */
@@ -1000,7 +1230,7 @@ static int probe_point_inline_cb(Dwarf_Die *in_die, void *data)
1000 pr_debug("found inline addr: 0x%jx\n", 1230 pr_debug("found inline addr: 0x%jx\n",
1001 (uintmax_t)pf->addr); 1231 (uintmax_t)pf->addr);
1002 1232
1003 param->retval = convert_probe_point(in_die, pf); 1233 param->retval = call_probe_finder(in_die, pf);
1004 if (param->retval < 0) 1234 if (param->retval < 0)
1005 return DWARF_CB_ABORT; 1235 return DWARF_CB_ABORT;
1006 } 1236 }
@@ -1038,7 +1268,7 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data)
1038 } 1268 }
1039 pf->addr += pp->offset; 1269 pf->addr += pp->offset;
1040 /* TODO: Check the address in this function */ 1270 /* TODO: Check the address in this function */
1041 param->retval = convert_probe_point(sp_die, pf); 1271 param->retval = call_probe_finder(sp_die, pf);
1042 } 1272 }
1043 } else { 1273 } else {
1044 struct dwarf_callback_param _param = {.data = (void *)pf, 1274 struct dwarf_callback_param _param = {.data = (void *)pf,
@@ -1060,90 +1290,276 @@ static int find_probe_point_by_func(struct probe_finder *pf)
1060 return _param.retval; 1290 return _param.retval;
1061} 1291}
1062 1292
1063/* Find probe_trace_events specified by perf_probe_event from debuginfo */ 1293/* Find probe points from debuginfo */
1064int find_probe_trace_events(int fd, struct perf_probe_event *pev, 1294static int find_probes(int fd, struct probe_finder *pf)
1065 struct probe_trace_event **tevs, int max_tevs)
1066{ 1295{
1067 struct probe_finder pf = {.pev = pev, .max_tevs = max_tevs}; 1296 struct perf_probe_point *pp = &pf->pev->point;
1068 struct perf_probe_point *pp = &pev->point;
1069 Dwarf_Off off, noff; 1297 Dwarf_Off off, noff;
1070 size_t cuhl; 1298 size_t cuhl;
1071 Dwarf_Die *diep; 1299 Dwarf_Die *diep;
1072 Dwarf *dbg; 1300 Dwarf *dbg = NULL;
1301 Dwfl *dwfl;
1302 Dwarf_Addr bias; /* Currently ignored */
1073 int ret = 0; 1303 int ret = 0;
1074 1304
1075 pf.tevs = zalloc(sizeof(struct probe_trace_event) * max_tevs); 1305 dbg = dwfl_init_offline_dwarf(fd, &dwfl, &bias);
1076 if (pf.tevs == NULL)
1077 return -ENOMEM;
1078 *tevs = pf.tevs;
1079 pf.ntevs = 0;
1080
1081 dbg = dwarf_begin(fd, DWARF_C_READ);
1082 if (!dbg) { 1306 if (!dbg) {
1083 pr_warning("No dwarf info found in the vmlinux - " 1307 pr_warning("No dwarf info found in the vmlinux - "
1084 "please rebuild with CONFIG_DEBUG_INFO=y.\n"); 1308 "please rebuild with CONFIG_DEBUG_INFO=y.\n");
1085 free(pf.tevs);
1086 *tevs = NULL;
1087 return -EBADF; 1309 return -EBADF;
1088 } 1310 }
1089 1311
1090#if _ELFUTILS_PREREQ(0, 142) 1312#if _ELFUTILS_PREREQ(0, 142)
1091 /* Get the call frame information from this dwarf */ 1313 /* Get the call frame information from this dwarf */
1092 pf.cfi = dwarf_getcfi(dbg); 1314 pf->cfi = dwarf_getcfi(dbg);
1093#endif 1315#endif
1094 1316
1095 off = 0; 1317 off = 0;
1096 line_list__init(&pf.lcache); 1318 line_list__init(&pf->lcache);
1097 /* Loop on CUs (Compilation Unit) */ 1319 /* Loop on CUs (Compilation Unit) */
1098 while (!dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL) && 1320 while (!dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL) &&
1099 ret >= 0) { 1321 ret >= 0) {
1100 /* Get the DIE(Debugging Information Entry) of this CU */ 1322 /* Get the DIE(Debugging Information Entry) of this CU */
1101 diep = dwarf_offdie(dbg, off + cuhl, &pf.cu_die); 1323 diep = dwarf_offdie(dbg, off + cuhl, &pf->cu_die);
1102 if (!diep) 1324 if (!diep)
1103 continue; 1325 continue;
1104 1326
1105 /* Check if target file is included. */ 1327 /* Check if target file is included. */
1106 if (pp->file) 1328 if (pp->file)
1107 pf.fname = cu_find_realpath(&pf.cu_die, pp->file); 1329 pf->fname = cu_find_realpath(&pf->cu_die, pp->file);
1108 else 1330 else
1109 pf.fname = NULL; 1331 pf->fname = NULL;
1110 1332
1111 if (!pp->file || pf.fname) { 1333 if (!pp->file || pf->fname) {
1112 if (pp->function) 1334 if (pp->function)
1113 ret = find_probe_point_by_func(&pf); 1335 ret = find_probe_point_by_func(pf);
1114 else if (pp->lazy_line) 1336 else if (pp->lazy_line)
1115 ret = find_probe_point_lazy(NULL, &pf); 1337 ret = find_probe_point_lazy(NULL, pf);
1116 else { 1338 else {
1117 pf.lno = pp->line; 1339 pf->lno = pp->line;
1118 ret = find_probe_point_by_line(&pf); 1340 ret = find_probe_point_by_line(pf);
1119 } 1341 }
1120 } 1342 }
1121 off = noff; 1343 off = noff;
1122 } 1344 }
1123 line_list__free(&pf.lcache); 1345 line_list__free(&pf->lcache);
1124 dwarf_end(dbg); 1346 if (dwfl)
1347 dwfl_end(dwfl);
1348
1349 return ret;
1350}
1351
1352/* Add a found probe point into trace event list */
1353static int add_probe_trace_event(Dwarf_Die *sp_die, struct probe_finder *pf)
1354{
1355 struct trace_event_finder *tf =
1356 container_of(pf, struct trace_event_finder, pf);
1357 struct probe_trace_event *tev;
1358 int ret, i;
1359
1360 /* Check number of tevs */
1361 if (tf->ntevs == tf->max_tevs) {
1362 pr_warning("Too many( > %d) probe point found.\n",
1363 tf->max_tevs);
1364 return -ERANGE;
1365 }
1366 tev = &tf->tevs[tf->ntevs++];
1367
1368 ret = convert_to_trace_point(sp_die, pf->addr, pf->pev->point.retprobe,
1369 &tev->point);
1370 if (ret < 0)
1371 return ret;
1372
1373 pr_debug("Probe point found: %s+%lu\n", tev->point.symbol,
1374 tev->point.offset);
1375
1376 /* Find each argument */
1377 tev->nargs = pf->pev->nargs;
1378 tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs);
1379 if (tev->args == NULL)
1380 return -ENOMEM;
1381 for (i = 0; i < pf->pev->nargs; i++) {
1382 pf->pvar = &pf->pev->args[i];
1383 pf->tvar = &tev->args[i];
1384 ret = find_variable(sp_die, pf);
1385 if (ret != 0)
1386 return ret;
1387 }
1125 1388
1126 return (ret < 0) ? ret : pf.ntevs; 1389 return 0;
1390}
1391
1392/* Find probe_trace_events specified by perf_probe_event from debuginfo */
1393int find_probe_trace_events(int fd, struct perf_probe_event *pev,
1394 struct probe_trace_event **tevs, int max_tevs)
1395{
1396 struct trace_event_finder tf = {
1397 .pf = {.pev = pev, .callback = add_probe_trace_event},
1398 .max_tevs = max_tevs};
1399 int ret;
1400
1401 /* Allocate result tevs array */
1402 *tevs = zalloc(sizeof(struct probe_trace_event) * max_tevs);
1403 if (*tevs == NULL)
1404 return -ENOMEM;
1405
1406 tf.tevs = *tevs;
1407 tf.ntevs = 0;
1408
1409 ret = find_probes(fd, &tf.pf);
1410 if (ret < 0) {
1411 free(*tevs);
1412 *tevs = NULL;
1413 return ret;
1414 }
1415
1416 return (ret < 0) ? ret : tf.ntevs;
1417}
1418
1419#define MAX_VAR_LEN 64
1420
1421/* Collect available variables in this scope */
1422static int collect_variables_cb(Dwarf_Die *die_mem, void *data)
1423{
1424 struct available_var_finder *af = data;
1425 struct variable_list *vl;
1426 char buf[MAX_VAR_LEN];
1427 int tag, ret;
1428
1429 vl = &af->vls[af->nvls - 1];
1430
1431 tag = dwarf_tag(die_mem);
1432 if (tag == DW_TAG_formal_parameter ||
1433 tag == DW_TAG_variable) {
1434 ret = convert_variable_location(die_mem, af->pf.addr,
1435 af->pf.fb_ops, NULL);
1436 if (ret == 0) {
1437 ret = die_get_varname(die_mem, buf, MAX_VAR_LEN);
1438 pr_debug2("Add new var: %s\n", buf);
1439 if (ret > 0)
1440 strlist__add(vl->vars, buf);
1441 }
1442 }
1443
1444 if (af->child && dwarf_haspc(die_mem, af->pf.addr))
1445 return DIE_FIND_CB_CONTINUE;
1446 else
1447 return DIE_FIND_CB_SIBLING;
1448}
1449
1450/* Add a found vars into available variables list */
1451static int add_available_vars(Dwarf_Die *sp_die, struct probe_finder *pf)
1452{
1453 struct available_var_finder *af =
1454 container_of(pf, struct available_var_finder, pf);
1455 struct variable_list *vl;
1456 Dwarf_Die die_mem, *scopes = NULL;
1457 int ret, nscopes;
1458
1459 /* Check number of tevs */
1460 if (af->nvls == af->max_vls) {
1461 pr_warning("Too many( > %d) probe point found.\n", af->max_vls);
1462 return -ERANGE;
1463 }
1464 vl = &af->vls[af->nvls++];
1465
1466 ret = convert_to_trace_point(sp_die, pf->addr, pf->pev->point.retprobe,
1467 &vl->point);
1468 if (ret < 0)
1469 return ret;
1470
1471 pr_debug("Probe point found: %s+%lu\n", vl->point.symbol,
1472 vl->point.offset);
1473
1474 /* Find local variables */
1475 vl->vars = strlist__new(true, NULL);
1476 if (vl->vars == NULL)
1477 return -ENOMEM;
1478 af->child = true;
1479 die_find_child(sp_die, collect_variables_cb, (void *)af, &die_mem);
1480
1481 /* Find external variables */
1482 if (!af->externs)
1483 goto out;
1484 /* Don't need to search child DIE for externs. */
1485 af->child = false;
1486 nscopes = dwarf_getscopes_die(sp_die, &scopes);
1487 while (nscopes-- > 1)
1488 die_find_child(&scopes[nscopes], collect_variables_cb,
1489 (void *)af, &die_mem);
1490 if (scopes)
1491 free(scopes);
1492
1493out:
1494 if (strlist__empty(vl->vars)) {
1495 strlist__delete(vl->vars);
1496 vl->vars = NULL;
1497 }
1498
1499 return ret;
1500}
1501
1502/* Find available variables at given probe point */
1503int find_available_vars_at(int fd, struct perf_probe_event *pev,
1504 struct variable_list **vls, int max_vls,
1505 bool externs)
1506{
1507 struct available_var_finder af = {
1508 .pf = {.pev = pev, .callback = add_available_vars},
1509 .max_vls = max_vls, .externs = externs};
1510 int ret;
1511
1512 /* Allocate result vls array */
1513 *vls = zalloc(sizeof(struct variable_list) * max_vls);
1514 if (*vls == NULL)
1515 return -ENOMEM;
1516
1517 af.vls = *vls;
1518 af.nvls = 0;
1519
1520 ret = find_probes(fd, &af.pf);
1521 if (ret < 0) {
1522 /* Free vlist for error */
1523 while (af.nvls--) {
1524 if (af.vls[af.nvls].point.symbol)
1525 free(af.vls[af.nvls].point.symbol);
1526 if (af.vls[af.nvls].vars)
1527 strlist__delete(af.vls[af.nvls].vars);
1528 }
1529 free(af.vls);
1530 *vls = NULL;
1531 return ret;
1532 }
1533
1534 return (ret < 0) ? ret : af.nvls;
1127} 1535}
1128 1536
1129/* Reverse search */ 1537/* Reverse search */
1130int find_perf_probe_point(int fd, unsigned long addr, 1538int find_perf_probe_point(unsigned long addr, struct perf_probe_point *ppt)
1131 struct perf_probe_point *ppt)
1132{ 1539{
1133 Dwarf_Die cudie, spdie, indie; 1540 Dwarf_Die cudie, spdie, indie;
1134 Dwarf *dbg; 1541 Dwarf *dbg = NULL;
1542 Dwfl *dwfl = NULL;
1135 Dwarf_Line *line; 1543 Dwarf_Line *line;
1136 Dwarf_Addr laddr, eaddr; 1544 Dwarf_Addr laddr, eaddr, bias = 0;
1137 const char *tmp; 1545 const char *tmp;
1138 int lineno, ret = 0; 1546 int lineno, ret = 0;
1139 bool found = false; 1547 bool found = false;
1140 1548
1141 dbg = dwarf_begin(fd, DWARF_C_READ); 1549 /* Open the live linux kernel */
1142 if (!dbg) 1550 dbg = dwfl_init_live_kernel_dwarf(addr, &dwfl, &bias);
1143 return -EBADF; 1551 if (!dbg) {
1552 pr_warning("No dwarf info found in the vmlinux - "
1553 "please rebuild with CONFIG_DEBUG_INFO=y.\n");
1554 ret = -EINVAL;
1555 goto end;
1556 }
1144 1557
1558 /* Adjust address with bias */
1559 addr += bias;
1145 /* Find cu die */ 1560 /* Find cu die */
1146 if (!dwarf_addrdie(dbg, (Dwarf_Addr)addr, &cudie)) { 1561 if (!dwarf_addrdie(dbg, (Dwarf_Addr)addr - bias, &cudie)) {
1562 pr_warning("No CU DIE is found at %lx\n", addr);
1147 ret = -EINVAL; 1563 ret = -EINVAL;
1148 goto end; 1564 goto end;
1149 } 1565 }
@@ -1206,7 +1622,8 @@ found:
1206 } 1622 }
1207 1623
1208end: 1624end:
1209 dwarf_end(dbg); 1625 if (dwfl)
1626 dwfl_end(dwfl);
1210 if (ret >= 0) 1627 if (ret >= 0)
1211 ret = found ? 1 : 0; 1628 ret = found ? 1 : 0;
1212 return ret; 1629 return ret;
@@ -1339,6 +1756,9 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data)
1339 struct line_finder *lf = param->data; 1756 struct line_finder *lf = param->data;
1340 struct line_range *lr = lf->lr; 1757 struct line_range *lr = lf->lr;
1341 1758
1759 pr_debug("find (%llx) %s\n",
1760 (unsigned long long)dwarf_dieoffset(sp_die),
1761 dwarf_diename(sp_die));
1342 if (dwarf_tag(sp_die) == DW_TAG_subprogram && 1762 if (dwarf_tag(sp_die) == DW_TAG_subprogram &&
1343 die_compare_name(sp_die, lr->function)) { 1763 die_compare_name(sp_die, lr->function)) {
1344 lf->fname = dwarf_decl_file(sp_die); 1764 lf->fname = dwarf_decl_file(sp_die);
@@ -1382,10 +1802,12 @@ int find_line_range(int fd, struct line_range *lr)
1382 Dwarf_Off off = 0, noff; 1802 Dwarf_Off off = 0, noff;
1383 size_t cuhl; 1803 size_t cuhl;
1384 Dwarf_Die *diep; 1804 Dwarf_Die *diep;
1385 Dwarf *dbg; 1805 Dwarf *dbg = NULL;
1806 Dwfl *dwfl;
1807 Dwarf_Addr bias; /* Currently ignored */
1386 const char *comp_dir; 1808 const char *comp_dir;
1387 1809
1388 dbg = dwarf_begin(fd, DWARF_C_READ); 1810 dbg = dwfl_init_offline_dwarf(fd, &dwfl, &bias);
1389 if (!dbg) { 1811 if (!dbg) {
1390 pr_warning("No dwarf info found in the vmlinux - " 1812 pr_warning("No dwarf info found in the vmlinux - "
1391 "please rebuild with CONFIG_DEBUG_INFO=y.\n"); 1813 "please rebuild with CONFIG_DEBUG_INFO=y.\n");
@@ -1431,8 +1853,7 @@ int find_line_range(int fd, struct line_range *lr)
1431 } 1853 }
1432 1854
1433 pr_debug("path: %s\n", lr->path); 1855 pr_debug("path: %s\n", lr->path);
1434 dwarf_end(dbg); 1856 dwfl_end(dwfl);
1435
1436 return (ret < 0) ? ret : lf.found; 1857 return (ret < 0) ? ret : lf.found;
1437} 1858}
1438 1859
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h
index 4507d519f183..bba69d455699 100644
--- a/tools/perf/util/probe-finder.h
+++ b/tools/perf/util/probe-finder.h
@@ -22,20 +22,27 @@ extern int find_probe_trace_events(int fd, struct perf_probe_event *pev,
22 int max_tevs); 22 int max_tevs);
23 23
24/* Find a perf_probe_point from debuginfo */ 24/* Find a perf_probe_point from debuginfo */
25extern int find_perf_probe_point(int fd, unsigned long addr, 25extern int find_perf_probe_point(unsigned long addr,
26 struct perf_probe_point *ppt); 26 struct perf_probe_point *ppt);
27 27
28/* Find a line range */
28extern int find_line_range(int fd, struct line_range *lr); 29extern int find_line_range(int fd, struct line_range *lr);
29 30
31/* Find available variables */
32extern int find_available_vars_at(int fd, struct perf_probe_event *pev,
33 struct variable_list **vls, int max_points,
34 bool externs);
35
30#include <dwarf.h> 36#include <dwarf.h>
31#include <libdw.h> 37#include <libdw.h>
38#include <libdwfl.h>
32#include <version.h> 39#include <version.h>
33 40
34struct probe_finder { 41struct probe_finder {
35 struct perf_probe_event *pev; /* Target probe event */ 42 struct perf_probe_event *pev; /* Target probe event */
36 struct probe_trace_event *tevs; /* Result trace events */ 43
37 int ntevs; /* Number of trace events */ 44 /* Callback when a probe point is found */
38 int max_tevs; /* Max number of trace events */ 45 int (*callback)(Dwarf_Die *sp_die, struct probe_finder *pf);
39 46
40 /* For function searching */ 47 /* For function searching */
41 int lno; /* Line number */ 48 int lno; /* Line number */
@@ -53,6 +60,22 @@ struct probe_finder {
53 struct probe_trace_arg *tvar; /* Current result variable */ 60 struct probe_trace_arg *tvar; /* Current result variable */
54}; 61};
55 62
63struct trace_event_finder {
64 struct probe_finder pf;
65 struct probe_trace_event *tevs; /* Found trace events */
66 int ntevs; /* Number of trace events */
67 int max_tevs; /* Max number of trace events */
68};
69
70struct available_var_finder {
71 struct probe_finder pf;
72 struct variable_list *vls; /* Found variable lists */
73 int nvls; /* Number of variable lists */
74 int max_vls; /* Max no. of variable lists */
75 bool externs; /* Find external vars too */
76 bool child; /* Search child scopes */
77};
78
56struct line_finder { 79struct line_finder {
57 struct line_range *lr; /* Target line range */ 80 struct line_range *lr; /* Target line range */
58 81
diff --git a/tools/perf/util/pstack.h b/tools/perf/util/pstack.h
index 5ad07023504b..4cedea59f518 100644
--- a/tools/perf/util/pstack.h
+++ b/tools/perf/util/pstack.h
@@ -1,6 +1,8 @@
1#ifndef _PERF_PSTACK_ 1#ifndef _PERF_PSTACK_
2#define _PERF_PSTACK_ 2#define _PERF_PSTACK_
3 3
4#include <stdbool.h>
5
4struct pstack; 6struct pstack;
5struct pstack *pstack__new(unsigned short max_nr_entries); 7struct pstack *pstack__new(unsigned short max_nr_entries);
6void pstack__delete(struct pstack *self); 8void pstack__delete(struct pstack *self);
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c
index 1c61a4f4aa8a..b62a553cc67d 100644
--- a/tools/perf/util/sort.c
+++ b/tools/perf/util/sort.c
@@ -196,7 +196,8 @@ static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf,
196 196
197 if (verbose) { 197 if (verbose) {
198 char o = self->ms.map ? dso__symtab_origin(self->ms.map->dso) : '!'; 198 char o = self->ms.map ? dso__symtab_origin(self->ms.map->dso) : '!';
199 ret += repsep_snprintf(bf, size, "%#018llx %c ", self->ip, o); 199 ret += repsep_snprintf(bf, size, "%*Lx %c ",
200 BITS_PER_LONG / 4, self->ip, o);
200 } 201 }
201 202
202 ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", self->level); 203 ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", self->level);
@@ -204,7 +205,8 @@ static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf,
204 ret += repsep_snprintf(bf + ret, size - ret, "%s", 205 ret += repsep_snprintf(bf + ret, size - ret, "%s",
205 self->ms.sym->name); 206 self->ms.sym->name);
206 else 207 else
207 ret += repsep_snprintf(bf + ret, size - ret, "%#016llx", self->ip); 208 ret += repsep_snprintf(bf + ret, size - ret, "%*Lx",
209 BITS_PER_LONG / 4, self->ip);
208 210
209 return ret; 211 return ret;
210} 212}
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h
index 46e531d09e8b..0b91053a7d11 100644
--- a/tools/perf/util/sort.h
+++ b/tools/perf/util/sort.h
@@ -70,7 +70,7 @@ struct hist_entry {
70 struct hist_entry *pair; 70 struct hist_entry *pair;
71 struct rb_root sorted_chain; 71 struct rb_root sorted_chain;
72 }; 72 };
73 struct callchain_node callchain[0]; 73 struct callchain_root callchain[0];
74}; 74};
75 75
76enum sort_type { 76enum sort_type {
diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c
index 0409fc7c0058..8fc0bd3a3a4a 100644
--- a/tools/perf/util/string.c
+++ b/tools/perf/util/string.c
@@ -259,7 +259,7 @@ static bool __match_glob(const char *str, const char *pat, bool ignore_space)
259 if (!*pat) /* Tail wild card matches all */ 259 if (!*pat) /* Tail wild card matches all */
260 return true; 260 return true;
261 while (*str) 261 while (*str)
262 if (strglobmatch(str++, pat)) 262 if (__match_glob(str++, pat, ignore_space))
263 return true; 263 return true;
264 } 264 }
265 return !*str && !*pat; 265 return !*str && !*pat;
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 6f0dd90c36ce..439ab947daf4 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -131,7 +131,8 @@ static void map_groups__fixup_end(struct map_groups *self)
131 __map_groups__fixup_end(self, i); 131 __map_groups__fixup_end(self, i);
132} 132}
133 133
134static struct symbol *symbol__new(u64 start, u64 len, const char *name) 134static struct symbol *symbol__new(u64 start, u64 len, u8 binding,
135 const char *name)
135{ 136{
136 size_t namelen = strlen(name) + 1; 137 size_t namelen = strlen(name) + 1;
137 struct symbol *self = calloc(1, (symbol_conf.priv_size + 138 struct symbol *self = calloc(1, (symbol_conf.priv_size +
@@ -144,6 +145,7 @@ static struct symbol *symbol__new(u64 start, u64 len, const char *name)
144 145
145 self->start = start; 146 self->start = start;
146 self->end = len ? start + len - 1 : start; 147 self->end = len ? start + len - 1 : start;
148 self->binding = binding;
147 self->namelen = namelen - 1; 149 self->namelen = namelen - 1;
148 150
149 pr_debug4("%s: %s %#Lx-%#Lx\n", __func__, name, start, self->end); 151 pr_debug4("%s: %s %#Lx-%#Lx\n", __func__, name, start, self->end);
@@ -160,8 +162,11 @@ void symbol__delete(struct symbol *self)
160 162
161static size_t symbol__fprintf(struct symbol *self, FILE *fp) 163static size_t symbol__fprintf(struct symbol *self, FILE *fp)
162{ 164{
163 return fprintf(fp, " %llx-%llx %s\n", 165 return fprintf(fp, " %llx-%llx %c %s\n",
164 self->start, self->end, self->name); 166 self->start, self->end,
167 self->binding == STB_GLOBAL ? 'g' :
168 self->binding == STB_LOCAL ? 'l' : 'w',
169 self->name);
165} 170}
166 171
167void dso__set_long_name(struct dso *self, char *name) 172void dso__set_long_name(struct dso *self, char *name)
@@ -290,7 +295,9 @@ static void symbols__insert_by_name(struct rb_root *self, struct symbol *sym)
290{ 295{
291 struct rb_node **p = &self->rb_node; 296 struct rb_node **p = &self->rb_node;
292 struct rb_node *parent = NULL; 297 struct rb_node *parent = NULL;
293 struct symbol_name_rb_node *symn = ((void *)sym) - sizeof(*parent), *s; 298 struct symbol_name_rb_node *symn, *s;
299
300 symn = container_of(sym, struct symbol_name_rb_node, sym);
294 301
295 while (*p != NULL) { 302 while (*p != NULL) {
296 parent = *p; 303 parent = *p;
@@ -383,6 +390,20 @@ size_t dso__fprintf_buildid(struct dso *self, FILE *fp)
383 return fprintf(fp, "%s", sbuild_id); 390 return fprintf(fp, "%s", sbuild_id);
384} 391}
385 392
393size_t dso__fprintf_symbols_by_name(struct dso *self, enum map_type type, FILE *fp)
394{
395 size_t ret = 0;
396 struct rb_node *nd;
397 struct symbol_name_rb_node *pos;
398
399 for (nd = rb_first(&self->symbol_names[type]); nd; nd = rb_next(nd)) {
400 pos = rb_entry(nd, struct symbol_name_rb_node, rb_node);
401 fprintf(fp, "%s\n", pos->sym.name);
402 }
403
404 return ret;
405}
406
386size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp) 407size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp)
387{ 408{
388 struct rb_node *nd; 409 struct rb_node *nd;
@@ -453,6 +474,14 @@ struct process_kallsyms_args {
453 struct dso *dso; 474 struct dso *dso;
454}; 475};
455 476
477static u8 kallsyms2elf_type(char type)
478{
479 if (type == 'W')
480 return STB_WEAK;
481
482 return isupper(type) ? STB_GLOBAL : STB_LOCAL;
483}
484
456static int map__process_kallsym_symbol(void *arg, const char *name, 485static int map__process_kallsym_symbol(void *arg, const char *name,
457 char type, u64 start) 486 char type, u64 start)
458{ 487{
@@ -466,7 +495,7 @@ static int map__process_kallsym_symbol(void *arg, const char *name,
466 /* 495 /*
467 * Will fix up the end later, when we have all symbols sorted. 496 * Will fix up the end later, when we have all symbols sorted.
468 */ 497 */
469 sym = symbol__new(start, 0, name); 498 sym = symbol__new(start, 0, kallsyms2elf_type(type), name);
470 499
471 if (sym == NULL) 500 if (sym == NULL)
472 return -ENOMEM; 501 return -ENOMEM;
@@ -503,7 +532,7 @@ static int dso__split_kallsyms(struct dso *self, struct map *map,
503 struct machine *machine = kmaps->machine; 532 struct machine *machine = kmaps->machine;
504 struct map *curr_map = map; 533 struct map *curr_map = map;
505 struct symbol *pos; 534 struct symbol *pos;
506 int count = 0; 535 int count = 0, moved = 0;
507 struct rb_root *root = &self->symbols[map->type]; 536 struct rb_root *root = &self->symbols[map->type];
508 struct rb_node *next = rb_first(root); 537 struct rb_node *next = rb_first(root);
509 int kernel_range = 0; 538 int kernel_range = 0;
@@ -561,6 +590,11 @@ static int dso__split_kallsyms(struct dso *self, struct map *map,
561 char dso_name[PATH_MAX]; 590 char dso_name[PATH_MAX];
562 struct dso *dso; 591 struct dso *dso;
563 592
593 if (count == 0) {
594 curr_map = map;
595 goto filter_symbol;
596 }
597
564 if (self->kernel == DSO_TYPE_GUEST_KERNEL) 598 if (self->kernel == DSO_TYPE_GUEST_KERNEL)
565 snprintf(dso_name, sizeof(dso_name), 599 snprintf(dso_name, sizeof(dso_name),
566 "[guest.kernel].%d", 600 "[guest.kernel].%d",
@@ -586,7 +620,7 @@ static int dso__split_kallsyms(struct dso *self, struct map *map,
586 map_groups__insert(kmaps, curr_map); 620 map_groups__insert(kmaps, curr_map);
587 ++kernel_range; 621 ++kernel_range;
588 } 622 }
589 623filter_symbol:
590 if (filter && filter(curr_map, pos)) { 624 if (filter && filter(curr_map, pos)) {
591discard_symbol: rb_erase(&pos->rb_node, root); 625discard_symbol: rb_erase(&pos->rb_node, root);
592 symbol__delete(pos); 626 symbol__delete(pos);
@@ -594,8 +628,9 @@ discard_symbol: rb_erase(&pos->rb_node, root);
594 if (curr_map != map) { 628 if (curr_map != map) {
595 rb_erase(&pos->rb_node, root); 629 rb_erase(&pos->rb_node, root);
596 symbols__insert(&curr_map->dso->symbols[curr_map->type], pos); 630 symbols__insert(&curr_map->dso->symbols[curr_map->type], pos);
597 } 631 ++moved;
598 count++; 632 } else
633 ++count;
599 } 634 }
600 } 635 }
601 636
@@ -605,7 +640,7 @@ discard_symbol: rb_erase(&pos->rb_node, root);
605 dso__set_loaded(curr_map->dso, curr_map->type); 640 dso__set_loaded(curr_map->dso, curr_map->type);
606 } 641 }
607 642
608 return count; 643 return count + moved;
609} 644}
610 645
611int dso__load_kallsyms(struct dso *self, const char *filename, 646int dso__load_kallsyms(struct dso *self, const char *filename,
@@ -661,7 +696,7 @@ static int dso__load_perf_map(struct dso *self, struct map *map,
661 if (len + 2 >= line_len) 696 if (len + 2 >= line_len)
662 continue; 697 continue;
663 698
664 sym = symbol__new(start, size, line + len); 699 sym = symbol__new(start, size, STB_GLOBAL, line + len);
665 700
666 if (sym == NULL) 701 if (sym == NULL)
667 goto out_delete_line; 702 goto out_delete_line;
@@ -873,7 +908,7 @@ static int dso__synthesize_plt_symbols(struct dso *self, struct map *map,
873 "%s@plt", elf_sym__name(&sym, symstrs)); 908 "%s@plt", elf_sym__name(&sym, symstrs));
874 909
875 f = symbol__new(plt_offset, shdr_plt.sh_entsize, 910 f = symbol__new(plt_offset, shdr_plt.sh_entsize,
876 sympltname); 911 STB_GLOBAL, sympltname);
877 if (!f) 912 if (!f)
878 goto out_elf_end; 913 goto out_elf_end;
879 914
@@ -895,7 +930,7 @@ static int dso__synthesize_plt_symbols(struct dso *self, struct map *map,
895 "%s@plt", elf_sym__name(&sym, symstrs)); 930 "%s@plt", elf_sym__name(&sym, symstrs));
896 931
897 f = symbol__new(plt_offset, shdr_plt.sh_entsize, 932 f = symbol__new(plt_offset, shdr_plt.sh_entsize,
898 sympltname); 933 STB_GLOBAL, sympltname);
899 if (!f) 934 if (!f)
900 goto out_elf_end; 935 goto out_elf_end;
901 936
@@ -1066,6 +1101,16 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name,
1066 if (!is_label && !elf_sym__is_a(&sym, map->type)) 1101 if (!is_label && !elf_sym__is_a(&sym, map->type))
1067 continue; 1102 continue;
1068 1103
1104 /* Reject ARM ELF "mapping symbols": these aren't unique and
1105 * don't identify functions, so will confuse the profile
1106 * output: */
1107 if (ehdr.e_machine == EM_ARM) {
1108 if (!strcmp(elf_name, "$a") ||
1109 !strcmp(elf_name, "$d") ||
1110 !strcmp(elf_name, "$t"))
1111 continue;
1112 }
1113
1069 if (opdsec && sym.st_shndx == opdidx) { 1114 if (opdsec && sym.st_shndx == opdidx) {
1070 u32 offset = sym.st_value - opdshdr.sh_addr; 1115 u32 offset = sym.st_value - opdshdr.sh_addr;
1071 u64 *opd = opddata->d_buf + offset; 1116 u64 *opd = opddata->d_buf + offset;
@@ -1146,7 +1191,8 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name,
1146 if (demangled != NULL) 1191 if (demangled != NULL)
1147 elf_name = demangled; 1192 elf_name = demangled;
1148new_symbol: 1193new_symbol:
1149 f = symbol__new(sym.st_value, sym.st_size, elf_name); 1194 f = symbol__new(sym.st_value, sym.st_size,
1195 GELF_ST_BIND(sym.st_info), elf_name);
1150 free(demangled); 1196 free(demangled);
1151 if (!f) 1197 if (!f)
1152 goto out_elf_end; 1198 goto out_elf_end;
@@ -1734,8 +1780,8 @@ out_failure:
1734 return -1; 1780 return -1;
1735} 1781}
1736 1782
1737static int dso__load_vmlinux(struct dso *self, struct map *map, 1783int dso__load_vmlinux(struct dso *self, struct map *map,
1738 const char *vmlinux, symbol_filter_t filter) 1784 const char *vmlinux, symbol_filter_t filter)
1739{ 1785{
1740 int err = -1, fd; 1786 int err = -1, fd;
1741 1787
@@ -2085,14 +2131,55 @@ static struct dso *machine__create_kernel(struct machine *self)
2085 return kernel; 2131 return kernel;
2086} 2132}
2087 2133
2134struct process_args {
2135 u64 start;
2136};
2137
2138static int symbol__in_kernel(void *arg, const char *name,
2139 char type __used, u64 start)
2140{
2141 struct process_args *args = arg;
2142
2143 if (strchr(name, '['))
2144 return 0;
2145
2146 args->start = start;
2147 return 1;
2148}
2149
2150/* Figure out the start address of kernel map from /proc/kallsyms */
2151static u64 machine__get_kernel_start_addr(struct machine *machine)
2152{
2153 const char *filename;
2154 char path[PATH_MAX];
2155 struct process_args args;
2156
2157 if (machine__is_host(machine)) {
2158 filename = "/proc/kallsyms";
2159 } else {
2160 if (machine__is_default_guest(machine))
2161 filename = (char *)symbol_conf.default_guest_kallsyms;
2162 else {
2163 sprintf(path, "%s/proc/kallsyms", machine->root_dir);
2164 filename = path;
2165 }
2166 }
2167
2168 if (kallsyms__parse(filename, &args, symbol__in_kernel) <= 0)
2169 return 0;
2170
2171 return args.start;
2172}
2173
2088int __machine__create_kernel_maps(struct machine *self, struct dso *kernel) 2174int __machine__create_kernel_maps(struct machine *self, struct dso *kernel)
2089{ 2175{
2090 enum map_type type; 2176 enum map_type type;
2177 u64 start = machine__get_kernel_start_addr(self);
2091 2178
2092 for (type = 0; type < MAP__NR_TYPES; ++type) { 2179 for (type = 0; type < MAP__NR_TYPES; ++type) {
2093 struct kmap *kmap; 2180 struct kmap *kmap;
2094 2181
2095 self->vmlinux_maps[type] = map__new2(0, kernel, type); 2182 self->vmlinux_maps[type] = map__new2(start, kernel, type);
2096 if (self->vmlinux_maps[type] == NULL) 2183 if (self->vmlinux_maps[type] == NULL)
2097 return -1; 2184 return -1;
2098 2185
@@ -2244,6 +2331,9 @@ static int setup_list(struct strlist **list, const char *list_str,
2244 2331
2245int symbol__init(void) 2332int symbol__init(void)
2246{ 2333{
2334 if (symbol_conf.initialized)
2335 return 0;
2336
2247 elf_version(EV_CURRENT); 2337 elf_version(EV_CURRENT);
2248 if (symbol_conf.sort_by_name) 2338 if (symbol_conf.sort_by_name)
2249 symbol_conf.priv_size += (sizeof(struct symbol_name_rb_node) - 2339 symbol_conf.priv_size += (sizeof(struct symbol_name_rb_node) -
@@ -2269,6 +2359,7 @@ int symbol__init(void)
2269 symbol_conf.sym_list_str, "symbol") < 0) 2359 symbol_conf.sym_list_str, "symbol") < 0)
2270 goto out_free_comm_list; 2360 goto out_free_comm_list;
2271 2361
2362 symbol_conf.initialized = true;
2272 return 0; 2363 return 0;
2273 2364
2274out_free_dso_list: 2365out_free_dso_list:
@@ -2280,11 +2371,14 @@ out_free_comm_list:
2280 2371
2281void symbol__exit(void) 2372void symbol__exit(void)
2282{ 2373{
2374 if (!symbol_conf.initialized)
2375 return;
2283 strlist__delete(symbol_conf.sym_list); 2376 strlist__delete(symbol_conf.sym_list);
2284 strlist__delete(symbol_conf.dso_list); 2377 strlist__delete(symbol_conf.dso_list);
2285 strlist__delete(symbol_conf.comm_list); 2378 strlist__delete(symbol_conf.comm_list);
2286 vmlinux_path__exit(); 2379 vmlinux_path__exit();
2287 symbol_conf.sym_list = symbol_conf.dso_list = symbol_conf.comm_list = NULL; 2380 symbol_conf.sym_list = symbol_conf.dso_list = symbol_conf.comm_list = NULL;
2381 symbol_conf.initialized = false;
2288} 2382}
2289 2383
2290int machines__create_kernel_maps(struct rb_root *self, pid_t pid) 2384int machines__create_kernel_maps(struct rb_root *self, pid_t pid)
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 906be20011d9..6c6eafdb932d 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -53,6 +53,7 @@ struct symbol {
53 u64 start; 53 u64 start;
54 u64 end; 54 u64 end;
55 u16 namelen; 55 u16 namelen;
56 u8 binding;
56 char name[0]; 57 char name[0];
57}; 58};
58 59
@@ -68,7 +69,8 @@ struct symbol_conf {
68 show_nr_samples, 69 show_nr_samples,
69 use_callchain, 70 use_callchain,
70 exclude_other, 71 exclude_other,
71 show_cpu_utilization; 72 show_cpu_utilization,
73 initialized;
72 const char *vmlinux_name, 74 const char *vmlinux_name,
73 *source_prefix, 75 *source_prefix,
74 *field_sep; 76 *field_sep;
@@ -164,6 +166,8 @@ void dso__sort_by_name(struct dso *self, enum map_type type);
164struct dso *__dsos__findnew(struct list_head *head, const char *name); 166struct dso *__dsos__findnew(struct list_head *head, const char *name);
165 167
166int dso__load(struct dso *self, struct map *map, symbol_filter_t filter); 168int dso__load(struct dso *self, struct map *map, symbol_filter_t filter);
169int dso__load_vmlinux(struct dso *self, struct map *map,
170 const char *vmlinux, symbol_filter_t filter);
167int dso__load_vmlinux_path(struct dso *self, struct map *map, 171int dso__load_vmlinux_path(struct dso *self, struct map *map,
168 symbol_filter_t filter); 172 symbol_filter_t filter);
169int dso__load_kallsyms(struct dso *self, const char *filename, struct map *map, 173int dso__load_kallsyms(struct dso *self, const char *filename, struct map *map,
@@ -180,6 +184,7 @@ size_t machines__fprintf_dsos(struct rb_root *self, FILE *fp);
180size_t machines__fprintf_dsos_buildid(struct rb_root *self, FILE *fp, bool with_hits); 184size_t machines__fprintf_dsos_buildid(struct rb_root *self, FILE *fp, bool with_hits);
181 185
182size_t dso__fprintf_buildid(struct dso *self, FILE *fp); 186size_t dso__fprintf_buildid(struct dso *self, FILE *fp);
187size_t dso__fprintf_symbols_by_name(struct dso *self, enum map_type type, FILE *fp);
183size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp); 188size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp);
184 189
185enum dso_origin { 190enum dso_origin {
diff --git a/tools/perf/util/trace-event-scripting.c b/tools/perf/util/trace-event-scripting.c
index 7ea983acfaea..f7af2fca965d 100644
--- a/tools/perf/util/trace-event-scripting.c
+++ b/tools/perf/util/trace-event-scripting.c
@@ -97,7 +97,7 @@ void setup_python_scripting(void)
97 register_python_scripting(&python_scripting_unsupported_ops); 97 register_python_scripting(&python_scripting_unsupported_ops);
98} 98}
99#else 99#else
100struct scripting_ops python_scripting_ops; 100extern struct scripting_ops python_scripting_ops;
101 101
102void setup_python_scripting(void) 102void setup_python_scripting(void)
103{ 103{
@@ -158,7 +158,7 @@ void setup_perl_scripting(void)
158 register_perl_scripting(&perl_scripting_unsupported_ops); 158 register_perl_scripting(&perl_scripting_unsupported_ops);
159} 159}
160#else 160#else
161struct scripting_ops perl_scripting_ops; 161extern struct scripting_ops perl_scripting_ops;
162 162
163void setup_perl_scripting(void) 163void setup_perl_scripting(void)
164{ 164{
diff --git a/tools/perf/util/ui/browser.c b/tools/perf/util/ui/browser.c
new file mode 100644
index 000000000000..8bc010edca25
--- /dev/null
+++ b/tools/perf/util/ui/browser.c
@@ -0,0 +1,337 @@
1#include "libslang.h"
2#include <linux/compiler.h>
3#include <linux/list.h>
4#include <linux/rbtree.h>
5#include <stdlib.h>
6#include <sys/ttydefaults.h>
7#include "browser.h"
8#include "helpline.h"
9#include "../color.h"
10#include "../util.h"
11#include <stdio.h>
12
13static int ui_browser__percent_color(double percent, bool current)
14{
15 if (current)
16 return HE_COLORSET_SELECTED;
17 if (percent >= MIN_RED)
18 return HE_COLORSET_TOP;
19 if (percent >= MIN_GREEN)
20 return HE_COLORSET_MEDIUM;
21 return HE_COLORSET_NORMAL;
22}
23
24void ui_browser__set_color(struct ui_browser *self __used, int color)
25{
26 SLsmg_set_color(color);
27}
28
29void ui_browser__set_percent_color(struct ui_browser *self,
30 double percent, bool current)
31{
32 int color = ui_browser__percent_color(percent, current);
33 ui_browser__set_color(self, color);
34}
35
36void ui_browser__gotorc(struct ui_browser *self, int y, int x)
37{
38 SLsmg_gotorc(self->y + y, self->x + x);
39}
40
41void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whence)
42{
43 struct list_head *head = self->entries;
44 struct list_head *pos;
45
46 switch (whence) {
47 case SEEK_SET:
48 pos = head->next;
49 break;
50 case SEEK_CUR:
51 pos = self->top;
52 break;
53 case SEEK_END:
54 pos = head->prev;
55 break;
56 default:
57 return;
58 }
59
60 if (offset > 0) {
61 while (offset-- != 0)
62 pos = pos->next;
63 } else {
64 while (offset++ != 0)
65 pos = pos->prev;
66 }
67
68 self->top = pos;
69}
70
71void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence)
72{
73 struct rb_root *root = self->entries;
74 struct rb_node *nd;
75
76 switch (whence) {
77 case SEEK_SET:
78 nd = rb_first(root);
79 break;
80 case SEEK_CUR:
81 nd = self->top;
82 break;
83 case SEEK_END:
84 nd = rb_last(root);
85 break;
86 default:
87 return;
88 }
89
90 if (offset > 0) {
91 while (offset-- != 0)
92 nd = rb_next(nd);
93 } else {
94 while (offset++ != 0)
95 nd = rb_prev(nd);
96 }
97
98 self->top = nd;
99}
100
101unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self)
102{
103 struct rb_node *nd;
104 int row = 0;
105
106 if (self->top == NULL)
107 self->top = rb_first(self->entries);
108
109 nd = self->top;
110
111 while (nd != NULL) {
112 ui_browser__gotorc(self, row, 0);
113 self->write(self, nd, row);
114 if (++row == self->height)
115 break;
116 nd = rb_next(nd);
117 }
118
119 return row;
120}
121
122bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row)
123{
124 return self->top_idx + row == self->index;
125}
126
127void ui_browser__refresh_dimensions(struct ui_browser *self)
128{
129 int cols, rows;
130 newtGetScreenSize(&cols, &rows);
131
132 self->width = cols - 1;
133 self->height = rows - 2;
134 self->y = 1;
135 self->x = 0;
136}
137
138void ui_browser__reset_index(struct ui_browser *self)
139{
140 self->index = self->top_idx = 0;
141 self->seek(self, 0, SEEK_SET);
142}
143
144void ui_browser__add_exit_key(struct ui_browser *self, int key)
145{
146 newtFormAddHotKey(self->form, key);
147}
148
149void ui_browser__add_exit_keys(struct ui_browser *self, int keys[])
150{
151 int i = 0;
152
153 while (keys[i] && i < 64) {
154 ui_browser__add_exit_key(self, keys[i]);
155 ++i;
156 }
157}
158
159int ui_browser__show(struct ui_browser *self, const char *title,
160 const char *helpline, ...)
161{
162 va_list ap;
163 int keys[] = { NEWT_KEY_UP, NEWT_KEY_DOWN, NEWT_KEY_PGUP,
164 NEWT_KEY_PGDN, NEWT_KEY_HOME, NEWT_KEY_END, ' ',
165 NEWT_KEY_LEFT, NEWT_KEY_ESCAPE, 'q', CTRL('c'), 0 };
166
167 if (self->form != NULL)
168 newtFormDestroy(self->form);
169
170 ui_browser__refresh_dimensions(self);
171 self->form = newtForm(NULL, NULL, 0);
172 if (self->form == NULL)
173 return -1;
174
175 self->sb = newtVerticalScrollbar(self->width, 1, self->height,
176 HE_COLORSET_NORMAL,
177 HE_COLORSET_SELECTED);
178 if (self->sb == NULL)
179 return -1;
180
181 SLsmg_gotorc(0, 0);
182 ui_browser__set_color(self, NEWT_COLORSET_ROOT);
183 slsmg_write_nstring(title, self->width);
184
185 ui_browser__add_exit_keys(self, keys);
186 newtFormAddComponent(self->form, self->sb);
187
188 va_start(ap, helpline);
189 ui_helpline__vpush(helpline, ap);
190 va_end(ap);
191 return 0;
192}
193
194void ui_browser__hide(struct ui_browser *self)
195{
196 newtFormDestroy(self->form);
197 self->form = NULL;
198 ui_helpline__pop();
199}
200
201int ui_browser__refresh(struct ui_browser *self)
202{
203 int row;
204
205 newtScrollbarSet(self->sb, self->index, self->nr_entries - 1);
206 row = self->refresh(self);
207 ui_browser__set_color(self, HE_COLORSET_NORMAL);
208 SLsmg_fill_region(self->y + row, self->x,
209 self->height - row, self->width, ' ');
210
211 return 0;
212}
213
214int ui_browser__run(struct ui_browser *self)
215{
216 struct newtExitStruct es;
217
218 if (ui_browser__refresh(self) < 0)
219 return -1;
220
221 while (1) {
222 off_t offset;
223
224 newtFormRun(self->form, &es);
225
226 if (es.reason != NEWT_EXIT_HOTKEY)
227 break;
228 switch (es.u.key) {
229 case NEWT_KEY_DOWN:
230 if (self->index == self->nr_entries - 1)
231 break;
232 ++self->index;
233 if (self->index == self->top_idx + self->height) {
234 ++self->top_idx;
235 self->seek(self, +1, SEEK_CUR);
236 }
237 break;
238 case NEWT_KEY_UP:
239 if (self->index == 0)
240 break;
241 --self->index;
242 if (self->index < self->top_idx) {
243 --self->top_idx;
244 self->seek(self, -1, SEEK_CUR);
245 }
246 break;
247 case NEWT_KEY_PGDN:
248 case ' ':
249 if (self->top_idx + self->height > self->nr_entries - 1)
250 break;
251
252 offset = self->height;
253 if (self->index + offset > self->nr_entries - 1)
254 offset = self->nr_entries - 1 - self->index;
255 self->index += offset;
256 self->top_idx += offset;
257 self->seek(self, +offset, SEEK_CUR);
258 break;
259 case NEWT_KEY_PGUP:
260 if (self->top_idx == 0)
261 break;
262
263 if (self->top_idx < self->height)
264 offset = self->top_idx;
265 else
266 offset = self->height;
267
268 self->index -= offset;
269 self->top_idx -= offset;
270 self->seek(self, -offset, SEEK_CUR);
271 break;
272 case NEWT_KEY_HOME:
273 ui_browser__reset_index(self);
274 break;
275 case NEWT_KEY_END:
276 offset = self->height - 1;
277 if (offset >= self->nr_entries)
278 offset = self->nr_entries - 1;
279
280 self->index = self->nr_entries - 1;
281 self->top_idx = self->index - offset;
282 self->seek(self, -offset, SEEK_END);
283 break;
284 default:
285 return es.u.key;
286 }
287 if (ui_browser__refresh(self) < 0)
288 return -1;
289 }
290 return -1;
291}
292
293unsigned int ui_browser__list_head_refresh(struct ui_browser *self)
294{
295 struct list_head *pos;
296 struct list_head *head = self->entries;
297 int row = 0;
298
299 if (self->top == NULL || self->top == self->entries)
300 self->top = head->next;
301
302 pos = self->top;
303
304 list_for_each_from(pos, head) {
305 ui_browser__gotorc(self, row, 0);
306 self->write(self, pos, row);
307 if (++row == self->height)
308 break;
309 }
310
311 return row;
312}
313
314static struct newtPercentTreeColors {
315 const char *topColorFg, *topColorBg;
316 const char *mediumColorFg, *mediumColorBg;
317 const char *normalColorFg, *normalColorBg;
318 const char *selColorFg, *selColorBg;
319 const char *codeColorFg, *codeColorBg;
320} defaultPercentTreeColors = {
321 "red", "lightgray",
322 "green", "lightgray",
323 "black", "lightgray",
324 "lightgray", "magenta",
325 "blue", "lightgray",
326};
327
328void ui_browser__init(void)
329{
330 struct newtPercentTreeColors *c = &defaultPercentTreeColors;
331
332 sltt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg);
333 sltt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg);
334 sltt_set_color(HE_COLORSET_NORMAL, NULL, c->normalColorFg, c->normalColorBg);
335 sltt_set_color(HE_COLORSET_SELECTED, NULL, c->selColorFg, c->selColorBg);
336 sltt_set_color(HE_COLORSET_CODE, NULL, c->codeColorFg, c->codeColorBg);
337}
diff --git a/tools/perf/util/ui/browser.h b/tools/perf/util/ui/browser.h
new file mode 100644
index 000000000000..0dc7e4da36f5
--- /dev/null
+++ b/tools/perf/util/ui/browser.h
@@ -0,0 +1,51 @@
1#ifndef _PERF_UI_BROWSER_H_
2#define _PERF_UI_BROWSER_H_ 1
3
4#include <stdbool.h>
5#include <newt.h>
6#include <sys/types.h>
7#include "../types.h"
8
9#define HE_COLORSET_TOP 50
10#define HE_COLORSET_MEDIUM 51
11#define HE_COLORSET_NORMAL 52
12#define HE_COLORSET_SELECTED 53
13#define HE_COLORSET_CODE 54
14
15struct ui_browser {
16 newtComponent form, sb;
17 u64 index, top_idx;
18 void *top, *entries;
19 u16 y, x, width, height;
20 void *priv;
21 unsigned int (*refresh)(struct ui_browser *self);
22 void (*write)(struct ui_browser *self, void *entry, int row);
23 void (*seek)(struct ui_browser *self, off_t offset, int whence);
24 u32 nr_entries;
25};
26
27
28void ui_browser__set_color(struct ui_browser *self, int color);
29void ui_browser__set_percent_color(struct ui_browser *self,
30 double percent, bool current);
31bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row);
32void ui_browser__refresh_dimensions(struct ui_browser *self);
33void ui_browser__reset_index(struct ui_browser *self);
34
35void ui_browser__gotorc(struct ui_browser *self, int y, int x);
36void ui_browser__add_exit_key(struct ui_browser *self, int key);
37void ui_browser__add_exit_keys(struct ui_browser *self, int keys[]);
38int ui_browser__show(struct ui_browser *self, const char *title,
39 const char *helpline, ...);
40void ui_browser__hide(struct ui_browser *self);
41int ui_browser__refresh(struct ui_browser *self);
42int ui_browser__run(struct ui_browser *self);
43
44void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence);
45unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self);
46
47void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whence);
48unsigned int ui_browser__list_head_refresh(struct ui_browser *self);
49
50void ui_browser__init(void);
51#endif /* _PERF_UI_BROWSER_H_ */
diff --git a/tools/perf/util/ui/browsers/annotate.c b/tools/perf/util/ui/browsers/annotate.c
new file mode 100644
index 000000000000..82b78f99251b
--- /dev/null
+++ b/tools/perf/util/ui/browsers/annotate.c
@@ -0,0 +1,237 @@
1#include "../browser.h"
2#include "../helpline.h"
3#include "../libslang.h"
4#include "../../hist.h"
5#include "../../sort.h"
6#include "../../symbol.h"
7
8static void ui__error_window(const char *fmt, ...)
9{
10 va_list ap;
11
12 va_start(ap, fmt);
13 newtWinMessagev((char *)"Error", (char *)"Ok", (char *)fmt, ap);
14 va_end(ap);
15}
16
17struct annotate_browser {
18 struct ui_browser b;
19 struct rb_root entries;
20 struct rb_node *curr_hot;
21};
22
23struct objdump_line_rb_node {
24 struct rb_node rb_node;
25 double percent;
26 u32 idx;
27};
28
29static inline
30struct objdump_line_rb_node *objdump_line__rb(struct objdump_line *self)
31{
32 return (struct objdump_line_rb_node *)(self + 1);
33}
34
35static void annotate_browser__write(struct ui_browser *self, void *entry, int row)
36{
37 struct objdump_line *ol = rb_entry(entry, struct objdump_line, node);
38 bool current_entry = ui_browser__is_current_entry(self, row);
39 int width = self->width;
40
41 if (ol->offset != -1) {
42 struct objdump_line_rb_node *olrb = objdump_line__rb(ol);
43 ui_browser__set_percent_color(self, olrb->percent, current_entry);
44 slsmg_printf(" %7.2f ", olrb->percent);
45 if (!current_entry)
46 ui_browser__set_color(self, HE_COLORSET_CODE);
47 } else {
48 ui_browser__set_percent_color(self, 0, current_entry);
49 slsmg_write_nstring(" ", 9);
50 }
51
52 SLsmg_write_char(':');
53 slsmg_write_nstring(" ", 8);
54 if (!*ol->line)
55 slsmg_write_nstring(" ", width - 18);
56 else
57 slsmg_write_nstring(ol->line, width - 18);
58}
59
60static double objdump_line__calc_percent(struct objdump_line *self,
61 struct list_head *head,
62 struct symbol *sym)
63{
64 double percent = 0.0;
65
66 if (self->offset != -1) {
67 int len = sym->end - sym->start;
68 unsigned int hits = 0;
69 struct sym_priv *priv = symbol__priv(sym);
70 struct sym_ext *sym_ext = priv->ext;
71 struct sym_hist *h = priv->hist;
72 s64 offset = self->offset;
73 struct objdump_line *next = objdump__get_next_ip_line(head, self);
74
75
76 while (offset < (s64)len &&
77 (next == NULL || offset < next->offset)) {
78 if (sym_ext) {
79 percent += sym_ext[offset].percent;
80 } else
81 hits += h->ip[offset];
82
83 ++offset;
84 }
85
86 if (sym_ext == NULL && h->sum)
87 percent = 100.0 * hits / h->sum;
88 }
89
90 return percent;
91}
92
93static void objdump__insert_line(struct rb_root *self,
94 struct objdump_line_rb_node *line)
95{
96 struct rb_node **p = &self->rb_node;
97 struct rb_node *parent = NULL;
98 struct objdump_line_rb_node *l;
99
100 while (*p != NULL) {
101 parent = *p;
102 l = rb_entry(parent, struct objdump_line_rb_node, rb_node);
103 if (line->percent < l->percent)
104 p = &(*p)->rb_left;
105 else
106 p = &(*p)->rb_right;
107 }
108 rb_link_node(&line->rb_node, parent, p);
109 rb_insert_color(&line->rb_node, self);
110}
111
112static void annotate_browser__set_top(struct annotate_browser *self,
113 struct rb_node *nd)
114{
115 struct objdump_line_rb_node *rbpos;
116 struct objdump_line *pos;
117 unsigned back;
118
119 ui_browser__refresh_dimensions(&self->b);
120 back = self->b.height / 2;
121 rbpos = rb_entry(nd, struct objdump_line_rb_node, rb_node);
122 pos = ((struct objdump_line *)rbpos) - 1;
123 self->b.top_idx = self->b.index = rbpos->idx;
124
125 while (self->b.top_idx != 0 && back != 0) {
126 pos = list_entry(pos->node.prev, struct objdump_line, node);
127
128 --self->b.top_idx;
129 --back;
130 }
131
132 self->b.top = pos;
133 self->curr_hot = nd;
134}
135
136static int annotate_browser__run(struct annotate_browser *self)
137{
138 struct rb_node *nd;
139 struct hist_entry *he = self->b.priv;
140 int key;
141
142 if (ui_browser__show(&self->b, he->ms.sym->name,
143 "<-, -> or ESC: exit, TAB/shift+TAB: cycle thru samples") < 0)
144 return -1;
145 /*
146 * To allow builtin-annotate to cycle thru multiple symbols by
147 * examining the exit key for this function.
148 */
149 ui_browser__add_exit_key(&self->b, NEWT_KEY_RIGHT);
150
151 nd = self->curr_hot;
152 if (nd) {
153 int tabs[] = { NEWT_KEY_TAB, NEWT_KEY_UNTAB, 0 };
154 ui_browser__add_exit_keys(&self->b, tabs);
155 }
156
157 while (1) {
158 key = ui_browser__run(&self->b);
159
160 switch (key) {
161 case NEWT_KEY_TAB:
162 nd = rb_prev(nd);
163 if (nd == NULL)
164 nd = rb_last(&self->entries);
165 annotate_browser__set_top(self, nd);
166 break;
167 case NEWT_KEY_UNTAB:
168 nd = rb_next(nd);
169 if (nd == NULL)
170 nd = rb_first(&self->entries);
171 annotate_browser__set_top(self, nd);
172 break;
173 default:
174 goto out;
175 }
176 }
177out:
178 ui_browser__hide(&self->b);
179 return key;
180}
181
182int hist_entry__tui_annotate(struct hist_entry *self)
183{
184 struct objdump_line *pos, *n;
185 struct objdump_line_rb_node *rbpos;
186 LIST_HEAD(head);
187 struct annotate_browser browser = {
188 .b = {
189 .entries = &head,
190 .refresh = ui_browser__list_head_refresh,
191 .seek = ui_browser__list_head_seek,
192 .write = annotate_browser__write,
193 .priv = self,
194 },
195 };
196 int ret;
197
198 if (self->ms.sym == NULL)
199 return -1;
200
201 if (self->ms.map->dso->annotate_warned)
202 return -1;
203
204 if (hist_entry__annotate(self, &head, sizeof(*rbpos)) < 0) {
205 ui__error_window(ui_helpline__last_msg);
206 return -1;
207 }
208
209 ui_helpline__push("Press <- or ESC to exit");
210
211 list_for_each_entry(pos, &head, node) {
212 size_t line_len = strlen(pos->line);
213 if (browser.b.width < line_len)
214 browser.b.width = line_len;
215 rbpos = objdump_line__rb(pos);
216 rbpos->idx = browser.b.nr_entries++;
217 rbpos->percent = objdump_line__calc_percent(pos, &head, self->ms.sym);
218 if (rbpos->percent < 0.01)
219 continue;
220 objdump__insert_line(&browser.entries, rbpos);
221 }
222
223 /*
224 * Position the browser at the hottest line.
225 */
226 browser.curr_hot = rb_last(&browser.entries);
227 if (browser.curr_hot)
228 annotate_browser__set_top(&browser, browser.curr_hot);
229
230 browser.b.width += 18; /* Percentage */
231 ret = annotate_browser__run(&browser);
232 list_for_each_entry_safe(pos, n, &head, node) {
233 list_del(&pos->node);
234 objdump_line__free(pos);
235 }
236 return ret;
237}
diff --git a/tools/perf/util/ui/browsers/hists.c b/tools/perf/util/ui/browsers/hists.c
new file mode 100644
index 000000000000..ebda8c3fde9e
--- /dev/null
+++ b/tools/perf/util/ui/browsers/hists.c
@@ -0,0 +1,1013 @@
1#define _GNU_SOURCE
2#include <stdio.h>
3#undef _GNU_SOURCE
4#include "../libslang.h"
5#include <stdlib.h>
6#include <string.h>
7#include <newt.h>
8#include <linux/rbtree.h>
9
10#include "../../hist.h"
11#include "../../pstack.h"
12#include "../../sort.h"
13#include "../../util.h"
14
15#include "../browser.h"
16#include "../helpline.h"
17#include "../util.h"
18#include "map.h"
19
20struct hist_browser {
21 struct ui_browser b;
22 struct hists *hists;
23 struct hist_entry *he_selection;
24 struct map_symbol *selection;
25};
26
27static void hist_browser__refresh_dimensions(struct hist_browser *self)
28{
29 /* 3 == +/- toggle symbol before actual hist_entry rendering */
30 self->b.width = 3 + (hists__sort_list_width(self->hists) +
31 sizeof("[k]"));
32}
33
34static void hist_browser__reset(struct hist_browser *self)
35{
36 self->b.nr_entries = self->hists->nr_entries;
37 hist_browser__refresh_dimensions(self);
38 ui_browser__reset_index(&self->b);
39}
40
41static char tree__folded_sign(bool unfolded)
42{
43 return unfolded ? '-' : '+';
44}
45
46static char map_symbol__folded(const struct map_symbol *self)
47{
48 return self->has_children ? tree__folded_sign(self->unfolded) : ' ';
49}
50
51static char hist_entry__folded(const struct hist_entry *self)
52{
53 return map_symbol__folded(&self->ms);
54}
55
56static char callchain_list__folded(const struct callchain_list *self)
57{
58 return map_symbol__folded(&self->ms);
59}
60
61static void map_symbol__set_folding(struct map_symbol *self, bool unfold)
62{
63 self->unfolded = unfold ? self->has_children : false;
64}
65
66static int callchain_node__count_rows_rb_tree(struct callchain_node *self)
67{
68 int n = 0;
69 struct rb_node *nd;
70
71 for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
72 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
73 struct callchain_list *chain;
74 char folded_sign = ' '; /* No children */
75
76 list_for_each_entry(chain, &child->val, list) {
77 ++n;
78 /* We need this because we may not have children */
79 folded_sign = callchain_list__folded(chain);
80 if (folded_sign == '+')
81 break;
82 }
83
84 if (folded_sign == '-') /* Have children and they're unfolded */
85 n += callchain_node__count_rows_rb_tree(child);
86 }
87
88 return n;
89}
90
91static int callchain_node__count_rows(struct callchain_node *node)
92{
93 struct callchain_list *chain;
94 bool unfolded = false;
95 int n = 0;
96
97 list_for_each_entry(chain, &node->val, list) {
98 ++n;
99 unfolded = chain->ms.unfolded;
100 }
101
102 if (unfolded)
103 n += callchain_node__count_rows_rb_tree(node);
104
105 return n;
106}
107
108static int callchain__count_rows(struct rb_root *chain)
109{
110 struct rb_node *nd;
111 int n = 0;
112
113 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
114 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
115 n += callchain_node__count_rows(node);
116 }
117
118 return n;
119}
120
121static bool map_symbol__toggle_fold(struct map_symbol *self)
122{
123 if (!self->has_children)
124 return false;
125
126 self->unfolded = !self->unfolded;
127 return true;
128}
129
130static void callchain_node__init_have_children_rb_tree(struct callchain_node *self)
131{
132 struct rb_node *nd = rb_first(&self->rb_root);
133
134 for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
135 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
136 struct callchain_list *chain;
137 bool first = true;
138
139 list_for_each_entry(chain, &child->val, list) {
140 if (first) {
141 first = false;
142 chain->ms.has_children = chain->list.next != &child->val ||
143 !RB_EMPTY_ROOT(&child->rb_root);
144 } else
145 chain->ms.has_children = chain->list.next == &child->val &&
146 !RB_EMPTY_ROOT(&child->rb_root);
147 }
148
149 callchain_node__init_have_children_rb_tree(child);
150 }
151}
152
153static void callchain_node__init_have_children(struct callchain_node *self)
154{
155 struct callchain_list *chain;
156
157 list_for_each_entry(chain, &self->val, list)
158 chain->ms.has_children = !RB_EMPTY_ROOT(&self->rb_root);
159
160 callchain_node__init_have_children_rb_tree(self);
161}
162
163static void callchain__init_have_children(struct rb_root *self)
164{
165 struct rb_node *nd;
166
167 for (nd = rb_first(self); nd; nd = rb_next(nd)) {
168 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
169 callchain_node__init_have_children(node);
170 }
171}
172
173static void hist_entry__init_have_children(struct hist_entry *self)
174{
175 if (!self->init_have_children) {
176 self->ms.has_children = !RB_EMPTY_ROOT(&self->sorted_chain);
177 callchain__init_have_children(&self->sorted_chain);
178 self->init_have_children = true;
179 }
180}
181
182static bool hist_browser__toggle_fold(struct hist_browser *self)
183{
184 if (map_symbol__toggle_fold(self->selection)) {
185 struct hist_entry *he = self->he_selection;
186
187 hist_entry__init_have_children(he);
188 self->hists->nr_entries -= he->nr_rows;
189
190 if (he->ms.unfolded)
191 he->nr_rows = callchain__count_rows(&he->sorted_chain);
192 else
193 he->nr_rows = 0;
194 self->hists->nr_entries += he->nr_rows;
195 self->b.nr_entries = self->hists->nr_entries;
196
197 return true;
198 }
199
200 /* If it doesn't have children, no toggling performed */
201 return false;
202}
203
204static int callchain_node__set_folding_rb_tree(struct callchain_node *self, bool unfold)
205{
206 int n = 0;
207 struct rb_node *nd;
208
209 for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
210 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
211 struct callchain_list *chain;
212 bool has_children = false;
213
214 list_for_each_entry(chain, &child->val, list) {
215 ++n;
216 map_symbol__set_folding(&chain->ms, unfold);
217 has_children = chain->ms.has_children;
218 }
219
220 if (has_children)
221 n += callchain_node__set_folding_rb_tree(child, unfold);
222 }
223
224 return n;
225}
226
227static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
228{
229 struct callchain_list *chain;
230 bool has_children = false;
231 int n = 0;
232
233 list_for_each_entry(chain, &node->val, list) {
234 ++n;
235 map_symbol__set_folding(&chain->ms, unfold);
236 has_children = chain->ms.has_children;
237 }
238
239 if (has_children)
240 n += callchain_node__set_folding_rb_tree(node, unfold);
241
242 return n;
243}
244
245static int callchain__set_folding(struct rb_root *chain, bool unfold)
246{
247 struct rb_node *nd;
248 int n = 0;
249
250 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
251 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
252 n += callchain_node__set_folding(node, unfold);
253 }
254
255 return n;
256}
257
258static void hist_entry__set_folding(struct hist_entry *self, bool unfold)
259{
260 hist_entry__init_have_children(self);
261 map_symbol__set_folding(&self->ms, unfold);
262
263 if (self->ms.has_children) {
264 int n = callchain__set_folding(&self->sorted_chain, unfold);
265 self->nr_rows = unfold ? n : 0;
266 } else
267 self->nr_rows = 0;
268}
269
270static void hists__set_folding(struct hists *self, bool unfold)
271{
272 struct rb_node *nd;
273
274 self->nr_entries = 0;
275
276 for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) {
277 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
278 hist_entry__set_folding(he, unfold);
279 self->nr_entries += 1 + he->nr_rows;
280 }
281}
282
283static void hist_browser__set_folding(struct hist_browser *self, bool unfold)
284{
285 hists__set_folding(self->hists, unfold);
286 self->b.nr_entries = self->hists->nr_entries;
287 /* Go to the start, we may be way after valid entries after a collapse */
288 ui_browser__reset_index(&self->b);
289}
290
291static int hist_browser__run(struct hist_browser *self, const char *title)
292{
293 int key;
294 int exit_keys[] = { 'a', '?', 'h', 'C', 'd', 'D', 'E', 't',
295 NEWT_KEY_ENTER, NEWT_KEY_RIGHT, NEWT_KEY_LEFT, 0, };
296
297 self->b.entries = &self->hists->entries;
298 self->b.nr_entries = self->hists->nr_entries;
299
300 hist_browser__refresh_dimensions(self);
301
302 if (ui_browser__show(&self->b, title,
303 "Press '?' for help on key bindings") < 0)
304 return -1;
305
306 ui_browser__add_exit_keys(&self->b, exit_keys);
307
308 while (1) {
309 key = ui_browser__run(&self->b);
310
311 switch (key) {
312 case 'D': { /* Debug */
313 static int seq;
314 struct hist_entry *h = rb_entry(self->b.top,
315 struct hist_entry, rb_node);
316 ui_helpline__pop();
317 ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
318 seq++, self->b.nr_entries,
319 self->hists->nr_entries,
320 self->b.height,
321 self->b.index,
322 self->b.top_idx,
323 h->row_offset, h->nr_rows);
324 }
325 break;
326 case 'C':
327 /* Collapse the whole world. */
328 hist_browser__set_folding(self, false);
329 break;
330 case 'E':
331 /* Expand the whole world. */
332 hist_browser__set_folding(self, true);
333 break;
334 case NEWT_KEY_ENTER:
335 if (hist_browser__toggle_fold(self))
336 break;
337 /* fall thru */
338 default:
339 goto out;
340 }
341 }
342out:
343 ui_browser__hide(&self->b);
344 return key;
345}
346
347static char *callchain_list__sym_name(struct callchain_list *self,
348 char *bf, size_t bfsize)
349{
350 if (self->ms.sym)
351 return self->ms.sym->name;
352
353 snprintf(bf, bfsize, "%#Lx", self->ip);
354 return bf;
355}
356
357#define LEVEL_OFFSET_STEP 3
358
359static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self,
360 struct callchain_node *chain_node,
361 u64 total, int level,
362 unsigned short row,
363 off_t *row_offset,
364 bool *is_current_entry)
365{
366 struct rb_node *node;
367 int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
368 u64 new_total, remaining;
369
370 if (callchain_param.mode == CHAIN_GRAPH_REL)
371 new_total = chain_node->children_hit;
372 else
373 new_total = total;
374
375 remaining = new_total;
376 node = rb_first(&chain_node->rb_root);
377 while (node) {
378 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
379 struct rb_node *next = rb_next(node);
380 u64 cumul = cumul_hits(child);
381 struct callchain_list *chain;
382 char folded_sign = ' ';
383 int first = true;
384 int extra_offset = 0;
385
386 remaining -= cumul;
387
388 list_for_each_entry(chain, &child->val, list) {
389 char ipstr[BITS_PER_LONG / 4 + 1], *alloc_str;
390 const char *str;
391 int color;
392 bool was_first = first;
393
394 if (first)
395 first = false;
396 else
397 extra_offset = LEVEL_OFFSET_STEP;
398
399 folded_sign = callchain_list__folded(chain);
400 if (*row_offset != 0) {
401 --*row_offset;
402 goto do_next;
403 }
404
405 alloc_str = NULL;
406 str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
407 if (was_first) {
408 double percent = cumul * 100.0 / new_total;
409
410 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
411 str = "Not enough memory!";
412 else
413 str = alloc_str;
414 }
415
416 color = HE_COLORSET_NORMAL;
417 width = self->b.width - (offset + extra_offset + 2);
418 if (ui_browser__is_current_entry(&self->b, row)) {
419 self->selection = &chain->ms;
420 color = HE_COLORSET_SELECTED;
421 *is_current_entry = true;
422 }
423
424 ui_browser__set_color(&self->b, color);
425 ui_browser__gotorc(&self->b, row, 0);
426 slsmg_write_nstring(" ", offset + extra_offset);
427 slsmg_printf("%c ", folded_sign);
428 slsmg_write_nstring(str, width);
429 free(alloc_str);
430
431 if (++row == self->b.height)
432 goto out;
433do_next:
434 if (folded_sign == '+')
435 break;
436 }
437
438 if (folded_sign == '-') {
439 const int new_level = level + (extra_offset ? 2 : 1);
440 row += hist_browser__show_callchain_node_rb_tree(self, child, new_total,
441 new_level, row, row_offset,
442 is_current_entry);
443 }
444 if (row == self->b.height)
445 goto out;
446 node = next;
447 }
448out:
449 return row - first_row;
450}
451
452static int hist_browser__show_callchain_node(struct hist_browser *self,
453 struct callchain_node *node,
454 int level, unsigned short row,
455 off_t *row_offset,
456 bool *is_current_entry)
457{
458 struct callchain_list *chain;
459 int first_row = row,
460 offset = level * LEVEL_OFFSET_STEP,
461 width = self->b.width - offset;
462 char folded_sign = ' ';
463
464 list_for_each_entry(chain, &node->val, list) {
465 char ipstr[BITS_PER_LONG / 4 + 1], *s;
466 int color;
467
468 folded_sign = callchain_list__folded(chain);
469
470 if (*row_offset != 0) {
471 --*row_offset;
472 continue;
473 }
474
475 color = HE_COLORSET_NORMAL;
476 if (ui_browser__is_current_entry(&self->b, row)) {
477 self->selection = &chain->ms;
478 color = HE_COLORSET_SELECTED;
479 *is_current_entry = true;
480 }
481
482 s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
483 ui_browser__gotorc(&self->b, row, 0);
484 ui_browser__set_color(&self->b, color);
485 slsmg_write_nstring(" ", offset);
486 slsmg_printf("%c ", folded_sign);
487 slsmg_write_nstring(s, width - 2);
488
489 if (++row == self->b.height)
490 goto out;
491 }
492
493 if (folded_sign == '-')
494 row += hist_browser__show_callchain_node_rb_tree(self, node,
495 self->hists->stats.total_period,
496 level + 1, row,
497 row_offset,
498 is_current_entry);
499out:
500 return row - first_row;
501}
502
503static int hist_browser__show_callchain(struct hist_browser *self,
504 struct rb_root *chain,
505 int level, unsigned short row,
506 off_t *row_offset,
507 bool *is_current_entry)
508{
509 struct rb_node *nd;
510 int first_row = row;
511
512 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
513 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
514
515 row += hist_browser__show_callchain_node(self, node, level,
516 row, row_offset,
517 is_current_entry);
518 if (row == self->b.height)
519 break;
520 }
521
522 return row - first_row;
523}
524
525static int hist_browser__show_entry(struct hist_browser *self,
526 struct hist_entry *entry,
527 unsigned short row)
528{
529 char s[256];
530 double percent;
531 int printed = 0;
532 int color, width = self->b.width;
533 char folded_sign = ' ';
534 bool current_entry = ui_browser__is_current_entry(&self->b, row);
535 off_t row_offset = entry->row_offset;
536
537 if (current_entry) {
538 self->he_selection = entry;
539 self->selection = &entry->ms;
540 }
541
542 if (symbol_conf.use_callchain) {
543 hist_entry__init_have_children(entry);
544 folded_sign = hist_entry__folded(entry);
545 }
546
547 if (row_offset == 0) {
548 hist_entry__snprintf(entry, s, sizeof(s), self->hists, NULL, false,
549 0, false, self->hists->stats.total_period);
550 percent = (entry->period * 100.0) / self->hists->stats.total_period;
551
552 color = HE_COLORSET_SELECTED;
553 if (!current_entry) {
554 if (percent >= MIN_RED)
555 color = HE_COLORSET_TOP;
556 else if (percent >= MIN_GREEN)
557 color = HE_COLORSET_MEDIUM;
558 else
559 color = HE_COLORSET_NORMAL;
560 }
561
562 ui_browser__set_color(&self->b, color);
563 ui_browser__gotorc(&self->b, row, 0);
564 if (symbol_conf.use_callchain) {
565 slsmg_printf("%c ", folded_sign);
566 width -= 2;
567 }
568 slsmg_write_nstring(s, width);
569 ++row;
570 ++printed;
571 } else
572 --row_offset;
573
574 if (folded_sign == '-' && row != self->b.height) {
575 printed += hist_browser__show_callchain(self, &entry->sorted_chain,
576 1, row, &row_offset,
577 &current_entry);
578 if (current_entry)
579 self->he_selection = entry;
580 }
581
582 return printed;
583}
584
585static unsigned int hist_browser__refresh(struct ui_browser *self)
586{
587 unsigned row = 0;
588 struct rb_node *nd;
589 struct hist_browser *hb = container_of(self, struct hist_browser, b);
590
591 if (self->top == NULL)
592 self->top = rb_first(&hb->hists->entries);
593
594 for (nd = self->top; nd; nd = rb_next(nd)) {
595 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
596
597 if (h->filtered)
598 continue;
599
600 row += hist_browser__show_entry(hb, h, row);
601 if (row == self->height)
602 break;
603 }
604
605 return row;
606}
607
608static struct rb_node *hists__filter_entries(struct rb_node *nd)
609{
610 while (nd != NULL) {
611 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
612 if (!h->filtered)
613 return nd;
614
615 nd = rb_next(nd);
616 }
617
618 return NULL;
619}
620
621static struct rb_node *hists__filter_prev_entries(struct rb_node *nd)
622{
623 while (nd != NULL) {
624 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
625 if (!h->filtered)
626 return nd;
627
628 nd = rb_prev(nd);
629 }
630
631 return NULL;
632}
633
634static void ui_browser__hists_seek(struct ui_browser *self,
635 off_t offset, int whence)
636{
637 struct hist_entry *h;
638 struct rb_node *nd;
639 bool first = true;
640
641 switch (whence) {
642 case SEEK_SET:
643 nd = hists__filter_entries(rb_first(self->entries));
644 break;
645 case SEEK_CUR:
646 nd = self->top;
647 goto do_offset;
648 case SEEK_END:
649 nd = hists__filter_prev_entries(rb_last(self->entries));
650 first = false;
651 break;
652 default:
653 return;
654 }
655
656 /*
657 * Moves not relative to the first visible entry invalidates its
658 * row_offset:
659 */
660 h = rb_entry(self->top, struct hist_entry, rb_node);
661 h->row_offset = 0;
662
663 /*
664 * Here we have to check if nd is expanded (+), if it is we can't go
665 * the next top level hist_entry, instead we must compute an offset of
666 * what _not_ to show and not change the first visible entry.
667 *
668 * This offset increments when we are going from top to bottom and
669 * decreases when we're going from bottom to top.
670 *
671 * As we don't have backpointers to the top level in the callchains
672 * structure, we need to always print the whole hist_entry callchain,
673 * skipping the first ones that are before the first visible entry
674 * and stop when we printed enough lines to fill the screen.
675 */
676do_offset:
677 if (offset > 0) {
678 do {
679 h = rb_entry(nd, struct hist_entry, rb_node);
680 if (h->ms.unfolded) {
681 u16 remaining = h->nr_rows - h->row_offset;
682 if (offset > remaining) {
683 offset -= remaining;
684 h->row_offset = 0;
685 } else {
686 h->row_offset += offset;
687 offset = 0;
688 self->top = nd;
689 break;
690 }
691 }
692 nd = hists__filter_entries(rb_next(nd));
693 if (nd == NULL)
694 break;
695 --offset;
696 self->top = nd;
697 } while (offset != 0);
698 } else if (offset < 0) {
699 while (1) {
700 h = rb_entry(nd, struct hist_entry, rb_node);
701 if (h->ms.unfolded) {
702 if (first) {
703 if (-offset > h->row_offset) {
704 offset += h->row_offset;
705 h->row_offset = 0;
706 } else {
707 h->row_offset += offset;
708 offset = 0;
709 self->top = nd;
710 break;
711 }
712 } else {
713 if (-offset > h->nr_rows) {
714 offset += h->nr_rows;
715 h->row_offset = 0;
716 } else {
717 h->row_offset = h->nr_rows + offset;
718 offset = 0;
719 self->top = nd;
720 break;
721 }
722 }
723 }
724
725 nd = hists__filter_prev_entries(rb_prev(nd));
726 if (nd == NULL)
727 break;
728 ++offset;
729 self->top = nd;
730 if (offset == 0) {
731 /*
732 * Last unfiltered hist_entry, check if it is
733 * unfolded, if it is then we should have
734 * row_offset at its last entry.
735 */
736 h = rb_entry(nd, struct hist_entry, rb_node);
737 if (h->ms.unfolded)
738 h->row_offset = h->nr_rows;
739 break;
740 }
741 first = false;
742 }
743 } else {
744 self->top = nd;
745 h = rb_entry(nd, struct hist_entry, rb_node);
746 h->row_offset = 0;
747 }
748}
749
750static struct hist_browser *hist_browser__new(struct hists *hists)
751{
752 struct hist_browser *self = zalloc(sizeof(*self));
753
754 if (self) {
755 self->hists = hists;
756 self->b.refresh = hist_browser__refresh;
757 self->b.seek = ui_browser__hists_seek;
758 }
759
760 return self;
761}
762
763static void hist_browser__delete(struct hist_browser *self)
764{
765 free(self);
766}
767
768static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self)
769{
770 return self->he_selection;
771}
772
773static struct thread *hist_browser__selected_thread(struct hist_browser *self)
774{
775 return self->he_selection->thread;
776}
777
778static int hists__browser_title(struct hists *self, char *bf, size_t size,
779 const char *ev_name, const struct dso *dso,
780 const struct thread *thread)
781{
782 char unit;
783 int printed;
784 unsigned long nr_events = self->stats.nr_events[PERF_RECORD_SAMPLE];
785
786 nr_events = convert_unit(nr_events, &unit);
787 printed = snprintf(bf, size, "Events: %lu%c %s", nr_events, unit, ev_name);
788
789 if (thread)
790 printed += snprintf(bf + printed, size - printed,
791 ", Thread: %s(%d)",
792 (thread->comm_set ? thread->comm : ""),
793 thread->pid);
794 if (dso)
795 printed += snprintf(bf + printed, size - printed,
796 ", DSO: %s", dso->short_name);
797 return printed;
798}
799
800int hists__browse(struct hists *self, const char *helpline, const char *ev_name)
801{
802 struct hist_browser *browser = hist_browser__new(self);
803 struct pstack *fstack;
804 const struct thread *thread_filter = NULL;
805 const struct dso *dso_filter = NULL;
806 char msg[160];
807 int key = -1;
808
809 if (browser == NULL)
810 return -1;
811
812 fstack = pstack__new(2);
813 if (fstack == NULL)
814 goto out;
815
816 ui_helpline__push(helpline);
817
818 hists__browser_title(self, msg, sizeof(msg), ev_name,
819 dso_filter, thread_filter);
820 while (1) {
821 const struct thread *thread;
822 const struct dso *dso;
823 char *options[16];
824 int nr_options = 0, choice = 0, i,
825 annotate = -2, zoom_dso = -2, zoom_thread = -2,
826 browse_map = -2;
827
828 key = hist_browser__run(browser, msg);
829
830 thread = hist_browser__selected_thread(browser);
831 dso = browser->selection->map ? browser->selection->map->dso : NULL;
832
833 switch (key) {
834 case NEWT_KEY_TAB:
835 case NEWT_KEY_UNTAB:
836 /*
837 * Exit the browser, let hists__browser_tree
838 * go to the next or previous
839 */
840 goto out_free_stack;
841 case 'a':
842 if (browser->selection->map == NULL &&
843 browser->selection->map->dso->annotate_warned)
844 continue;
845 goto do_annotate;
846 case 'd':
847 goto zoom_dso;
848 case 't':
849 goto zoom_thread;
850 case NEWT_KEY_F1:
851 case 'h':
852 case '?':
853 ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n"
854 "<- Zoom out\n"
855 "a Annotate current symbol\n"
856 "h/?/F1 Show this window\n"
857 "C Collapse all callchains\n"
858 "E Expand all callchains\n"
859 "d Zoom into current DSO\n"
860 "t Zoom into current Thread\n"
861 "q/CTRL+C Exit browser");
862 continue;
863 case NEWT_KEY_ENTER:
864 case NEWT_KEY_RIGHT:
865 /* menu */
866 break;
867 case NEWT_KEY_LEFT: {
868 const void *top;
869
870 if (pstack__empty(fstack))
871 continue;
872 top = pstack__pop(fstack);
873 if (top == &dso_filter)
874 goto zoom_out_dso;
875 if (top == &thread_filter)
876 goto zoom_out_thread;
877 continue;
878 }
879 case NEWT_KEY_ESCAPE:
880 if (!ui__dialog_yesno("Do you really want to exit?"))
881 continue;
882 /* Fall thru */
883 default:
884 goto out_free_stack;
885 }
886
887 if (browser->selection->sym != NULL &&
888 !browser->selection->map->dso->annotate_warned &&
889 asprintf(&options[nr_options], "Annotate %s",
890 browser->selection->sym->name) > 0)
891 annotate = nr_options++;
892
893 if (thread != NULL &&
894 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
895 (thread_filter ? "out of" : "into"),
896 (thread->comm_set ? thread->comm : ""),
897 thread->pid) > 0)
898 zoom_thread = nr_options++;
899
900 if (dso != NULL &&
901 asprintf(&options[nr_options], "Zoom %s %s DSO",
902 (dso_filter ? "out of" : "into"),
903 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
904 zoom_dso = nr_options++;
905
906 if (browser->selection->map != NULL &&
907 asprintf(&options[nr_options], "Browse map details") > 0)
908 browse_map = nr_options++;
909
910 options[nr_options++] = (char *)"Exit";
911
912 choice = ui__popup_menu(nr_options, options);
913
914 for (i = 0; i < nr_options - 1; ++i)
915 free(options[i]);
916
917 if (choice == nr_options - 1)
918 break;
919
920 if (choice == -1)
921 continue;
922
923 if (choice == annotate) {
924 struct hist_entry *he;
925do_annotate:
926 if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) {
927 browser->selection->map->dso->annotate_warned = 1;
928 ui_helpline__puts("No vmlinux file found, can't "
929 "annotate with just a "
930 "kallsyms file");
931 continue;
932 }
933
934 he = hist_browser__selected_entry(browser);
935 if (he == NULL)
936 continue;
937
938 hist_entry__tui_annotate(he);
939 } else if (choice == browse_map)
940 map__browse(browser->selection->map);
941 else if (choice == zoom_dso) {
942zoom_dso:
943 if (dso_filter) {
944 pstack__remove(fstack, &dso_filter);
945zoom_out_dso:
946 ui_helpline__pop();
947 dso_filter = NULL;
948 } else {
949 if (dso == NULL)
950 continue;
951 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
952 dso->kernel ? "the Kernel" : dso->short_name);
953 dso_filter = dso;
954 pstack__push(fstack, &dso_filter);
955 }
956 hists__filter_by_dso(self, dso_filter);
957 hists__browser_title(self, msg, sizeof(msg), ev_name,
958 dso_filter, thread_filter);
959 hist_browser__reset(browser);
960 } else if (choice == zoom_thread) {
961zoom_thread:
962 if (thread_filter) {
963 pstack__remove(fstack, &thread_filter);
964zoom_out_thread:
965 ui_helpline__pop();
966 thread_filter = NULL;
967 } else {
968 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
969 thread->comm_set ? thread->comm : "",
970 thread->pid);
971 thread_filter = thread;
972 pstack__push(fstack, &thread_filter);
973 }
974 hists__filter_by_thread(self, thread_filter);
975 hists__browser_title(self, msg, sizeof(msg), ev_name,
976 dso_filter, thread_filter);
977 hist_browser__reset(browser);
978 }
979 }
980out_free_stack:
981 pstack__delete(fstack);
982out:
983 hist_browser__delete(browser);
984 return key;
985}
986
987int hists__tui_browse_tree(struct rb_root *self, const char *help)
988{
989 struct rb_node *first = rb_first(self), *nd = first, *next;
990 int key = 0;
991
992 while (nd) {
993 struct hists *hists = rb_entry(nd, struct hists, rb_node);
994 const char *ev_name = __event_name(hists->type, hists->config);
995
996 key = hists__browse(hists, help, ev_name);
997 switch (key) {
998 case NEWT_KEY_TAB:
999 next = rb_next(nd);
1000 if (next)
1001 nd = next;
1002 break;
1003 case NEWT_KEY_UNTAB:
1004 if (nd == first)
1005 continue;
1006 nd = rb_prev(nd);
1007 default:
1008 return key;
1009 }
1010 }
1011
1012 return key;
1013}
diff --git a/tools/perf/util/ui/browsers/map.c b/tools/perf/util/ui/browsers/map.c
new file mode 100644
index 000000000000..e35437dfa5b4
--- /dev/null
+++ b/tools/perf/util/ui/browsers/map.c
@@ -0,0 +1,155 @@
1#include "../libslang.h"
2#include <elf.h>
3#include <sys/ttydefaults.h>
4#include <ctype.h>
5#include <string.h>
6#include <linux/bitops.h>
7#include "../../debug.h"
8#include "../../symbol.h"
9#include "../browser.h"
10#include "../helpline.h"
11#include "map.h"
12
13static int ui_entry__read(const char *title, char *bf, size_t size, int width)
14{
15 struct newtExitStruct es;
16 newtComponent form, entry;
17 const char *result;
18 int err = -1;
19
20 newtCenteredWindow(width, 1, title);
21 form = newtForm(NULL, NULL, 0);
22 if (form == NULL)
23 return -1;
24
25 entry = newtEntry(0, 0, "0x", width, &result, NEWT_FLAG_SCROLL);
26 if (entry == NULL)
27 goto out_free_form;
28
29 newtFormAddComponent(form, entry);
30 newtFormAddHotKey(form, NEWT_KEY_ENTER);
31 newtFormAddHotKey(form, NEWT_KEY_ESCAPE);
32 newtFormAddHotKey(form, NEWT_KEY_LEFT);
33 newtFormAddHotKey(form, CTRL('c'));
34 newtFormRun(form, &es);
35
36 if (result != NULL) {
37 strncpy(bf, result, size);
38 err = 0;
39 }
40out_free_form:
41 newtPopWindow();
42 newtFormDestroy(form);
43 return 0;
44}
45
46struct map_browser {
47 struct ui_browser b;
48 struct map *map;
49 u8 addrlen;
50};
51
52static void map_browser__write(struct ui_browser *self, void *nd, int row)
53{
54 struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
55 struct map_browser *mb = container_of(self, struct map_browser, b);
56 bool current_entry = ui_browser__is_current_entry(self, row);
57 int width;
58
59 ui_browser__set_percent_color(self, 0, current_entry);
60 slsmg_printf("%*llx %*llx %c ",
61 mb->addrlen, sym->start, mb->addrlen, sym->end,
62 sym->binding == STB_GLOBAL ? 'g' :
63 sym->binding == STB_LOCAL ? 'l' : 'w');
64 width = self->width - ((mb->addrlen * 2) + 4);
65 if (width > 0)
66 slsmg_write_nstring(sym->name, width);
67}
68
69/* FIXME uber-kludgy, see comment on cmd_report... */
70static u32 *symbol__browser_index(struct symbol *self)
71{
72 return ((void *)self) - sizeof(struct rb_node) - sizeof(u32);
73}
74
75static int map_browser__search(struct map_browser *self)
76{
77 char target[512];
78 struct symbol *sym;
79 int err = ui_entry__read("Search by name/addr", target, sizeof(target), 40);
80
81 if (err)
82 return err;
83
84 if (target[0] == '0' && tolower(target[1]) == 'x') {
85 u64 addr = strtoull(target, NULL, 16);
86 sym = map__find_symbol(self->map, addr, NULL);
87 } else
88 sym = map__find_symbol_by_name(self->map, target, NULL);
89
90 if (sym != NULL) {
91 u32 *idx = symbol__browser_index(sym);
92
93 self->b.top = &sym->rb_node;
94 self->b.index = self->b.top_idx = *idx;
95 } else
96 ui_helpline__fpush("%s not found!", target);
97
98 return 0;
99}
100
101static int map_browser__run(struct map_browser *self)
102{
103 int key;
104
105 if (ui_browser__show(&self->b, self->map->dso->long_name,
106 "Press <- or ESC to exit, %s / to search",
107 verbose ? "" : "restart with -v to use") < 0)
108 return -1;
109
110 if (verbose)
111 ui_browser__add_exit_key(&self->b, '/');
112
113 while (1) {
114 key = ui_browser__run(&self->b);
115
116 if (verbose && key == '/')
117 map_browser__search(self);
118 else
119 break;
120 }
121
122 ui_browser__hide(&self->b);
123 return key;
124}
125
126int map__browse(struct map *self)
127{
128 struct map_browser mb = {
129 .b = {
130 .entries = &self->dso->symbols[self->type],
131 .refresh = ui_browser__rb_tree_refresh,
132 .seek = ui_browser__rb_tree_seek,
133 .write = map_browser__write,
134 },
135 .map = self,
136 };
137 struct rb_node *nd;
138 char tmp[BITS_PER_LONG / 4];
139 u64 maxaddr = 0;
140
141 for (nd = rb_first(mb.b.entries); nd; nd = rb_next(nd)) {
142 struct symbol *pos = rb_entry(nd, struct symbol, rb_node);
143
144 if (maxaddr < pos->end)
145 maxaddr = pos->end;
146 if (verbose) {
147 u32 *idx = symbol__browser_index(pos);
148 *idx = mb.b.nr_entries;
149 }
150 ++mb.b.nr_entries;
151 }
152
153 mb.addrlen = snprintf(tmp, sizeof(tmp), "%llx", maxaddr);
154 return map_browser__run(&mb);
155}
diff --git a/tools/perf/util/ui/browsers/map.h b/tools/perf/util/ui/browsers/map.h
new file mode 100644
index 000000000000..df8581a43e17
--- /dev/null
+++ b/tools/perf/util/ui/browsers/map.h
@@ -0,0 +1,6 @@
1#ifndef _PERF_UI_MAP_BROWSER_H_
2#define _PERF_UI_MAP_BROWSER_H_ 1
3struct map;
4
5int map__browse(struct map *self);
6#endif /* _PERF_UI_MAP_BROWSER_H_ */
diff --git a/tools/perf/util/ui/helpline.c b/tools/perf/util/ui/helpline.c
new file mode 100644
index 000000000000..8d79daa4458a
--- /dev/null
+++ b/tools/perf/util/ui/helpline.c
@@ -0,0 +1,69 @@
1#define _GNU_SOURCE
2#include <stdio.h>
3#include <stdlib.h>
4#include <newt.h>
5
6#include "../debug.h"
7#include "helpline.h"
8
9void ui_helpline__pop(void)
10{
11 newtPopHelpLine();
12}
13
14void ui_helpline__push(const char *msg)
15{
16 newtPushHelpLine(msg);
17}
18
19void ui_helpline__vpush(const char *fmt, va_list ap)
20{
21 char *s;
22
23 if (vasprintf(&s, fmt, ap) < 0)
24 vfprintf(stderr, fmt, ap);
25 else {
26 ui_helpline__push(s);
27 free(s);
28 }
29}
30
31void ui_helpline__fpush(const char *fmt, ...)
32{
33 va_list ap;
34
35 va_start(ap, fmt);
36 ui_helpline__vpush(fmt, ap);
37 va_end(ap);
38}
39
40void ui_helpline__puts(const char *msg)
41{
42 ui_helpline__pop();
43 ui_helpline__push(msg);
44}
45
46void ui_helpline__init(void)
47{
48 ui_helpline__puts(" ");
49}
50
51char ui_helpline__last_msg[1024];
52
53int ui_helpline__show_help(const char *format, va_list ap)
54{
55 int ret;
56 static int backlog;
57
58 ret = vsnprintf(ui_helpline__last_msg + backlog,
59 sizeof(ui_helpline__last_msg) - backlog, format, ap);
60 backlog += ret;
61
62 if (ui_helpline__last_msg[backlog - 1] == '\n') {
63 ui_helpline__puts(ui_helpline__last_msg);
64 newtRefresh();
65 backlog = 0;
66 }
67
68 return ret;
69}
diff --git a/tools/perf/util/ui/helpline.h b/tools/perf/util/ui/helpline.h
new file mode 100644
index 000000000000..ab6028d0c401
--- /dev/null
+++ b/tools/perf/util/ui/helpline.h
@@ -0,0 +1,11 @@
1#ifndef _PERF_UI_HELPLINE_H_
2#define _PERF_UI_HELPLINE_H_ 1
3
4void ui_helpline__init(void);
5void ui_helpline__pop(void);
6void ui_helpline__push(const char *msg);
7void ui_helpline__vpush(const char *fmt, va_list ap);
8void ui_helpline__fpush(const char *fmt, ...);
9void ui_helpline__puts(const char *msg);
10
11#endif /* _PERF_UI_HELPLINE_H_ */
diff --git a/tools/perf/util/ui/libslang.h b/tools/perf/util/ui/libslang.h
new file mode 100644
index 000000000000..5623da8e8080
--- /dev/null
+++ b/tools/perf/util/ui/libslang.h
@@ -0,0 +1,27 @@
1#ifndef _PERF_UI_SLANG_H_
2#define _PERF_UI_SLANG_H_ 1
3/*
4 * slang versions <= 2.0.6 have a "#if HAVE_LONG_LONG" that breaks
5 * the build if it isn't defined. Use the equivalent one that glibc
6 * has on features.h.
7 */
8#include <features.h>
9#ifndef HAVE_LONG_LONG
10#define HAVE_LONG_LONG __GLIBC_HAVE_LONG_LONG
11#endif
12#include <slang.h>
13
14#if SLANG_VERSION < 20104
15#define slsmg_printf(msg, args...) \
16 SLsmg_printf((char *)msg, ##args)
17#define slsmg_write_nstring(msg, len) \
18 SLsmg_write_nstring((char *)msg, len)
19#define sltt_set_color(obj, name, fg, bg) \
20 SLtt_set_color(obj,(char *)name, (char *)fg, (char *)bg)
21#else
22#define slsmg_printf SLsmg_printf
23#define slsmg_write_nstring SLsmg_write_nstring
24#define sltt_set_color SLtt_set_color
25#endif
26
27#endif /* _PERF_UI_SLANG_H_ */
diff --git a/tools/perf/util/ui/progress.c b/tools/perf/util/ui/progress.c
new file mode 100644
index 000000000000..d7fc399d36b3
--- /dev/null
+++ b/tools/perf/util/ui/progress.c
@@ -0,0 +1,60 @@
1#include <stdlib.h>
2#include <newt.h>
3#include "../cache.h"
4#include "progress.h"
5
6struct ui_progress {
7 newtComponent form, scale;
8};
9
10struct ui_progress *ui_progress__new(const char *title, u64 total)
11{
12 struct ui_progress *self = malloc(sizeof(*self));
13
14 if (self != NULL) {
15 int cols;
16
17 if (use_browser <= 0)
18 return self;
19 newtGetScreenSize(&cols, NULL);
20 cols -= 4;
21 newtCenteredWindow(cols, 1, title);
22 self->form = newtForm(NULL, NULL, 0);
23 if (self->form == NULL)
24 goto out_free_self;
25 self->scale = newtScale(0, 0, cols, total);
26 if (self->scale == NULL)
27 goto out_free_form;
28 newtFormAddComponent(self->form, self->scale);
29 newtRefresh();
30 }
31
32 return self;
33
34out_free_form:
35 newtFormDestroy(self->form);
36out_free_self:
37 free(self);
38 return NULL;
39}
40
41void ui_progress__update(struct ui_progress *self, u64 curr)
42{
43 /*
44 * FIXME: We should have a per UI backend way of showing progress,
45 * stdio will just show a percentage as NN%, etc.
46 */
47 if (use_browser <= 0)
48 return;
49 newtScaleSet(self->scale, curr);
50 newtRefresh();
51}
52
53void ui_progress__delete(struct ui_progress *self)
54{
55 if (use_browser > 0) {
56 newtFormDestroy(self->form);
57 newtPopWindow();
58 }
59 free(self);
60}
diff --git a/tools/perf/util/ui/progress.h b/tools/perf/util/ui/progress.h
new file mode 100644
index 000000000000..a3820a0beb5b
--- /dev/null
+++ b/tools/perf/util/ui/progress.h
@@ -0,0 +1,11 @@
1#ifndef _PERF_UI_PROGRESS_H_
2#define _PERF_UI_PROGRESS_H_ 1
3
4struct ui_progress;
5
6struct ui_progress *ui_progress__new(const char *title, u64 total);
7void ui_progress__delete(struct ui_progress *self);
8
9void ui_progress__update(struct ui_progress *self, u64 curr);
10
11#endif
diff --git a/tools/perf/util/ui/setup.c b/tools/perf/util/ui/setup.c
new file mode 100644
index 000000000000..662085032eb7
--- /dev/null
+++ b/tools/perf/util/ui/setup.c
@@ -0,0 +1,42 @@
1#include <newt.h>
2#include <signal.h>
3#include <stdbool.h>
4
5#include "../cache.h"
6#include "../debug.h"
7#include "browser.h"
8#include "helpline.h"
9
10static void newt_suspend(void *d __used)
11{
12 newtSuspend();
13 raise(SIGTSTP);
14 newtResume();
15}
16
17void setup_browser(void)
18{
19 if (!isatty(1) || !use_browser || dump_trace) {
20 use_browser = 0;
21 setup_pager();
22 return;
23 }
24
25 use_browser = 1;
26 newtInit();
27 newtCls();
28 newtSetSuspendCallback(newt_suspend, NULL);
29 ui_helpline__init();
30 ui_browser__init();
31}
32
33void exit_browser(bool wait_for_ok)
34{
35 if (use_browser > 0) {
36 if (wait_for_ok) {
37 char title[] = "Fatal Error", ok[] = "Ok";
38 newtWinMessage(title, ok, ui_helpline__last_msg);
39 }
40 newtFinished();
41 }
42}
diff --git a/tools/perf/util/ui/util.c b/tools/perf/util/ui/util.c
new file mode 100644
index 000000000000..056c69521a38
--- /dev/null
+++ b/tools/perf/util/ui/util.c
@@ -0,0 +1,113 @@
1#include <newt.h>
2#include <signal.h>
3#include <stdio.h>
4#include <stdbool.h>
5#include <string.h>
6#include <sys/ttydefaults.h>
7
8#include "../cache.h"
9#include "../debug.h"
10#include "browser.h"
11#include "helpline.h"
12#include "util.h"
13
14static void newt_form__set_exit_keys(newtComponent self)
15{
16 newtFormAddHotKey(self, NEWT_KEY_LEFT);
17 newtFormAddHotKey(self, NEWT_KEY_ESCAPE);
18 newtFormAddHotKey(self, 'Q');
19 newtFormAddHotKey(self, 'q');
20 newtFormAddHotKey(self, CTRL('c'));
21}
22
23static newtComponent newt_form__new(void)
24{
25 newtComponent self = newtForm(NULL, NULL, 0);
26 if (self)
27 newt_form__set_exit_keys(self);
28 return self;
29}
30
31int ui__popup_menu(int argc, char * const argv[])
32{
33 struct newtExitStruct es;
34 int i, rc = -1, max_len = 5;
35 newtComponent listbox, form = newt_form__new();
36
37 if (form == NULL)
38 return -1;
39
40 listbox = newtListbox(0, 0, argc, NEWT_FLAG_RETURNEXIT);
41 if (listbox == NULL)
42 goto out_destroy_form;
43
44 newtFormAddComponent(form, listbox);
45
46 for (i = 0; i < argc; ++i) {
47 int len = strlen(argv[i]);
48 if (len > max_len)
49 max_len = len;
50 if (newtListboxAddEntry(listbox, argv[i], (void *)(long)i))
51 goto out_destroy_form;
52 }
53
54 newtCenteredWindow(max_len, argc, NULL);
55 newtFormRun(form, &es);
56 rc = newtListboxGetCurrent(listbox) - NULL;
57 if (es.reason == NEWT_EXIT_HOTKEY)
58 rc = -1;
59 newtPopWindow();
60out_destroy_form:
61 newtFormDestroy(form);
62 return rc;
63}
64
65int ui__help_window(const char *text)
66{
67 struct newtExitStruct es;
68 newtComponent tb, form = newt_form__new();
69 int rc = -1;
70 int max_len = 0, nr_lines = 0;
71 const char *t;
72
73 if (form == NULL)
74 return -1;
75
76 t = text;
77 while (1) {
78 const char *sep = strchr(t, '\n');
79 int len;
80
81 if (sep == NULL)
82 sep = strchr(t, '\0');
83 len = sep - t;
84 if (max_len < len)
85 max_len = len;
86 ++nr_lines;
87 if (*sep == '\0')
88 break;
89 t = sep + 1;
90 }
91
92 tb = newtTextbox(0, 0, max_len, nr_lines, 0);
93 if (tb == NULL)
94 goto out_destroy_form;
95
96 newtTextboxSetText(tb, text);
97 newtFormAddComponent(form, tb);
98 newtCenteredWindow(max_len, nr_lines, NULL);
99 newtFormRun(form, &es);
100 newtPopWindow();
101 rc = 0;
102out_destroy_form:
103 newtFormDestroy(form);
104 return rc;
105}
106
107static const char yes[] = "Yes", no[] = "No";
108
109bool ui__dialog_yesno(const char *msg)
110{
111 /* newtWinChoice should really be accepting const char pointers... */
112 return newtWinChoice(NULL, (char *)yes, (char *)no, (char *)msg) == 1;
113}
diff --git a/tools/perf/util/ui/util.h b/tools/perf/util/ui/util.h
new file mode 100644
index 000000000000..afcbc1d99531
--- /dev/null
+++ b/tools/perf/util/ui/util.h
@@ -0,0 +1,10 @@
1#ifndef _PERF_UI_UTIL_H_
2#define _PERF_UI_UTIL_H_ 1
3
4#include <stdbool.h>
5
6int ui__popup_menu(int argc, char * const argv[]);
7int ui__help_window(const char *text);
8bool ui__dialog_yesno(const char *msg);
9
10#endif /* _PERF_UI_UTIL_H_ */
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h
index f380fed74359..7562707ddd1c 100644
--- a/tools/perf/util/util.h
+++ b/tools/perf/util/util.h
@@ -266,19 +266,6 @@ bool strglobmatch(const char *str, const char *pat);
266bool strlazymatch(const char *str, const char *pat); 266bool strlazymatch(const char *str, const char *pat);
267unsigned long convert_unit(unsigned long value, char *unit); 267unsigned long convert_unit(unsigned long value, char *unit);
268 268
269#ifndef ESC
270#define ESC 27
271#endif
272
273static inline bool is_exit_key(int key)
274{
275 char up;
276 if (key == CTRL('c') || key == ESC)
277 return true;
278 up = toupper(key);
279 return up == 'Q';
280}
281
282#define _STR(x) #x 269#define _STR(x) #x
283#define STR(x) _STR(x) 270#define STR(x) _STR(x)
284 271