aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf
diff options
context:
space:
mode:
authorPaul Mundt <lethal@linux-sh.org>2011-01-13 01:06:28 -0500
committerPaul Mundt <lethal@linux-sh.org>2011-01-13 01:06:28 -0500
commitf43dc23d5ea91fca257be02138a255f02d98e806 (patch)
treeb29722f6e965316e90ac97abf79923ced250dc21 /tools/perf
parentf8e53553f452dcbf67cb89c8cba63a1cd6eb4cc0 (diff)
parent4162cf64973df51fc885825bc9ca4d055891c49f (diff)
Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 into common/serial-rework
Conflicts: arch/sh/kernel/cpu/sh2/setup-sh7619.c arch/sh/kernel/cpu/sh2a/setup-mxg.c arch/sh/kernel/cpu/sh2a/setup-sh7201.c arch/sh/kernel/cpu/sh2a/setup-sh7203.c arch/sh/kernel/cpu/sh2a/setup-sh7206.c arch/sh/kernel/cpu/sh3/setup-sh7705.c arch/sh/kernel/cpu/sh3/setup-sh770x.c arch/sh/kernel/cpu/sh3/setup-sh7710.c arch/sh/kernel/cpu/sh3/setup-sh7720.c arch/sh/kernel/cpu/sh4/setup-sh4-202.c arch/sh/kernel/cpu/sh4/setup-sh7750.c arch/sh/kernel/cpu/sh4/setup-sh7760.c arch/sh/kernel/cpu/sh4a/setup-sh7343.c arch/sh/kernel/cpu/sh4a/setup-sh7366.c arch/sh/kernel/cpu/sh4a/setup-sh7722.c arch/sh/kernel/cpu/sh4a/setup-sh7723.c arch/sh/kernel/cpu/sh4a/setup-sh7724.c arch/sh/kernel/cpu/sh4a/setup-sh7763.c arch/sh/kernel/cpu/sh4a/setup-sh7770.c arch/sh/kernel/cpu/sh4a/setup-sh7780.c arch/sh/kernel/cpu/sh4a/setup-sh7785.c arch/sh/kernel/cpu/sh4a/setup-sh7786.c arch/sh/kernel/cpu/sh4a/setup-shx3.c arch/sh/kernel/cpu/sh5/setup-sh5.c drivers/serial/sh-sci.c drivers/serial/sh-sci.h include/linux/serial_sci.h
Diffstat (limited to 'tools/perf')
-rw-r--r--tools/perf/.gitignore6
-rw-r--r--tools/perf/CREDITS30
-rw-r--r--tools/perf/Documentation/Makefile6
-rw-r--r--tools/perf/Documentation/examples.txt225
-rw-r--r--tools/perf/Documentation/perf-annotate.txt48
-rw-r--r--tools/perf/Documentation/perf-archive.txt22
-rw-r--r--tools/perf/Documentation/perf-bench.txt120
-rw-r--r--tools/perf/Documentation/perf-buildid-cache.txt33
-rw-r--r--tools/perf/Documentation/perf-buildid-list.txt37
-rw-r--r--tools/perf/Documentation/perf-diff.txt74
-rw-r--r--tools/perf/Documentation/perf-inject.txt35
-rw-r--r--tools/perf/Documentation/perf-kmem.txt47
-rw-r--r--tools/perf/Documentation/perf-kvm.txt74
-rw-r--r--tools/perf/Documentation/perf-list.txt50
-rw-r--r--tools/perf/Documentation/perf-lock.txt44
-rw-r--r--tools/perf/Documentation/perf-probe.txt166
-rw-r--r--tools/perf/Documentation/perf-record.txt109
-rw-r--r--tools/perf/Documentation/perf-report.txt100
-rw-r--r--tools/perf/Documentation/perf-sched.txt55
-rw-r--r--tools/perf/Documentation/perf-script-perl.txt217
-rw-r--r--tools/perf/Documentation/perf-script-python.txt623
-rw-r--r--tools/perf/Documentation/perf-script.txt118
-rw-r--r--tools/perf/Documentation/perf-stat.txt58
-rw-r--r--tools/perf/Documentation/perf-test.txt22
-rw-r--r--tools/perf/Documentation/perf-timechart.txt46
-rw-r--r--tools/perf/Documentation/perf-top.txt134
-rw-r--r--tools/perf/Documentation/perf.txt2
-rw-r--r--tools/perf/MANIFEST13
-rw-r--r--tools/perf/Makefile625
-rw-r--r--tools/perf/arch/arm/Makefile4
-rw-r--r--tools/perf/arch/arm/util/dwarf-regs.c64
-rw-r--r--tools/perf/arch/powerpc/Makefile4
-rw-r--r--tools/perf/arch/powerpc/util/dwarf-regs.c88
-rw-r--r--tools/perf/arch/s390/Makefile4
-rw-r--r--tools/perf/arch/s390/util/dwarf-regs.c22
-rw-r--r--tools/perf/arch/sh/Makefile4
-rw-r--r--tools/perf/arch/sh/util/dwarf-regs.c55
-rw-r--r--tools/perf/arch/sparc/Makefile4
-rw-r--r--tools/perf/arch/sparc/util/dwarf-regs.c43
-rw-r--r--tools/perf/arch/x86/Makefile4
-rw-r--r--tools/perf/arch/x86/util/dwarf-regs.c75
-rw-r--r--tools/perf/bench/bench.h17
-rw-r--r--tools/perf/bench/mem-memcpy-arch.h12
-rw-r--r--tools/perf/bench/mem-memcpy-x86-64-asm-def.h4
-rw-r--r--tools/perf/bench/mem-memcpy-x86-64-asm.S2
-rw-r--r--tools/perf/bench/mem-memcpy.c297
-rw-r--r--tools/perf/bench/sched-messaging.c336
-rw-r--r--tools/perf/bench/sched-pipe.c127
-rw-r--r--tools/perf/builtin-annotate.c1477
-rw-r--r--tools/perf/builtin-bench.c245
-rw-r--r--tools/perf/builtin-buildid-cache.c132
-rw-r--r--tools/perf/builtin-buildid-list.c60
-rw-r--r--tools/perf/builtin-diff.c232
-rw-r--r--tools/perf/builtin-help.c36
-rw-r--r--tools/perf/builtin-inject.c237
-rw-r--r--tools/perf/builtin-kmem.c777
-rw-r--r--tools/perf/builtin-kvm.c144
-rw-r--r--tools/perf/builtin-list.c5
-rw-r--r--tools/perf/builtin-lock.c1004
-rw-r--r--tools/perf/builtin-probe.c330
-rw-r--r--tools/perf/builtin-record.c1033
-rw-r--r--tools/perf/builtin-report.c1772
-rw-r--r--tools/perf/builtin-sched.c1921
-rw-r--r--tools/perf/builtin-script.c821
-rw-r--r--tools/perf/builtin-stat.c813
-rw-r--r--tools/perf/builtin-test.c497
-rw-r--r--tools/perf/builtin-timechart.c1104
-rw-r--r--tools/perf/builtin-top.c1361
-rw-r--r--tools/perf/builtin.h15
-rw-r--r--tools/perf/command-list.txt14
-rw-r--r--tools/perf/design.txt71
-rw-r--r--tools/perf/feature-tests.mak130
-rw-r--r--tools/perf/perf-archive.sh46
-rw-r--r--tools/perf/perf.c142
-rw-r--r--tools/perf/perf.h92
-rw-r--r--tools/perf/scripts/perl/Perf-Trace-Util/Context.c135
-rw-r--r--tools/perf/scripts/perl/Perf-Trace-Util/Context.xs42
-rw-r--r--tools/perf/scripts/perl/Perf-Trace-Util/Makefile.PL17
-rw-r--r--tools/perf/scripts/perl/Perf-Trace-Util/README59
-rw-r--r--tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Context.pm55
-rw-r--r--tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Core.pm192
-rw-r--r--tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Util.pm94
-rw-r--r--tools/perf/scripts/perl/Perf-Trace-Util/typemap1
-rw-r--r--tools/perf/scripts/perl/bin/check-perf-trace-record2
-rw-r--r--tools/perf/scripts/perl/bin/failed-syscalls-record2
-rw-r--r--tools/perf/scripts/perl/bin/failed-syscalls-report10
-rw-r--r--tools/perf/scripts/perl/bin/rw-by-file-record3
-rw-r--r--tools/perf/scripts/perl/bin/rw-by-file-report10
-rw-r--r--tools/perf/scripts/perl/bin/rw-by-pid-record2
-rw-r--r--tools/perf/scripts/perl/bin/rw-by-pid-report3
-rw-r--r--tools/perf/scripts/perl/bin/rwtop-record2
-rw-r--r--tools/perf/scripts/perl/bin/rwtop-report20
-rw-r--r--tools/perf/scripts/perl/bin/wakeup-latency-record6
-rw-r--r--tools/perf/scripts/perl/bin/wakeup-latency-report3
-rw-r--r--tools/perf/scripts/perl/bin/workqueue-stats-record2
-rw-r--r--tools/perf/scripts/perl/bin/workqueue-stats-report3
-rw-r--r--tools/perf/scripts/perl/check-perf-trace.pl106
-rw-r--r--tools/perf/scripts/perl/failed-syscalls.pl42
-rw-r--r--tools/perf/scripts/perl/rw-by-file.pl106
-rw-r--r--tools/perf/scripts/perl/rw-by-pid.pl184
-rw-r--r--tools/perf/scripts/perl/rwtop.pl199
-rw-r--r--tools/perf/scripts/perl/wakeup-latency.pl107
-rw-r--r--tools/perf/scripts/perl/workqueue-stats.pl129
-rw-r--r--tools/perf/scripts/python/Perf-Trace-Util/Context.c88
-rw-r--r--tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Core.py121
-rw-r--r--tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/SchedGui.py184
-rw-r--r--tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py86
-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-report10
-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-report3
-rw-r--r--tools/perf/scripts/python/bin/sctop-record2
-rw-r--r--tools/perf/scripts/python/bin/sctop-report24
-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-report10
-rw-r--r--tools/perf/scripts/python/bin/syscall-counts-record2
-rw-r--r--tools/perf/scripts/python/bin/syscall-counts-report10
-rw-r--r--tools/perf/scripts/python/check-perf-trace.py82
-rw-r--r--tools/perf/scripts/python/failed-syscalls-by-pid.py73
-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/sched-migration.py461
-rw-r--r--tools/perf/scripts/python/sctop.py75
-rw-r--r--tools/perf/scripts/python/syscall-counts-by-pid.py69
-rw-r--r--tools/perf/scripts/python/syscall-counts.py59
-rwxr-xr-xtools/perf/util/PERF-VERSION-GEN23
-rw-r--r--tools/perf/util/abspath.c80
-rw-r--r--tools/perf/util/alias.c2
-rw-r--r--tools/perf/util/bitmap.c21
-rw-r--r--tools/perf/util/build-id.c80
-rw-r--r--tools/perf/util/build-id.h10
-rw-r--r--tools/perf/util/cache.h84
-rw-r--r--tools/perf/util/callchain.c464
-rw-r--r--tools/perf/util/callchain.h75
-rw-r--r--tools/perf/util/color.c95
-rw-r--r--tools/perf/util/color.h16
-rw-r--r--tools/perf/util/config.c515
-rw-r--r--tools/perf/util/cpumap.c179
-rw-r--r--tools/perf/util/cpumap.h13
-rw-r--r--tools/perf/util/ctype.c8
-rw-r--r--tools/perf/util/debug.c94
-rw-r--r--tools/perf/util/debug.h40
-rw-r--r--tools/perf/util/debugfs.c240
-rw-r--r--tools/perf/util/debugfs.h25
-rw-r--r--tools/perf/util/event.c952
-rw-r--r--tools/perf/util/event.h177
-rw-r--r--tools/perf/util/evsel.c201
-rw-r--r--tools/perf/util/evsel.h115
-rw-r--r--tools/perf/util/exec_cmd.c8
-rw-r--r--tools/perf/util/exec_cmd.h7
-rw-r--r--tools/perf/util/header.c1232
-rw-r--r--tools/perf/util/header.h128
-rw-r--r--tools/perf/util/help.c71
-rw-r--r--tools/perf/util/help.h12
-rw-r--r--tools/perf/util/hist.c1189
-rw-r--r--tools/perf/util/hist.h150
-rw-r--r--tools/perf/util/hweight.c31
-rw-r--r--tools/perf/util/include/asm/asm-offsets.h1
-rw-r--r--tools/perf/util/include/asm/bug.h22
-rw-r--r--tools/perf/util/include/asm/byteorder.h2
-rw-r--r--tools/perf/util/include/asm/cpufeature.h9
-rw-r--r--tools/perf/util/include/asm/dwarf2.h11
-rw-r--r--tools/perf/util/include/asm/hweight.h8
-rw-r--r--tools/perf/util/include/asm/swab.h1
-rw-r--r--tools/perf/util/include/asm/system.h1
-rw-r--r--tools/perf/util/include/asm/uaccess.h14
-rw-r--r--tools/perf/util/include/dwarf-regs.h8
-rw-r--r--tools/perf/util/include/linux/bitmap.h35
-rw-r--r--tools/perf/util/include/linux/bitops.h32
-rw-r--r--tools/perf/util/include/linux/compiler.h12
-rw-r--r--tools/perf/util/include/linux/ctype.h1
-rw-r--r--tools/perf/util/include/linux/hash.h5
-rw-r--r--tools/perf/util/include/linux/kernel.h111
-rw-r--r--tools/perf/util/include/linux/linkage.h13
-rw-r--r--tools/perf/util/include/linux/list.h26
-rw-r--r--tools/perf/util/include/linux/module.h6
-rw-r--r--tools/perf/util/include/linux/poison.h1
-rw-r--r--tools/perf/util/include/linux/prefetch.h6
-rw-r--r--tools/perf/util/include/linux/rbtree.h1
-rw-r--r--tools/perf/util/include/linux/string.h1
-rw-r--r--tools/perf/util/include/linux/types.h21
-rw-r--r--tools/perf/util/levenshtein.h6
-rw-r--r--tools/perf/util/list.h603
-rw-r--r--tools/perf/util/map.c682
-rw-r--r--tools/perf/util/map.h237
-rw-r--r--tools/perf/util/pager.c5
-rw-r--r--tools/perf/util/parse-events.c1004
-rw-r--r--tools/perf/util/parse-events.h35
-rw-r--r--tools/perf/util/parse-options.c81
-rw-r--r--tools/perf/util/parse-options.h56
-rw-r--r--tools/perf/util/path.c210
-rw-r--r--tools/perf/util/probe-event.c1915
-rw-r--r--tools/perf/util/probe-event.h135
-rw-r--r--tools/perf/util/probe-finder.c1861
-rw-r--r--tools/perf/util/probe-finder.h91
-rw-r--r--tools/perf/util/pstack.c75
-rw-r--r--tools/perf/util/pstack.h14
-rw-r--r--tools/perf/util/quote.c429
-rw-r--r--tools/perf/util/quote.h45
-rw-r--r--tools/perf/util/rbtree.c383
-rw-r--r--tools/perf/util/rbtree.h171
-rw-r--r--tools/perf/util/run-command.c181
-rw-r--r--tools/perf/util/run-command.h41
-rw-r--r--tools/perf/util/scripting-engines/trace-event-perl.c565
-rw-r--r--tools/perf/util/scripting-engines/trace-event-python.c594
-rw-r--r--tools/perf/util/session.c1134
-rw-r--r--tools/perf/util/session.h157
-rw-r--r--tools/perf/util/sigchain.c2
-rw-r--r--tools/perf/util/sigchain.h7
-rw-r--r--tools/perf/util/sort.c345
-rw-r--r--tools/perf/util/sort.h124
-rw-r--r--tools/perf/util/strbuf.c232
-rw-r--r--tools/perf/util/strbuf.h57
-rw-r--r--tools/perf/util/string.c302
-rw-r--r--tools/perf/util/string.h8
-rw-r--r--tools/perf/util/strlist.c200
-rw-r--r--tools/perf/util/strlist.h78
-rw-r--r--tools/perf/util/svghelper.c500
-rw-r--r--tools/perf/util/svghelper.h28
-rw-r--r--tools/perf/util/symbol.c2392
-rw-r--r--tools/perf/util/symbol.h237
-rw-r--r--tools/perf/util/thread.c195
-rw-r--r--tools/perf/util/thread.h62
-rw-r--r--tools/perf/util/trace-event-info.c565
-rw-r--r--tools/perf/util/trace-event-parse.c3233
-rw-r--r--tools/perf/util/trace-event-read.c539
-rw-r--r--tools/perf/util/trace-event-scripting.c167
-rw-r--r--tools/perf/util/trace-event.h299
-rw-r--r--tools/perf/util/types.h (renamed from tools/perf/types.h)6
-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.c127
-rw-r--r--tools/perf/util/ui/util.h10
-rw-r--r--tools/perf/util/util.c133
-rw-r--r--tools/perf/util/util.h227
-rw-r--r--tools/perf/util/values.c231
-rw-r--r--tools/perf/util/values.h27
-rw-r--r--tools/perf/util/wrapper.c168
-rw-r--r--tools/perf/util/xyarray.c20
-rw-r--r--tools/perf/util/xyarray.h20
254 files changed, 44544 insertions, 7661 deletions
diff --git a/tools/perf/.gitignore b/tools/perf/.gitignore
index d69a759a104..cb43289e447 100644
--- a/tools/perf/.gitignore
+++ b/tools/perf/.gitignore
@@ -10,7 +10,13 @@ perf-stat
10perf-top 10perf-top
11perf*.1 11perf*.1
12perf*.xml 12perf*.xml
13perf*.html
13common-cmds.h 14common-cmds.h
15perf.data
16perf.data.old
17perf-archive
14tags 18tags
15TAGS 19TAGS
16cscope* 20cscope*
21config.mak
22config.mak.autogen
diff --git a/tools/perf/CREDITS b/tools/perf/CREDITS
new file mode 100644
index 00000000000..c2ddcb3acbd
--- /dev/null
+++ b/tools/perf/CREDITS
@@ -0,0 +1,30 @@
1Most of the infrastructure that 'perf' uses here has been reused
2from the Git project, as of version:
3
4 66996ec: Sync with 1.6.2.4
5
6Here is an (incomplete!) list of main contributors to those files
7in util/* and elsewhere:
8
9 Alex Riesen
10 Christian Couder
11 Dmitry Potapov
12 Jeff King
13 Johannes Schindelin
14 Johannes Sixt
15 Junio C Hamano
16 Linus Torvalds
17 Matthias Kestenholz
18 Michal Ostrowski
19 Miklos Vajna
20 Petr Baudis
21 Pierre Habouzit
22 René Scharfe
23 Samuel Tardieu
24 Shawn O. Pearce
25 Steffen Prohaska
26 Steve Haslam
27
28Thanks guys!
29
30The full history of the files can be found in the upstream Git commits.
diff --git a/tools/perf/Documentation/Makefile b/tools/perf/Documentation/Makefile
index 5457192e1b4..bd498d49695 100644
--- a/tools/perf/Documentation/Makefile
+++ b/tools/perf/Documentation/Makefile
@@ -24,7 +24,10 @@ DOC_MAN1=$(patsubst %.txt,%.1,$(MAN1_TXT))
24DOC_MAN5=$(patsubst %.txt,%.5,$(MAN5_TXT)) 24DOC_MAN5=$(patsubst %.txt,%.5,$(MAN5_TXT))
25DOC_MAN7=$(patsubst %.txt,%.7,$(MAN7_TXT)) 25DOC_MAN7=$(patsubst %.txt,%.7,$(MAN7_TXT))
26 26
27# Make the path relative to DESTDIR, not prefix
28ifndef DESTDIR
27prefix?=$(HOME) 29prefix?=$(HOME)
30endif
28bindir?=$(prefix)/bin 31bindir?=$(prefix)/bin
29htmldir?=$(prefix)/share/doc/perf-doc 32htmldir?=$(prefix)/share/doc/perf-doc
30pdfdir?=$(prefix)/share/doc/perf-doc 33pdfdir?=$(prefix)/share/doc/perf-doc
@@ -32,10 +35,9 @@ mandir?=$(prefix)/share/man
32man1dir=$(mandir)/man1 35man1dir=$(mandir)/man1
33man5dir=$(mandir)/man5 36man5dir=$(mandir)/man5
34man7dir=$(mandir)/man7 37man7dir=$(mandir)/man7
35# DESTDIR=
36 38
37ASCIIDOC=asciidoc 39ASCIIDOC=asciidoc
38ASCIIDOC_EXTRA = 40ASCIIDOC_EXTRA = --unsafe
39MANPAGE_XSL = manpage-normal.xsl 41MANPAGE_XSL = manpage-normal.xsl
40XMLTO_EXTRA = 42XMLTO_EXTRA =
41INSTALL?=install 43INSTALL?=install
diff --git a/tools/perf/Documentation/examples.txt b/tools/perf/Documentation/examples.txt
new file mode 100644
index 00000000000..8eb6c489fb1
--- /dev/null
+++ b/tools/perf/Documentation/examples.txt
@@ -0,0 +1,225 @@
1
2 ------------------------------
3 ****** perf by examples ******
4 ------------------------------
5
6[ From an e-mail by Ingo Molnar, http://lkml.org/lkml/2009/8/4/346 ]
7
8
9First, discovery/enumeration of available counters can be done via
10'perf list':
11
12titan:~> perf list
13 [...]
14 kmem:kmalloc [Tracepoint event]
15 kmem:kmem_cache_alloc [Tracepoint event]
16 kmem:kmalloc_node [Tracepoint event]
17 kmem:kmem_cache_alloc_node [Tracepoint event]
18 kmem:kfree [Tracepoint event]
19 kmem:kmem_cache_free [Tracepoint event]
20 kmem:mm_page_free_direct [Tracepoint event]
21 kmem:mm_pagevec_free [Tracepoint event]
22 kmem:mm_page_alloc [Tracepoint event]
23 kmem:mm_page_alloc_zone_locked [Tracepoint event]
24 kmem:mm_page_pcpu_drain [Tracepoint event]
25 kmem:mm_page_alloc_extfrag [Tracepoint event]
26
27Then any (or all) of the above event sources can be activated and
28measured. For example the page alloc/free properties of a 'hackbench
29run' are:
30
31 titan:~> perf stat -e kmem:mm_page_pcpu_drain -e kmem:mm_page_alloc
32 -e kmem:mm_pagevec_free -e kmem:mm_page_free_direct ./hackbench 10
33 Time: 0.575
34
35 Performance counter stats for './hackbench 10':
36
37 13857 kmem:mm_page_pcpu_drain
38 27576 kmem:mm_page_alloc
39 6025 kmem:mm_pagevec_free
40 20934 kmem:mm_page_free_direct
41
42 0.613972165 seconds time elapsed
43
44You can observe the statistical properties as well, by using the
45'repeat the workload N times' feature of perf stat:
46
47 titan:~> perf stat --repeat 5 -e kmem:mm_page_pcpu_drain -e
48 kmem:mm_page_alloc -e kmem:mm_pagevec_free -e
49 kmem:mm_page_free_direct ./hackbench 10
50 Time: 0.627
51 Time: 0.644
52 Time: 0.564
53 Time: 0.559
54 Time: 0.626
55
56 Performance counter stats for './hackbench 10' (5 runs):
57
58 12920 kmem:mm_page_pcpu_drain ( +- 3.359% )
59 25035 kmem:mm_page_alloc ( +- 3.783% )
60 6104 kmem:mm_pagevec_free ( +- 0.934% )
61 18376 kmem:mm_page_free_direct ( +- 4.941% )
62
63 0.643954516 seconds time elapsed ( +- 2.363% )
64
65Furthermore, these tracepoints can be used to sample the workload as
66well. For example the page allocations done by a 'git gc' can be
67captured the following way:
68
69 titan:~/git> perf record -f -e kmem:mm_page_alloc -c 1 ./git gc
70 Counting objects: 1148, done.
71 Delta compression using up to 2 threads.
72 Compressing objects: 100% (450/450), done.
73 Writing objects: 100% (1148/1148), done.
74 Total 1148 (delta 690), reused 1148 (delta 690)
75 [ perf record: Captured and wrote 0.267 MB perf.data (~11679 samples) ]
76
77To check which functions generated page allocations:
78
79 titan:~/git> perf report
80 # Samples: 10646
81 #
82 # Overhead Command Shared Object
83 # ........ ............... ..........................
84 #
85 23.57% git-repack /lib64/libc-2.5.so
86 21.81% git /lib64/libc-2.5.so
87 14.59% git ./git
88 11.79% git-repack ./git
89 7.12% git /lib64/ld-2.5.so
90 3.16% git-repack /lib64/libpthread-2.5.so
91 2.09% git-repack /bin/bash
92 1.97% rm /lib64/libc-2.5.so
93 1.39% mv /lib64/ld-2.5.so
94 1.37% mv /lib64/libc-2.5.so
95 1.12% git-repack /lib64/ld-2.5.so
96 0.95% rm /lib64/ld-2.5.so
97 0.90% git-update-serv /lib64/libc-2.5.so
98 0.73% git-update-serv /lib64/ld-2.5.so
99 0.68% perf /lib64/libpthread-2.5.so
100 0.64% git-repack /usr/lib64/libz.so.1.2.3
101
102Or to see it on a more finegrained level:
103
104titan:~/git> perf report --sort comm,dso,symbol
105# Samples: 10646
106#
107# Overhead Command Shared Object Symbol
108# ........ ............... .......................... ......
109#
110 9.35% git-repack ./git [.] insert_obj_hash
111 9.12% git ./git [.] insert_obj_hash
112 7.31% git /lib64/libc-2.5.so [.] memcpy
113 6.34% git-repack /lib64/libc-2.5.so [.] _int_malloc
114 6.24% git-repack /lib64/libc-2.5.so [.] memcpy
115 5.82% git-repack /lib64/libc-2.5.so [.] __GI___fork
116 5.47% git /lib64/libc-2.5.so [.] _int_malloc
117 2.99% git /lib64/libc-2.5.so [.] memset
118
119Furthermore, call-graph sampling can be done too, of page
120allocations - to see precisely what kind of page allocations there
121are:
122
123 titan:~/git> perf record -f -g -e kmem:mm_page_alloc -c 1 ./git gc
124 Counting objects: 1148, done.
125 Delta compression using up to 2 threads.
126 Compressing objects: 100% (450/450), done.
127 Writing objects: 100% (1148/1148), done.
128 Total 1148 (delta 690), reused 1148 (delta 690)
129 [ perf record: Captured and wrote 0.963 MB perf.data (~42069 samples) ]
130
131 titan:~/git> perf report -g
132 # Samples: 10686
133 #
134 # Overhead Command Shared Object
135 # ........ ............... ..........................
136 #
137 23.25% git-repack /lib64/libc-2.5.so
138 |
139 |--50.00%-- _int_free
140 |
141 |--37.50%-- __GI___fork
142 | make_child
143 |
144 |--12.50%-- ptmalloc_unlock_all2
145 | make_child
146 |
147 --6.25%-- __GI_strcpy
148 21.61% git /lib64/libc-2.5.so
149 |
150 |--30.00%-- __GI_read
151 | |
152 | --83.33%-- git_config_from_file
153 | git_config
154 | |
155 [...]
156
157Or you can observe the whole system's page allocations for 10
158seconds:
159
160titan:~/git> perf stat -a -e kmem:mm_page_pcpu_drain -e
161kmem:mm_page_alloc -e kmem:mm_pagevec_free -e
162kmem:mm_page_free_direct sleep 10
163
164 Performance counter stats for 'sleep 10':
165
166 171585 kmem:mm_page_pcpu_drain
167 322114 kmem:mm_page_alloc
168 73623 kmem:mm_pagevec_free
169 254115 kmem:mm_page_free_direct
170
171 10.000591410 seconds time elapsed
172
173Or observe how fluctuating the page allocations are, via statistical
174analysis done over ten 1-second intervals:
175
176 titan:~/git> perf stat --repeat 10 -a -e kmem:mm_page_pcpu_drain -e
177 kmem:mm_page_alloc -e kmem:mm_pagevec_free -e
178 kmem:mm_page_free_direct sleep 1
179
180 Performance counter stats for 'sleep 1' (10 runs):
181
182 17254 kmem:mm_page_pcpu_drain ( +- 3.709% )
183 34394 kmem:mm_page_alloc ( +- 4.617% )
184 7509 kmem:mm_pagevec_free ( +- 4.820% )
185 25653 kmem:mm_page_free_direct ( +- 3.672% )
186
187 1.058135029 seconds time elapsed ( +- 3.089% )
188
189Or you can annotate the recorded 'git gc' run on a per symbol basis
190and check which instructions/source-code generated page allocations:
191
192 titan:~/git> perf annotate __GI___fork
193 ------------------------------------------------
194 Percent | Source code & Disassembly of libc-2.5.so
195 ------------------------------------------------
196 :
197 :
198 : Disassembly of section .plt:
199 : Disassembly of section .text:
200 :
201 : 00000031a2e95560 <__fork>:
202 [...]
203 0.00 : 31a2e95602: b8 38 00 00 00 mov $0x38,%eax
204 0.00 : 31a2e95607: 0f 05 syscall
205 83.42 : 31a2e95609: 48 3d 00 f0 ff ff cmp $0xfffffffffffff000,%rax
206 0.00 : 31a2e9560f: 0f 87 4d 01 00 00 ja 31a2e95762 <__fork+0x202>
207 0.00 : 31a2e95615: 85 c0 test %eax,%eax
208
209( this shows that 83.42% of __GI___fork's page allocations come from
210 the 0x38 system call it performs. )
211
212etc. etc. - a lot more is possible. I could list a dozen of
213other different usecases straight away - neither of which is
214possible via /proc/vmstat.
215
216/proc/vmstat is not in the same league really, in terms of
217expressive power of system analysis and performance
218analysis.
219
220All that the above results needed were those new tracepoints
221in include/tracing/events/kmem.h.
222
223 Ingo
224
225
diff --git a/tools/perf/Documentation/perf-annotate.txt b/tools/perf/Documentation/perf-annotate.txt
index c9dcade0683..6f5a498608b 100644
--- a/tools/perf/Documentation/perf-annotate.txt
+++ b/tools/perf/Documentation/perf-annotate.txt
@@ -1,5 +1,5 @@
1perf-annotate(1) 1perf-annotate(1)
2============== 2================
3 3
4NAME 4NAME
5---- 5----
@@ -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,48 @@ OPTIONS
24--input=:: 24--input=::
25 Input file name. (default: perf.data) 25 Input file name. (default: perf.data)
26 26
27-d::
28--dsos=<dso[,dso...]>::
29 Only consider symbols in these dsos.
30-s::
31--symbol=<symbol>::
32 Symbol to annotate.
33
34-f::
35--force::
36 Don't complain, do it.
37
38-v::
39--verbose::
40 Be more verbose. (Show symbol address, etc)
41
42-D::
43--dump-raw-trace::
44 Dump raw trace in ASCII.
45
46-k::
47--vmlinux=<file>::
48 vmlinux pathname.
49
50-m::
51--modules::
52 Load module symbols. WARNING: use only with -k and LIVE kernel.
53
54-l::
55--print-line::
56 Print matching source lines (may be slow).
57
58-P::
59--full-paths::
60 Don't shorten the displayed pathnames.
61
62--stdio:: Use the stdio interface.
63
64--tui:: Use the TUI interface Use of --tui requires a tty, if one is not
65 present, as when piping to other commands, the stdio interface is
66 used. This interfaces starts by centering on the line with more
67 samples, TAB/UNTAB cycles through the lines with more samples.
68
27SEE ALSO 69SEE ALSO
28-------- 70--------
29linkperf:perf-record[1] 71linkperf:perf-record[1], linkperf:perf-report[1]
diff --git a/tools/perf/Documentation/perf-archive.txt b/tools/perf/Documentation/perf-archive.txt
new file mode 100644
index 00000000000..fae174dc7d0
--- /dev/null
+++ b/tools/perf/Documentation/perf-archive.txt
@@ -0,0 +1,22 @@
1perf-archive(1)
2===============
3
4NAME
5----
6perf-archive - Create archive with object files with build-ids found in perf.data file
7
8SYNOPSIS
9--------
10[verse]
11'perf archive' [file]
12
13DESCRIPTION
14-----------
15This command runs runs perf-buildid-list --with-hits, and collects the files
16with the buildids found so that analisys of perf.data contents can be possible
17on another machine.
18
19
20SEE ALSO
21--------
22linkperf:perf-record[1], linkperf:perf-buildid-list[1], linkperf:perf-report[1]
diff --git a/tools/perf/Documentation/perf-bench.txt b/tools/perf/Documentation/perf-bench.txt
new file mode 100644
index 00000000000..a3dbadb26ef
--- /dev/null
+++ b/tools/perf/Documentation/perf-bench.txt
@@ -0,0 +1,120 @@
1perf-bench(1)
2=============
3
4NAME
5----
6perf-bench - General framework for benchmark suites
7
8SYNOPSIS
9--------
10[verse]
11'perf bench' [<common options>] <subsystem> <suite> [<options>]
12
13DESCRIPTION
14-----------
15This 'perf bench' command is general framework for benchmark suites.
16
17COMMON OPTIONS
18--------------
19-f::
20--format=::
21Specify format style.
22Current available format styles are:
23
24'default'::
25Default style. This is mainly for human reading.
26---------------------
27% perf bench sched pipe # with no style specified
28(executing 1000000 pipe operations between two tasks)
29 Total time:5.855 sec
30 5.855061 usecs/op
31 170792 ops/sec
32---------------------
33
34'simple'::
35This simple style is friendly for automated
36processing by scripts.
37---------------------
38% perf bench --format=simple sched pipe # specified simple
395.988
40---------------------
41
42SUBSYSTEM
43---------
44
45'sched'::
46 Scheduler and IPC mechanisms.
47
48SUITES FOR 'sched'
49~~~~~~~~~~~~~~~~~~
50*messaging*::
51Suite for evaluating performance of scheduler and IPC mechanisms.
52Based on hackbench by Rusty Russell.
53
54Options of *pipe*
55^^^^^^^^^^^^^^^^^
56-p::
57--pipe::
58Use pipe() instead of socketpair()
59
60-t::
61--thread::
62Be multi thread instead of multi process
63
64-g::
65--group=::
66Specify number of groups
67
68-l::
69--loop=::
70Specify number of loops
71
72Example of *messaging*
73^^^^^^^^^^^^^^^^^^^^^^
74
75---------------------
76% perf bench sched messaging # run with default
77options (20 sender and receiver processes per group)
78(10 groups == 400 processes run)
79
80 Total time:0.308 sec
81
82% perf bench sched messaging -t -g 20 # be multi-thread, with 20 groups
83(20 sender and receiver threads per group)
84(20 groups == 800 threads run)
85
86 Total time:0.582 sec
87---------------------
88
89*pipe*::
90Suite for pipe() system call.
91Based on pipe-test-1m.c by Ingo Molnar.
92
93Options of *pipe*
94^^^^^^^^^^^^^^^^^
95-l::
96--loop=::
97Specify number of loops.
98
99Example of *pipe*
100^^^^^^^^^^^^^^^^^
101
102---------------------
103% perf bench sched pipe
104(executing 1000000 pipe operations between two tasks)
105
106 Total time:8.091 sec
107 8.091833 usecs/op
108 123581 ops/sec
109
110% perf bench sched pipe -l 1000 # loop 1000
111(executing 1000 pipe operations between two tasks)
112
113 Total time:0.016 sec
114 16.948000 usecs/op
115 59004 ops/sec
116---------------------
117
118SEE ALSO
119--------
120linkperf:perf[1]
diff --git a/tools/perf/Documentation/perf-buildid-cache.txt b/tools/perf/Documentation/perf-buildid-cache.txt
new file mode 100644
index 00000000000..c1057701a7d
--- /dev/null
+++ b/tools/perf/Documentation/perf-buildid-cache.txt
@@ -0,0 +1,33 @@
1perf-buildid-cache(1)
2=====================
3
4NAME
5----
6perf-buildid-cache - Manage build-id cache.
7
8SYNOPSIS
9--------
10[verse]
11'perf buildid-cache <options>'
12
13DESCRIPTION
14-----------
15This command manages the build-id cache. It can add and remove files to/from
16the cache. In the future it should as well purge older entries, set upper
17limits for the space used by the cache, etc.
18
19OPTIONS
20-------
21-a::
22--add=::
23 Add specified file to the cache.
24-r::
25--remove=::
26 Remove specified file from the cache.
27-v::
28--verbose::
29 Be more verbose.
30
31SEE ALSO
32--------
33linkperf:perf-record[1], linkperf:perf-report[1], linkperf:perf-buildid-list[1]
diff --git a/tools/perf/Documentation/perf-buildid-list.txt b/tools/perf/Documentation/perf-buildid-list.txt
new file mode 100644
index 00000000000..5eaac6f26d5
--- /dev/null
+++ b/tools/perf/Documentation/perf-buildid-list.txt
@@ -0,0 +1,37 @@
1perf-buildid-list(1)
2====================
3
4NAME
5----
6perf-buildid-list - List the buildids in a perf.data file
7
8SYNOPSIS
9--------
10[verse]
11'perf buildid-list <options>'
12
13DESCRIPTION
14-----------
15This command displays the buildids found in a perf.data file, so that other
16tools can be used to fetch packages with matching symbol tables for use by
17perf report.
18
19OPTIONS
20-------
21-H::
22--with-hits::
23 Show only DSOs with hits.
24-i::
25--input=::
26 Input file name. (default: perf.data)
27-f::
28--force::
29 Don't do ownership validation.
30-v::
31--verbose::
32 Be more verbose.
33
34SEE ALSO
35--------
36linkperf:perf-record[1], linkperf:perf-top[1],
37linkperf:perf-report[1]
diff --git a/tools/perf/Documentation/perf-diff.txt b/tools/perf/Documentation/perf-diff.txt
new file mode 100644
index 00000000000..74d7481ed7a
--- /dev/null
+++ b/tools/perf/Documentation/perf-diff.txt
@@ -0,0 +1,74 @@
1perf-diff(1)
2============
3
4NAME
5----
6perf-diff - Read two perf.data files and display the differential profile
7
8SYNOPSIS
9--------
10[verse]
11'perf diff' [oldfile] [newfile]
12
13DESCRIPTION
14-----------
15This command displays the performance difference amongst two perf.data files
16captured via perf record.
17
18If no parameters are passed it will assume perf.data.old and perf.data.
19
20OPTIONS
21-------
22-M::
23--displacement::
24 Show position displacement relative to baseline.
25
26-D::
27--dump-raw-trace::
28 Dump raw trace in ASCII.
29
30-m::
31--modules::
32 Load module symbols. WARNING: use only with -k and LIVE kernel
33
34-d::
35--dsos=::
36 Only consider symbols in these dsos. CSV that understands
37 file://filename entries.
38
39-C::
40--comms=::
41 Only consider symbols in these comms. CSV that understands
42 file://filename entries.
43
44-S::
45--symbols=::
46 Only consider these symbols. CSV that understands
47 file://filename entries.
48
49-s::
50--sort=::
51 Sort by key(s): pid, comm, dso, symbol.
52
53-t::
54--field-separator=::
55
56 Use a special separator character and don't pad with spaces, replacing
57 all occurrences of this separator in symbol names (and other output)
58 with a '.' character, that thus it's the only non valid separator.
59
60-v::
61--verbose::
62 Be verbose, for instance, show the raw counts in addition to the
63 diff.
64
65-f::
66--force::
67 Don't complain, do it.
68
69--symfs=<directory>::
70 Look for files with symbols relative to this directory.
71
72SEE ALSO
73--------
74linkperf:perf-record[1]
diff --git a/tools/perf/Documentation/perf-inject.txt b/tools/perf/Documentation/perf-inject.txt
new file mode 100644
index 00000000000..025630d43cd
--- /dev/null
+++ b/tools/perf/Documentation/perf-inject.txt
@@ -0,0 +1,35 @@
1perf-inject(1)
2==============
3
4NAME
5----
6perf-inject - Filter to augment the events stream with additional information
7
8SYNOPSIS
9--------
10[verse]
11'perf inject <options>'
12
13DESCRIPTION
14-----------
15perf-inject reads a perf-record event stream and repipes it to stdout. At any
16point the processing code can inject other events into the event stream - in
17this case build-ids (-b option) are read and injected as needed into the event
18stream.
19
20Build-ids are just the first user of perf-inject - potentially anything that
21needs userspace processing to augment the events stream with additional
22information could make use of this facility.
23
24OPTIONS
25-------
26-b::
27--build-ids=::
28 Inject build-ids into the output stream
29-v::
30--verbose::
31 Be more verbose.
32
33SEE ALSO
34--------
35linkperf:perf-record[1], linkperf:perf-report[1], linkperf:perf-archive[1]
diff --git a/tools/perf/Documentation/perf-kmem.txt b/tools/perf/Documentation/perf-kmem.txt
new file mode 100644
index 00000000000..a52fcde894c
--- /dev/null
+++ b/tools/perf/Documentation/perf-kmem.txt
@@ -0,0 +1,47 @@
1perf-kmem(1)
2============
3
4NAME
5----
6perf-kmem - Tool to trace/measure kernel memory(slab) properties
7
8SYNOPSIS
9--------
10[verse]
11'perf kmem' {record|stat} [<options>]
12
13DESCRIPTION
14-----------
15There are two variants of perf kmem:
16
17 'perf kmem record <command>' to record the kmem events
18 of an arbitrary workload.
19
20 'perf kmem stat' to report kernel memory statistics.
21
22OPTIONS
23-------
24-i <file>::
25--input=<file>::
26 Select the input file (default: perf.data)
27
28--caller::
29 Show per-callsite statistics
30
31--alloc::
32 Show per-allocation statistics
33
34-s <key[,key2...]>::
35--sort=<key[,key2...]>::
36 Sort the output (default: frag,hit,bytes)
37
38-l <num>::
39--line=<num>::
40 Print n lines only
41
42--raw-ip::
43 Print raw ip instead of symbol
44
45SEE ALSO
46--------
47linkperf:perf-record[1]
diff --git a/tools/perf/Documentation/perf-kvm.txt b/tools/perf/Documentation/perf-kvm.txt
new file mode 100644
index 00000000000..dd84cb2f0a8
--- /dev/null
+++ b/tools/perf/Documentation/perf-kvm.txt
@@ -0,0 +1,74 @@
1perf-kvm(1)
2===========
3
4NAME
5----
6perf-kvm - Tool to trace/measure kvm guest os
7
8SYNOPSIS
9--------
10[verse]
11'perf kvm' [--host] [--guest] [--guestmount=<path>
12 [--guestkallsyms=<path> --guestmodules=<path> | --guestvmlinux=<path>]]
13 {top|record|report|diff|buildid-list}
14'perf kvm' [--host] [--guest] [--guestkallsyms=<path> --guestmodules=<path>
15 | --guestvmlinux=<path>] {top|record|report|diff|buildid-list}
16
17DESCRIPTION
18-----------
19There are a couple of variants of perf kvm:
20
21 'perf kvm [options] top <command>' to generates and displays
22 a performance counter profile of guest os in realtime
23 of an arbitrary workload.
24
25 'perf kvm record <command>' to record the performance counter profile
26 of an arbitrary workload and save it into a perf data file. If both
27 --host and --guest are input, the perf data file name is perf.data.kvm.
28 If there is no --host but --guest, the file name is perf.data.guest.
29 If there is no --guest but --host, the file name is perf.data.host.
30
31 'perf kvm report' to display the performance counter profile information
32 recorded via perf kvm record.
33
34 'perf kvm diff' to displays the performance difference amongst two perf.data
35 files captured via perf record.
36
37 'perf kvm buildid-list' to display the buildids found in a perf data file,
38 so that other tools can be used to fetch packages with matching symbol tables
39 for use by perf report.
40
41OPTIONS
42-------
43-i::
44--input=::
45 Input file name.
46-o::
47--output::
48 Output file name.
49--host=::
50 Collect host side performance profile.
51--guest=::
52 Collect guest side performance profile.
53--guestmount=<path>::
54 Guest os root file system mount directory. Users mounts guest os
55 root directories under <path> by a specific filesystem access method,
56 typically, sshfs. For example, start 2 guest os. The one's pid is 8888
57 and the other's is 9999.
58 #mkdir ~/guestmount; cd ~/guestmount
59 #sshfs -o allow_other,direct_io -p 5551 localhost:/ 8888/
60 #sshfs -o allow_other,direct_io -p 5552 localhost:/ 9999/
61 #perf kvm --host --guest --guestmount=~/guestmount top
62--guestkallsyms=<path>::
63 Guest os /proc/kallsyms file copy. 'perf' kvm' reads it to get guest
64 kernel symbols. Users copy it out from guest os.
65--guestmodules=<path>::
66 Guest os /proc/modules file copy. 'perf' kvm' reads it to get guest
67 kernel module information. Users copy it out from guest os.
68--guestvmlinux=<path>::
69 Guest os kernel vmlinux.
70
71SEE ALSO
72--------
73linkperf:perf-top[1], linkperf:perf-record[1], linkperf:perf-report[1],
74linkperf:perf-diff[1], linkperf:perf-buildid-list[1]
diff --git a/tools/perf/Documentation/perf-list.txt b/tools/perf/Documentation/perf-list.txt
index 8290b942266..399751befee 100644
--- a/tools/perf/Documentation/perf-list.txt
+++ b/tools/perf/Documentation/perf-list.txt
@@ -15,6 +15,52 @@ 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
35RAW HARDWARE EVENT DESCRIPTOR
36-----------------------------
37Even when an event is not available in a symbolic form within perf right now,
38it can be encoded in a per processor specific way.
39
40For instance For x86 CPUs NNN represents the raw register encoding with the
41layout of IA32_PERFEVTSELx MSRs (see [Intel® 64 and IA-32 Architectures Software Developer's Manual Volume 3B: System Programming Guide] Figure 30-1 Layout
42of IA32_PERFEVTSELx MSRs) or AMD's PerfEvtSeln (see [AMD64 Architecture Programmer’s Manual Volume 2: System Programming], Page 344,
43Figure 13-7 Performance Event-Select Register (PerfEvtSeln)).
44
45Example:
46
47If the Intel docs for a QM720 Core i7 describe an event as:
48
49 Event Umask Event Mask
50 Num. Value Mnemonic Description Comment
51
52 A8H 01H LSD.UOPS Counts the number of micro-ops Use cmask=1 and
53 delivered by loop stream detector invert to count
54 cycles
55
56raw encoding of 0x1A8 can be used:
57
58 perf stat -e r1a8 -a sleep 1
59 perf record -e r1a8 ...
60
61You should refer to the processor specific documentation for getting these
62details. Some of them are referenced in the SEE ALSO section below.
63
18OPTIONS 64OPTIONS
19------- 65-------
20None 66None
@@ -22,4 +68,6 @@ None
22SEE ALSO 68SEE ALSO
23-------- 69--------
24linkperf:perf-stat[1], linkperf:perf-top[1], 70linkperf:perf-stat[1], linkperf:perf-top[1],
25linkperf:perf-record[1] 71linkperf:perf-record[1],
72http://www.intel.com/Assets/PDF/manual/253669.pdf[Intel® 64 and IA-32 Architectures Software Developer's Manual Volume 3B: System Programming Guide],
73http://support.amd.com/us/Processor_TechDocs/24593.pdf[AMD64 Architecture Programmer’s Manual Volume 2: System Programming]
diff --git a/tools/perf/Documentation/perf-lock.txt b/tools/perf/Documentation/perf-lock.txt
new file mode 100644
index 00000000000..921de259ea1
--- /dev/null
+++ b/tools/perf/Documentation/perf-lock.txt
@@ -0,0 +1,44 @@
1perf-lock(1)
2============
3
4NAME
5----
6perf-lock - Analyze lock events
7
8SYNOPSIS
9--------
10[verse]
11'perf lock' {record|report|trace}
12
13DESCRIPTION
14-----------
15You can analyze various lock behaviours
16and statistics with this 'perf lock' command.
17
18 'perf lock record <command>' records lock events
19 between start and end <command>. And this command
20 produces the file "perf.data" which contains tracing
21 results of lock events.
22
23 'perf lock trace' shows raw lock events.
24
25 'perf lock report' reports statistical data.
26
27OPTIONS
28-------
29
30-i::
31--input=<file>::
32 Input file name.
33
34-v::
35--verbose::
36 Be more verbose (show symbol address, etc).
37
38-D::
39--dump-raw-trace::
40 Dump raw trace in ASCII.
41
42SEE ALSO
43--------
44linkperf:perf[1]
diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt
new file mode 100644
index 00000000000..86b797a35aa
--- /dev/null
+++ b/tools/perf/Documentation/perf-probe.txt
@@ -0,0 +1,166 @@
1perf-probe(1)
2=============
3
4NAME
5----
6perf-probe - Define new dynamic tracepoints
7
8SYNOPSIS
9--------
10[verse]
11'perf probe' [options] --add='PROBE' [...]
12or
13'perf probe' [options] PROBE
14or
15'perf probe' [options] --del='[GROUP:]EVENT' [...]
16or
17'perf probe' --list
18or
19'perf probe' [options] --line='FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]'
20or
21'perf probe' [options] --vars='PROBEPOINT'
22
23DESCRIPTION
24-----------
25This command defines dynamic tracepoint events, by symbol and registers
26without debuginfo, or by C expressions (C line numbers, C function names,
27and C local variables) with debuginfo.
28
29
30OPTIONS
31-------
32-k::
33--vmlinux=PATH::
34 Specify vmlinux path which has debuginfo (Dwarf binary).
35
36-m::
37--module=MODNAME::
38 Specify module name in which perf-probe searches probe points
39 or lines.
40
41-s::
42--source=PATH::
43 Specify path to kernel source.
44
45-v::
46--verbose::
47 Be more verbose (show parsed arguments, etc).
48
49-a::
50--add=::
51 Define a probe event (see PROBE SYNTAX for detail).
52
53-d::
54--del=::
55 Delete probe events. This accepts glob wildcards('*', '?') and character
56 classes(e.g. [a-z], [!A-Z]).
57
58-l::
59--list::
60 List up current probe events.
61
62-L::
63--line=::
64 Show source code lines which can be probed. This needs an argument
65 which specifies a range of the source code. (see LINE SYNTAX for detail)
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
76-f::
77--force::
78 Forcibly add events with existing name.
79
80-n::
81--dry-run::
82 Dry run. With this option, --add and --del doesn't execute actual
83 adding and removal operations.
84
85--max-probes::
86 Set the maximum number of probe points for an event. Default is 128.
87
88PROBE SYNTAX
89------------
90Probe points are defined by following syntax.
91
92 1) Define event based on function name
93 [EVENT=]FUNC[@SRC][:RLN|+OFFS|%return|;PTN] [ARG ...]
94
95 2) Define event based on source file with line number
96 [EVENT=]SRC:ALN [ARG ...]
97
98 3) Define event based on source file with lazy pattern
99 [EVENT=]SRC;PTN [ARG ...]
100
101
102'EVENT' specifies the name of new event, if omitted, it will be set the name of the probed function. Currently, event group name is set as 'probe'.
103'FUNC' specifies a probed function name, and it may have one of the following options; '+OFFS' is the offset from function entry address in bytes, ':RLN' is the relative-line number from function entry line, and '%return' means that it probes function return. And ';PTN' means lazy matching pattern (see LAZY MATCHING). Note that ';PTN' must be the end of the probe point definition. In addition, '@SRC' specifies a source file which has that function.
104It is also possible to specify a probe point by the source line number or lazy matching by using 'SRC:ALN' or 'SRC;PTN' syntax, where 'SRC' is the source file path, ':ALN' is the line number and ';PTN' is the lazy matching pattern.
105'ARG' specifies the arguments of this probe point, (see PROBE ARGUMENT).
106
107PROBE ARGUMENT
108--------------
109Each probe argument follows below syntax.
110
111 [NAME=]LOCALVAR|$retval|%REG|@SYMBOL[:TYPE]
112
113'NAME' specifies the name of this argument (optional). You can use the name of local variable, local data structure member (e.g. var->field, var.field2), local array with fixed index (e.g. array[1], var->array[0], var->pointer[2]), or kprobe-tracer argument format (e.g. $retval, %ax, etc). Note that the name of this argument will be set as the last member name if you specify a local data structure member (e.g. field2 for 'var->field1.field2'.)
114'TYPE' casts the type of this argument (optional). If omitted, perf probe automatically set the type based on debuginfo. You can specify 'string' type only for the local variable or structure member which is an array of or a pointer to 'char' or 'unsigned char' type.
115
116LINE SYNTAX
117-----------
118Line range is described by following syntax.
119
120 "FUNC[:RLN[+NUM|-RLN2]]|SRC[:ALN[+NUM|-ALN2]]"
121
122FUNC specifies the function name of showing lines. 'RLN' is the start line
123number from function entry line, and 'RLN2' is the end line number. As same as
124probe syntax, 'SRC' means the source file path, 'ALN' is start line number,
125and 'ALN2' is end line number in the file. It is also possible to specify how
126many lines to show by using 'NUM'.
127So, "source.c:100-120" shows lines between 100th to l20th in source.c file. And "func:10+20" shows 20 lines from 10th line of func function.
128
129LAZY MATCHING
130-------------
131 The lazy line matching is similar to glob matching but ignoring spaces in both of pattern and target. So this accepts wildcards('*', '?') and character classes(e.g. [a-z], [!A-Z]).
132
133e.g.
134 'a=*' can matches 'a=b', 'a = b', 'a == b' and so on.
135
136This provides some sort of flexibility and robustness to probe point definitions against minor code changes. For example, actual 10th line of schedule() can be moved easily by modifying schedule(), but the same line matching 'rq=cpu_rq*' may still exist in the function.)
137
138
139EXAMPLES
140--------
141Display which lines in schedule() can be probed:
142
143 ./perf probe --line schedule
144
145Add a probe on schedule() function 12th line with recording cpu local variable:
146
147 ./perf probe schedule:12 cpu
148 or
149 ./perf probe --add='schedule:12 cpu'
150
151 this will add one or more probes which has the name start with "schedule".
152
153 Add probes on lines in schedule() function which calls update_rq_clock().
154
155 ./perf probe 'schedule;update_rq_clock*'
156 or
157 ./perf probe --add='schedule;update_rq_clock*'
158
159Delete all probes on schedule().
160
161 ./perf probe --del='schedule*'
162
163
164SEE ALSO
165--------
166linkperf:perf-trace[1], linkperf:perf-record[1]
diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt
index 1dbc1eeb4c0..52462ae2645 100644
--- a/tools/perf/Documentation/perf-record.txt
+++ b/tools/perf/Documentation/perf-record.txt
@@ -26,16 +26,113 @@ OPTIONS
26 26
27-e:: 27-e::
28--event=:: 28--event=::
29 Select the PMU event. Selection can be a symbolic event name 29 Select the PMU event. Selection can be:
30 (use 'perf list' to list all events) or a raw PMU 30
31 event (eventsel+umask) in the form of rNNN where NNN is a 31 - a symbolic event name (use 'perf list' to list all events)
32 hexadecimal event descriptor. 32
33 - a raw PMU event (eventsel+umask) in the form of rNNN where NNN is a
34 hexadecimal event descriptor.
35
36 - a hardware breakpoint event in the form of '\mem:addr[:access]'
37 where addr is the address in memory you want to break in.
38 Access is the memory access type (read, write, execute) it can
39 be passed as follows: '\mem:addr[:[r][w][x]]'.
40 If you want to profile read-write accesses in 0x1000, just set
41 'mem:0x1000:rw'.
42
43--filter=<filter>::
44 Event filter.
33 45
34-a:: 46-a::
35 system-wide collection 47--all-cpus::
48 System-wide collection from all CPUs.
36 49
37-l:: 50-l::
38 scale counter values 51 Scale counter values.
52
53-p::
54--pid=::
55 Record events on existing process ID.
56
57-t::
58--tid=::
59 Record events on existing thread ID.
60
61-r::
62--realtime=::
63 Collect data with this RT SCHED_FIFO priority.
64-A::
65--append::
66 Append to the output file to do incremental profiling.
67
68-f::
69--force::
70 Overwrite existing data file. (deprecated)
71
72-c::
73--count=::
74 Event period to sample.
75
76-o::
77--output=::
78 Output file name.
79
80-i::
81--no-inherit::
82 Child tasks do not inherit counters.
83-F::
84--freq=::
85 Profile at this frequency.
86
87-m::
88--mmap-pages=::
89 Number of mmap data pages.
90
91-g::
92--call-graph::
93 Do call-graph (stack chain/backtrace) recording.
94
95-q::
96--quiet::
97 Don't print any message, useful for scripting.
98
99-v::
100--verbose::
101 Be more verbose (show counter open errors, etc).
102
103-s::
104--stat::
105 Per thread counts.
106
107-d::
108--data::
109 Sample addresses.
110
111-T::
112--timestamp::
113 Sample timestamps. Use it with 'perf report -D' to see the timestamps,
114 for instance.
115
116-n::
117--no-samples::
118 Don't sample.
119
120-R::
121--raw-samples::
122Collect raw sample records from all opened counters (default for tracepoint counters).
123
124-C::
125--cpu::
126Collect samples only on the list of CPUs provided. Multiple CPUs can be provided as a
127comma-separated list with no space: 0,1. Ranges of CPUs are specified with -: 0-2.
128In per-thread mode with inheritance mode on (default), samples are captured only when
129the thread executes on the designated CPUs. Default is to monitor all CPUs.
130
131-N::
132--no-buildid-cache::
133Do not update the builid cache. This saves some overhead in situations
134where the information in the perf.data file (which includes buildids)
135is sufficient.
39 136
40SEE ALSO 137SEE ALSO
41-------- 138--------
diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt
index 52d3fc6846a..8ba03d6e539 100644
--- a/tools/perf/Documentation/perf-report.txt
+++ b/tools/perf/Documentation/perf-report.txt
@@ -13,7 +13,7 @@ SYNOPSIS
13DESCRIPTION 13DESCRIPTION
14----------- 14-----------
15This command displays the performance counter profile information recorded 15This command displays the performance counter profile information recorded
16via perf report. 16via perf record.
17 17
18OPTIONS 18OPTIONS
19------- 19-------
@@ -21,6 +21,104 @@ OPTIONS
21--input=:: 21--input=::
22 Input file name. (default: perf.data) 22 Input file name. (default: perf.data)
23 23
24-v::
25--verbose::
26 Be more verbose. (show symbol address, etc)
27
28-d::
29--dsos=::
30 Only consider symbols in these dsos. CSV that understands
31 file://filename entries.
32-n::
33--show-nr-samples::
34 Show the number of samples for each symbol
35
36--showcpuutilization::
37 Show sample percentage for different cpu modes.
38
39-T::
40--threads::
41 Show per-thread event counters
42-C::
43--comms=::
44 Only consider symbols in these comms. CSV that understands
45 file://filename entries.
46-S::
47--symbols=::
48 Only consider these symbols. CSV that understands
49 file://filename entries.
50
51-U::
52--hide-unresolved::
53 Only display entries resolved to a symbol.
54
55-s::
56--sort=::
57 Sort by key(s): pid, comm, dso, symbol, parent.
58
59-p::
60--parent=<regex>::
61 regex filter to identify parent, see: '--sort parent'
62
63-x::
64--exclude-other::
65 Only display entries with parent-match.
66
67-w::
68--column-widths=<width[,width...]>::
69 Force each column width to the provided list, for large terminal
70 readability.
71
72-t::
73--field-separator=::
74
75 Use a special separator character and don't pad with spaces, replacing
76 all occurrences of this separator in symbol names (and other output)
77 with a '.' character, that thus it's the only non valid separator.
78
79-D::
80--dump-raw-trace::
81 Dump raw trace in ASCII.
82
83-g [type,min]::
84--call-graph::
85 Display call chains using type and min percent threshold.
86 type can be either:
87 - flat: single column, linear exposure of call chains.
88 - graph: use a graph tree, displaying absolute overhead rates.
89 - fractal: like graph, but displays relative rates. Each branch of
90 the tree is considered as a new profiled object. +
91 Default: fractal,0.5.
92
93--pretty=<key>::
94 Pretty printing style. key: normal, raw
95
96--stdio:: Use the stdio interface.
97
98--tui:: Use the TUI interface, that is integrated with annotate and allows
99 zooming into DSOs or threads, among other features. Use of --tui
100 requires a tty, if one is not present, as when piping to other
101 commands, the stdio interface is used.
102
103-k::
104--vmlinux=<file>::
105 vmlinux pathname
106
107--kallsyms=<file>::
108 kallsyms pathname
109
110-m::
111--modules::
112 Load module symbols. WARNING: This should only be used with -k and
113 a LIVE kernel.
114
115-f::
116--force::
117 Don't complain, do it.
118
119--symfs=<directory>::
120 Look for files with symbols relative to this directory.
121
24SEE ALSO 122SEE ALSO
25-------- 123--------
26linkperf:perf-stat[1] 124linkperf:perf-stat[1]
diff --git a/tools/perf/Documentation/perf-sched.txt b/tools/perf/Documentation/perf-sched.txt
new file mode 100644
index 00000000000..46822d5fde1
--- /dev/null
+++ b/tools/perf/Documentation/perf-sched.txt
@@ -0,0 +1,55 @@
1perf-sched(1)
2==============
3
4NAME
5----
6perf-sched - Tool to trace/measure scheduler properties (latencies)
7
8SYNOPSIS
9--------
10[verse]
11'perf sched' {record|latency|map|replay|trace}
12
13DESCRIPTION
14-----------
15There are five variants of perf sched:
16
17 'perf sched record <command>' to record the scheduling events
18 of an arbitrary workload.
19
20 'perf sched latency' to report the per task scheduling latencies
21 and other scheduling properties of the workload.
22
23 'perf sched trace' to see a detailed trace of the workload that
24 was recorded.
25
26 'perf sched replay' to simulate the workload that was recorded
27 via perf sched record. (this is done by starting up mockup threads
28 that mimic the workload based on the events in the trace. These
29 threads can then replay the timings (CPU runtime and sleep patterns)
30 of the workload as it occurred when it was recorded - and can repeat
31 it a number of times, measuring its performance.)
32
33 'perf sched map' to print a textual context-switching outline of
34 workload captured via perf sched record. Columns stand for
35 individual CPUs, and the two-letter shortcuts stand for tasks that
36 are running on a CPU. A '*' denotes the CPU that had the event, and
37 a dot signals an idle CPU.
38
39OPTIONS
40-------
41-i::
42--input=<file>::
43 Input file name. (default: perf.data)
44
45-v::
46--verbose::
47 Be more verbose. (show symbol address, etc)
48
49-D::
50--dump-raw-trace=::
51 Display verbose dump of the sched data.
52
53SEE ALSO
54--------
55linkperf:perf-record[1]
diff --git a/tools/perf/Documentation/perf-script-perl.txt b/tools/perf/Documentation/perf-script-perl.txt
new file mode 100644
index 00000000000..5bb41e55a3a
--- /dev/null
+++ b/tools/perf/Documentation/perf-script-perl.txt
@@ -0,0 +1,217 @@
1perf-script-perl(1)
2==================
3
4NAME
5----
6perf-script-perl - Process trace data with a Perl script
7
8SYNOPSIS
9--------
10[verse]
11'perf script' [-s [Perl]:script[.pl] ]
12
13DESCRIPTION
14-----------
15
16This perf script option is used to process perf script data using perf's
17built-in Perl interpreter. It reads and processes the input file and
18displays the results of the trace analysis implemented in the given
19Perl script, if any.
20
21STARTER SCRIPTS
22---------------
23
24You can avoid reading the rest of this document by running 'perf script
25-g perl' in the same directory as an existing perf.data trace file.
26That will generate a starter script containing a handler for each of
27the event types in the trace file; it simply prints every available
28field for each event in the trace file.
29
30You can also look at the existing scripts in
31~/libexec/perf-core/scripts/perl for typical examples showing how to
32do basic things like aggregate event data, print results, etc. Also,
33the check-perf-script.pl script, while not interesting for its results,
34attempts to exercise all of the main scripting features.
35
36EVENT HANDLERS
37--------------
38
39When perf script is invoked using a trace script, a user-defined
40'handler function' is called for each event in the trace. If there's
41no handler function defined for a given event type, the event is
42ignored (or passed to a 'trace_handled' function, see below) and the
43next event is processed.
44
45Most of the event's field values are passed as arguments to the
46handler function; some of the less common ones aren't - those are
47available as calls back into the perf executable (see below).
48
49As an example, the following perf record command can be used to record
50all sched_wakeup events in the system:
51
52 # perf record -a -e sched:sched_wakeup
53
54Traces meant to be processed using a script should be recorded with
55the above option: -a to enable system-wide collection.
56
57The format file for the sched_wakep event defines the following fields
58(see /sys/kernel/debug/tracing/events/sched/sched_wakeup/format):
59
60----
61 format:
62 field:unsigned short common_type;
63 field:unsigned char common_flags;
64 field:unsigned char common_preempt_count;
65 field:int common_pid;
66 field:int common_lock_depth;
67
68 field:char comm[TASK_COMM_LEN];
69 field:pid_t pid;
70 field:int prio;
71 field:int success;
72 field:int target_cpu;
73----
74
75The handler function for this event would be defined as:
76
77----
78sub sched::sched_wakeup
79{
80 my ($event_name, $context, $common_cpu, $common_secs,
81 $common_nsecs, $common_pid, $common_comm,
82 $comm, $pid, $prio, $success, $target_cpu) = @_;
83}
84----
85
86The handler function takes the form subsystem::event_name.
87
88The $common_* arguments in the handler's argument list are the set of
89arguments passed to all event handlers; some of the fields correspond
90to the common_* fields in the format file, but some are synthesized,
91and some of the common_* fields aren't common enough to to be passed
92to every event as arguments but are available as library functions.
93
94Here's a brief description of each of the invariant event args:
95
96 $event_name the name of the event as text
97 $context an opaque 'cookie' used in calls back into perf
98 $common_cpu the cpu the event occurred on
99 $common_secs the secs portion of the event timestamp
100 $common_nsecs the nsecs portion of the event timestamp
101 $common_pid the pid of the current task
102 $common_comm the name of the current process
103
104All of the remaining fields in the event's format file have
105counterparts as handler function arguments of the same name, as can be
106seen in the example above.
107
108The above provides the basics needed to directly access every field of
109every event in a trace, which covers 90% of what you need to know to
110write a useful trace script. The sections below cover the rest.
111
112SCRIPT LAYOUT
113-------------
114
115Every perf script Perl script should start by setting up a Perl module
116search path and 'use'ing a few support modules (see module
117descriptions below):
118
119----
120 use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/perf-script-Util/lib";
121 use lib "./perf-script-Util/lib";
122 use Perf::Trace::Core;
123 use Perf::Trace::Context;
124 use Perf::Trace::Util;
125----
126
127The rest of the script can contain handler functions and support
128functions in any order.
129
130Aside from the event handler functions discussed above, every script
131can implement a set of optional functions:
132
133*trace_begin*, if defined, is called before any event is processed and
134gives scripts a chance to do setup tasks:
135
136----
137 sub trace_begin
138 {
139 }
140----
141
142*trace_end*, if defined, is called after all events have been
143 processed and gives scripts a chance to do end-of-script tasks, such
144 as display results:
145
146----
147sub trace_end
148{
149}
150----
151
152*trace_unhandled*, if defined, is called after for any event that
153 doesn't have a handler explicitly defined for it. The standard set
154 of common arguments are passed into it:
155
156----
157sub trace_unhandled
158{
159 my ($event_name, $context, $common_cpu, $common_secs,
160 $common_nsecs, $common_pid, $common_comm) = @_;
161}
162----
163
164The remaining sections provide descriptions of each of the available
165built-in perf script Perl modules and their associated functions.
166
167AVAILABLE MODULES AND FUNCTIONS
168-------------------------------
169
170The following sections describe the functions and variables available
171via the various Perf::Trace::* Perl modules. To use the functions and
172variables from the given module, add the corresponding 'use
173Perf::Trace::XXX' line to your perf script script.
174
175Perf::Trace::Core Module
176~~~~~~~~~~~~~~~~~~~~~~~~
177
178These functions provide some essential functions to user scripts.
179
180The *flag_str* and *symbol_str* functions provide human-readable
181strings for flag and symbolic fields. These correspond to the strings
182and values parsed from the 'print fmt' fields of the event format
183files:
184
185 flag_str($event_name, $field_name, $field_value) - returns the string represention corresponding to $field_value for the flag field $field_name of event $event_name
186 symbol_str($event_name, $field_name, $field_value) - returns the string represention corresponding to $field_value for the symbolic field $field_name of event $event_name
187
188Perf::Trace::Context Module
189~~~~~~~~~~~~~~~~~~~~~~~~~~~
190
191Some of the 'common' fields in the event format file aren't all that
192common, but need to be made accessible to user scripts nonetheless.
193
194Perf::Trace::Context defines a set of functions that can be used to
195access this data in the context of the current event. Each of these
196functions expects a $context variable, which is the same as the
197$context variable passed into every event handler as the second
198argument.
199
200 common_pc($context) - returns common_preempt count for the current event
201 common_flags($context) - returns common_flags for the current event
202 common_lock_depth($context) - returns common_lock_depth for the current event
203
204Perf::Trace::Util Module
205~~~~~~~~~~~~~~~~~~~~~~~~
206
207Various utility functions for use with perf script:
208
209 nsecs($secs, $nsecs) - returns total nsecs given secs/nsecs pair
210 nsecs_secs($nsecs) - returns whole secs portion given nsecs
211 nsecs_nsecs($nsecs) - returns nsecs remainder given nsecs
212 nsecs_str($nsecs) - returns printable string in the form secs.nsecs
213 avg($total, $n) - returns average given a sum and a total number of values
214
215SEE ALSO
216--------
217linkperf:perf-script[1]
diff --git a/tools/perf/Documentation/perf-script-python.txt b/tools/perf/Documentation/perf-script-python.txt
new file mode 100644
index 00000000000..36b38277422
--- /dev/null
+++ b/tools/perf/Documentation/perf-script-python.txt
@@ -0,0 +1,623 @@
1perf-script-python(1)
2====================
3
4NAME
5----
6perf-script-python - Process trace data with a Python script
7
8SYNOPSIS
9--------
10[verse]
11'perf script' [-s [Python]:script[.py] ]
12
13DESCRIPTION
14-----------
15
16This perf script option is used to process perf script data using perf's
17built-in Python interpreter. It reads and processes the input file and
18displays the results of the trace analysis implemented in the given
19Python script, if any.
20
21A QUICK EXAMPLE
22---------------
23
24This section shows the process, start to finish, of creating a working
25Python script that aggregates and extracts useful information from a
26raw perf script stream. You can avoid reading the rest of this
27document if an example is enough for you; the rest of the document
28provides more details on each step and lists the library functions
29available to script writers.
30
31This example actually details the steps that were used to create the
32'syscall-counts' script you see when you list the available perf script
33scripts via 'perf script -l'. As such, this script also shows how to
34integrate your script into the list of general-purpose 'perf script'
35scripts listed by that command.
36
37The syscall-counts script is a simple script, but demonstrates all the
38basic ideas necessary to create a useful script. Here's an example
39of its output (syscall names are not yet supported, they will appear
40as numbers):
41
42----
43syscall events:
44
45event count
46---------------------------------------- -----------
47sys_write 455067
48sys_getdents 4072
49sys_close 3037
50sys_swapoff 1769
51sys_read 923
52sys_sched_setparam 826
53sys_open 331
54sys_newfstat 326
55sys_mmap 217
56sys_munmap 216
57sys_futex 141
58sys_select 102
59sys_poll 84
60sys_setitimer 12
61sys_writev 8
6215 8
63sys_lseek 7
64sys_rt_sigprocmask 6
65sys_wait4 3
66sys_ioctl 3
67sys_set_robust_list 1
68sys_exit 1
6956 1
70sys_access 1
71----
72
73Basically our task is to keep a per-syscall tally that gets updated
74every time a system call occurs in the system. Our script will do
75that, but first we need to record the data that will be processed by
76that script. Theoretically, there are a couple of ways we could do
77that:
78
79- we could enable every event under the tracing/events/syscalls
80 directory, but this is over 600 syscalls, well beyond the number
81 allowable by perf. These individual syscall events will however be
82 useful if we want to later use the guidance we get from the
83 general-purpose scripts to drill down and get more detail about
84 individual syscalls of interest.
85
86- we can enable the sys_enter and/or sys_exit syscalls found under
87 tracing/events/raw_syscalls. These are called for all syscalls; the
88 'id' field can be used to distinguish between individual syscall
89 numbers.
90
91For this script, we only need to know that a syscall was entered; we
92don't care how it exited, so we'll use 'perf record' to record only
93the sys_enter events:
94
95----
96# perf record -a -e raw_syscalls:sys_enter
97
98^C[ perf record: Woken up 1 times to write data ]
99[ perf record: Captured and wrote 56.545 MB perf.data (~2470503 samples) ]
100----
101
102The options basically say to collect data for every syscall event
103system-wide and multiplex the per-cpu output into a single stream.
104That single stream will be recorded in a file in the current directory
105called perf.data.
106
107Once we have a perf.data file containing our data, we can use the -g
108'perf script' option to generate a Python script that will contain a
109callback handler for each event type found in the perf.data trace
110stream (for more details, see the STARTER SCRIPTS section).
111
112----
113# perf script -g python
114generated Python script: perf-script.py
115
116The output file created also in the current directory is named
117perf-script.py. Here's the file in its entirety:
118
119# perf script event handlers, generated by perf script -g python
120# Licensed under the terms of the GNU GPL License version 2
121
122# The common_* event handler fields are the most useful fields common to
123# all events. They don't necessarily correspond to the 'common_*' fields
124# in the format files. Those fields not available as handler params can
125# be retrieved using Python functions of the form common_*(context).
126# See the perf-script-python Documentation for the list of available functions.
127
128import os
129import sys
130
131sys.path.append(os.environ['PERF_EXEC_PATH'] + \
132 '/scripts/python/perf-script-Util/lib/Perf/Trace')
133
134from perf_trace_context import *
135from Core import *
136
137def trace_begin():
138 print "in trace_begin"
139
140def trace_end():
141 print "in trace_end"
142
143def raw_syscalls__sys_enter(event_name, context, common_cpu,
144 common_secs, common_nsecs, common_pid, common_comm,
145 id, args):
146 print_header(event_name, common_cpu, common_secs, common_nsecs,
147 common_pid, common_comm)
148
149 print "id=%d, args=%s\n" % \
150 (id, args),
151
152def trace_unhandled(event_name, context, common_cpu, common_secs, common_nsecs,
153 common_pid, common_comm):
154 print_header(event_name, common_cpu, common_secs, common_nsecs,
155 common_pid, common_comm)
156
157def print_header(event_name, cpu, secs, nsecs, pid, comm):
158 print "%-20s %5u %05u.%09u %8u %-20s " % \
159 (event_name, cpu, secs, nsecs, pid, comm),
160----
161
162At the top is a comment block followed by some import statements and a
163path append which every perf script script should include.
164
165Following that are a couple generated functions, trace_begin() and
166trace_end(), which are called at the beginning and the end of the
167script respectively (for more details, see the SCRIPT_LAYOUT section
168below).
169
170Following those are the 'event handler' functions generated one for
171every event in the 'perf record' output. The handler functions take
172the form subsystem__event_name, and contain named parameters, one for
173each field in the event; in this case, there's only one event,
174raw_syscalls__sys_enter(). (see the EVENT HANDLERS section below for
175more info on event handlers).
176
177The final couple of functions are, like the begin and end functions,
178generated for every script. The first, trace_unhandled(), is called
179every time the script finds an event in the perf.data file that
180doesn't correspond to any event handler in the script. This could
181mean either that the record step recorded event types that it wasn't
182really interested in, or the script was run against a trace file that
183doesn't correspond to the script.
184
185The script generated by -g option simply prints a line for each
186event found in the trace stream i.e. it basically just dumps the event
187and its parameter values to stdout. The print_header() function is
188simply a utility function used for that purpose. Let's rename the
189script and run it to see the default output:
190
191----
192# mv perf-script.py syscall-counts.py
193# perf script -s syscall-counts.py
194
195raw_syscalls__sys_enter 1 00840.847582083 7506 perf id=1, args=
196raw_syscalls__sys_enter 1 00840.847595764 7506 perf id=1, args=
197raw_syscalls__sys_enter 1 00840.847620860 7506 perf id=1, args=
198raw_syscalls__sys_enter 1 00840.847710478 6533 npviewer.bin id=78, args=
199raw_syscalls__sys_enter 1 00840.847719204 6533 npviewer.bin id=142, args=
200raw_syscalls__sys_enter 1 00840.847755445 6533 npviewer.bin id=3, args=
201raw_syscalls__sys_enter 1 00840.847775601 6533 npviewer.bin id=3, args=
202raw_syscalls__sys_enter 1 00840.847781820 6533 npviewer.bin id=3, args=
203.
204.
205.
206----
207
208Of course, for this script, we're not interested in printing every
209trace event, but rather aggregating it in a useful way. So we'll get
210rid of everything to do with printing as well as the trace_begin() and
211trace_unhandled() functions, which we won't be using. That leaves us
212with this minimalistic skeleton:
213
214----
215import os
216import sys
217
218sys.path.append(os.environ['PERF_EXEC_PATH'] + \
219 '/scripts/python/perf-script-Util/lib/Perf/Trace')
220
221from perf_trace_context import *
222from Core import *
223
224def trace_end():
225 print "in trace_end"
226
227def raw_syscalls__sys_enter(event_name, context, common_cpu,
228 common_secs, common_nsecs, common_pid, common_comm,
229 id, args):
230----
231
232In trace_end(), we'll simply print the results, but first we need to
233generate some results to print. To do that we need to have our
234sys_enter() handler do the necessary tallying until all events have
235been counted. A hash table indexed by syscall id is a good way to
236store that information; every time the sys_enter() handler is called,
237we simply increment a count associated with that hash entry indexed by
238that syscall id:
239
240----
241 syscalls = autodict()
242
243 try:
244 syscalls[id] += 1
245 except TypeError:
246 syscalls[id] = 1
247----
248
249The syscalls 'autodict' object is a special kind of Python dictionary
250(implemented in Core.py) that implements Perl's 'autovivifying' hashes
251in Python i.e. with autovivifying hashes, you can assign nested hash
252values without having to go to the trouble of creating intermediate
253levels if they don't exist e.g syscalls[comm][pid][id] = 1 will create
254the intermediate hash levels and finally assign the value 1 to the
255hash entry for 'id' (because the value being assigned isn't a hash
256object itself, the initial value is assigned in the TypeError
257exception. Well, there may be a better way to do this in Python but
258that's what works for now).
259
260Putting that code into the raw_syscalls__sys_enter() handler, we
261effectively end up with a single-level dictionary keyed on syscall id
262and having the counts we've tallied as values.
263
264The print_syscall_totals() function iterates over the entries in the
265dictionary and displays a line for each entry containing the syscall
266name (the dictonary keys contain the syscall ids, which are passed to
267the Util function syscall_name(), which translates the raw syscall
268numbers to the corresponding syscall name strings). The output is
269displayed after all the events in the trace have been processed, by
270calling the print_syscall_totals() function from the trace_end()
271handler called at the end of script processing.
272
273The final script producing the output shown above is shown in its
274entirety below (syscall_name() helper is not yet available, you can
275only deal with id's for now):
276
277----
278import os
279import sys
280
281sys.path.append(os.environ['PERF_EXEC_PATH'] + \
282 '/scripts/python/perf-script-Util/lib/Perf/Trace')
283
284from perf_trace_context import *
285from Core import *
286from Util import *
287
288syscalls = autodict()
289
290def trace_end():
291 print_syscall_totals()
292
293def raw_syscalls__sys_enter(event_name, context, common_cpu,
294 common_secs, common_nsecs, common_pid, common_comm,
295 id, args):
296 try:
297 syscalls[id] += 1
298 except TypeError:
299 syscalls[id] = 1
300
301def print_syscall_totals():
302 if for_comm is not None:
303 print "\nsyscall events for %s:\n\n" % (for_comm),
304 else:
305 print "\nsyscall events:\n\n",
306
307 print "%-40s %10s\n" % ("event", "count"),
308 print "%-40s %10s\n" % ("----------------------------------------", \
309 "-----------"),
310
311 for id, val in sorted(syscalls.iteritems(), key = lambda(k, v): (v, k), \
312 reverse = True):
313 print "%-40s %10d\n" % (syscall_name(id), val),
314----
315
316The script can be run just as before:
317
318 # perf script -s syscall-counts.py
319
320So those are the essential steps in writing and running a script. The
321process can be generalized to any tracepoint or set of tracepoints
322you're interested in - basically find the tracepoint(s) you're
323interested in by looking at the list of available events shown by
324'perf list' and/or look in /sys/kernel/debug/tracing events for
325detailed event and field info, record the corresponding trace data
326using 'perf record', passing it the list of interesting events,
327generate a skeleton script using 'perf script -g python' and modify the
328code to aggregate and display it for your particular needs.
329
330After you've done that you may end up with a general-purpose script
331that you want to keep around and have available for future use. By
332writing a couple of very simple shell scripts and putting them in the
333right place, you can have your script listed alongside the other
334scripts listed by the 'perf script -l' command e.g.:
335
336----
337root@tropicana:~# perf script -l
338List of available trace scripts:
339 workqueue-stats workqueue stats (ins/exe/create/destroy)
340 wakeup-latency system-wide min/max/avg wakeup latency
341 rw-by-file <comm> r/w activity for a program, by file
342 rw-by-pid system-wide r/w activity
343----
344
345A nice side effect of doing this is that you also then capture the
346probably lengthy 'perf record' command needed to record the events for
347the script.
348
349To have the script appear as a 'built-in' script, you write two simple
350scripts, one for recording and one for 'reporting'.
351
352The 'record' script is a shell script with the same base name as your
353script, but with -record appended. The shell script should be put
354into the perf/scripts/python/bin directory in the kernel source tree.
355In that script, you write the 'perf record' command-line needed for
356your script:
357
358----
359# cat kernel-source/tools/perf/scripts/python/bin/syscall-counts-record
360
361#!/bin/bash
362perf record -a -e raw_syscalls:sys_enter
363----
364
365The 'report' script is also a shell script with the same base name as
366your script, but with -report appended. It should also be located in
367the perf/scripts/python/bin directory. In that script, you write the
368'perf script -s' command-line needed for running your script:
369
370----
371# cat kernel-source/tools/perf/scripts/python/bin/syscall-counts-report
372
373#!/bin/bash
374# description: system-wide syscall counts
375perf script -s ~/libexec/perf-core/scripts/python/syscall-counts.py
376----
377
378Note that the location of the Python script given in the shell script
379is in the libexec/perf-core/scripts/python directory - this is where
380the script will be copied by 'make install' when you install perf.
381For the installation to install your script there, your script needs
382to be located in the perf/scripts/python directory in the kernel
383source tree:
384
385----
386# ls -al kernel-source/tools/perf/scripts/python
387
388root@tropicana:/home/trz/src/tip# ls -al tools/perf/scripts/python
389total 32
390drwxr-xr-x 4 trz trz 4096 2010-01-26 22:30 .
391drwxr-xr-x 4 trz trz 4096 2010-01-26 22:29 ..
392drwxr-xr-x 2 trz trz 4096 2010-01-26 22:29 bin
393-rw-r--r-- 1 trz trz 2548 2010-01-26 22:29 check-perf-script.py
394drwxr-xr-x 3 trz trz 4096 2010-01-26 22:49 perf-script-Util
395-rw-r--r-- 1 trz trz 1462 2010-01-26 22:30 syscall-counts.py
396----
397
398Once you've done that (don't forget to do a new 'make install',
399otherwise your script won't show up at run-time), 'perf script -l'
400should show a new entry for your script:
401
402----
403root@tropicana:~# perf script -l
404List of available trace scripts:
405 workqueue-stats workqueue stats (ins/exe/create/destroy)
406 wakeup-latency system-wide min/max/avg wakeup latency
407 rw-by-file <comm> r/w activity for a program, by file
408 rw-by-pid system-wide r/w activity
409 syscall-counts system-wide syscall counts
410----
411
412You can now perform the record step via 'perf script record':
413
414 # perf script record syscall-counts
415
416and display the output using 'perf script report':
417
418 # perf script report syscall-counts
419
420STARTER SCRIPTS
421---------------
422
423You can quickly get started writing a script for a particular set of
424trace data by generating a skeleton script using 'perf script -g
425python' in the same directory as an existing perf.data trace file.
426That will generate a starter script containing a handler for each of
427the event types in the trace file; it simply prints every available
428field for each event in the trace file.
429
430You can also look at the existing scripts in
431~/libexec/perf-core/scripts/python for typical examples showing how to
432do basic things like aggregate event data, print results, etc. Also,
433the check-perf-script.py script, while not interesting for its results,
434attempts to exercise all of the main scripting features.
435
436EVENT HANDLERS
437--------------
438
439When perf script is invoked using a trace script, a user-defined
440'handler function' is called for each event in the trace. If there's
441no handler function defined for a given event type, the event is
442ignored (or passed to a 'trace_handled' function, see below) and the
443next event is processed.
444
445Most of the event's field values are passed as arguments to the
446handler function; some of the less common ones aren't - those are
447available as calls back into the perf executable (see below).
448
449As an example, the following perf record command can be used to record
450all sched_wakeup events in the system:
451
452 # perf record -a -e sched:sched_wakeup
453
454Traces meant to be processed using a script should be recorded with
455the above option: -a to enable system-wide collection.
456
457The format file for the sched_wakep event defines the following fields
458(see /sys/kernel/debug/tracing/events/sched/sched_wakeup/format):
459
460----
461 format:
462 field:unsigned short common_type;
463 field:unsigned char common_flags;
464 field:unsigned char common_preempt_count;
465 field:int common_pid;
466 field:int common_lock_depth;
467
468 field:char comm[TASK_COMM_LEN];
469 field:pid_t pid;
470 field:int prio;
471 field:int success;
472 field:int target_cpu;
473----
474
475The handler function for this event would be defined as:
476
477----
478def sched__sched_wakeup(event_name, context, common_cpu, common_secs,
479 common_nsecs, common_pid, common_comm,
480 comm, pid, prio, success, target_cpu):
481 pass
482----
483
484The handler function takes the form subsystem__event_name.
485
486The common_* arguments in the handler's argument list are the set of
487arguments passed to all event handlers; some of the fields correspond
488to the common_* fields in the format file, but some are synthesized,
489and some of the common_* fields aren't common enough to to be passed
490to every event as arguments but are available as library functions.
491
492Here's a brief description of each of the invariant event args:
493
494 event_name the name of the event as text
495 context an opaque 'cookie' used in calls back into perf
496 common_cpu the cpu the event occurred on
497 common_secs the secs portion of the event timestamp
498 common_nsecs the nsecs portion of the event timestamp
499 common_pid the pid of the current task
500 common_comm the name of the current process
501
502All of the remaining fields in the event's format file have
503counterparts as handler function arguments of the same name, as can be
504seen in the example above.
505
506The above provides the basics needed to directly access every field of
507every event in a trace, which covers 90% of what you need to know to
508write a useful trace script. The sections below cover the rest.
509
510SCRIPT LAYOUT
511-------------
512
513Every perf script Python script should start by setting up a Python
514module search path and 'import'ing a few support modules (see module
515descriptions below):
516
517----
518 import os
519 import sys
520
521 sys.path.append(os.environ['PERF_EXEC_PATH'] + \
522 '/scripts/python/perf-script-Util/lib/Perf/Trace')
523
524 from perf_trace_context import *
525 from Core import *
526----
527
528The rest of the script can contain handler functions and support
529functions in any order.
530
531Aside from the event handler functions discussed above, every script
532can implement a set of optional functions:
533
534*trace_begin*, if defined, is called before any event is processed and
535gives scripts a chance to do setup tasks:
536
537----
538def trace_begin:
539 pass
540----
541
542*trace_end*, if defined, is called after all events have been
543 processed and gives scripts a chance to do end-of-script tasks, such
544 as display results:
545
546----
547def trace_end:
548 pass
549----
550
551*trace_unhandled*, if defined, is called after for any event that
552 doesn't have a handler explicitly defined for it. The standard set
553 of common arguments are passed into it:
554
555----
556def trace_unhandled(event_name, context, common_cpu, common_secs,
557 common_nsecs, common_pid, common_comm):
558 pass
559----
560
561The remaining sections provide descriptions of each of the available
562built-in perf script Python modules and their associated functions.
563
564AVAILABLE MODULES AND FUNCTIONS
565-------------------------------
566
567The following sections describe the functions and variables available
568via the various perf script Python modules. To use the functions and
569variables from the given module, add the corresponding 'from XXXX
570import' line to your perf script script.
571
572Core.py Module
573~~~~~~~~~~~~~~
574
575These functions provide some essential functions to user scripts.
576
577The *flag_str* and *symbol_str* functions provide human-readable
578strings for flag and symbolic fields. These correspond to the strings
579and values parsed from the 'print fmt' fields of the event format
580files:
581
582 flag_str(event_name, field_name, field_value) - returns the string represention corresponding to field_value for the flag field field_name of event event_name
583 symbol_str(event_name, field_name, field_value) - returns the string represention corresponding to field_value for the symbolic field field_name of event event_name
584
585The *autodict* function returns a special kind of Python
586dictionary that implements Perl's 'autovivifying' hashes in Python
587i.e. with autovivifying hashes, you can assign nested hash values
588without having to go to the trouble of creating intermediate levels if
589they don't exist.
590
591 autodict() - returns an autovivifying dictionary instance
592
593
594perf_trace_context Module
595~~~~~~~~~~~~~~~~~~~~~~~~~
596
597Some of the 'common' fields in the event format file aren't all that
598common, but need to be made accessible to user scripts nonetheless.
599
600perf_trace_context defines a set of functions that can be used to
601access this data in the context of the current event. Each of these
602functions expects a context variable, which is the same as the
603context variable passed into every event handler as the second
604argument.
605
606 common_pc(context) - returns common_preempt count for the current event
607 common_flags(context) - returns common_flags for the current event
608 common_lock_depth(context) - returns common_lock_depth for the current event
609
610Util.py Module
611~~~~~~~~~~~~~~
612
613Various utility functions for use with perf script:
614
615 nsecs(secs, nsecs) - returns total nsecs given secs/nsecs pair
616 nsecs_secs(nsecs) - returns whole secs portion given nsecs
617 nsecs_nsecs(nsecs) - returns nsecs remainder given nsecs
618 nsecs_str(nsecs) - returns printable string in the form secs.nsecs
619 avg(total, n) - returns average given a sum and a total number of values
620
621SEE ALSO
622--------
623linkperf:perf-script[1]
diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt
new file mode 100644
index 00000000000..29ad94293cd
--- /dev/null
+++ b/tools/perf/Documentation/perf-script.txt
@@ -0,0 +1,118 @@
1perf-script(1)
2=============
3
4NAME
5----
6perf-script - Read perf.data (created by perf record) and display trace output
7
8SYNOPSIS
9--------
10[verse]
11'perf script' [<options>]
12'perf script' [<options>] record <script> [<record-options>] <command>
13'perf script' [<options>] report <script> [script-args]
14'perf script' [<options>] <script> <required-script-args> [<record-options>] <command>
15'perf script' [<options>] <top-script> [script-args]
16
17DESCRIPTION
18-----------
19This command reads the input file and displays the trace recorded.
20
21There are several variants of perf script:
22
23 'perf script' to see a detailed trace of the workload that was
24 recorded.
25
26 You can also run a set of pre-canned scripts that aggregate and
27 summarize the raw trace data in various ways (the list of scripts is
28 available via 'perf script -l'). The following variants allow you to
29 record and run those scripts:
30
31 'perf script record <script> <command>' to record the events required
32 for 'perf script report'. <script> is the name displayed in the
33 output of 'perf script --list' i.e. the actual script name minus any
34 language extension. If <command> is not specified, the events are
35 recorded using the -a (system-wide) 'perf record' option.
36
37 'perf script report <script> [args]' to run and display the results
38 of <script>. <script> is the name displayed in the output of 'perf
39 trace --list' i.e. the actual script name minus any language
40 extension. The perf.data output from a previous run of 'perf script
41 record <script>' is used and should be present for this command to
42 succeed. [args] refers to the (mainly optional) args expected by
43 the script.
44
45 'perf script <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 script --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 script record'
55 and 'perf script 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 script <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 script --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 script
67 record' and 'live-mode' variants; this isn't possible however for
68 <top-script> 'live-mode' or 'perf script report' variants.
69
70 See the 'SEE ALSO' section for links to language-specific
71 information on how to write and run your own trace scripts.
72
73OPTIONS
74-------
75<command>...::
76 Any command you can specify in a shell.
77
78-D::
79--dump-raw-script=::
80 Display verbose dump of the trace data.
81
82-L::
83--Latency=::
84 Show latency attributes (irqs/preemption disabled, etc).
85
86-l::
87--list=::
88 Display a list of available trace scripts.
89
90-s ['lang']::
91--script=::
92 Process trace data with the given script ([lang]:script[.ext]).
93 If the string 'lang' is specified in place of a script name, a
94 list of supported languages will be displayed instead.
95
96-g::
97--gen-script=::
98 Generate perf-script.[ext] starter script for given language,
99 using current perf.data.
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-i::
108--input=::
109 Input file name.
110
111-d::
112--debug-mode::
113 Do various checks like samples ordering and lost events.
114
115SEE ALSO
116--------
117linkperf:perf-record[1], linkperf:perf-script-perl[1],
118linkperf:perf-script-python[1]
diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt
index c368a72721d..b6da7affbbe 100644
--- a/tools/perf/Documentation/perf-stat.txt
+++ b/tools/perf/Documentation/perf-stat.txt
@@ -8,8 +8,8 @@ perf-stat - Run a command and gather performance counter statistics
8SYNOPSIS 8SYNOPSIS
9-------- 9--------
10[verse] 10[verse]
11'perf stat' [-e <EVENT> | --event=EVENT] [-l] [-a] <command> 11'perf stat' [-e <EVENT> | --event=EVENT] [-a] <command>
12'perf stat' [-e <EVENT> | --event=EVENT] [-l] [-a] -- <command> [<options>] 12'perf stat' [-e <EVENT> | --event=EVENT] [-a] -- <command> [<options>]
13 13
14DESCRIPTION 14DESCRIPTION
15----------- 15-----------
@@ -31,17 +31,57 @@ OPTIONS
31 hexadecimal event descriptor. 31 hexadecimal event descriptor.
32 32
33-i:: 33-i::
34--inherit:: 34--no-inherit::
35 child tasks inherit counters 35 child tasks do not inherit counters
36-p:: 36-p::
37--pid=<pid>:: 37--pid=<pid>::
38 stat events on existing pid 38 stat events on existing process id
39
40-t::
41--tid=<tid>::
42 stat events on existing thread id
39 43
40-a::
41 system-wide collection
42 44
43-l:: 45-a::
44 scale counter values 46--all-cpus::
47 system-wide collection from all CPUs
48
49-c::
50--scale::
51 scale/normalize counter values
52
53-r::
54--repeat=<n>::
55 repeat command and print average + stddev (max: 100)
56
57-B::
58--big-num::
59 print large numbers with thousands' separators according to locale
60
61-C::
62--cpu=::
63Count only on the list of CPUs provided. Multiple CPUs can be provided as a
64comma-separated list with no space: 0,1. Ranges of CPUs are specified with -: 0-2.
65In per-thread mode, this option is ignored. The -a option is still necessary
66to activate system-wide monitoring. Default is to count on all CPUs.
67
68-A::
69--no-aggr::
70Do not aggregate counts across all monitored CPUs in system-wide mode (-a).
71This option is only valid in system-wide mode.
72
73-n::
74--null::
75 null run - don't start any counters
76
77-v::
78--verbose::
79 be more verbose (show counter open errors, etc)
80
81-x SEP::
82--field-separator SEP::
83print counts using a CSV-style output to make it easy to import directly into
84spreadsheets. Columns are separated by the string specified in SEP.
45 85
46EXAMPLES 86EXAMPLES
47-------- 87--------
diff --git a/tools/perf/Documentation/perf-test.txt b/tools/perf/Documentation/perf-test.txt
new file mode 100644
index 00000000000..2c3b462f64b
--- /dev/null
+++ b/tools/perf/Documentation/perf-test.txt
@@ -0,0 +1,22 @@
1perf-test(1)
2============
3
4NAME
5----
6perf-test - Runs sanity tests.
7
8SYNOPSIS
9--------
10[verse]
11'perf test <options>'
12
13DESCRIPTION
14-----------
15This command does assorted sanity tests, initially through linked routines but
16also will look for a directory with more tests in the form of scripts.
17
18OPTIONS
19-------
20-v::
21--verbose::
22 Be more verbose.
diff --git a/tools/perf/Documentation/perf-timechart.txt b/tools/perf/Documentation/perf-timechart.txt
new file mode 100644
index 00000000000..d7b79e2ba2a
--- /dev/null
+++ b/tools/perf/Documentation/perf-timechart.txt
@@ -0,0 +1,46 @@
1perf-timechart(1)
2=================
3
4NAME
5----
6perf-timechart - Tool to visualize total system behavior during a workload
7
8SYNOPSIS
9--------
10[verse]
11'perf timechart' {record}
12
13DESCRIPTION
14-----------
15There are two variants of perf timechart:
16
17 'perf timechart record <command>' to record the system level events
18 of an arbitrary workload.
19
20 'perf timechart' to turn a trace into a Scalable Vector Graphics file,
21 that can be viewed with popular SVG viewers such as 'Inkscape'.
22
23OPTIONS
24-------
25-o::
26--output=::
27 Select the output file (default: output.svg)
28-i::
29--input=::
30 Select the input file (default: perf.data)
31-w::
32--width=::
33 Select the width of the SVG file (default: 1000)
34-P::
35--power-only::
36 Only output the CPU power section of the diagram
37-p::
38--process::
39 Select the processes to display, by name or PID
40
41--symfs=<directory>::
42 Look for files with symbols relative to this directory.
43
44SEE ALSO
45--------
46linkperf:perf-record[1]
diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt
index 539d0128972..f6eb1cdafb7 100644
--- a/tools/perf/Documentation/perf-top.txt
+++ b/tools/perf/Documentation/perf-top.txt
@@ -3,36 +3,144 @@ perf-top(1)
3 3
4NAME 4NAME
5---- 5----
6perf-top - Run a command and profile it 6perf-top - System profiling tool.
7 7
8SYNOPSIS 8SYNOPSIS
9-------- 9--------
10[verse] 10[verse]
11'perf top' [-e <EVENT> | --event=EVENT] [-l] [-a] <command> 11'perf top' [-e <EVENT> | --event=EVENT] [<options>]
12 12
13DESCRIPTION 13DESCRIPTION
14----------- 14-----------
15This command runs a command and gathers a performance counter profile 15This command generates and displays a performance counter profile in real time.
16from it.
17 16
18 17
19OPTIONS 18OPTIONS
20------- 19-------
21<command>...:: 20-a::
22 Any command you can specify in a shell. 21--all-cpus::
22 System-wide collection. (default)
23
24-c <count>::
25--count=<count>::
26 Event period to sample.
27
28-C <cpu-list>::
29--cpu=<cpu>::
30Monitor only on the list of CPUs provided. Multiple CPUs can be provided as a
31comma-separated list with no space: 0,1. Ranges of CPUs are specified with -: 0-2.
32Default is to monitor all CPUS.
33
34-d <seconds>::
35--delay=<seconds>::
36 Number of seconds to delay between refreshes.
23 37
24-e:: 38-e <event>::
25--event=:: 39--event=<event>::
26 Select the PMU event. Selection can be a symbolic event name 40 Select the PMU event. Selection can be a symbolic event name
27 (use 'perf list' to list all events) or a raw PMU 41 (use 'perf list' to list all events) or a raw PMU
28 event (eventsel+umask) in the form of rNNN where NNN is a 42 event (eventsel+umask) in the form of rNNN where NNN is a
29 hexadecimal event descriptor. 43 hexadecimal event descriptor.
30 44
31-a:: 45-E <entries>::
32 system-wide collection 46--entries=<entries>::
47 Display this many functions.
48
49-f <count>::
50--count-filter=<count>::
51 Only display functions with more events than this.
52
53-g::
54--group::
55 Put the counters into a counter group.
56
57-F <freq>::
58--freq=<freq>::
59 Profile at this frequency.
60
61-i::
62--inherit::
63 Child tasks inherit counters, only makes sens with -p option.
64
65-k <path>::
66--vmlinux=<path>::
67 Path to vmlinux. Required for annotation functionality.
68
69-m <pages>::
70--mmap-pages=<pages>::
71 Number of mmapped data pages.
72
73-p <pid>::
74--pid=<pid>::
75 Profile events on existing Process ID.
76
77-t <tid>::
78--tid=<tid>::
79 Profile events on existing thread ID.
80
81-r <priority>::
82--realtime=<priority>::
83 Collect data with this RT SCHED_FIFO priority.
84
85-s <symbol>::
86--sym-annotate=<symbol>::
87 Annotate this symbol.
88
89-K::
90--hide_kernel_symbols::
91 Hide kernel symbols.
92
93-U::
94--hide_user_symbols::
95 Hide user symbols.
96
97-D::
98--dump-symtab::
99 Dump the symbol table used for profiling.
100
101-v::
102--verbose::
103 Be more verbose (show counter open errors, etc).
104
105-z::
106--zero::
107 Zero history across display updates.
108
109INTERACTIVE PROMPTING KEYS
110--------------------------
111
112[d]::
113 Display refresh delay.
114
115[e]::
116 Number of entries to display.
117
118[E]::
119 Event to display when multiple counters are active.
120
121[f]::
122 Profile display filter (>= hit count).
123
124[F]::
125 Annotation display filter (>= % of total).
126
127[s]::
128 Annotate symbol.
129
130[S]::
131 Stop annotation, return to full profile display.
132
133[w]::
134 Toggle between weighted sum and individual count[E]r profile.
135
136[z]::
137 Toggle event count zeroing across display updates.
138
139[qQ]::
140 Quit.
141
142Pressing any unmapped key displays a menu, and prompts for input.
33 143
34-l::
35 scale counter values
36 144
37SEE ALSO 145SEE ALSO
38-------- 146--------
diff --git a/tools/perf/Documentation/perf.txt b/tools/perf/Documentation/perf.txt
index 69c83255719..0eeb247dc7d 100644
--- a/tools/perf/Documentation/perf.txt
+++ b/tools/perf/Documentation/perf.txt
@@ -12,7 +12,7 @@ SYNOPSIS
12 12
13DESCRIPTION 13DESCRIPTION
14----------- 14-----------
15Performance counters for Linux are are a new kernel-based subsystem 15Performance counters for Linux are a new kernel-based subsystem
16that provide a framework for all things performance analysis. It 16that provide a framework for all things performance analysis. It
17covers hardware level (CPU/PMU, Performance Monitoring Unit) features 17covers hardware level (CPU/PMU, Performance Monitoring Unit) features
18and software features (software counters, tracepoints) as well. 18and software features (software counters, tracepoints) as well.
diff --git a/tools/perf/MANIFEST b/tools/perf/MANIFEST
new file mode 100644
index 00000000000..c12659d8cb2
--- /dev/null
+++ b/tools/perf/MANIFEST
@@ -0,0 +1,13 @@
1tools/perf
2include/linux/perf_event.h
3include/linux/rbtree.h
4include/linux/list.h
5include/linux/hash.h
6include/linux/stringify.h
7lib/rbtree.c
8include/linux/swab.h
9arch/*/include/asm/unistd*.h
10arch/*/lib/memcpy*.S
11include/linux/poison.h
12include/linux/magic.h
13include/linux/hw_breakpoint.h
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index 36d7eef4991..2b5387d53ba 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -1,7 +1,18 @@
1ifeq ("$(origin O)", "command line")
2 OUTPUT := $(O)/
3endif
4
1# The default target of this Makefile is... 5# The default target of this Makefile is...
2all:: 6all::
3 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
4# Define V=1 to have a more verbose compile. 14# Define V=1 to have a more verbose compile.
15# Define V=2 to have an even more verbose compile.
5# 16#
6# Define SNPRINTF_RETURNS_BOGUS if your are on a system which snprintf() 17# Define SNPRINTF_RETURNS_BOGUS if your are on a system which snprintf()
7# or vsnprintf() return -1 instead of number of characters which would 18# or vsnprintf() return -1 instead of number of characters which would
@@ -145,10 +156,16 @@ all::
145# Define NO_EXTERNAL_GREP if you don't want "perf grep" to ever call 156# Define NO_EXTERNAL_GREP if you don't want "perf grep" to ever call
146# your external grep (e.g., if your system lacks grep, if its grep is 157# your external grep (e.g., if your system lacks grep, if its grep is
147# broken, or spawning external process is slower than built-in grep perf has). 158# broken, or spawning external process is slower than built-in grep perf has).
159#
160# Define LDFLAGS=-static to build a static binary.
161#
162# Define EXTRA_CFLAGS=-m64 or EXTRA_CFLAGS=-m32 as appropriate for cross-builds.
163#
164# Define NO_DWARF if you do not want debug-info analysis feature at all.
148 165
149PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE 166$(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE
150 @$(SHELL_PATH) util/PERF-VERSION-GEN 167 @$(SHELL_PATH) util/PERF-VERSION-GEN $(OUTPUT)
151-include PERF-VERSION-FILE 168-include $(OUTPUT)PERF-VERSION-FILE
152 169
153uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not') 170uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
154uname_M := $(shell sh -c 'uname -m 2>/dev/null || echo not') 171uname_M := $(shell sh -c 'uname -m 2>/dev/null || echo not')
@@ -157,16 +174,62 @@ uname_R := $(shell sh -c 'uname -r 2>/dev/null || echo not')
157uname_P := $(shell sh -c 'uname -p 2>/dev/null || echo not') 174uname_P := $(shell sh -c 'uname -p 2>/dev/null || echo not')
158uname_V := $(shell sh -c 'uname -v 2>/dev/null || echo not') 175uname_V := $(shell sh -c 'uname -v 2>/dev/null || echo not')
159 176
160# If we're on a 64-bit kernel, use -m64 177ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \
161ifneq ($(patsubst %64,%,$(uname_M)),$(uname_M)) 178 -e s/arm.*/arm/ -e s/sa110/arm/ \
162 M64 := -m64 179 -e s/s390x/s390/ -e s/parisc64/parisc/ \
180 -e s/ppc.*/powerpc/ -e s/mips.*/mips/ \
181 -e s/sh[234].*/sh/ )
182
183# Additional ARCH settings for x86
184ifeq ($(ARCH),i386)
185 ARCH := x86
186endif
187ifeq ($(ARCH),x86_64)
188 RAW_ARCH := x86_64
189 ARCH := x86
190 ARCH_CFLAGS := -DARCH_X86_64
191 ARCH_INCLUDE = ../../arch/x86/lib/memcpy_64.S
163endif 192endif
164 193
165# CFLAGS and LDFLAGS are for the users to override from the command line. 194# CFLAGS and LDFLAGS are for the users to override from the command line.
166 195
167CFLAGS = $(M64) -ggdb3 -Wall -Wstrict-prototypes -Wmissing-declarations -Wmissing-prototypes -std=gnu99 -Wdeclaration-after-statement -Werror -O6 196#
168LDFLAGS = -lpthread -lrt -lelf -lm 197# Include saner warnings here, which can catch bugs:
169ALL_CFLAGS = $(CFLAGS) 198#
199
200EXTRA_WARNINGS := -Wformat
201EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wformat-security
202EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wformat-y2k
203EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wshadow
204EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Winit-self
205EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wpacked
206EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wredundant-decls
207EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wstack-protector
208EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wstrict-aliasing=3
209EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wswitch-default
210EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wswitch-enum
211EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wno-system-headers
212EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wundef
213EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wvolatile-register-var
214EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wwrite-strings
215EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wbad-function-cast
216EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wmissing-declarations
217EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wmissing-prototypes
218EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wnested-externs
219EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wold-style-definition
220EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wstrict-prototypes
221EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wdeclaration-after-statement
222
223ifeq ("$(origin DEBUG)", "command line")
224 PERF_DEBUG = $(DEBUG)
225endif
226ifndef PERF_DEBUG
227 CFLAGS_OPTIMIZE = -O6
228endif
229
230CFLAGS = -fno-omit-frame-pointer -ggdb3 -Wall -Wextra -std=gnu99 -Werror $(CFLAGS_OPTIMIZE) -D_FORTIFY_SOURCE=2 $(EXTRA_WARNINGS) $(EXTRA_CFLAGS)
231EXTLIBS = -lpthread -lrt -lelf -lm
232ALL_CFLAGS = $(CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
170ALL_LDFLAGS = $(LDFLAGS) 233ALL_LDFLAGS = $(LDFLAGS)
171STRIP ?= strip 234STRIP ?= strip
172 235
@@ -182,7 +245,10 @@ STRIP ?= strip
182# runtime figures out where they are based on the path to the executable. 245# runtime figures out where they are based on the path to the executable.
183# This can help installing the suite in a relocatable way. 246# This can help installing the suite in a relocatable way.
184 247
248# Make the path relative to DESTDIR, not to prefix
249ifndef DESTDIR
185prefix = $(HOME) 250prefix = $(HOME)
251endif
186bindir_relative = bin 252bindir_relative = bin
187bindir = $(prefix)/$(bindir_relative) 253bindir = $(prefix)/$(bindir_relative)
188mandir = share/man 254mandir = share/man
@@ -199,13 +265,13 @@ sysconfdir = $(prefix)/etc
199ETC_PERFCONFIG = etc/perfconfig 265ETC_PERFCONFIG = etc/perfconfig
200endif 266endif
201lib = lib 267lib = lib
202# DESTDIR=
203 268
204export prefix bindir sharedir sysconfdir 269export prefix bindir sharedir sysconfdir
205 270
206CC = gcc 271CC = $(CROSS_COMPILE)gcc
207AR = ar 272AR = $(CROSS_COMPILE)ar
208RM = rm -f 273RM = rm -f
274MKDIR = mkdir
209TAR = tar 275TAR = tar
210FIND = find 276FIND = find
211INSTALL = install 277INSTALL = install
@@ -216,6 +282,17 @@ PTHREAD_LIBS = -lpthread
216# explicitly what architecture to check for. Fix this up for yours.. 282# explicitly what architecture to check for. Fix this up for yours..
217SPARSE_FLAGS = -D__BIG_ENDIAN__ -D__powerpc__ 283SPARSE_FLAGS = -D__BIG_ENDIAN__ -D__powerpc__
218 284
285ifeq ($(V), 2)
286 QUIET_STDERR = ">/dev/null"
287else
288 QUIET_STDERR = ">/dev/null 2>&1"
289endif
290
291-include feature-tests.mak
292
293ifeq ($(call try-cc,$(SOURCE_HELLO),-Werror -fstack-protector-all),y)
294 CFLAGS := $(CFLAGS) -fstack-protector-all
295endif
219 296
220 297
221### --- END CONFIGURATION SECTION --- 298### --- END CONFIGURATION SECTION ---
@@ -223,7 +300,7 @@ SPARSE_FLAGS = -D__BIG_ENDIAN__ -D__powerpc__
223# Those must not be GNU-specific; they are shared with perl/ which may 300# Those must not be GNU-specific; they are shared with perl/ which may
224# be built by a different compiler. (Note that this is an artifact now 301# be built by a different compiler. (Note that this is an artifact now
225# but it still might be nice to keep that distinction.) 302# but it still might be nice to keep that distinction.)
226BASIC_CFLAGS = 303BASIC_CFLAGS = -Iutil/include -Iarch/$(ARCH)/include
227BASIC_LDFLAGS = 304BASIC_LDFLAGS =
228 305
229# Guard against environment variables 306# Guard against environment variables
@@ -237,11 +314,10 @@ SCRIPT_PERL =
237SCRIPT_SH = 314SCRIPT_SH =
238TEST_PROGRAMS = 315TEST_PROGRAMS =
239 316
240# 317SCRIPT_SH += perf-archive.sh
241# No scripts right now:
242#
243 318
244# SCRIPT_SH += perf-am.sh 319grep-libs = $(filter -l%,$(1))
320strip-libs = $(filter-out -l%,$(1))
245 321
246# 322#
247# No Perl scripts right now: 323# No Perl scripts right now:
@@ -261,20 +337,17 @@ PROGRAMS += $(EXTRA_PROGRAMS)
261# 337#
262# Single 'perf' binary right now: 338# Single 'perf' binary right now:
263# 339#
264PROGRAMS += perf 340PROGRAMS += $(OUTPUT)perf
265 341
266# List built-in command $C whose implementation cmd_$C() is not in 342# List built-in command $C whose implementation cmd_$C() is not in
267# builtin-$C.o but is linked in as part of some other command. 343# builtin-$C.o but is linked in as part of some other command.
268# 344#
269# None right now:
270#
271# BUILT_INS += perf-init $X
272 345
273# what 'all' will build and 'install' will install, in perfexecdir 346# what 'all' will build and 'install' will install, in perfexecdir
274ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS) 347ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS)
275 348
276# what 'all' will build but not install in perfexecdir 349# what 'all' will build but not install in perfexecdir
277OTHER_PROGRAMS = perf$X 350OTHER_PROGRAMS = $(OUTPUT)perf$X
278 351
279# Set paths to tools early so that they can be used for version tests. 352# Set paths to tools early so that they can be used for version tests.
280ifndef SHELL_PATH 353ifndef SHELL_PATH
@@ -286,59 +359,155 @@ endif
286 359
287export PERL_PATH 360export PERL_PATH
288 361
289LIB_FILE=libperf.a 362LIB_FILE=$(OUTPUT)libperf.a
290 363
291LIB_H += ../../include/linux/perf_counter.h 364LIB_H += ../../include/linux/perf_event.h
365LIB_H += ../../include/linux/rbtree.h
366LIB_H += ../../include/linux/list.h
367LIB_H += ../../include/linux/hash.h
368LIB_H += ../../include/linux/stringify.h
369LIB_H += util/include/linux/bitmap.h
370LIB_H += util/include/linux/bitops.h
371LIB_H += util/include/linux/compiler.h
372LIB_H += util/include/linux/ctype.h
373LIB_H += util/include/linux/kernel.h
374LIB_H += util/include/linux/list.h
375LIB_H += util/include/linux/module.h
376LIB_H += util/include/linux/poison.h
377LIB_H += util/include/linux/prefetch.h
378LIB_H += util/include/linux/rbtree.h
379LIB_H += util/include/linux/string.h
380LIB_H += util/include/linux/types.h
381LIB_H += util/include/linux/linkage.h
382LIB_H += util/include/asm/asm-offsets.h
383LIB_H += util/include/asm/bug.h
384LIB_H += util/include/asm/byteorder.h
385LIB_H += util/include/asm/hweight.h
386LIB_H += util/include/asm/swab.h
387LIB_H += util/include/asm/system.h
388LIB_H += util/include/asm/uaccess.h
389LIB_H += util/include/dwarf-regs.h
390LIB_H += util/include/asm/dwarf2.h
391LIB_H += util/include/asm/cpufeature.h
292LIB_H += perf.h 392LIB_H += perf.h
293LIB_H += types.h 393LIB_H += util/cache.h
294LIB_H += util/list.h 394LIB_H += util/callchain.h
295LIB_H += util/rbtree.h 395LIB_H += util/build-id.h
396LIB_H += util/debug.h
397LIB_H += util/debugfs.h
398LIB_H += util/event.h
399LIB_H += util/evsel.h
400LIB_H += util/exec_cmd.h
401LIB_H += util/types.h
296LIB_H += util/levenshtein.h 402LIB_H += util/levenshtein.h
403LIB_H += util/map.h
297LIB_H += util/parse-options.h 404LIB_H += util/parse-options.h
298LIB_H += util/parse-events.h 405LIB_H += util/parse-events.h
299LIB_H += util/quote.h 406LIB_H += util/quote.h
300LIB_H += util/util.h 407LIB_H += util/util.h
408LIB_H += util/xyarray.h
409LIB_H += util/header.h
301LIB_H += util/help.h 410LIB_H += util/help.h
411LIB_H += util/session.h
302LIB_H += util/strbuf.h 412LIB_H += util/strbuf.h
303LIB_H += util/string.h 413LIB_H += util/strlist.h
414LIB_H += util/svghelper.h
304LIB_H += util/run-command.h 415LIB_H += util/run-command.h
305LIB_H += util/sigchain.h 416LIB_H += util/sigchain.h
306LIB_H += util/symbol.h 417LIB_H += util/symbol.h
307LIB_H += util/color.h 418LIB_H += util/color.h
308 419LIB_H += util/values.h
309LIB_OBJS += util/abspath.o 420LIB_H += util/sort.h
310LIB_OBJS += util/alias.o 421LIB_H += util/hist.h
311LIB_OBJS += util/config.o 422LIB_H += util/thread.h
312LIB_OBJS += util/ctype.o 423LIB_H += util/trace-event.h
313LIB_OBJS += util/environment.o 424LIB_H += util/probe-finder.h
314LIB_OBJS += util/exec_cmd.o 425LIB_H += util/probe-event.h
315LIB_OBJS += util/help.o 426LIB_H += util/pstack.h
316LIB_OBJS += util/levenshtein.o 427LIB_H += util/cpumap.h
317LIB_OBJS += util/parse-options.o 428LIB_H += $(ARCH_INCLUDE)
318LIB_OBJS += util/parse-events.o 429
319LIB_OBJS += util/path.o 430LIB_OBJS += $(OUTPUT)util/abspath.o
320LIB_OBJS += util/rbtree.o 431LIB_OBJS += $(OUTPUT)util/alias.o
321LIB_OBJS += util/run-command.o 432LIB_OBJS += $(OUTPUT)util/build-id.o
322LIB_OBJS += util/quote.o 433LIB_OBJS += $(OUTPUT)util/config.o
323LIB_OBJS += util/strbuf.o 434LIB_OBJS += $(OUTPUT)util/ctype.o
324LIB_OBJS += util/string.o 435LIB_OBJS += $(OUTPUT)util/debugfs.o
325LIB_OBJS += util/usage.o 436LIB_OBJS += $(OUTPUT)util/environment.o
326LIB_OBJS += util/wrapper.o 437LIB_OBJS += $(OUTPUT)util/event.o
327LIB_OBJS += util/sigchain.o 438LIB_OBJS += $(OUTPUT)util/evsel.o
328LIB_OBJS += util/symbol.o 439LIB_OBJS += $(OUTPUT)util/exec_cmd.o
329LIB_OBJS += util/color.o 440LIB_OBJS += $(OUTPUT)util/help.o
330LIB_OBJS += util/pager.o 441LIB_OBJS += $(OUTPUT)util/levenshtein.o
331 442LIB_OBJS += $(OUTPUT)util/parse-options.o
332BUILTIN_OBJS += builtin-annotate.o 443LIB_OBJS += $(OUTPUT)util/parse-events.o
333BUILTIN_OBJS += builtin-help.o 444LIB_OBJS += $(OUTPUT)util/path.o
334BUILTIN_OBJS += builtin-list.o 445LIB_OBJS += $(OUTPUT)util/rbtree.o
335BUILTIN_OBJS += builtin-record.o 446LIB_OBJS += $(OUTPUT)util/bitmap.o
336BUILTIN_OBJS += builtin-report.o 447LIB_OBJS += $(OUTPUT)util/hweight.o
337BUILTIN_OBJS += builtin-stat.o 448LIB_OBJS += $(OUTPUT)util/run-command.o
338BUILTIN_OBJS += builtin-top.o 449LIB_OBJS += $(OUTPUT)util/quote.o
450LIB_OBJS += $(OUTPUT)util/strbuf.o
451LIB_OBJS += $(OUTPUT)util/string.o
452LIB_OBJS += $(OUTPUT)util/strlist.o
453LIB_OBJS += $(OUTPUT)util/usage.o
454LIB_OBJS += $(OUTPUT)util/wrapper.o
455LIB_OBJS += $(OUTPUT)util/sigchain.o
456LIB_OBJS += $(OUTPUT)util/symbol.o
457LIB_OBJS += $(OUTPUT)util/color.o
458LIB_OBJS += $(OUTPUT)util/pager.o
459LIB_OBJS += $(OUTPUT)util/header.o
460LIB_OBJS += $(OUTPUT)util/callchain.o
461LIB_OBJS += $(OUTPUT)util/values.o
462LIB_OBJS += $(OUTPUT)util/debug.o
463LIB_OBJS += $(OUTPUT)util/map.o
464LIB_OBJS += $(OUTPUT)util/pstack.o
465LIB_OBJS += $(OUTPUT)util/session.o
466LIB_OBJS += $(OUTPUT)util/thread.o
467LIB_OBJS += $(OUTPUT)util/trace-event-parse.o
468LIB_OBJS += $(OUTPUT)util/trace-event-read.o
469LIB_OBJS += $(OUTPUT)util/trace-event-info.o
470LIB_OBJS += $(OUTPUT)util/trace-event-scripting.o
471LIB_OBJS += $(OUTPUT)util/svghelper.o
472LIB_OBJS += $(OUTPUT)util/sort.o
473LIB_OBJS += $(OUTPUT)util/hist.o
474LIB_OBJS += $(OUTPUT)util/probe-event.o
475LIB_OBJS += $(OUTPUT)util/util.o
476LIB_OBJS += $(OUTPUT)util/xyarray.o
477LIB_OBJS += $(OUTPUT)util/cpumap.o
478
479BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o
480
481BUILTIN_OBJS += $(OUTPUT)builtin-bench.o
482
483# Benchmark modules
484BUILTIN_OBJS += $(OUTPUT)bench/sched-messaging.o
485BUILTIN_OBJS += $(OUTPUT)bench/sched-pipe.o
486ifeq ($(RAW_ARCH),x86_64)
487BUILTIN_OBJS += $(OUTPUT)bench/mem-memcpy-x86-64-asm.o
488endif
489BUILTIN_OBJS += $(OUTPUT)bench/mem-memcpy.o
490
491BUILTIN_OBJS += $(OUTPUT)builtin-diff.o
492BUILTIN_OBJS += $(OUTPUT)builtin-help.o
493BUILTIN_OBJS += $(OUTPUT)builtin-sched.o
494BUILTIN_OBJS += $(OUTPUT)builtin-buildid-list.o
495BUILTIN_OBJS += $(OUTPUT)builtin-buildid-cache.o
496BUILTIN_OBJS += $(OUTPUT)builtin-list.o
497BUILTIN_OBJS += $(OUTPUT)builtin-record.o
498BUILTIN_OBJS += $(OUTPUT)builtin-report.o
499BUILTIN_OBJS += $(OUTPUT)builtin-stat.o
500BUILTIN_OBJS += $(OUTPUT)builtin-timechart.o
501BUILTIN_OBJS += $(OUTPUT)builtin-top.o
502BUILTIN_OBJS += $(OUTPUT)builtin-script.o
503BUILTIN_OBJS += $(OUTPUT)builtin-probe.o
504BUILTIN_OBJS += $(OUTPUT)builtin-kmem.o
505BUILTIN_OBJS += $(OUTPUT)builtin-lock.o
506BUILTIN_OBJS += $(OUTPUT)builtin-kvm.o
507BUILTIN_OBJS += $(OUTPUT)builtin-test.o
508BUILTIN_OBJS += $(OUTPUT)builtin-inject.o
339 509
340PERFLIBS = $(LIB_FILE) 510PERFLIBS = $(LIB_FILE)
341EXTLIBS =
342 511
343# 512#
344# Platform specific tweaks 513# Platform specific tweaks
@@ -351,6 +520,16 @@ EXTLIBS =
351-include config.mak.autogen 520-include config.mak.autogen
352-include config.mak 521-include config.mak
353 522
523ifndef NO_DWARF
524FLAGS_DWARF=$(ALL_CFLAGS) -ldw -lelf $(ALL_LDFLAGS) $(EXTLIBS)
525ifneq ($(call try-cc,$(SOURCE_DWARF),$(FLAGS_DWARF)),y)
526 msg := $(warning No libdw.h found or old libdw.h found or elfutils is older than 0.138, disables dwarf support. Please install new elfutils-devel/libdw-dev);
527 NO_DWARF := 1
528endif # Dwarf support
529endif # NO_DWARF
530
531-include arch/$(ARCH)/Makefile
532
354ifeq ($(uname_S),Darwin) 533ifeq ($(uname_S),Darwin)
355 ifndef NO_FINK 534 ifndef NO_FINK
356 ifeq ($(shell test -d /sw/lib && echo y),y) 535 ifeq ($(shell test -d /sw/lib && echo y),y)
@@ -367,6 +546,145 @@ ifeq ($(uname_S),Darwin)
367 PTHREAD_LIBS = 546 PTHREAD_LIBS =
368endif 547endif
369 548
549ifneq ($(OUTPUT),)
550 BASIC_CFLAGS += -I$(OUTPUT)
551endif
552
553FLAGS_LIBELF=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS)
554ifneq ($(call try-cc,$(SOURCE_LIBELF),$(FLAGS_LIBELF)),y)
555 FLAGS_GLIBC=$(ALL_CFLAGS) $(ALL_LDFLAGS)
556 ifneq ($(call try-cc,$(SOURCE_GLIBC),$(FLAGS_GLIBC)),y)
557 msg := $(error No gnu/libc-version.h found, please install glibc-dev[el]/glibc-static);
558 else
559 msg := $(error No libelf.h/libelf found, please install libelf-dev/elfutils-libelf-devel);
560 endif
561endif
562
563ifneq ($(call try-cc,$(SOURCE_ELF_MMAP),$(FLAGS_COMMON)),y)
564 BASIC_CFLAGS += -DLIBELF_NO_MMAP
565endif
566
567ifndef NO_DWARF
568ifeq ($(origin PERF_HAVE_DWARF_REGS), undefined)
569 msg := $(warning DWARF register mappings have not been defined for architecture $(ARCH), DWARF support disabled);
570else
571 BASIC_CFLAGS += -DDWARF_SUPPORT
572 EXTLIBS += -lelf -ldw
573 LIB_OBJS += $(OUTPUT)util/probe-finder.o
574endif # PERF_HAVE_DWARF_REGS
575endif # NO_DWARF
576
577ifdef NO_NEWT
578 BASIC_CFLAGS += -DNO_NEWT_SUPPORT
579else
580 FLAGS_NEWT=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -lnewt
581 ifneq ($(call try-cc,$(SOURCE_NEWT),$(FLAGS_NEWT)),y)
582 msg := $(warning newt not found, disables TUI support. Please install newt-devel or libnewt-dev);
583 BASIC_CFLAGS += -DNO_NEWT_SUPPORT
584 else
585 # Fedora has /usr/include/slang/slang.h, but ubuntu /usr/include/slang.h
586 BASIC_CFLAGS += -I/usr/include/slang
587 EXTLIBS += -lnewt -lslang
588 LIB_OBJS += $(OUTPUT)util/ui/setup.o
589 LIB_OBJS += $(OUTPUT)util/ui/browser.o
590 LIB_OBJS += $(OUTPUT)util/ui/browsers/annotate.o
591 LIB_OBJS += $(OUTPUT)util/ui/browsers/hists.o
592 LIB_OBJS += $(OUTPUT)util/ui/browsers/map.o
593 LIB_OBJS += $(OUTPUT)util/ui/helpline.o
594 LIB_OBJS += $(OUTPUT)util/ui/progress.o
595 LIB_OBJS += $(OUTPUT)util/ui/util.o
596 LIB_H += util/ui/browser.h
597 LIB_H += util/ui/browsers/map.h
598 LIB_H += util/ui/helpline.h
599 LIB_H += util/ui/libslang.h
600 LIB_H += util/ui/progress.h
601 LIB_H += util/ui/util.h
602 endif
603endif
604
605ifdef NO_LIBPERL
606 BASIC_CFLAGS += -DNO_LIBPERL
607else
608 PERL_EMBED_LDOPTS = $(shell perl -MExtUtils::Embed -e ldopts 2>/dev/null)
609 PERL_EMBED_LDFLAGS = $(call strip-libs,$(PERL_EMBED_LDOPTS))
610 PERL_EMBED_LIBADD = $(call grep-libs,$(PERL_EMBED_LDOPTS))
611 PERL_EMBED_CCOPTS = `perl -MExtUtils::Embed -e ccopts 2>/dev/null`
612 FLAGS_PERL_EMBED=$(PERL_EMBED_CCOPTS) $(PERL_EMBED_LDOPTS)
613
614 ifneq ($(call try-cc,$(SOURCE_PERL_EMBED),$(FLAGS_PERL_EMBED)),y)
615 BASIC_CFLAGS += -DNO_LIBPERL
616 else
617 ALL_LDFLAGS += $(PERL_EMBED_LDFLAGS)
618 EXTLIBS += $(PERL_EMBED_LIBADD)
619 LIB_OBJS += $(OUTPUT)util/scripting-engines/trace-event-perl.o
620 LIB_OBJS += $(OUTPUT)scripts/perl/Perf-Trace-Util/Context.o
621 endif
622endif
623
624ifdef NO_LIBPYTHON
625 BASIC_CFLAGS += -DNO_LIBPYTHON
626else
627 PYTHON_EMBED_LDOPTS = $(shell python-config --ldflags 2>/dev/null)
628 PYTHON_EMBED_LDFLAGS = $(call strip-libs,$(PYTHON_EMBED_LDOPTS))
629 PYTHON_EMBED_LIBADD = $(call grep-libs,$(PYTHON_EMBED_LDOPTS))
630 PYTHON_EMBED_CCOPTS = `python-config --cflags 2>/dev/null`
631 FLAGS_PYTHON_EMBED=$(PYTHON_EMBED_CCOPTS) $(PYTHON_EMBED_LDOPTS)
632 ifneq ($(call try-cc,$(SOURCE_PYTHON_EMBED),$(FLAGS_PYTHON_EMBED)),y)
633 BASIC_CFLAGS += -DNO_LIBPYTHON
634 else
635 ALL_LDFLAGS += $(PYTHON_EMBED_LDFLAGS)
636 EXTLIBS += $(PYTHON_EMBED_LIBADD)
637 LIB_OBJS += $(OUTPUT)util/scripting-engines/trace-event-python.o
638 LIB_OBJS += $(OUTPUT)scripts/python/Perf-Trace-Util/Context.o
639 endif
640endif
641
642ifdef NO_DEMANGLE
643 BASIC_CFLAGS += -DNO_DEMANGLE
644else
645 ifdef HAVE_CPLUS_DEMANGLE
646 EXTLIBS += -liberty
647 BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE
648 else
649 FLAGS_BFD=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -lbfd
650 has_bfd := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD))
651 ifeq ($(has_bfd),y)
652 EXTLIBS += -lbfd
653 else
654 FLAGS_BFD_IBERTY=$(FLAGS_BFD) -liberty
655 has_bfd_iberty := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD_IBERTY))
656 ifeq ($(has_bfd_iberty),y)
657 EXTLIBS += -lbfd -liberty
658 else
659 FLAGS_BFD_IBERTY_Z=$(FLAGS_BFD_IBERTY) -lz
660 has_bfd_iberty_z := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD_IBERTY_Z))
661 ifeq ($(has_bfd_iberty_z),y)
662 EXTLIBS += -lbfd -liberty -lz
663 else
664 FLAGS_CPLUS_DEMANGLE=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -liberty
665 has_cplus_demangle := $(call try-cc,$(SOURCE_CPLUS_DEMANGLE),$(FLAGS_CPLUS_DEMANGLE))
666 ifeq ($(has_cplus_demangle),y)
667 EXTLIBS += -liberty
668 BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE
669 else
670 msg := $(warning No bfd.h/libbfd found, install binutils-dev[el]/zlib-static to gain symbol demangling)
671 BASIC_CFLAGS += -DNO_DEMANGLE
672 endif
673 endif
674 endif
675 endif
676 endif
677endif
678
679
680ifdef NO_STRLCPY
681 BASIC_CFLAGS += -DNO_STRLCPY
682else
683 ifneq ($(call try-cc,$(SOURCE_STRLCPY),),y)
684 BASIC_CFLAGS += -DNO_STRLCPY
685 endif
686endif
687
370ifndef CC_LD_DYNPATH 688ifndef CC_LD_DYNPATH
371 ifdef NO_R_TO_GCC_LINKER 689 ifdef NO_R_TO_GCC_LINKER
372 # Some gcc does not accept and pass -R to the linker to specify 690 # Some gcc does not accept and pass -R to the linker to specify
@@ -377,12 +695,6 @@ ifndef CC_LD_DYNPATH
377 endif 695 endif
378endif 696endif
379 697
380ifdef ZLIB_PATH
381 BASIC_CFLAGS += -I$(ZLIB_PATH)/include
382 EXTLIBS += -L$(ZLIB_PATH)/$(lib) $(CC_LD_DYNPATH)$(ZLIB_PATH)/$(lib)
383endif
384EXTLIBS += -lz
385
386ifdef NEEDS_SOCKET 698ifdef NEEDS_SOCKET
387 EXTLIBS += -lsocket 699 EXTLIBS += -lsocket
388endif 700endif
@@ -412,53 +724,53 @@ ifdef NO_C99_FORMAT
412endif 724endif
413ifdef SNPRINTF_RETURNS_BOGUS 725ifdef SNPRINTF_RETURNS_BOGUS
414 COMPAT_CFLAGS += -DSNPRINTF_RETURNS_BOGUS 726 COMPAT_CFLAGS += -DSNPRINTF_RETURNS_BOGUS
415 COMPAT_OBJS += compat/snprintf.o 727 COMPAT_OBJS += $(OUTPUT)compat/snprintf.o
416endif 728endif
417ifdef FREAD_READS_DIRECTORIES 729ifdef FREAD_READS_DIRECTORIES
418 COMPAT_CFLAGS += -DFREAD_READS_DIRECTORIES 730 COMPAT_CFLAGS += -DFREAD_READS_DIRECTORIES
419 COMPAT_OBJS += compat/fopen.o 731 COMPAT_OBJS += $(OUTPUT)compat/fopen.o
420endif 732endif
421ifdef NO_SYMLINK_HEAD 733ifdef NO_SYMLINK_HEAD
422 BASIC_CFLAGS += -DNO_SYMLINK_HEAD 734 BASIC_CFLAGS += -DNO_SYMLINK_HEAD
423endif 735endif
424ifdef NO_STRCASESTR 736ifdef NO_STRCASESTR
425 COMPAT_CFLAGS += -DNO_STRCASESTR 737 COMPAT_CFLAGS += -DNO_STRCASESTR
426 COMPAT_OBJS += compat/strcasestr.o 738 COMPAT_OBJS += $(OUTPUT)compat/strcasestr.o
427endif 739endif
428ifdef NO_STRTOUMAX 740ifdef NO_STRTOUMAX
429 COMPAT_CFLAGS += -DNO_STRTOUMAX 741 COMPAT_CFLAGS += -DNO_STRTOUMAX
430 COMPAT_OBJS += compat/strtoumax.o 742 COMPAT_OBJS += $(OUTPUT)compat/strtoumax.o
431endif 743endif
432ifdef NO_STRTOULL 744ifdef NO_STRTOULL
433 COMPAT_CFLAGS += -DNO_STRTOULL 745 COMPAT_CFLAGS += -DNO_STRTOULL
434endif 746endif
435ifdef NO_SETENV 747ifdef NO_SETENV
436 COMPAT_CFLAGS += -DNO_SETENV 748 COMPAT_CFLAGS += -DNO_SETENV
437 COMPAT_OBJS += compat/setenv.o 749 COMPAT_OBJS += $(OUTPUT)compat/setenv.o
438endif 750endif
439ifdef NO_MKDTEMP 751ifdef NO_MKDTEMP
440 COMPAT_CFLAGS += -DNO_MKDTEMP 752 COMPAT_CFLAGS += -DNO_MKDTEMP
441 COMPAT_OBJS += compat/mkdtemp.o 753 COMPAT_OBJS += $(OUTPUT)compat/mkdtemp.o
442endif 754endif
443ifdef NO_UNSETENV 755ifdef NO_UNSETENV
444 COMPAT_CFLAGS += -DNO_UNSETENV 756 COMPAT_CFLAGS += -DNO_UNSETENV
445 COMPAT_OBJS += compat/unsetenv.o 757 COMPAT_OBJS += $(OUTPUT)compat/unsetenv.o
446endif 758endif
447ifdef NO_SYS_SELECT_H 759ifdef NO_SYS_SELECT_H
448 BASIC_CFLAGS += -DNO_SYS_SELECT_H 760 BASIC_CFLAGS += -DNO_SYS_SELECT_H
449endif 761endif
450ifdef NO_MMAP 762ifdef NO_MMAP
451 COMPAT_CFLAGS += -DNO_MMAP 763 COMPAT_CFLAGS += -DNO_MMAP
452 COMPAT_OBJS += compat/mmap.o 764 COMPAT_OBJS += $(OUTPUT)compat/mmap.o
453else 765else
454 ifdef USE_WIN32_MMAP 766 ifdef USE_WIN32_MMAP
455 COMPAT_CFLAGS += -DUSE_WIN32_MMAP 767 COMPAT_CFLAGS += -DUSE_WIN32_MMAP
456 COMPAT_OBJS += compat/win32mmap.o 768 COMPAT_OBJS += $(OUTPUT)compat/win32mmap.o
457 endif 769 endif
458endif 770endif
459ifdef NO_PREAD 771ifdef NO_PREAD
460 COMPAT_CFLAGS += -DNO_PREAD 772 COMPAT_CFLAGS += -DNO_PREAD
461 COMPAT_OBJS += compat/pread.o 773 COMPAT_OBJS += $(OUTPUT)compat/pread.o
462endif 774endif
463ifdef NO_FAST_WORKING_DIRECTORY 775ifdef NO_FAST_WORKING_DIRECTORY
464 BASIC_CFLAGS += -DNO_FAST_WORKING_DIRECTORY 776 BASIC_CFLAGS += -DNO_FAST_WORKING_DIRECTORY
@@ -480,10 +792,10 @@ else
480endif 792endif
481endif 793endif
482ifdef NO_INET_NTOP 794ifdef NO_INET_NTOP
483 LIB_OBJS += compat/inet_ntop.o 795 LIB_OBJS += $(OUTPUT)compat/inet_ntop.o
484endif 796endif
485ifdef NO_INET_PTON 797ifdef NO_INET_PTON
486 LIB_OBJS += compat/inet_pton.o 798 LIB_OBJS += $(OUTPUT)compat/inet_pton.o
487endif 799endif
488 800
489ifdef NO_ICONV 801ifdef NO_ICONV
@@ -500,15 +812,15 @@ endif
500 812
501ifdef PPC_SHA1 813ifdef PPC_SHA1
502 SHA1_HEADER = "ppc/sha1.h" 814 SHA1_HEADER = "ppc/sha1.h"
503 LIB_OBJS += ppc/sha1.o ppc/sha1ppc.o 815 LIB_OBJS += $(OUTPUT)ppc/sha1.o ppc/sha1ppc.o
504else 816else
505ifdef ARM_SHA1 817ifdef ARM_SHA1
506 SHA1_HEADER = "arm/sha1.h" 818 SHA1_HEADER = "arm/sha1.h"
507 LIB_OBJS += arm/sha1.o arm/sha1_arm.o 819 LIB_OBJS += $(OUTPUT)arm/sha1.o $(OUTPUT)arm/sha1_arm.o
508else 820else
509ifdef MOZILLA_SHA1 821ifdef MOZILLA_SHA1
510 SHA1_HEADER = "mozilla-sha1/sha1.h" 822 SHA1_HEADER = "mozilla-sha1/sha1.h"
511 LIB_OBJS += mozilla-sha1/sha1.o 823 LIB_OBJS += $(OUTPUT)mozilla-sha1/sha1.o
512else 824else
513 SHA1_HEADER = <openssl/sha.h> 825 SHA1_HEADER = <openssl/sha.h>
514 EXTLIBS += $(LIB_4_CRYPTO) 826 EXTLIBS += $(LIB_4_CRYPTO)
@@ -520,15 +832,15 @@ ifdef NO_PERL_MAKEMAKER
520endif 832endif
521ifdef NO_HSTRERROR 833ifdef NO_HSTRERROR
522 COMPAT_CFLAGS += -DNO_HSTRERROR 834 COMPAT_CFLAGS += -DNO_HSTRERROR
523 COMPAT_OBJS += compat/hstrerror.o 835 COMPAT_OBJS += $(OUTPUT)compat/hstrerror.o
524endif 836endif
525ifdef NO_MEMMEM 837ifdef NO_MEMMEM
526 COMPAT_CFLAGS += -DNO_MEMMEM 838 COMPAT_CFLAGS += -DNO_MEMMEM
527 COMPAT_OBJS += compat/memmem.o 839 COMPAT_OBJS += $(OUTPUT)compat/memmem.o
528endif 840endif
529ifdef INTERNAL_QSORT 841ifdef INTERNAL_QSORT
530 COMPAT_CFLAGS += -DINTERNAL_QSORT 842 COMPAT_CFLAGS += -DINTERNAL_QSORT
531 COMPAT_OBJS += compat/qsort.o 843 COMPAT_OBJS += $(OUTPUT)compat/qsort.o
532endif 844endif
533ifdef RUNTIME_PREFIX 845ifdef RUNTIME_PREFIX
534 COMPAT_CFLAGS += -DRUNTIME_PREFIX 846 COMPAT_CFLAGS += -DRUNTIME_PREFIX
@@ -559,6 +871,7 @@ ifndef V
559 QUIET_CC = @echo ' ' CC $@; 871 QUIET_CC = @echo ' ' CC $@;
560 QUIET_AR = @echo ' ' AR $@; 872 QUIET_AR = @echo ' ' AR $@;
561 QUIET_LINK = @echo ' ' LINK $@; 873 QUIET_LINK = @echo ' ' LINK $@;
874 QUIET_MKDIR = @echo ' ' MKDIR $@;
562 QUIET_BUILT_IN = @echo ' ' BUILTIN $@; 875 QUIET_BUILT_IN = @echo ' ' BUILTIN $@;
563 QUIET_GEN = @echo ' ' GEN $@; 876 QUIET_GEN = @echo ' ' GEN $@;
564 QUIET_SUBDIR0 = +@subdir= 877 QUIET_SUBDIR0 = +@subdir=
@@ -592,13 +905,14 @@ prefix_SQ = $(subst ','\'',$(prefix))
592SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH)) 905SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
593PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH)) 906PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
594 907
595LIBS = $(PERFLIBS) $(EXTLIBS) 908LIBS = -Wl,--whole-archive $(PERFLIBS) -Wl,--no-whole-archive $(EXTLIBS)
596 909
597BASIC_CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER_SQ)' \ 910BASIC_CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER_SQ)' \
598 $(COMPAT_CFLAGS) 911 $(COMPAT_CFLAGS)
599LIB_OBJS += $(COMPAT_OBJS) 912LIB_OBJS += $(COMPAT_OBJS)
600 913
601ALL_CFLAGS += $(BASIC_CFLAGS) 914ALL_CFLAGS += $(BASIC_CFLAGS)
915ALL_CFLAGS += $(ARCH_CFLAGS)
602ALL_LDFLAGS += $(BASIC_LDFLAGS) 916ALL_LDFLAGS += $(BASIC_LDFLAGS)
603 917
604export TAR INSTALL DESTDIR SHELL_PATH 918export TAR INSTALL DESTDIR SHELL_PATH
@@ -608,7 +922,7 @@ export TAR INSTALL DESTDIR SHELL_PATH
608 922
609SHELL = $(SHELL_PATH) 923SHELL = $(SHELL_PATH)
610 924
611all:: shell_compatibility_test $(ALL_PROGRAMS) $(BUILT_INS) $(OTHER_PROGRAMS) PERF-BUILD-OPTIONS 925all:: shell_compatibility_test $(ALL_PROGRAMS) $(BUILT_INS) $(OTHER_PROGRAMS) $(OUTPUT)PERF-BUILD-OPTIONS
612ifneq (,$X) 926ifneq (,$X)
613 $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) perf$X)), test '$p' -ef '$p$X' || $(RM) '$p';) 927 $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) perf$X)), test '$p' -ef '$p$X' || $(RM) '$p';)
614endif 928endif
@@ -620,45 +934,51 @@ please_set_SHELL_PATH_to_a_more_modern_shell:
620 934
621shell_compatibility_test: please_set_SHELL_PATH_to_a_more_modern_shell 935shell_compatibility_test: please_set_SHELL_PATH_to_a_more_modern_shell
622 936
623strip: $(PROGRAMS) perf$X 937strip: $(PROGRAMS) $(OUTPUT)perf$X
624 $(STRIP) $(STRIP_OPTS) $(PROGRAMS) perf$X 938 $(STRIP) $(STRIP_OPTS) $(PROGRAMS) $(OUTPUT)perf$X
625 939
626perf.o: perf.c common-cmds.h PERF-CFLAGS 940$(OUTPUT)perf.o: perf.c $(OUTPUT)common-cmds.h $(OUTPUT)PERF-CFLAGS
627 $(QUIET_CC)$(CC) -DPERF_VERSION='"$(PERF_VERSION)"' \ 941 $(QUIET_CC)$(CC) -DPERF_VERSION='"$(PERF_VERSION)"' \
628 '-DPERF_HTML_PATH="$(htmldir_SQ)"' \ 942 '-DPERF_HTML_PATH="$(htmldir_SQ)"' \
629 $(ALL_CFLAGS) -c $(filter %.c,$^) 943 $(ALL_CFLAGS) -c $(filter %.c,$^) -o $@
944
945$(OUTPUT)perf$X: $(OUTPUT)perf.o $(BUILTIN_OBJS) $(PERFLIBS)
946 $(QUIET_LINK)$(CC) $(ALL_CFLAGS) $(ALL_LDFLAGS) $(OUTPUT)perf.o \
947 $(BUILTIN_OBJS) $(LIBS) -o $@
630 948
631perf$X: perf.o $(BUILTIN_OBJS) $(PERFLIBS) 949$(OUTPUT)builtin-help.o: builtin-help.c $(OUTPUT)common-cmds.h $(OUTPUT)PERF-CFLAGS
632 $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ perf.o \ 950 $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \
633 $(BUILTIN_OBJS) $(ALL_LDFLAGS) $(LIBS) 951 '-DPERF_HTML_PATH="$(htmldir_SQ)"' \
952 '-DPERF_MAN_PATH="$(mandir_SQ)"' \
953 '-DPERF_INFO_PATH="$(infodir_SQ)"' $<
634 954
635builtin-help.o: builtin-help.c common-cmds.h PERF-CFLAGS 955$(OUTPUT)builtin-timechart.o: builtin-timechart.c $(OUTPUT)common-cmds.h $(OUTPUT)PERF-CFLAGS
636 $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) \ 956 $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \
637 '-DPERF_HTML_PATH="$(htmldir_SQ)"' \ 957 '-DPERF_HTML_PATH="$(htmldir_SQ)"' \
638 '-DPERF_MAN_PATH="$(mandir_SQ)"' \ 958 '-DPERF_MAN_PATH="$(mandir_SQ)"' \
639 '-DPERF_INFO_PATH="$(infodir_SQ)"' $< 959 '-DPERF_INFO_PATH="$(infodir_SQ)"' $<
640 960
641$(BUILT_INS): perf$X 961$(BUILT_INS): $(OUTPUT)perf$X
642 $(QUIET_BUILT_IN)$(RM) $@ && \ 962 $(QUIET_BUILT_IN)$(RM) $@ && \
643 ln perf$X $@ 2>/dev/null || \ 963 ln perf$X $@ 2>/dev/null || \
644 ln -s perf$X $@ 2>/dev/null || \ 964 ln -s perf$X $@ 2>/dev/null || \
645 cp perf$X $@ 965 cp perf$X $@
646 966
647common-cmds.h: util/generate-cmdlist.sh command-list.txt 967$(OUTPUT)common-cmds.h: util/generate-cmdlist.sh command-list.txt
648 968
649common-cmds.h: $(wildcard Documentation/perf-*.txt) 969$(OUTPUT)common-cmds.h: $(wildcard Documentation/perf-*.txt)
650 $(QUIET_GEN)util/generate-cmdlist.sh > $@+ && mv $@+ $@ 970 $(QUIET_GEN). util/generate-cmdlist.sh > $@+ && mv $@+ $@
651 971
652$(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh 972$(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
653 $(QUIET_GEN)$(RM) $@ $@+ && \ 973 $(QUIET_GEN)$(RM) $(OUTPUT)$@ $(OUTPUT)$@+ && \
654 sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \ 974 sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
655 -e 's|@SHELL_PATH@|$(SHELL_PATH_SQ)|' \ 975 -e 's|@SHELL_PATH@|$(SHELL_PATH_SQ)|' \
656 -e 's|@@PERL@@|$(PERL_PATH_SQ)|g' \ 976 -e 's|@@PERL@@|$(PERL_PATH_SQ)|g' \
657 -e 's/@@PERF_VERSION@@/$(PERF_VERSION)/g' \ 977 -e 's/@@PERF_VERSION@@/$(PERF_VERSION)/g' \
658 -e 's/@@NO_CURL@@/$(NO_CURL)/g' \ 978 -e 's/@@NO_CURL@@/$(NO_CURL)/g' \
659 $@.sh >$@+ && \ 979 $@.sh > $(OUTPUT)$@+ && \
660 chmod +x $@+ && \ 980 chmod +x $(OUTPUT)$@+ && \
661 mv $@+ $@ 981 mv $(OUTPUT)$@+ $(OUTPUT)$@
662 982
663configure: configure.ac 983configure: configure.ac
664 $(QUIET_GEN)$(RM) $@ $<+ && \ 984 $(QUIET_GEN)$(RM) $@ $<+ && \
@@ -668,38 +988,73 @@ configure: configure.ac
668 $(RM) $<+ 988 $(RM) $<+
669 989
670# These can record PERF_VERSION 990# These can record PERF_VERSION
671perf.o perf.spec \ 991$(OUTPUT)perf.o perf.spec \
672 $(patsubst %.sh,%,$(SCRIPT_SH)) \ 992 $(patsubst %.sh,%,$(SCRIPT_SH)) \
673 $(patsubst %.perl,%,$(SCRIPT_PERL)) \ 993 $(patsubst %.perl,%,$(SCRIPT_PERL)) \
674 : PERF-VERSION-FILE 994 : $(OUTPUT)PERF-VERSION-FILE
675 995
676%.o: %.c PERF-CFLAGS 996$(OUTPUT)%.o: %.c $(OUTPUT)PERF-CFLAGS
677 $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) $< 997 $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $<
678%.s: %.c PERF-CFLAGS 998$(OUTPUT)%.s: %.c $(OUTPUT)PERF-CFLAGS
679 $(QUIET_CC)$(CC) -S $(ALL_CFLAGS) $< 999 $(QUIET_CC)$(CC) -S $(ALL_CFLAGS) $<
680%.o: %.S 1000$(OUTPUT)%.o: %.S
681 $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) $< 1001 $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $<
682 1002
683util/exec_cmd.o: util/exec_cmd.c PERF-CFLAGS 1003$(OUTPUT)util/exec_cmd.o: util/exec_cmd.c $(OUTPUT)PERF-CFLAGS
684 $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) \ 1004 $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \
685 '-DPERF_EXEC_PATH="$(perfexecdir_SQ)"' \ 1005 '-DPERF_EXEC_PATH="$(perfexecdir_SQ)"' \
686 '-DBINDIR="$(bindir_relative_SQ)"' \ 1006 '-DBINDIR="$(bindir_relative_SQ)"' \
687 '-DPREFIX="$(prefix_SQ)"' \ 1007 '-DPREFIX="$(prefix_SQ)"' \
688 $< 1008 $<
689 1009
690builtin-init-db.o: builtin-init-db.c PERF-CFLAGS 1010$(OUTPUT)builtin-init-db.o: builtin-init-db.c $(OUTPUT)PERF-CFLAGS
691 $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DDEFAULT_PERF_TEMPLATE_DIR='"$(template_dir_SQ)"' $< 1011 $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DDEFAULT_PERF_TEMPLATE_DIR='"$(template_dir_SQ)"' $<
1012
1013$(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS
1014 $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $<
1015
1016$(OUTPUT)util/ui/browser.o: util/ui/browser.c $(OUTPUT)PERF-CFLAGS
1017 $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $<
692 1018
693util/config.o: util/config.c PERF-CFLAGS 1019$(OUTPUT)util/ui/browsers/annotate.o: util/ui/browsers/annotate.c $(OUTPUT)PERF-CFLAGS
694 $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< 1020 $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $<
695 1021
696perf-%$X: %.o $(PERFLIBS) 1022$(OUTPUT)util/ui/browsers/hists.o: util/ui/browsers/hists.c $(OUTPUT)PERF-CFLAGS
1023 $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $<
1024
1025$(OUTPUT)util/ui/browsers/map.o: util/ui/browsers/map.c $(OUTPUT)PERF-CFLAGS
1026 $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $<
1027
1028$(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS
1029 $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $<
1030
1031$(OUTPUT)util/scripting-engines/trace-event-perl.o: util/scripting-engines/trace-event-perl.c $(OUTPUT)PERF-CFLAGS
1032 $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow $<
1033
1034$(OUTPUT)scripts/perl/Perf-Trace-Util/Context.o: scripts/perl/Perf-Trace-Util/Context.c $(OUTPUT)PERF-CFLAGS
1035 $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-nested-externs $<
1036
1037$(OUTPUT)util/scripting-engines/trace-event-python.o: util/scripting-engines/trace-event-python.c $(OUTPUT)PERF-CFLAGS
1038 $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $(PYTHON_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow $<
1039
1040$(OUTPUT)scripts/python/Perf-Trace-Util/Context.o: scripts/python/Perf-Trace-Util/Context.c $(OUTPUT)PERF-CFLAGS
1041 $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $(PYTHON_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-nested-externs $<
1042
1043$(OUTPUT)perf-%$X: %.o $(PERFLIBS)
697 $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) 1044 $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
698 1045
699$(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H) 1046$(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H)
700$(patsubst perf-%$X,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h) 1047$(patsubst perf-%$X,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h)
701builtin-revert.o wt-status.o: wt-status.h 1048builtin-revert.o wt-status.o: wt-status.h
702 1049
1050# we compile into subdirectories. if the target directory is not the source directory, they might not exists. So
1051# we depend the various files onto their directories.
1052DIRECTORY_DEPS = $(LIB_OBJS) $(BUILTIN_OBJS) $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)common-cmds.h
1053$(DIRECTORY_DEPS): | $(sort $(dir $(DIRECTORY_DEPS)))
1054# In the second step, we make a rule to actually create these directories
1055$(sort $(dir $(DIRECTORY_DEPS))):
1056 $(QUIET_MKDIR)$(MKDIR) -p $@ 2>/dev/null
1057
703$(LIB_FILE): $(LIB_OBJS) 1058$(LIB_FILE): $(LIB_OBJS)
704 $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIB_OBJS) 1059 $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIB_OBJS)
705 1060
@@ -734,17 +1089,17 @@ cscope:
734TRACK_CFLAGS = $(subst ','\'',$(ALL_CFLAGS)):\ 1089TRACK_CFLAGS = $(subst ','\'',$(ALL_CFLAGS)):\
735 $(bindir_SQ):$(perfexecdir_SQ):$(template_dir_SQ):$(prefix_SQ) 1090 $(bindir_SQ):$(perfexecdir_SQ):$(template_dir_SQ):$(prefix_SQ)
736 1091
737PERF-CFLAGS: .FORCE-PERF-CFLAGS 1092$(OUTPUT)PERF-CFLAGS: .FORCE-PERF-CFLAGS
738 @FLAGS='$(TRACK_CFLAGS)'; \ 1093 @FLAGS='$(TRACK_CFLAGS)'; \
739 if test x"$$FLAGS" != x"`cat PERF-CFLAGS 2>/dev/null`" ; then \ 1094 if test x"$$FLAGS" != x"`cat $(OUTPUT)PERF-CFLAGS 2>/dev/null`" ; then \
740 echo 1>&2 " * new build flags or prefix"; \ 1095 echo 1>&2 " * new build flags or prefix"; \
741 echo "$$FLAGS" >PERF-CFLAGS; \ 1096 echo "$$FLAGS" >$(OUTPUT)PERF-CFLAGS; \
742 fi 1097 fi
743 1098
744# We need to apply sq twice, once to protect from the shell 1099# We need to apply sq twice, once to protect from the shell
745# that runs PERF-BUILD-OPTIONS, and then again to protect it 1100# that runs $(OUTPUT)PERF-BUILD-OPTIONS, and then again to protect it
746# and the first level quoting from the shell that runs "echo". 1101# and the first level quoting from the shell that runs "echo".
747PERF-BUILD-OPTIONS: .FORCE-PERF-BUILD-OPTIONS 1102$(OUTPUT)PERF-BUILD-OPTIONS: .FORCE-PERF-BUILD-OPTIONS
748 @echo SHELL_PATH=\''$(subst ','\'',$(SHELL_PATH_SQ))'\' >$@ 1103 @echo SHELL_PATH=\''$(subst ','\'',$(SHELL_PATH_SQ))'\' >$@
749 @echo TAR=\''$(subst ','\'',$(subst ','\'',$(TAR)))'\' >>$@ 1104 @echo TAR=\''$(subst ','\'',$(subst ','\'',$(TAR)))'\' >>$@
750 @echo NO_CURL=\''$(subst ','\'',$(subst ','\'',$(NO_CURL)))'\' >>$@ 1105 @echo NO_CURL=\''$(subst ','\'',$(subst ','\'',$(NO_CURL)))'\' >>$@
@@ -765,7 +1120,7 @@ all:: $(TEST_PROGRAMS)
765 1120
766export NO_SVN_TESTS 1121export NO_SVN_TESTS
767 1122
768check: common-cmds.h 1123check: $(OUTPUT)common-cmds.h
769 if sparse; \ 1124 if sparse; \
770 then \ 1125 then \
771 for i in *.c */*.c; \ 1126 for i in *.c */*.c; \
@@ -799,12 +1154,24 @@ export perfexec_instdir
799 1154
800install: all 1155install: all
801 $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)' 1156 $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)'
802 $(INSTALL) perf$X '$(DESTDIR_SQ)$(bindir_SQ)' 1157 $(INSTALL) $(OUTPUT)perf$X '$(DESTDIR_SQ)$(bindir_SQ)'
1158 $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace'
1159 $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/bin'
1160 $(INSTALL) $(OUTPUT)perf-archive -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)'
1161 $(INSTALL) scripts/perl/Perf-Trace-Util/lib/Perf/Trace/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace'
1162 $(INSTALL) scripts/perl/*.pl -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl'
1163 $(INSTALL) scripts/perl/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/bin'
1164 $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/Perf-Trace-Util/lib/Perf/Trace'
1165 $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/bin'
1166 $(INSTALL) scripts/python/Perf-Trace-Util/lib/Perf/Trace/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/Perf-Trace-Util/lib/Perf/Trace'
1167 $(INSTALL) scripts/python/*.py -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python'
1168 $(INSTALL) scripts/python/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/bin'
1169
803ifdef BUILT_INS 1170ifdef BUILT_INS
804 $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' 1171 $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)'
805 $(INSTALL) $(BUILT_INS) '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' 1172 $(INSTALL) $(BUILT_INS) '$(DESTDIR_SQ)$(perfexec_instdir_SQ)'
806ifneq (,$X) 1173ifneq (,$X)
807 $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) perf$X)), $(RM) '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/$p';) 1174 $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) $(OUTPUT)perf$X)), $(RM) '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/$p';)
808endif 1175endif
809endif 1176endif
810 1177
@@ -885,17 +1252,17 @@ distclean: clean
885# $(RM) configure 1252# $(RM) configure
886 1253
887clean: 1254clean:
888 $(RM) *.o */*.o $(LIB_FILE) 1255 $(RM) *.o */*.o */*/*.o */*/*/*.o $(LIB_FILE)
889 $(RM) $(ALL_PROGRAMS) $(BUILT_INS) perf$X 1256 $(RM) $(ALL_PROGRAMS) $(BUILT_INS) perf$X
890 $(RM) $(TEST_PROGRAMS) 1257 $(RM) $(TEST_PROGRAMS)
891 $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h TAGS tags cscope* 1258 $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope*
892 $(RM) -r autom4te.cache 1259 $(RM) -r autom4te.cache
893 $(RM) config.log config.mak.autogen config.mak.append config.status config.cache 1260 $(RM) config.log config.mak.autogen config.mak.append config.status config.cache
894 $(RM) -r $(PERF_TARNAME) .doc-tmp-dir 1261 $(RM) -r $(PERF_TARNAME) .doc-tmp-dir
895 $(RM) $(PERF_TARNAME).tar.gz perf-core_$(PERF_VERSION)-*.tar.gz 1262 $(RM) $(PERF_TARNAME).tar.gz perf-core_$(PERF_VERSION)-*.tar.gz
896 $(RM) $(htmldocs).tar.gz $(manpages).tar.gz 1263 $(RM) $(htmldocs).tar.gz $(manpages).tar.gz
897 $(MAKE) -C Documentation/ clean 1264 $(MAKE) -C Documentation/ clean
898 $(RM) PERF-VERSION-FILE PERF-CFLAGS PERF-BUILD-OPTIONS 1265 $(RM) $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)PERF-CFLAGS $(OUTPUT)PERF-BUILD-OPTIONS
899 1266
900.PHONY: all install clean strip 1267.PHONY: all install clean strip
901.PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell 1268.PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell
diff --git a/tools/perf/arch/arm/Makefile b/tools/perf/arch/arm/Makefile
new file mode 100644
index 00000000000..15130b50dfe
--- /dev/null
+++ b/tools/perf/arch/arm/Makefile
@@ -0,0 +1,4 @@
1ifndef NO_DWARF
2PERF_HAVE_DWARF_REGS := 1
3LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o
4endif
diff --git a/tools/perf/arch/arm/util/dwarf-regs.c b/tools/perf/arch/arm/util/dwarf-regs.c
new file mode 100644
index 00000000000..fff6450c8c9
--- /dev/null
+++ b/tools/perf/arch/arm/util/dwarf-regs.c
@@ -0,0 +1,64 @@
1/*
2 * Mapping of DWARF debug register numbers into register names.
3 *
4 * Copyright (C) 2010 Will Deacon, ARM Ltd.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 */
10
11#include <libio.h>
12#include <dwarf-regs.h>
13
14struct pt_regs_dwarfnum {
15 const char *name;
16 unsigned int dwarfnum;
17};
18
19#define STR(s) #s
20#define REG_DWARFNUM_NAME(r, num) {.name = r, .dwarfnum = num}
21#define GPR_DWARFNUM_NAME(num) \
22 {.name = STR(%r##num), .dwarfnum = num}
23#define REG_DWARFNUM_END {.name = NULL, .dwarfnum = 0}
24
25/*
26 * Reference:
27 * http://infocenter.arm.com/help/topic/com.arm.doc.ihi0040a/IHI0040A_aadwarf.pdf
28 */
29static const struct pt_regs_dwarfnum regdwarfnum_table[] = {
30 GPR_DWARFNUM_NAME(0),
31 GPR_DWARFNUM_NAME(1),
32 GPR_DWARFNUM_NAME(2),
33 GPR_DWARFNUM_NAME(3),
34 GPR_DWARFNUM_NAME(4),
35 GPR_DWARFNUM_NAME(5),
36 GPR_DWARFNUM_NAME(6),
37 GPR_DWARFNUM_NAME(7),
38 GPR_DWARFNUM_NAME(8),
39 GPR_DWARFNUM_NAME(9),
40 GPR_DWARFNUM_NAME(10),
41 REG_DWARFNUM_NAME("%fp", 11),
42 REG_DWARFNUM_NAME("%ip", 12),
43 REG_DWARFNUM_NAME("%sp", 13),
44 REG_DWARFNUM_NAME("%lr", 14),
45 REG_DWARFNUM_NAME("%pc", 15),
46 REG_DWARFNUM_END,
47};
48
49/**
50 * get_arch_regstr() - lookup register name from it's DWARF register number
51 * @n: the DWARF register number
52 *
53 * get_arch_regstr() returns the name of the register in struct
54 * regdwarfnum_table from it's DWARF register number. If the register is not
55 * found in the table, this returns NULL;
56 */
57const char *get_arch_regstr(unsigned int n)
58{
59 const struct pt_regs_dwarfnum *roff;
60 for (roff = regdwarfnum_table; roff->name != NULL; roff++)
61 if (roff->dwarfnum == n)
62 return roff->name;
63 return NULL;
64}
diff --git a/tools/perf/arch/powerpc/Makefile b/tools/perf/arch/powerpc/Makefile
new file mode 100644
index 00000000000..15130b50dfe
--- /dev/null
+++ b/tools/perf/arch/powerpc/Makefile
@@ -0,0 +1,4 @@
1ifndef NO_DWARF
2PERF_HAVE_DWARF_REGS := 1
3LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o
4endif
diff --git a/tools/perf/arch/powerpc/util/dwarf-regs.c b/tools/perf/arch/powerpc/util/dwarf-regs.c
new file mode 100644
index 00000000000..48ae0c5e3f7
--- /dev/null
+++ b/tools/perf/arch/powerpc/util/dwarf-regs.c
@@ -0,0 +1,88 @@
1/*
2 * Mapping of DWARF debug register numbers into register names.
3 *
4 * Copyright (C) 2010 Ian Munsie, IBM Corporation.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 */
11
12#include <libio.h>
13#include <dwarf-regs.h>
14
15
16struct pt_regs_dwarfnum {
17 const char *name;
18 unsigned int dwarfnum;
19};
20
21#define STR(s) #s
22#define REG_DWARFNUM_NAME(r, num) {.name = r, .dwarfnum = num}
23#define GPR_DWARFNUM_NAME(num) \
24 {.name = STR(%gpr##num), .dwarfnum = num}
25#define REG_DWARFNUM_END {.name = NULL, .dwarfnum = 0}
26
27/*
28 * Reference:
29 * http://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi-1.9.html
30 */
31static const struct pt_regs_dwarfnum regdwarfnum_table[] = {
32 GPR_DWARFNUM_NAME(0),
33 GPR_DWARFNUM_NAME(1),
34 GPR_DWARFNUM_NAME(2),
35 GPR_DWARFNUM_NAME(3),
36 GPR_DWARFNUM_NAME(4),
37 GPR_DWARFNUM_NAME(5),
38 GPR_DWARFNUM_NAME(6),
39 GPR_DWARFNUM_NAME(7),
40 GPR_DWARFNUM_NAME(8),
41 GPR_DWARFNUM_NAME(9),
42 GPR_DWARFNUM_NAME(10),
43 GPR_DWARFNUM_NAME(11),
44 GPR_DWARFNUM_NAME(12),
45 GPR_DWARFNUM_NAME(13),
46 GPR_DWARFNUM_NAME(14),
47 GPR_DWARFNUM_NAME(15),
48 GPR_DWARFNUM_NAME(16),
49 GPR_DWARFNUM_NAME(17),
50 GPR_DWARFNUM_NAME(18),
51 GPR_DWARFNUM_NAME(19),
52 GPR_DWARFNUM_NAME(20),
53 GPR_DWARFNUM_NAME(21),
54 GPR_DWARFNUM_NAME(22),
55 GPR_DWARFNUM_NAME(23),
56 GPR_DWARFNUM_NAME(24),
57 GPR_DWARFNUM_NAME(25),
58 GPR_DWARFNUM_NAME(26),
59 GPR_DWARFNUM_NAME(27),
60 GPR_DWARFNUM_NAME(28),
61 GPR_DWARFNUM_NAME(29),
62 GPR_DWARFNUM_NAME(30),
63 GPR_DWARFNUM_NAME(31),
64 REG_DWARFNUM_NAME("%msr", 66),
65 REG_DWARFNUM_NAME("%ctr", 109),
66 REG_DWARFNUM_NAME("%link", 108),
67 REG_DWARFNUM_NAME("%xer", 101),
68 REG_DWARFNUM_NAME("%dar", 119),
69 REG_DWARFNUM_NAME("%dsisr", 118),
70 REG_DWARFNUM_END,
71};
72
73/**
74 * get_arch_regstr() - lookup register name from it's DWARF register number
75 * @n: the DWARF register number
76 *
77 * get_arch_regstr() returns the name of the register in struct
78 * regdwarfnum_table from it's DWARF register number. If the register is not
79 * found in the table, this returns NULL;
80 */
81const char *get_arch_regstr(unsigned int n)
82{
83 const struct pt_regs_dwarfnum *roff;
84 for (roff = regdwarfnum_table; roff->name != NULL; roff++)
85 if (roff->dwarfnum == n)
86 return roff->name;
87 return NULL;
88}
diff --git a/tools/perf/arch/s390/Makefile b/tools/perf/arch/s390/Makefile
new file mode 100644
index 00000000000..15130b50dfe
--- /dev/null
+++ b/tools/perf/arch/s390/Makefile
@@ -0,0 +1,4 @@
1ifndef NO_DWARF
2PERF_HAVE_DWARF_REGS := 1
3LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o
4endif
diff --git a/tools/perf/arch/s390/util/dwarf-regs.c b/tools/perf/arch/s390/util/dwarf-regs.c
new file mode 100644
index 00000000000..e19653e025f
--- /dev/null
+++ b/tools/perf/arch/s390/util/dwarf-regs.c
@@ -0,0 +1,22 @@
1/*
2 * Mapping of DWARF debug register numbers into register names.
3 *
4 * Copyright IBM Corp. 2010
5 * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>,
6 *
7 */
8
9#include <libio.h>
10#include <dwarf-regs.h>
11
12#define NUM_GPRS 16
13
14static const char *gpr_names[NUM_GPRS] = {
15 "%r0", "%r1", "%r2", "%r3", "%r4", "%r5", "%r6", "%r7",
16 "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15",
17};
18
19const char *get_arch_regstr(unsigned int n)
20{
21 return (n >= NUM_GPRS) ? NULL : gpr_names[n];
22}
diff --git a/tools/perf/arch/sh/Makefile b/tools/perf/arch/sh/Makefile
new file mode 100644
index 00000000000..15130b50dfe
--- /dev/null
+++ b/tools/perf/arch/sh/Makefile
@@ -0,0 +1,4 @@
1ifndef NO_DWARF
2PERF_HAVE_DWARF_REGS := 1
3LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o
4endif
diff --git a/tools/perf/arch/sh/util/dwarf-regs.c b/tools/perf/arch/sh/util/dwarf-regs.c
new file mode 100644
index 00000000000..a11edb007a6
--- /dev/null
+++ b/tools/perf/arch/sh/util/dwarf-regs.c
@@ -0,0 +1,55 @@
1/*
2 * Mapping of DWARF debug register numbers into register names.
3 *
4 * Copyright (C) 2010 Matt Fleming <matt@console-pimps.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 *
20 */
21
22#include <libio.h>
23#include <dwarf-regs.h>
24
25/*
26 * Generic dwarf analysis helpers
27 */
28
29#define SH_MAX_REGS 18
30const char *sh_regs_table[SH_MAX_REGS] = {
31 "r0",
32 "r1",
33 "r2",
34 "r3",
35 "r4",
36 "r5",
37 "r6",
38 "r7",
39 "r8",
40 "r9",
41 "r10",
42 "r11",
43 "r12",
44 "r13",
45 "r14",
46 "r15",
47 "pc",
48 "pr",
49};
50
51/* Return architecture dependent register string (for kprobe-tracer) */
52const char *get_arch_regstr(unsigned int n)
53{
54 return (n <= SH_MAX_REGS) ? sh_regs_table[n] : NULL;
55}
diff --git a/tools/perf/arch/sparc/Makefile b/tools/perf/arch/sparc/Makefile
new file mode 100644
index 00000000000..15130b50dfe
--- /dev/null
+++ b/tools/perf/arch/sparc/Makefile
@@ -0,0 +1,4 @@
1ifndef NO_DWARF
2PERF_HAVE_DWARF_REGS := 1
3LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o
4endif
diff --git a/tools/perf/arch/sparc/util/dwarf-regs.c b/tools/perf/arch/sparc/util/dwarf-regs.c
new file mode 100644
index 00000000000..0ab88483720
--- /dev/null
+++ b/tools/perf/arch/sparc/util/dwarf-regs.c
@@ -0,0 +1,43 @@
1/*
2 * Mapping of DWARF debug register numbers into register names.
3 *
4 * Copyright (C) 2010 David S. Miller <davem@davemloft.net>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 */
11
12#include <libio.h>
13#include <dwarf-regs.h>
14
15#define SPARC_MAX_REGS 96
16
17const char *sparc_regs_table[SPARC_MAX_REGS] = {
18 "%g0", "%g1", "%g2", "%g3", "%g4", "%g5", "%g6", "%g7",
19 "%o0", "%o1", "%o2", "%o3", "%o4", "%o5", "%sp", "%o7",
20 "%l0", "%l1", "%l2", "%l3", "%l4", "%l5", "%l6", "%l7",
21 "%i0", "%i1", "%i2", "%i3", "%i4", "%i5", "%fp", "%i7",
22 "%f0", "%f1", "%f2", "%f3", "%f4", "%f5", "%f6", "%f7",
23 "%f8", "%f9", "%f10", "%f11", "%f12", "%f13", "%f14", "%f15",
24 "%f16", "%f17", "%f18", "%f19", "%f20", "%f21", "%f22", "%f23",
25 "%f24", "%f25", "%f26", "%f27", "%f28", "%f29", "%f30", "%f31",
26 "%f32", "%f33", "%f34", "%f35", "%f36", "%f37", "%f38", "%f39",
27 "%f40", "%f41", "%f42", "%f43", "%f44", "%f45", "%f46", "%f47",
28 "%f48", "%f49", "%f50", "%f51", "%f52", "%f53", "%f54", "%f55",
29 "%f56", "%f57", "%f58", "%f59", "%f60", "%f61", "%f62", "%f63",
30};
31
32/**
33 * get_arch_regstr() - lookup register name from it's DWARF register number
34 * @n: the DWARF register number
35 *
36 * get_arch_regstr() returns the name of the register in struct
37 * regdwarfnum_table from it's DWARF register number. If the register is not
38 * found in the table, this returns NULL;
39 */
40const char *get_arch_regstr(unsigned int n)
41{
42 return (n <= SPARC_MAX_REGS) ? sparc_regs_table[n] : NULL;
43}
diff --git a/tools/perf/arch/x86/Makefile b/tools/perf/arch/x86/Makefile
new file mode 100644
index 00000000000..15130b50dfe
--- /dev/null
+++ b/tools/perf/arch/x86/Makefile
@@ -0,0 +1,4 @@
1ifndef NO_DWARF
2PERF_HAVE_DWARF_REGS := 1
3LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o
4endif
diff --git a/tools/perf/arch/x86/util/dwarf-regs.c b/tools/perf/arch/x86/util/dwarf-regs.c
new file mode 100644
index 00000000000..a794d308192
--- /dev/null
+++ b/tools/perf/arch/x86/util/dwarf-regs.c
@@ -0,0 +1,75 @@
1/*
2 * dwarf-regs.c : Mapping of DWARF debug register numbers into register names.
3 * Extracted from probe-finder.c
4 *
5 * Written by Masami Hiramatsu <mhiramat@redhat.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 *
21 */
22
23#include <libio.h>
24#include <dwarf-regs.h>
25
26/*
27 * Generic dwarf analysis helpers
28 */
29
30#define X86_32_MAX_REGS 8
31const char *x86_32_regs_table[X86_32_MAX_REGS] = {
32 "%ax",
33 "%cx",
34 "%dx",
35 "%bx",
36 "$stack", /* Stack address instead of %sp */
37 "%bp",
38 "%si",
39 "%di",
40};
41
42#define X86_64_MAX_REGS 16
43const char *x86_64_regs_table[X86_64_MAX_REGS] = {
44 "%ax",
45 "%dx",
46 "%cx",
47 "%bx",
48 "%si",
49 "%di",
50 "%bp",
51 "%sp",
52 "%r8",
53 "%r9",
54 "%r10",
55 "%r11",
56 "%r12",
57 "%r13",
58 "%r14",
59 "%r15",
60};
61
62/* TODO: switching by dwarf address size */
63#ifdef __x86_64__
64#define ARCH_MAX_REGS X86_64_MAX_REGS
65#define arch_regs_table x86_64_regs_table
66#else
67#define ARCH_MAX_REGS X86_32_MAX_REGS
68#define arch_regs_table x86_32_regs_table
69#endif
70
71/* Return architecture dependent register string (for kprobe-tracer) */
72const char *get_arch_regstr(unsigned int n)
73{
74 return (n <= ARCH_MAX_REGS) ? arch_regs_table[n] : NULL;
75}
diff --git a/tools/perf/bench/bench.h b/tools/perf/bench/bench.h
new file mode 100644
index 00000000000..f7781c6267c
--- /dev/null
+++ b/tools/perf/bench/bench.h
@@ -0,0 +1,17 @@
1#ifndef BENCH_H
2#define BENCH_H
3
4extern int bench_sched_messaging(int argc, const char **argv, const char *prefix);
5extern int bench_sched_pipe(int argc, const char **argv, const char *prefix);
6extern int bench_mem_memcpy(int argc, const char **argv, const char *prefix __used);
7
8#define BENCH_FORMAT_DEFAULT_STR "default"
9#define BENCH_FORMAT_DEFAULT 0
10#define BENCH_FORMAT_SIMPLE_STR "simple"
11#define BENCH_FORMAT_SIMPLE 1
12
13#define BENCH_FORMAT_UNKNOWN -1
14
15extern int bench_format;
16
17#endif
diff --git a/tools/perf/bench/mem-memcpy-arch.h b/tools/perf/bench/mem-memcpy-arch.h
new file mode 100644
index 00000000000..a72e36cb539
--- /dev/null
+++ b/tools/perf/bench/mem-memcpy-arch.h
@@ -0,0 +1,12 @@
1
2#ifdef ARCH_X86_64
3
4#define MEMCPY_FN(fn, name, desc) \
5 extern void *fn(void *, const void *, size_t);
6
7#include "mem-memcpy-x86-64-asm-def.h"
8
9#undef MEMCPY_FN
10
11#endif
12
diff --git a/tools/perf/bench/mem-memcpy-x86-64-asm-def.h b/tools/perf/bench/mem-memcpy-x86-64-asm-def.h
new file mode 100644
index 00000000000..d588b87696f
--- /dev/null
+++ b/tools/perf/bench/mem-memcpy-x86-64-asm-def.h
@@ -0,0 +1,4 @@
1
2MEMCPY_FN(__memcpy,
3 "x86-64-unrolled",
4 "unrolled memcpy() in arch/x86/lib/memcpy_64.S")
diff --git a/tools/perf/bench/mem-memcpy-x86-64-asm.S b/tools/perf/bench/mem-memcpy-x86-64-asm.S
new file mode 100644
index 00000000000..a57b66e853c
--- /dev/null
+++ b/tools/perf/bench/mem-memcpy-x86-64-asm.S
@@ -0,0 +1,2 @@
1
2#include "../../../arch/x86/lib/memcpy_64.S"
diff --git a/tools/perf/bench/mem-memcpy.c b/tools/perf/bench/mem-memcpy.c
new file mode 100644
index 00000000000..db82021f4b9
--- /dev/null
+++ b/tools/perf/bench/mem-memcpy.c
@@ -0,0 +1,297 @@
1/*
2 * mem-memcpy.c
3 *
4 * memcpy: Simple memory copy in various ways
5 *
6 * Written by Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp>
7 */
8#include <ctype.h>
9
10#include "../perf.h"
11#include "../util/util.h"
12#include "../util/parse-options.h"
13#include "../util/header.h"
14#include "bench.h"
15#include "mem-memcpy-arch.h"
16
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20#include <sys/time.h>
21#include <errno.h>
22
23#define K 1024
24
25static const char *length_str = "1MB";
26static const char *routine = "default";
27static bool use_clock;
28static int clock_fd;
29static bool only_prefault;
30static bool no_prefault;
31
32static const struct option options[] = {
33 OPT_STRING('l', "length", &length_str, "1MB",
34 "Specify length of memory to copy. "
35 "available unit: B, MB, GB (upper and lower)"),
36 OPT_STRING('r', "routine", &routine, "default",
37 "Specify routine to copy"),
38 OPT_BOOLEAN('c', "clock", &use_clock,
39 "Use CPU clock for measuring"),
40 OPT_BOOLEAN('o', "only-prefault", &only_prefault,
41 "Show only the result with page faults before memcpy()"),
42 OPT_BOOLEAN('n', "no-prefault", &no_prefault,
43 "Show only the result without page faults before memcpy()"),
44 OPT_END()
45};
46
47typedef void *(*memcpy_t)(void *, const void *, size_t);
48
49struct routine {
50 const char *name;
51 const char *desc;
52 memcpy_t fn;
53};
54
55struct routine routines[] = {
56 { "default",
57 "Default memcpy() provided by glibc",
58 memcpy },
59#ifdef ARCH_X86_64
60
61#define MEMCPY_FN(fn, name, desc) { name, desc, fn },
62#include "mem-memcpy-x86-64-asm-def.h"
63#undef MEMCPY_FN
64
65#endif
66
67 { NULL,
68 NULL,
69 NULL }
70};
71
72static const char * const bench_mem_memcpy_usage[] = {
73 "perf bench mem memcpy <options>",
74 NULL
75};
76
77static struct perf_event_attr clock_attr = {
78 .type = PERF_TYPE_HARDWARE,
79 .config = PERF_COUNT_HW_CPU_CYCLES
80};
81
82static void init_clock(void)
83{
84 clock_fd = sys_perf_event_open(&clock_attr, getpid(), -1, -1, 0);
85
86 if (clock_fd < 0 && errno == ENOSYS)
87 die("No CONFIG_PERF_EVENTS=y kernel support configured?\n");
88 else
89 BUG_ON(clock_fd < 0);
90}
91
92static u64 get_clock(void)
93{
94 int ret;
95 u64 clk;
96
97 ret = read(clock_fd, &clk, sizeof(u64));
98 BUG_ON(ret != sizeof(u64));
99
100 return clk;
101}
102
103static double timeval2double(struct timeval *ts)
104{
105 return (double)ts->tv_sec +
106 (double)ts->tv_usec / (double)1000000;
107}
108
109static void alloc_mem(void **dst, void **src, size_t length)
110{
111 *dst = zalloc(length);
112 if (!dst)
113 die("memory allocation failed - maybe length is too large?\n");
114
115 *src = zalloc(length);
116 if (!src)
117 die("memory allocation failed - maybe length is too large?\n");
118}
119
120static u64 do_memcpy_clock(memcpy_t fn, size_t len, bool prefault)
121{
122 u64 clock_start = 0ULL, clock_end = 0ULL;
123 void *src = NULL, *dst = NULL;
124
125 alloc_mem(&src, &dst, len);
126
127 if (prefault)
128 fn(dst, src, len);
129
130 clock_start = get_clock();
131 fn(dst, src, len);
132 clock_end = get_clock();
133
134 free(src);
135 free(dst);
136 return clock_end - clock_start;
137}
138
139static double do_memcpy_gettimeofday(memcpy_t fn, size_t len, bool prefault)
140{
141 struct timeval tv_start, tv_end, tv_diff;
142 void *src = NULL, *dst = NULL;
143
144 alloc_mem(&src, &dst, len);
145
146 if (prefault)
147 fn(dst, src, len);
148
149 BUG_ON(gettimeofday(&tv_start, NULL));
150 fn(dst, src, len);
151 BUG_ON(gettimeofday(&tv_end, NULL));
152
153 timersub(&tv_end, &tv_start, &tv_diff);
154
155 free(src);
156 free(dst);
157 return (double)((double)len / timeval2double(&tv_diff));
158}
159
160#define pf (no_prefault ? 0 : 1)
161
162#define print_bps(x) do { \
163 if (x < K) \
164 printf(" %14lf B/Sec", x); \
165 else if (x < K * K) \
166 printf(" %14lfd KB/Sec", x / K); \
167 else if (x < K * K * K) \
168 printf(" %14lf MB/Sec", x / K / K); \
169 else \
170 printf(" %14lf GB/Sec", x / K / K / K); \
171 } while (0)
172
173int bench_mem_memcpy(int argc, const char **argv,
174 const char *prefix __used)
175{
176 int i;
177 size_t len;
178 double result_bps[2];
179 u64 result_clock[2];
180
181 argc = parse_options(argc, argv, options,
182 bench_mem_memcpy_usage, 0);
183
184 if (use_clock)
185 init_clock();
186
187 len = (size_t)perf_atoll((char *)length_str);
188
189 result_clock[0] = result_clock[1] = 0ULL;
190 result_bps[0] = result_bps[1] = 0.0;
191
192 if ((s64)len <= 0) {
193 fprintf(stderr, "Invalid length:%s\n", length_str);
194 return 1;
195 }
196
197 /* same to without specifying either of prefault and no-prefault */
198 if (only_prefault && no_prefault)
199 only_prefault = no_prefault = false;
200
201 for (i = 0; routines[i].name; i++) {
202 if (!strcmp(routines[i].name, routine))
203 break;
204 }
205 if (!routines[i].name) {
206 printf("Unknown routine:%s\n", routine);
207 printf("Available routines...\n");
208 for (i = 0; routines[i].name; i++) {
209 printf("\t%s ... %s\n",
210 routines[i].name, routines[i].desc);
211 }
212 return 1;
213 }
214
215 if (bench_format == BENCH_FORMAT_DEFAULT)
216 printf("# Copying %s Bytes ...\n\n", length_str);
217
218 if (!only_prefault && !no_prefault) {
219 /* show both of results */
220 if (use_clock) {
221 result_clock[0] =
222 do_memcpy_clock(routines[i].fn, len, false);
223 result_clock[1] =
224 do_memcpy_clock(routines[i].fn, len, true);
225 } else {
226 result_bps[0] =
227 do_memcpy_gettimeofday(routines[i].fn,
228 len, false);
229 result_bps[1] =
230 do_memcpy_gettimeofday(routines[i].fn,
231 len, true);
232 }
233 } else {
234 if (use_clock) {
235 result_clock[pf] =
236 do_memcpy_clock(routines[i].fn,
237 len, only_prefault);
238 } else {
239 result_bps[pf] =
240 do_memcpy_gettimeofday(routines[i].fn,
241 len, only_prefault);
242 }
243 }
244
245 switch (bench_format) {
246 case BENCH_FORMAT_DEFAULT:
247 if (!only_prefault && !no_prefault) {
248 if (use_clock) {
249 printf(" %14lf Clock/Byte\n",
250 (double)result_clock[0]
251 / (double)len);
252 printf(" %14lf Clock/Byte (with prefault)\n",
253 (double)result_clock[1]
254 / (double)len);
255 } else {
256 print_bps(result_bps[0]);
257 printf("\n");
258 print_bps(result_bps[1]);
259 printf(" (with prefault)\n");
260 }
261 } else {
262 if (use_clock) {
263 printf(" %14lf Clock/Byte",
264 (double)result_clock[pf]
265 / (double)len);
266 } else
267 print_bps(result_bps[pf]);
268
269 printf("%s\n", only_prefault ? " (with prefault)" : "");
270 }
271 break;
272 case BENCH_FORMAT_SIMPLE:
273 if (!only_prefault && !no_prefault) {
274 if (use_clock) {
275 printf("%lf %lf\n",
276 (double)result_clock[0] / (double)len,
277 (double)result_clock[1] / (double)len);
278 } else {
279 printf("%lf %lf\n",
280 result_bps[0], result_bps[1]);
281 }
282 } else {
283 if (use_clock) {
284 printf("%lf\n", (double)result_clock[pf]
285 / (double)len);
286 } else
287 printf("%lf\n", result_bps[pf]);
288 }
289 break;
290 default:
291 /* reaching this means there's some disaster: */
292 die("unknown format: %d\n", bench_format);
293 break;
294 }
295
296 return 0;
297}
diff --git a/tools/perf/bench/sched-messaging.c b/tools/perf/bench/sched-messaging.c
new file mode 100644
index 00000000000..d1d1b30f99c
--- /dev/null
+++ b/tools/perf/bench/sched-messaging.c
@@ -0,0 +1,336 @@
1/*
2 *
3 * sched-messaging.c
4 *
5 * messaging: Benchmark for scheduler and IPC mechanisms
6 *
7 * Based on hackbench by Rusty Russell <rusty@rustcorp.com.au>
8 * Ported to perf by Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp>
9 *
10 */
11
12#include "../perf.h"
13#include "../util/util.h"
14#include "../util/parse-options.h"
15#include "../builtin.h"
16#include "bench.h"
17
18/* Test groups of 20 processes spraying to 20 receivers */
19#include <pthread.h>
20#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
23#include <errno.h>
24#include <unistd.h>
25#include <sys/types.h>
26#include <sys/socket.h>
27#include <sys/wait.h>
28#include <sys/time.h>
29#include <sys/poll.h>
30#include <limits.h>
31
32#define DATASIZE 100
33
34static bool use_pipes = false;
35static unsigned int loops = 100;
36static bool thread_mode = false;
37static unsigned int num_groups = 10;
38
39struct sender_context {
40 unsigned int num_fds;
41 int ready_out;
42 int wakefd;
43 int out_fds[0];
44};
45
46struct receiver_context {
47 unsigned int num_packets;
48 int in_fds[2];
49 int ready_out;
50 int wakefd;
51};
52
53static void barf(const char *msg)
54{
55 fprintf(stderr, "%s (error: %s)\n", msg, strerror(errno));
56 exit(1);
57}
58
59static void fdpair(int fds[2])
60{
61 if (use_pipes) {
62 if (pipe(fds) == 0)
63 return;
64 } else {
65 if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0)
66 return;
67 }
68
69 barf(use_pipes ? "pipe()" : "socketpair()");
70}
71
72/* Block until we're ready to go */
73static void ready(int ready_out, int wakefd)
74{
75 char dummy;
76 struct pollfd pollfd = { .fd = wakefd, .events = POLLIN };
77
78 /* Tell them we're ready. */
79 if (write(ready_out, &dummy, 1) != 1)
80 barf("CLIENT: ready write");
81
82 /* Wait for "GO" signal */
83 if (poll(&pollfd, 1, -1) != 1)
84 barf("poll");
85}
86
87/* Sender sprays loops messages down each file descriptor */
88static void *sender(struct sender_context *ctx)
89{
90 char data[DATASIZE];
91 unsigned int i, j;
92
93 ready(ctx->ready_out, ctx->wakefd);
94
95 /* Now pump to every receiver. */
96 for (i = 0; i < loops; i++) {
97 for (j = 0; j < ctx->num_fds; j++) {
98 int ret, done = 0;
99
100again:
101 ret = write(ctx->out_fds[j], data + done,
102 sizeof(data)-done);
103 if (ret < 0)
104 barf("SENDER: write");
105 done += ret;
106 if (done < DATASIZE)
107 goto again;
108 }
109 }
110
111 return NULL;
112}
113
114
115/* One receiver per fd */
116static void *receiver(struct receiver_context* ctx)
117{
118 unsigned int i;
119
120 if (!thread_mode)
121 close(ctx->in_fds[1]);
122
123 /* Wait for start... */
124 ready(ctx->ready_out, ctx->wakefd);
125
126 /* Receive them all */
127 for (i = 0; i < ctx->num_packets; i++) {
128 char data[DATASIZE];
129 int ret, done = 0;
130
131again:
132 ret = read(ctx->in_fds[0], data + done, DATASIZE - done);
133 if (ret < 0)
134 barf("SERVER: read");
135 done += ret;
136 if (done < DATASIZE)
137 goto again;
138 }
139
140 return NULL;
141}
142
143static pthread_t create_worker(void *ctx, void *(*func)(void *))
144{
145 pthread_attr_t attr;
146 pthread_t childid;
147 int err;
148
149 if (!thread_mode) {
150 /* process mode */
151 /* Fork the receiver. */
152 switch (fork()) {
153 case -1:
154 barf("fork()");
155 break;
156 case 0:
157 (*func) (ctx);
158 exit(0);
159 break;
160 default:
161 break;
162 }
163
164 return (pthread_t)0;
165 }
166
167 if (pthread_attr_init(&attr) != 0)
168 barf("pthread_attr_init:");
169
170#ifndef __ia64__
171 if (pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN) != 0)
172 barf("pthread_attr_setstacksize");
173#endif
174
175 err = pthread_create(&childid, &attr, func, ctx);
176 if (err != 0) {
177 fprintf(stderr, "pthread_create failed: %s (%d)\n",
178 strerror(err), err);
179 exit(-1);
180 }
181 return childid;
182}
183
184static void reap_worker(pthread_t id)
185{
186 int proc_status;
187 void *thread_status;
188
189 if (!thread_mode) {
190 /* process mode */
191 wait(&proc_status);
192 if (!WIFEXITED(proc_status))
193 exit(1);
194 } else {
195 pthread_join(id, &thread_status);
196 }
197}
198
199/* One group of senders and receivers */
200static unsigned int group(pthread_t *pth,
201 unsigned int num_fds,
202 int ready_out,
203 int wakefd)
204{
205 unsigned int i;
206 struct sender_context *snd_ctx = malloc(sizeof(struct sender_context)
207 + num_fds * sizeof(int));
208
209 if (!snd_ctx)
210 barf("malloc()");
211
212 for (i = 0; i < num_fds; i++) {
213 int fds[2];
214 struct receiver_context *ctx = malloc(sizeof(*ctx));
215
216 if (!ctx)
217 barf("malloc()");
218
219
220 /* Create the pipe between client and server */
221 fdpair(fds);
222
223 ctx->num_packets = num_fds * loops;
224 ctx->in_fds[0] = fds[0];
225 ctx->in_fds[1] = fds[1];
226 ctx->ready_out = ready_out;
227 ctx->wakefd = wakefd;
228
229 pth[i] = create_worker(ctx, (void *)receiver);
230
231 snd_ctx->out_fds[i] = fds[1];
232 if (!thread_mode)
233 close(fds[0]);
234 }
235
236 /* Now we have all the fds, fork the senders */
237 for (i = 0; i < num_fds; i++) {
238 snd_ctx->ready_out = ready_out;
239 snd_ctx->wakefd = wakefd;
240 snd_ctx->num_fds = num_fds;
241
242 pth[num_fds+i] = create_worker(snd_ctx, (void *)sender);
243 }
244
245 /* Close the fds we have left */
246 if (!thread_mode)
247 for (i = 0; i < num_fds; i++)
248 close(snd_ctx->out_fds[i]);
249
250 /* Return number of children to reap */
251 return num_fds * 2;
252}
253
254static const struct option options[] = {
255 OPT_BOOLEAN('p', "pipe", &use_pipes,
256 "Use pipe() instead of socketpair()"),
257 OPT_BOOLEAN('t', "thread", &thread_mode,
258 "Be multi thread instead of multi process"),
259 OPT_UINTEGER('g', "group", &num_groups, "Specify number of groups"),
260 OPT_UINTEGER('l', "loop", &loops, "Specify number of loops"),
261 OPT_END()
262};
263
264static const char * const bench_sched_message_usage[] = {
265 "perf bench sched messaging <options>",
266 NULL
267};
268
269int bench_sched_messaging(int argc, const char **argv,
270 const char *prefix __used)
271{
272 unsigned int i, total_children;
273 struct timeval start, stop, diff;
274 unsigned int num_fds = 20;
275 int readyfds[2], wakefds[2];
276 char dummy;
277 pthread_t *pth_tab;
278
279 argc = parse_options(argc, argv, options,
280 bench_sched_message_usage, 0);
281
282 pth_tab = malloc(num_fds * 2 * num_groups * sizeof(pthread_t));
283 if (!pth_tab)
284 barf("main:malloc()");
285
286 fdpair(readyfds);
287 fdpair(wakefds);
288
289 total_children = 0;
290 for (i = 0; i < num_groups; i++)
291 total_children += group(pth_tab+total_children, num_fds,
292 readyfds[1], wakefds[0]);
293
294 /* Wait for everyone to be ready */
295 for (i = 0; i < total_children; i++)
296 if (read(readyfds[0], &dummy, 1) != 1)
297 barf("Reading for readyfds");
298
299 gettimeofday(&start, NULL);
300
301 /* Kick them off */
302 if (write(wakefds[1], &dummy, 1) != 1)
303 barf("Writing to start them");
304
305 /* Reap them all */
306 for (i = 0; i < total_children; i++)
307 reap_worker(pth_tab[i]);
308
309 gettimeofday(&stop, NULL);
310
311 timersub(&stop, &start, &diff);
312
313 switch (bench_format) {
314 case BENCH_FORMAT_DEFAULT:
315 printf("# %d sender and receiver %s per group\n",
316 num_fds, thread_mode ? "threads" : "processes");
317 printf("# %d groups == %d %s run\n\n",
318 num_groups, num_groups * 2 * num_fds,
319 thread_mode ? "threads" : "processes");
320 printf(" %14s: %lu.%03lu [sec]\n", "Total time",
321 diff.tv_sec,
322 (unsigned long) (diff.tv_usec/1000));
323 break;
324 case BENCH_FORMAT_SIMPLE:
325 printf("%lu.%03lu\n", diff.tv_sec,
326 (unsigned long) (diff.tv_usec/1000));
327 break;
328 default:
329 /* reaching here is something disaster */
330 fprintf(stderr, "Unknown format:%d\n", bench_format);
331 exit(1);
332 break;
333 }
334
335 return 0;
336}
diff --git a/tools/perf/bench/sched-pipe.c b/tools/perf/bench/sched-pipe.c
new file mode 100644
index 00000000000..d9ab3ce446a
--- /dev/null
+++ b/tools/perf/bench/sched-pipe.c
@@ -0,0 +1,127 @@
1/*
2 *
3 * sched-pipe.c
4 *
5 * pipe: Benchmark for pipe()
6 *
7 * Based on pipe-test-1m.c by Ingo Molnar <mingo@redhat.com>
8 * http://people.redhat.com/mingo/cfs-scheduler/tools/pipe-test-1m.c
9 * Ported to perf by Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp>
10 *
11 */
12
13#include "../perf.h"
14#include "../util/util.h"
15#include "../util/parse-options.h"
16#include "../builtin.h"
17#include "bench.h"
18
19#include <unistd.h>
20#include <stdio.h>
21#include <stdlib.h>
22#include <signal.h>
23#include <sys/wait.h>
24#include <linux/unistd.h>
25#include <string.h>
26#include <errno.h>
27#include <assert.h>
28#include <sys/time.h>
29#include <sys/types.h>
30
31#define LOOPS_DEFAULT 1000000
32static int loops = LOOPS_DEFAULT;
33
34static const struct option options[] = {
35 OPT_INTEGER('l', "loop", &loops,
36 "Specify number of loops"),
37 OPT_END()
38};
39
40static const char * const bench_sched_pipe_usage[] = {
41 "perf bench sched pipe <options>",
42 NULL
43};
44
45int bench_sched_pipe(int argc, const char **argv,
46 const char *prefix __used)
47{
48 int pipe_1[2], pipe_2[2];
49 int m = 0, i;
50 struct timeval start, stop, diff;
51 unsigned long long result_usec = 0;
52
53 /*
54 * why does "ret" exist?
55 * discarding returned value of read(), write()
56 * causes error in building environment for perf
57 */
58 int ret, wait_stat;
59 pid_t pid, retpid;
60
61 argc = parse_options(argc, argv, options,
62 bench_sched_pipe_usage, 0);
63
64 assert(!pipe(pipe_1));
65 assert(!pipe(pipe_2));
66
67 pid = fork();
68 assert(pid >= 0);
69
70 gettimeofday(&start, NULL);
71
72 if (!pid) {
73 for (i = 0; i < loops; i++) {
74 ret = read(pipe_1[0], &m, sizeof(int));
75 ret = write(pipe_2[1], &m, sizeof(int));
76 }
77 } else {
78 for (i = 0; i < loops; i++) {
79 ret = write(pipe_1[1], &m, sizeof(int));
80 ret = read(pipe_2[0], &m, sizeof(int));
81 }
82 }
83
84 gettimeofday(&stop, NULL);
85 timersub(&stop, &start, &diff);
86
87 if (pid) {
88 retpid = waitpid(pid, &wait_stat, 0);
89 assert((retpid == pid) && WIFEXITED(wait_stat));
90 } else {
91 exit(0);
92 }
93
94 switch (bench_format) {
95 case BENCH_FORMAT_DEFAULT:
96 printf("# Executed %d pipe operations between two tasks\n\n",
97 loops);
98
99 result_usec = diff.tv_sec * 1000000;
100 result_usec += diff.tv_usec;
101
102 printf(" %14s: %lu.%03lu [sec]\n\n", "Total time",
103 diff.tv_sec,
104 (unsigned long) (diff.tv_usec/1000));
105
106 printf(" %14lf usecs/op\n",
107 (double)result_usec / (double)loops);
108 printf(" %14d ops/sec\n",
109 (int)((double)loops /
110 ((double)result_usec / (double)1000000)));
111 break;
112
113 case BENCH_FORMAT_SIMPLE:
114 printf("%lu.%03lu\n",
115 diff.tv_sec,
116 (unsigned long) (diff.tv_usec / 1000));
117 break;
118
119 default:
120 /* reaching here is something disaster */
121 fprintf(stderr, "Unknown format:%d\n", bench_format);
122 exit(1);
123 break;
124 }
125
126 return 0;
127}
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index 7e58e3ad150..c056cdc0691 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -10,1119 +10,109 @@
10#include "util/util.h" 10#include "util/util.h"
11 11
12#include "util/color.h" 12#include "util/color.h"
13#include "util/list.h" 13#include <linux/list.h>
14#include "util/cache.h" 14#include "util/cache.h"
15#include "util/rbtree.h" 15#include <linux/rbtree.h>
16#include "util/symbol.h" 16#include "util/symbol.h"
17#include "util/string.h"
18 17
19#include "perf.h" 18#include "perf.h"
19#include "util/debug.h"
20 20
21#include "util/event.h"
21#include "util/parse-options.h" 22#include "util/parse-options.h"
22#include "util/parse-events.h" 23#include "util/parse-events.h"
23 24#include "util/thread.h"
24#define SHOW_KERNEL 1 25#include "util/sort.h"
25#define SHOW_USER 2 26#include "util/hist.h"
26#define SHOW_HV 4 27#include "util/session.h"
27
28#define MIN_GREEN 0.5
29#define MIN_RED 5.0
30
31 28
32static char const *input_name = "perf.data"; 29static char const *input_name = "perf.data";
33static char *vmlinux = "vmlinux";
34
35static char default_sort_order[] = "comm,symbol";
36static char *sort_order = default_sort_order;
37
38static int input;
39static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV;
40
41static int dump_trace = 0;
42#define dprintf(x...) do { if (dump_trace) printf(x); } while (0)
43
44static int verbose;
45
46static int print_line;
47
48static unsigned long page_size;
49static unsigned long mmap_window = 32;
50
51struct ip_event {
52 struct perf_event_header header;
53 u64 ip;
54 u32 pid, tid;
55};
56
57struct mmap_event {
58 struct perf_event_header header;
59 u32 pid, tid;
60 u64 start;
61 u64 len;
62 u64 pgoff;
63 char filename[PATH_MAX];
64};
65
66struct comm_event {
67 struct perf_event_header header;
68 u32 pid, tid;
69 char comm[16];
70};
71
72struct fork_event {
73 struct perf_event_header header;
74 u32 pid, ppid;
75};
76
77struct period_event {
78 struct perf_event_header header;
79 u64 time;
80 u64 id;
81 u64 sample_period;
82};
83
84typedef union event_union {
85 struct perf_event_header header;
86 struct ip_event ip;
87 struct mmap_event mmap;
88 struct comm_event comm;
89 struct fork_event fork;
90 struct period_event period;
91} event_t;
92
93
94struct sym_ext {
95 struct rb_node node;
96 double percent;
97 char *path;
98};
99
100static LIST_HEAD(dsos);
101static struct dso *kernel_dso;
102static struct dso *vdso;
103
104
105static void dsos__add(struct dso *dso)
106{
107 list_add_tail(&dso->node, &dsos);
108}
109
110static struct dso *dsos__find(const char *name)
111{
112 struct dso *pos;
113
114 list_for_each_entry(pos, &dsos, node)
115 if (strcmp(pos->name, name) == 0)
116 return pos;
117 return NULL;
118}
119
120static struct dso *dsos__findnew(const char *name)
121{
122 struct dso *dso = dsos__find(name);
123 int nr;
124
125 if (dso)
126 return dso;
127
128 dso = dso__new(name, 0);
129 if (!dso)
130 goto out_delete_dso;
131
132 nr = dso__load(dso, NULL, verbose);
133 if (nr < 0) {
134 if (verbose)
135 fprintf(stderr, "Failed to open: %s\n", name);
136 goto out_delete_dso;
137 }
138 if (!nr && verbose) {
139 fprintf(stderr,
140 "No symbols found in: %s, maybe install a debug package?\n",
141 name);
142 }
143
144 dsos__add(dso);
145
146 return dso;
147
148out_delete_dso:
149 dso__delete(dso);
150 return NULL;
151}
152
153static void dsos__fprintf(FILE *fp)
154{
155 struct dso *pos;
156
157 list_for_each_entry(pos, &dsos, node)
158 dso__fprintf(pos, fp);
159}
160
161static struct symbol *vdso__find_symbol(struct dso *dso, u64 ip)
162{
163 return dso__find_symbol(kernel_dso, ip);
164}
165
166static int load_kernel(void)
167{
168 int err;
169
170 kernel_dso = dso__new("[kernel]", 0);
171 if (!kernel_dso)
172 return -1;
173
174 err = dso__load_kernel(kernel_dso, vmlinux, NULL, verbose);
175 if (err) {
176 dso__delete(kernel_dso);
177 kernel_dso = NULL;
178 } else
179 dsos__add(kernel_dso);
180
181 vdso = dso__new("[vdso]", 0);
182 if (!vdso)
183 return -1;
184
185 vdso->find_symbol = vdso__find_symbol;
186
187 dsos__add(vdso);
188
189 return err;
190}
191
192struct map {
193 struct list_head node;
194 u64 start;
195 u64 end;
196 u64 pgoff;
197 u64 (*map_ip)(struct map *, u64);
198 struct dso *dso;
199};
200
201static u64 map__map_ip(struct map *map, u64 ip)
202{
203 return ip - map->start + map->pgoff;
204}
205
206static u64 vdso__map_ip(struct map *map, u64 ip)
207{
208 return ip;
209}
210
211static struct map *map__new(struct mmap_event *event)
212{
213 struct map *self = malloc(sizeof(*self));
214
215 if (self != NULL) {
216 const char *filename = event->filename;
217
218 self->start = event->start;
219 self->end = event->start + event->len;
220 self->pgoff = event->pgoff;
221
222 self->dso = dsos__findnew(filename);
223 if (self->dso == NULL)
224 goto out_delete;
225
226 if (self->dso == vdso)
227 self->map_ip = vdso__map_ip;
228 else
229 self->map_ip = map__map_ip;
230 }
231 return self;
232out_delete:
233 free(self);
234 return NULL;
235}
236
237static struct map *map__clone(struct map *self)
238{
239 struct map *map = malloc(sizeof(*self));
240
241 if (!map)
242 return NULL;
243
244 memcpy(map, self, sizeof(*self));
245
246 return map;
247}
248
249static int map__overlap(struct map *l, struct map *r)
250{
251 if (l->start > r->start) {
252 struct map *t = l;
253 l = r;
254 r = t;
255 }
256
257 if (l->end > r->start)
258 return 1;
259
260 return 0;
261}
262
263static size_t map__fprintf(struct map *self, FILE *fp)
264{
265 return fprintf(fp, " %Lx-%Lx %Lx %s\n",
266 self->start, self->end, self->pgoff, self->dso->name);
267}
268
269
270struct thread {
271 struct rb_node rb_node;
272 struct list_head maps;
273 pid_t pid;
274 char *comm;
275};
276
277static struct thread *thread__new(pid_t pid)
278{
279 struct thread *self = malloc(sizeof(*self));
280
281 if (self != NULL) {
282 self->pid = pid;
283 self->comm = malloc(32);
284 if (self->comm)
285 snprintf(self->comm, 32, ":%d", self->pid);
286 INIT_LIST_HEAD(&self->maps);
287 }
288
289 return self;
290}
291
292static int thread__set_comm(struct thread *self, const char *comm)
293{
294 if (self->comm)
295 free(self->comm);
296 self->comm = strdup(comm);
297 return self->comm ? 0 : -ENOMEM;
298}
299
300static size_t thread__fprintf(struct thread *self, FILE *fp)
301{
302 struct map *pos;
303 size_t ret = fprintf(fp, "Thread %d %s\n", self->pid, self->comm);
304
305 list_for_each_entry(pos, &self->maps, node)
306 ret += map__fprintf(pos, fp);
307
308 return ret;
309}
310
311
312static struct rb_root threads;
313static struct thread *last_match;
314
315static struct thread *threads__findnew(pid_t pid)
316{
317 struct rb_node **p = &threads.rb_node;
318 struct rb_node *parent = NULL;
319 struct thread *th;
320
321 /*
322 * Font-end cache - PID lookups come in blocks,
323 * so most of the time we dont have to look up
324 * the full rbtree:
325 */
326 if (last_match && last_match->pid == pid)
327 return last_match;
328
329 while (*p != NULL) {
330 parent = *p;
331 th = rb_entry(parent, struct thread, rb_node);
332
333 if (th->pid == pid) {
334 last_match = th;
335 return th;
336 }
337 30
338 if (pid < th->pid) 31static bool force, use_tui, use_stdio;
339 p = &(*p)->rb_left;
340 else
341 p = &(*p)->rb_right;
342 }
343
344 th = thread__new(pid);
345 if (th != NULL) {
346 rb_link_node(&th->rb_node, parent, p);
347 rb_insert_color(&th->rb_node, &threads);
348 last_match = th;
349 }
350
351 return th;
352}
353
354static void thread__insert_map(struct thread *self, struct map *map)
355{
356 struct map *pos, *tmp;
357
358 list_for_each_entry_safe(pos, tmp, &self->maps, node) {
359 if (map__overlap(pos, map)) {
360 list_del_init(&pos->node);
361 /* XXX leaks dsos */
362 free(pos);
363 }
364 }
365
366 list_add_tail(&map->node, &self->maps);
367}
368
369static int thread__fork(struct thread *self, struct thread *parent)
370{
371 struct map *map;
372
373 if (self->comm)
374 free(self->comm);
375 self->comm = strdup(parent->comm);
376 if (!self->comm)
377 return -ENOMEM;
378
379 list_for_each_entry(map, &parent->maps, node) {
380 struct map *new = map__clone(map);
381 if (!new)
382 return -ENOMEM;
383 thread__insert_map(self, new);
384 }
385
386 return 0;
387}
388 32
389static struct map *thread__find_map(struct thread *self, u64 ip) 33static bool full_paths;
390{
391 struct map *pos;
392
393 if (self == NULL)
394 return NULL;
395
396 list_for_each_entry(pos, &self->maps, node)
397 if (ip >= pos->start && ip <= pos->end)
398 return pos;
399
400 return NULL;
401}
402
403static size_t threads__fprintf(FILE *fp)
404{
405 size_t ret = 0;
406 struct rb_node *nd;
407 34
408 for (nd = rb_first(&threads); nd; nd = rb_next(nd)) { 35static bool print_line;
409 struct thread *pos = rb_entry(nd, struct thread, rb_node);
410 36
411 ret += thread__fprintf(pos, fp); 37static const char *sym_hist_filter;
412 }
413
414 return ret;
415}
416
417/*
418 * histogram, sorted on item, collects counts
419 */
420
421static struct rb_root hist;
422
423struct hist_entry {
424 struct rb_node rb_node;
425
426 struct thread *thread;
427 struct map *map;
428 struct dso *dso;
429 struct symbol *sym;
430 u64 ip;
431 char level;
432
433 uint32_t count;
434};
435
436/*
437 * configurable sorting bits
438 */
439
440struct sort_entry {
441 struct list_head list;
442
443 char *header;
444
445 int64_t (*cmp)(struct hist_entry *, struct hist_entry *);
446 int64_t (*collapse)(struct hist_entry *, struct hist_entry *);
447 size_t (*print)(FILE *fp, struct hist_entry *);
448};
449
450/* --sort pid */
451
452static int64_t
453sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)
454{
455 return right->thread->pid - left->thread->pid;
456}
457
458static size_t
459sort__thread_print(FILE *fp, struct hist_entry *self)
460{
461 return fprintf(fp, "%16s:%5d", self->thread->comm ?: "", self->thread->pid);
462}
463
464static struct sort_entry sort_thread = {
465 .header = " Command: Pid",
466 .cmp = sort__thread_cmp,
467 .print = sort__thread_print,
468};
469
470/* --sort comm */
471
472static int64_t
473sort__comm_cmp(struct hist_entry *left, struct hist_entry *right)
474{
475 return right->thread->pid - left->thread->pid;
476}
477 38
478static int64_t 39static int hists__add_entry(struct hists *self, struct addr_location *al)
479sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)
480{ 40{
481 char *comm_l = left->thread->comm;
482 char *comm_r = right->thread->comm;
483
484 if (!comm_l || !comm_r) {
485 if (!comm_l && !comm_r)
486 return 0;
487 else if (!comm_l)
488 return -1;
489 else
490 return 1;
491 }
492
493 return strcmp(comm_l, comm_r);
494}
495
496static size_t
497sort__comm_print(FILE *fp, struct hist_entry *self)
498{
499 return fprintf(fp, "%16s", self->thread->comm);
500}
501
502static struct sort_entry sort_comm = {
503 .header = " Command",
504 .cmp = sort__comm_cmp,
505 .collapse = sort__comm_collapse,
506 .print = sort__comm_print,
507};
508
509/* --sort dso */
510
511static int64_t
512sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
513{
514 struct dso *dso_l = left->dso;
515 struct dso *dso_r = right->dso;
516
517 if (!dso_l || !dso_r) {
518 if (!dso_l && !dso_r)
519 return 0;
520 else if (!dso_l)
521 return -1;
522 else
523 return 1;
524 }
525
526 return strcmp(dso_l->name, dso_r->name);
527}
528
529static size_t
530sort__dso_print(FILE *fp, struct hist_entry *self)
531{
532 if (self->dso)
533 return fprintf(fp, "%-25s", self->dso->name);
534
535 return fprintf(fp, "%016llx ", (u64)self->ip);
536}
537
538static struct sort_entry sort_dso = {
539 .header = "Shared Object ",
540 .cmp = sort__dso_cmp,
541 .print = sort__dso_print,
542};
543
544/* --sort symbol */
545
546static int64_t
547sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
548{
549 u64 ip_l, ip_r;
550
551 if (left->sym == right->sym)
552 return 0;
553
554 ip_l = left->sym ? left->sym->start : left->ip;
555 ip_r = right->sym ? right->sym->start : right->ip;
556
557 return (int64_t)(ip_r - ip_l);
558}
559
560static size_t
561sort__sym_print(FILE *fp, struct hist_entry *self)
562{
563 size_t ret = 0;
564
565 if (verbose)
566 ret += fprintf(fp, "%#018llx ", (u64)self->ip);
567
568 if (self->sym) {
569 ret += fprintf(fp, "[%c] %s",
570 self->dso == kernel_dso ? 'k' : '.', self->sym->name);
571 } else {
572 ret += fprintf(fp, "%#016llx", (u64)self->ip);
573 }
574
575 return ret;
576}
577
578static struct sort_entry sort_sym = {
579 .header = "Symbol",
580 .cmp = sort__sym_cmp,
581 .print = sort__sym_print,
582};
583
584static int sort__need_collapse = 0;
585
586struct sort_dimension {
587 char *name;
588 struct sort_entry *entry;
589 int taken;
590};
591
592static struct sort_dimension sort_dimensions[] = {
593 { .name = "pid", .entry = &sort_thread, },
594 { .name = "comm", .entry = &sort_comm, },
595 { .name = "dso", .entry = &sort_dso, },
596 { .name = "symbol", .entry = &sort_sym, },
597};
598
599static LIST_HEAD(hist_entry__sort_list);
600
601static int sort_dimension__add(char *tok)
602{
603 int i;
604
605 for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) {
606 struct sort_dimension *sd = &sort_dimensions[i];
607
608 if (sd->taken)
609 continue;
610
611 if (strncasecmp(tok, sd->name, strlen(tok)))
612 continue;
613
614 if (sd->entry->collapse)
615 sort__need_collapse = 1;
616
617 list_add_tail(&sd->entry->list, &hist_entry__sort_list);
618 sd->taken = 1;
619
620 return 0;
621 }
622
623 return -ESRCH;
624}
625
626static int64_t
627hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)
628{
629 struct sort_entry *se;
630 int64_t cmp = 0;
631
632 list_for_each_entry(se, &hist_entry__sort_list, list) {
633 cmp = se->cmp(left, right);
634 if (cmp)
635 break;
636 }
637
638 return cmp;
639}
640
641static int64_t
642hist_entry__collapse(struct hist_entry *left, struct hist_entry *right)
643{
644 struct sort_entry *se;
645 int64_t cmp = 0;
646
647 list_for_each_entry(se, &hist_entry__sort_list, list) {
648 int64_t (*f)(struct hist_entry *, struct hist_entry *);
649
650 f = se->collapse ?: se->cmp;
651
652 cmp = f(left, right);
653 if (cmp)
654 break;
655 }
656
657 return cmp;
658}
659
660/*
661 * collect histogram counts
662 */
663static void hist_hit(struct hist_entry *he, u64 ip)
664{
665 unsigned int sym_size, offset;
666 struct symbol *sym = he->sym;
667
668 he->count++;
669
670 if (!sym || !sym->hist)
671 return;
672
673 sym_size = sym->end - sym->start;
674 offset = ip - sym->start;
675
676 if (offset >= sym_size)
677 return;
678
679 sym->hist_sum++;
680 sym->hist[offset]++;
681
682 if (verbose >= 3)
683 printf("%p %s: count++ [ip: %p, %08Lx] => %Ld\n",
684 (void *)(unsigned long)he->sym->start,
685 he->sym->name,
686 (void *)(unsigned long)ip, ip - he->sym->start,
687 sym->hist[offset]);
688}
689
690static int
691hist_entry__add(struct thread *thread, struct map *map, struct dso *dso,
692 struct symbol *sym, u64 ip, char level)
693{
694 struct rb_node **p = &hist.rb_node;
695 struct rb_node *parent = NULL;
696 struct hist_entry *he; 41 struct hist_entry *he;
697 struct hist_entry entry = {
698 .thread = thread,
699 .map = map,
700 .dso = dso,
701 .sym = sym,
702 .ip = ip,
703 .level = level,
704 .count = 1,
705 };
706 int cmp;
707 42
708 while (*p != NULL) { 43 if (sym_hist_filter != NULL &&
709 parent = *p; 44 (al->sym == NULL || strcmp(sym_hist_filter, al->sym->name) != 0)) {
710 he = rb_entry(parent, struct hist_entry, rb_node); 45 /* We're only interested in a symbol named sym_hist_filter */
711 46 if (al->sym != NULL) {
712 cmp = hist_entry__cmp(&entry, he); 47 rb_erase(&al->sym->rb_node,
713 48 &al->map->dso->symbols[al->map->type]);
714 if (!cmp) { 49 symbol__delete(al->sym);
715 hist_hit(he, ip);
716
717 return 0;
718 } 50 }
719
720 if (cmp < 0)
721 p = &(*p)->rb_left;
722 else
723 p = &(*p)->rb_right;
724 }
725
726 he = malloc(sizeof(*he));
727 if (!he)
728 return -ENOMEM;
729 *he = entry;
730 rb_link_node(&he->rb_node, parent, p);
731 rb_insert_color(&he->rb_node, &hist);
732
733 return 0;
734}
735
736static void hist_entry__free(struct hist_entry *he)
737{
738 free(he);
739}
740
741/*
742 * collapse the histogram
743 */
744
745static struct rb_root collapse_hists;
746
747static void collapse__insert_entry(struct hist_entry *he)
748{
749 struct rb_node **p = &collapse_hists.rb_node;
750 struct rb_node *parent = NULL;
751 struct hist_entry *iter;
752 int64_t cmp;
753
754 while (*p != NULL) {
755 parent = *p;
756 iter = rb_entry(parent, struct hist_entry, rb_node);
757
758 cmp = hist_entry__collapse(iter, he);
759
760 if (!cmp) {
761 iter->count += he->count;
762 hist_entry__free(he);
763 return;
764 }
765
766 if (cmp < 0)
767 p = &(*p)->rb_left;
768 else
769 p = &(*p)->rb_right;
770 }
771
772 rb_link_node(&he->rb_node, parent, p);
773 rb_insert_color(&he->rb_node, &collapse_hists);
774}
775
776static void collapse__resort(void)
777{
778 struct rb_node *next;
779 struct hist_entry *n;
780
781 if (!sort__need_collapse)
782 return;
783
784 next = rb_first(&hist);
785 while (next) {
786 n = rb_entry(next, struct hist_entry, rb_node);
787 next = rb_next(&n->rb_node);
788
789 rb_erase(&n->rb_node, &hist);
790 collapse__insert_entry(n);
791 }
792}
793
794/*
795 * reverse the map, sort on count.
796 */
797
798static struct rb_root output_hists;
799
800static void output__insert_entry(struct hist_entry *he)
801{
802 struct rb_node **p = &output_hists.rb_node;
803 struct rb_node *parent = NULL;
804 struct hist_entry *iter;
805
806 while (*p != NULL) {
807 parent = *p;
808 iter = rb_entry(parent, struct hist_entry, rb_node);
809
810 if (he->count > iter->count)
811 p = &(*p)->rb_left;
812 else
813 p = &(*p)->rb_right;
814 }
815
816 rb_link_node(&he->rb_node, parent, p);
817 rb_insert_color(&he->rb_node, &output_hists);
818}
819
820static void output__resort(void)
821{
822 struct rb_node *next;
823 struct hist_entry *n;
824 struct rb_root *tree = &hist;
825
826 if (sort__need_collapse)
827 tree = &collapse_hists;
828
829 next = rb_first(tree);
830
831 while (next) {
832 n = rb_entry(next, struct hist_entry, rb_node);
833 next = rb_next(&n->rb_node);
834
835 rb_erase(&n->rb_node, tree);
836 output__insert_entry(n);
837 }
838}
839
840static void register_idle_thread(void)
841{
842 struct thread *thread = threads__findnew(0);
843
844 if (thread == NULL ||
845 thread__set_comm(thread, "[idle]")) {
846 fprintf(stderr, "problem inserting idle task.\n");
847 exit(-1);
848 }
849}
850
851static unsigned long total = 0,
852 total_mmap = 0,
853 total_comm = 0,
854 total_fork = 0,
855 total_unknown = 0;
856
857static int
858process_overflow_event(event_t *event, unsigned long offset, unsigned long head)
859{
860 char level;
861 int show = 0;
862 struct dso *dso = NULL;
863 struct thread *thread = threads__findnew(event->ip.pid);
864 u64 ip = event->ip.ip;
865 struct map *map = NULL;
866
867 dprintf("%p [%p]: PERF_EVENT (IP, %d): %d: %p\n",
868 (void *)(offset + head),
869 (void *)(long)(event->header.size),
870 event->header.misc,
871 event->ip.pid,
872 (void *)(long)ip);
873
874 dprintf(" ... thread: %s:%d\n", thread->comm, thread->pid);
875
876 if (thread == NULL) {
877 fprintf(stderr, "problem processing %d event, skipping it.\n",
878 event->header.type);
879 return -1;
880 }
881
882 if (event->header.misc & PERF_EVENT_MISC_KERNEL) {
883 show = SHOW_KERNEL;
884 level = 'k';
885
886 dso = kernel_dso;
887
888 dprintf(" ...... dso: %s\n", dso->name);
889
890 } else if (event->header.misc & PERF_EVENT_MISC_USER) {
891
892 show = SHOW_USER;
893 level = '.';
894
895 map = thread__find_map(thread, ip);
896 if (map != NULL) {
897 ip = map->map_ip(map, ip);
898 dso = map->dso;
899 } else {
900 /*
901 * If this is outside of all known maps,
902 * and is a negative address, try to look it
903 * up in the kernel dso, as it might be a
904 * vsyscall (which executes in user-mode):
905 */
906 if ((long long)ip < 0)
907 dso = kernel_dso;
908 }
909 dprintf(" ...... dso: %s\n", dso ? dso->name : "<not found>");
910
911 } else {
912 show = SHOW_HV;
913 level = 'H';
914 dprintf(" ...... dso: [hypervisor]\n");
915 }
916
917 if (show & show_mask) {
918 struct symbol *sym = NULL;
919
920 if (dso)
921 sym = dso->find_symbol(dso, ip);
922
923 if (hist_entry__add(thread, map, dso, sym, ip, level)) {
924 fprintf(stderr,
925 "problem incrementing symbol count, skipping event\n");
926 return -1;
927 }
928 }
929 total++;
930
931 return 0;
932}
933
934static int
935process_mmap_event(event_t *event, unsigned long offset, unsigned long head)
936{
937 struct thread *thread = threads__findnew(event->mmap.pid);
938 struct map *map = map__new(&event->mmap);
939
940 dprintf("%p [%p]: PERF_EVENT_MMAP %d: [%p(%p) @ %p]: %s\n",
941 (void *)(offset + head),
942 (void *)(long)(event->header.size),
943 event->mmap.pid,
944 (void *)(long)event->mmap.start,
945 (void *)(long)event->mmap.len,
946 (void *)(long)event->mmap.pgoff,
947 event->mmap.filename);
948
949 if (thread == NULL || map == NULL) {
950 dprintf("problem processing PERF_EVENT_MMAP, skipping event.\n");
951 return 0; 51 return 0;
952 } 52 }
953 53
954 thread__insert_map(thread, map); 54 he = __hists__add_entry(self, al, NULL, 1);
955 total_mmap++; 55 if (he == NULL)
956 56 return -ENOMEM;
957 return 0;
958}
959
960static int
961process_comm_event(event_t *event, unsigned long offset, unsigned long head)
962{
963 struct thread *thread = threads__findnew(event->comm.pid);
964
965 dprintf("%p [%p]: PERF_EVENT_COMM: %s:%d\n",
966 (void *)(offset + head),
967 (void *)(long)(event->header.size),
968 event->comm.comm, event->comm.pid);
969
970 if (thread == NULL ||
971 thread__set_comm(thread, event->comm.comm)) {
972 dprintf("problem processing PERF_EVENT_COMM, skipping event.\n");
973 return -1;
974 }
975 total_comm++;
976 57
977 return 0; 58 return hist_entry__inc_addr_samples(he, al->addr);
978} 59}
979 60
980static int 61static int process_sample_event(event_t *event, struct sample_data *sample,
981process_fork_event(event_t *event, unsigned long offset, unsigned long head) 62 struct perf_session *session)
982{ 63{
983 struct thread *thread = threads__findnew(event->fork.pid); 64 struct addr_location al;
984 struct thread *parent = threads__findnew(event->fork.ppid);
985
986 dprintf("%p [%p]: PERF_EVENT_FORK: %d:%d\n",
987 (void *)(offset + head),
988 (void *)(long)(event->header.size),
989 event->fork.pid, event->fork.ppid);
990 65
991 if (!thread || !parent || thread__fork(thread, parent)) { 66 if (event__preprocess_sample(event, session, &al, sample, NULL) < 0) {
992 dprintf("problem processing PERF_EVENT_FORK, skipping event.\n"); 67 pr_warning("problem processing %d event, skipping it.\n",
68 event->header.type);
993 return -1; 69 return -1;
994 } 70 }
995 total_fork++;
996
997 return 0;
998}
999
1000static int
1001process_period_event(event_t *event, unsigned long offset, unsigned long head)
1002{
1003 dprintf("%p [%p]: PERF_EVENT_PERIOD: time:%Ld, id:%Ld: period:%Ld\n",
1004 (void *)(offset + head),
1005 (void *)(long)(event->header.size),
1006 event->period.time,
1007 event->period.id,
1008 event->period.sample_period);
1009
1010 return 0;
1011}
1012
1013static int
1014process_event(event_t *event, unsigned long offset, unsigned long head)
1015{
1016 if (event->header.misc & PERF_EVENT_MISC_OVERFLOW)
1017 return process_overflow_event(event, offset, head);
1018
1019 switch (event->header.type) {
1020 case PERF_EVENT_MMAP:
1021 return process_mmap_event(event, offset, head);
1022
1023 case PERF_EVENT_COMM:
1024 return process_comm_event(event, offset, head);
1025
1026 case PERF_EVENT_FORK:
1027 return process_fork_event(event, offset, head);
1028 71
1029 case PERF_EVENT_PERIOD: 72 if (!al.filtered && hists__add_entry(&session->hists, &al)) {
1030 return process_period_event(event, offset, head); 73 pr_warning("problem incrementing symbol count, "
1031 /* 74 "skipping event\n");
1032 * We dont process them right now but they are fine:
1033 */
1034
1035 case PERF_EVENT_THROTTLE:
1036 case PERF_EVENT_UNTHROTTLE:
1037 return 0;
1038
1039 default:
1040 return -1; 75 return -1;
1041 } 76 }
1042 77
1043 return 0; 78 return 0;
1044} 79}
1045 80
1046static char *get_color(double percent) 81static int objdump_line__print(struct objdump_line *self,
1047{ 82 struct list_head *head,
1048 char *color = PERF_COLOR_NORMAL; 83 struct hist_entry *he, u64 len)
1049
1050 /*
1051 * We color high-overhead entries in red, mid-overhead
1052 * entries in green - and keep the low overhead places
1053 * normal:
1054 */
1055 if (percent >= MIN_RED)
1056 color = PERF_COLOR_RED;
1057 else {
1058 if (percent > MIN_GREEN)
1059 color = PERF_COLOR_GREEN;
1060 }
1061 return color;
1062}
1063
1064static int
1065parse_line(FILE *file, struct symbol *sym, u64 start, u64 len)
1066{ 84{
1067 char *line = NULL, *tmp, *tmp2; 85 struct symbol *sym = he->ms.sym;
1068 static const char *prev_line; 86 static const char *prev_line;
1069 static const char *prev_color; 87 static const char *prev_color;
1070 unsigned int offset;
1071 size_t line_len;
1072 u64 line_ip;
1073 int ret;
1074 char *c;
1075 88
1076 if (getline(&line, &line_len, file) < 0) 89 if (self->offset != -1) {
1077 return -1;
1078 if (!line)
1079 return -1;
1080
1081 c = strchr(line, '\n');
1082 if (c)
1083 *c = 0;
1084
1085 line_ip = -1;
1086 offset = 0;
1087 ret = -2;
1088
1089 /*
1090 * Strip leading spaces:
1091 */
1092 tmp = line;
1093 while (*tmp) {
1094 if (*tmp != ' ')
1095 break;
1096 tmp++;
1097 }
1098
1099 if (*tmp) {
1100 /*
1101 * Parse hexa addresses followed by ':'
1102 */
1103 line_ip = strtoull(tmp, &tmp2, 16);
1104 if (*tmp2 != ':')
1105 line_ip = -1;
1106 }
1107
1108 if (line_ip != -1) {
1109 const char *path = NULL; 90 const char *path = NULL;
1110 unsigned int hits = 0; 91 unsigned int hits = 0;
1111 double percent = 0.0; 92 double percent = 0.0;
1112 char *color; 93 const char *color;
1113 struct sym_ext *sym_ext = sym->priv; 94 struct sym_priv *priv = symbol__priv(sym);
1114 95 struct sym_ext *sym_ext = priv->ext;
1115 offset = line_ip - start; 96 struct sym_hist *h = priv->hist;
1116 if (offset < len) 97 s64 offset = self->offset;
1117 hits = sym->hist[offset]; 98 struct objdump_line *next = objdump__get_next_ip_line(head, self);
99
100 while (offset < (s64)len &&
101 (next == NULL || offset < next->offset)) {
102 if (sym_ext) {
103 if (path == NULL)
104 path = sym_ext[offset].path;
105 percent += sym_ext[offset].percent;
106 } else
107 hits += h->ip[offset];
108
109 ++offset;
110 }
1118 111
1119 if (offset < len && sym_ext) { 112 if (sym_ext == NULL && h->sum)
1120 path = sym_ext[offset].path; 113 percent = 100.0 * hits / h->sum;
1121 percent = sym_ext[offset].percent;
1122 } else if (sym->hist_sum)
1123 percent = 100.0 * hits / sym->hist_sum;
1124 114
1125 color = get_color(percent); 115 color = get_percent_color(percent);
1126 116
1127 /* 117 /*
1128 * Also color the filename and line if needed, with 118 * Also color the filename and line if needed, with
@@ -1140,12 +130,12 @@ parse_line(FILE *file, struct symbol *sym, u64 start, u64 len)
1140 130
1141 color_fprintf(stdout, color, " %7.2f", percent); 131 color_fprintf(stdout, color, " %7.2f", percent);
1142 printf(" : "); 132 printf(" : ");
1143 color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", line); 133 color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", self->line);
1144 } else { 134 } else {
1145 if (!*line) 135 if (!*self->line)
1146 printf(" :\n"); 136 printf(" :\n");
1147 else 137 else
1148 printf(" : %s\n", line); 138 printf(" : %s\n", self->line);
1149 } 139 }
1150 140
1151 return 0; 141 return 0;
@@ -1173,9 +163,10 @@ static void insert_source_line(struct sym_ext *sym_ext)
1173 rb_insert_color(&sym_ext->node, &root_sym_ext); 163 rb_insert_color(&sym_ext->node, &root_sym_ext);
1174} 164}
1175 165
1176static void free_source_line(struct symbol *sym, int len) 166static void free_source_line(struct hist_entry *he, int len)
1177{ 167{
1178 struct sym_ext *sym_ext = sym->priv; 168 struct sym_priv *priv = symbol__priv(he->ms.sym);
169 struct sym_ext *sym_ext = priv->ext;
1179 int i; 170 int i;
1180 171
1181 if (!sym_ext) 172 if (!sym_ext)
@@ -1185,26 +176,30 @@ static void free_source_line(struct symbol *sym, int len)
1185 free(sym_ext[i].path); 176 free(sym_ext[i].path);
1186 free(sym_ext); 177 free(sym_ext);
1187 178
1188 sym->priv = NULL; 179 priv->ext = NULL;
1189 root_sym_ext = RB_ROOT; 180 root_sym_ext = RB_ROOT;
1190} 181}
1191 182
1192/* Get the filename:line for the colored entries */ 183/* Get the filename:line for the colored entries */
1193static void 184static void
1194get_source_line(struct symbol *sym, u64 start, int len, char *filename) 185get_source_line(struct hist_entry *he, int len, const char *filename)
1195{ 186{
187 struct symbol *sym = he->ms.sym;
188 u64 start;
1196 int i; 189 int i;
1197 char cmd[PATH_MAX * 2]; 190 char cmd[PATH_MAX * 2];
1198 struct sym_ext *sym_ext; 191 struct sym_ext *sym_ext;
192 struct sym_priv *priv = symbol__priv(sym);
193 struct sym_hist *h = priv->hist;
1199 194
1200 if (!sym->hist_sum) 195 if (!h->sum)
1201 return; 196 return;
1202 197
1203 sym->priv = calloc(len, sizeof(struct sym_ext)); 198 sym_ext = priv->ext = calloc(len, sizeof(struct sym_ext));
1204 if (!sym->priv) 199 if (!priv->ext)
1205 return; 200 return;
1206 201
1207 sym_ext = sym->priv; 202 start = he->ms.map->unmap_ip(he->ms.map, sym->start);
1208 203
1209 for (i = 0; i < len; i++) { 204 for (i = 0; i < len; i++) {
1210 char *path = NULL; 205 char *path = NULL;
@@ -1212,7 +207,7 @@ get_source_line(struct symbol *sym, u64 start, int len, char *filename)
1212 u64 offset; 207 u64 offset;
1213 FILE *fp; 208 FILE *fp;
1214 209
1215 sym_ext[i].percent = 100.0 * sym->hist[i] / sym->hist_sum; 210 sym_ext[i].percent = 100.0 * h->ip[i] / h->sum;
1216 if (sym_ext[i].percent <= 0.5) 211 if (sym_ext[i].percent <= 0.5)
1217 continue; 212 continue;
1218 213
@@ -1237,7 +232,7 @@ get_source_line(struct symbol *sym, u64 start, int len, char *filename)
1237 } 232 }
1238} 233}
1239 234
1240static void print_summary(char *filename) 235static void print_summary(const char *filename)
1241{ 236{
1242 struct sym_ext *sym_ext; 237 struct sym_ext *sym_ext;
1243 struct rb_node *node; 238 struct rb_node *node;
@@ -1253,12 +248,12 @@ static void print_summary(char *filename)
1253 node = rb_first(&root_sym_ext); 248 node = rb_first(&root_sym_ext);
1254 while (node) { 249 while (node) {
1255 double percent; 250 double percent;
1256 char *color; 251 const char *color;
1257 char *path; 252 char *path;
1258 253
1259 sym_ext = rb_entry(node, struct sym_ext, node); 254 sym_ext = rb_entry(node, struct sym_ext, node);
1260 percent = sym_ext->percent; 255 percent = sym_ext->percent;
1261 color = get_color(percent); 256 color = get_percent_color(percent);
1262 path = sym_ext->path; 257 path = sym_ext->path;
1263 258
1264 color_fprintf(stdout, color, " %7.2f %s", percent, path); 259 color_fprintf(stdout, color, " %7.2f %s", percent, path);
@@ -1266,195 +261,155 @@ static void print_summary(char *filename)
1266 } 261 }
1267} 262}
1268 263
1269static void annotate_sym(struct dso *dso, struct symbol *sym) 264static void hist_entry__print_hits(struct hist_entry *self)
1270{ 265{
1271 char *filename = dso->name; 266 struct symbol *sym = self->ms.sym;
1272 u64 start, end, len; 267 struct sym_priv *priv = symbol__priv(sym);
1273 char command[PATH_MAX*2]; 268 struct sym_hist *h = priv->hist;
1274 FILE *file; 269 u64 len = sym->end - sym->start, offset;
1275 270
1276 if (!filename) 271 for (offset = 0; offset < len; ++offset)
1277 return; 272 if (h->ip[offset] != 0)
1278 if (dso == kernel_dso) 273 printf("%*Lx: %Lu\n", BITS_PER_LONG / 2,
1279 filename = vmlinux; 274 sym->start + offset, h->ip[offset]);
275 printf("%*s: %Lu\n", BITS_PER_LONG / 2, "h->sum", h->sum);
276}
277
278static int hist_entry__tty_annotate(struct hist_entry *he)
279{
280 struct map *map = he->ms.map;
281 struct dso *dso = map->dso;
282 struct symbol *sym = he->ms.sym;
283 const char *filename = dso->long_name, *d_filename;
284 u64 len;
285 LIST_HEAD(head);
286 struct objdump_line *pos, *n;
287
288 if (hist_entry__annotate(he, &head, 0) < 0)
289 return -1;
1280 290
1281 start = sym->obj_start; 291 if (full_paths)
1282 if (!start) 292 d_filename = filename;
1283 start = sym->start; 293 else
294 d_filename = basename(filename);
1284 295
1285 end = start + sym->end - sym->start + 1;
1286 len = sym->end - sym->start; 296 len = sym->end - sym->start;
1287 297
1288 if (print_line) { 298 if (print_line) {
1289 get_source_line(sym, start, len, filename); 299 get_source_line(he, len, filename);
1290 print_summary(filename); 300 print_summary(filename);
1291 } 301 }
1292 302
1293 printf("\n\n------------------------------------------------\n"); 303 printf("\n\n------------------------------------------------\n");
1294 printf(" Percent | Source code & Disassembly of %s\n", filename); 304 printf(" Percent | Source code & Disassembly of %s\n", d_filename);
1295 printf("------------------------------------------------\n"); 305 printf("------------------------------------------------\n");
1296 306
1297 if (verbose >= 2) 307 if (verbose)
1298 printf("annotating [%p] %30s : [%p] %30s\n", dso, dso->name, sym, sym->name); 308 hist_entry__print_hits(he);
1299
1300 sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s", (u64)start, (u64)end, filename);
1301
1302 if (verbose >= 3)
1303 printf("doing: %s\n", command);
1304
1305 file = popen(command, "r");
1306 if (!file)
1307 return;
1308 309
1309 while (!feof(file)) { 310 list_for_each_entry_safe(pos, n, &head, node) {
1310 if (parse_line(file, sym, start, len) < 0) 311 objdump_line__print(pos, &head, he, len);
1311 break; 312 list_del(&pos->node);
313 objdump_line__free(pos);
1312 } 314 }
1313 315
1314 pclose(file);
1315 if (print_line) 316 if (print_line)
1316 free_source_line(sym, len); 317 free_source_line(he, len);
318
319 return 0;
1317} 320}
1318 321
1319static void find_annotations(void) 322static void hists__find_annotations(struct hists *self)
1320{ 323{
1321 struct rb_node *nd; 324 struct rb_node *nd = rb_first(&self->entries), *next;
1322 struct dso *dso; 325 int key = KEY_RIGHT;
1323 int count = 0; 326
327 while (nd) {
328 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
329 struct sym_priv *priv;
1324 330
1325 list_for_each_entry(dso, &dsos, node) { 331 if (he->ms.sym == NULL || he->ms.map->dso->annotate_warned)
332 goto find_next;
1326 333
1327 for (nd = rb_first(&dso->syms); nd; nd = rb_next(nd)) { 334 priv = symbol__priv(he->ms.sym);
1328 struct symbol *sym = rb_entry(nd, struct symbol, rb_node); 335 if (priv->hist == NULL) {
336find_next:
337 if (key == KEY_LEFT)
338 nd = rb_prev(nd);
339 else
340 nd = rb_next(nd);
341 continue;
342 }
1329 343
1330 if (sym->hist) { 344 if (use_browser > 0) {
1331 annotate_sym(dso, sym); 345 key = hist_entry__tui_annotate(he);
1332 count++; 346 switch (key) {
347 case KEY_RIGHT:
348 next = rb_next(nd);
349 break;
350 case KEY_LEFT:
351 next = rb_prev(nd);
352 break;
353 default:
354 return;
1333 } 355 }
356
357 if (next != NULL)
358 nd = next;
359 } else {
360 hist_entry__tty_annotate(he);
361 nd = rb_next(nd);
362 /*
363 * Since we have a hist_entry per IP for the same
364 * symbol, free he->ms.sym->hist to signal we already
365 * processed this symbol.
366 */
367 free(priv->hist);
368 priv->hist = NULL;
1334 } 369 }
1335 } 370 }
1336
1337 if (!count)
1338 printf(" Error: symbol '%s' not present amongst the samples.\n", sym_hist_filter);
1339} 371}
1340 372
373static struct perf_event_ops event_ops = {
374 .sample = process_sample_event,
375 .mmap = event__process_mmap,
376 .comm = event__process_comm,
377 .fork = event__process_task,
378 .ordered_samples = true,
379 .ordering_requires_timestamps = true,
380};
381
1341static int __cmd_annotate(void) 382static int __cmd_annotate(void)
1342{ 383{
1343 int ret, rc = EXIT_FAILURE; 384 int ret;
1344 unsigned long offset = 0; 385 struct perf_session *session;
1345 unsigned long head = 0;
1346 struct stat stat;
1347 event_t *event;
1348 uint32_t size;
1349 char *buf;
1350
1351 register_idle_thread();
1352
1353 input = open(input_name, O_RDONLY);
1354 if (input < 0) {
1355 perror("failed to open file");
1356 exit(-1);
1357 }
1358
1359 ret = fstat(input, &stat);
1360 if (ret < 0) {
1361 perror("failed to stat file");
1362 exit(-1);
1363 }
1364
1365 if (!stat.st_size) {
1366 fprintf(stderr, "zero-sized file, nothing to do!\n");
1367 exit(0);
1368 }
1369
1370 if (load_kernel() < 0) {
1371 perror("failed to load kernel symbols");
1372 return EXIT_FAILURE;
1373 }
1374
1375remap:
1376 buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ,
1377 MAP_SHARED, input, offset);
1378 if (buf == MAP_FAILED) {
1379 perror("failed to mmap file");
1380 exit(-1);
1381 }
1382
1383more:
1384 event = (event_t *)(buf + head);
1385
1386 size = event->header.size;
1387 if (!size)
1388 size = 8;
1389
1390 if (head + event->header.size >= page_size * mmap_window) {
1391 unsigned long shift = page_size * (head / page_size);
1392 int ret;
1393
1394 ret = munmap(buf, page_size * mmap_window);
1395 assert(ret == 0);
1396
1397 offset += shift;
1398 head -= shift;
1399 goto remap;
1400 }
1401
1402 size = event->header.size;
1403
1404 dprintf("%p [%p]: event: %d\n",
1405 (void *)(offset + head),
1406 (void *)(long)event->header.size,
1407 event->header.type);
1408
1409 if (!size || process_event(event, offset, head) < 0) {
1410
1411 dprintf("%p [%p]: skipping unknown header type: %d\n",
1412 (void *)(offset + head),
1413 (void *)(long)(event->header.size),
1414 event->header.type);
1415
1416 total_unknown++;
1417 386
1418 /* 387 session = perf_session__new(input_name, O_RDONLY, force, false, &event_ops);
1419 * assume we lost track of the stream, check alignment, and 388 if (session == NULL)
1420 * increment a single u64 in the hope to catch on again 'soon'. 389 return -ENOMEM;
1421 */
1422 390
1423 if (unlikely(head & 7)) 391 ret = perf_session__process_events(session, &event_ops);
1424 head &= ~7ULL; 392 if (ret)
393 goto out_delete;
1425 394
1426 size = 8; 395 if (dump_trace) {
396 perf_session__fprintf_nr_events(session, stdout);
397 goto out_delete;
1427 } 398 }
1428 399
1429 head += size; 400 if (verbose > 3)
1430 401 perf_session__fprintf(session, stdout);
1431 if (offset + head < stat.st_size)
1432 goto more;
1433
1434 rc = EXIT_SUCCESS;
1435 close(input);
1436
1437 dprintf(" IP events: %10ld\n", total);
1438 dprintf(" mmap events: %10ld\n", total_mmap);
1439 dprintf(" comm events: %10ld\n", total_comm);
1440 dprintf(" fork events: %10ld\n", total_fork);
1441 dprintf(" unknown events: %10ld\n", total_unknown);
1442
1443 if (dump_trace)
1444 return 0;
1445
1446 if (verbose >= 3)
1447 threads__fprintf(stdout);
1448 402
1449 if (verbose >= 2) 403 if (verbose > 2)
1450 dsos__fprintf(stdout); 404 perf_session__fprintf_dsos(session, stdout);
1451 405
1452 collapse__resort(); 406 hists__collapse_resort(&session->hists);
1453 output__resort(); 407 hists__output_resort(&session->hists);
1454 408 hists__find_annotations(&session->hists);
1455 find_annotations(); 409out_delete:
410 perf_session__delete(session);
1456 411
1457 return rc; 412 return ret;
1458} 413}
1459 414
1460static const char * const annotate_usage[] = { 415static const char * const annotate_usage[] = {
@@ -1465,42 +420,46 @@ static const char * const annotate_usage[] = {
1465static const struct option options[] = { 420static const struct option options[] = {
1466 OPT_STRING('i', "input", &input_name, "file", 421 OPT_STRING('i', "input", &input_name, "file",
1467 "input file name"), 422 "input file name"),
423 OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]",
424 "only consider symbols in these dsos"),
1468 OPT_STRING('s', "symbol", &sym_hist_filter, "symbol", 425 OPT_STRING('s', "symbol", &sym_hist_filter, "symbol",
1469 "symbol to annotate"), 426 "symbol to annotate"),
1470 OPT_BOOLEAN('v', "verbose", &verbose, 427 OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
428 OPT_INCR('v', "verbose", &verbose,
1471 "be more verbose (show symbol address, etc)"), 429 "be more verbose (show symbol address, etc)"),
1472 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, 430 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
1473 "dump raw trace in ASCII"), 431 "dump raw trace in ASCII"),
1474 OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"), 432 OPT_BOOLEAN(0, "tui", &use_tui, "Use the TUI interface"),
433 OPT_BOOLEAN(0, "stdio", &use_stdio, "Use the stdio interface"),
434 OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
435 "file", "vmlinux pathname"),
436 OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules,
437 "load module symbols - WARNING: use only with -k and LIVE kernel"),
1475 OPT_BOOLEAN('l', "print-line", &print_line, 438 OPT_BOOLEAN('l', "print-line", &print_line,
1476 "print matching source lines (may be slow)"), 439 "print matching source lines (may be slow)"),
440 OPT_BOOLEAN('P', "full-paths", &full_paths,
441 "Don't shorten the displayed pathnames"),
1477 OPT_END() 442 OPT_END()
1478}; 443};
1479 444
1480static void setup_sorting(void) 445int cmd_annotate(int argc, const char **argv, const char *prefix __used)
1481{ 446{
1482 char *tmp, *tok, *str = strdup(sort_order); 447 argc = parse_options(argc, argv, options, annotate_usage, 0);
1483 448
1484 for (tok = strtok_r(str, ", ", &tmp); 449 if (use_stdio)
1485 tok; tok = strtok_r(NULL, ", ", &tmp)) { 450 use_browser = 0;
1486 if (sort_dimension__add(tok) < 0) { 451 else if (use_tui)
1487 error("Unknown --sort key: `%s'", tok); 452 use_browser = 1;
1488 usage_with_options(annotate_usage, options);
1489 }
1490 }
1491 453
1492 free(str); 454 setup_browser();
1493}
1494 455
1495int cmd_annotate(int argc, const char **argv, const char *prefix) 456 symbol_conf.priv_size = sizeof(struct sym_priv);
1496{ 457 symbol_conf.try_vmlinux_path = true;
1497 symbol__init();
1498
1499 page_size = getpagesize();
1500 458
1501 argc = parse_options(argc, argv, options, annotate_usage, 0); 459 if (symbol__init() < 0)
460 return -1;
1502 461
1503 setup_sorting(); 462 setup_sorting(annotate_usage, options);
1504 463
1505 if (argc) { 464 if (argc) {
1506 /* 465 /*
@@ -1513,10 +472,10 @@ int cmd_annotate(int argc, const char **argv, const char *prefix)
1513 sym_hist_filter = argv[0]; 472 sym_hist_filter = argv[0];
1514 } 473 }
1515 474
1516 if (!sym_hist_filter) 475 if (field_sep && *field_sep == '.') {
1517 usage_with_options(annotate_usage, options); 476 pr_err("'.' is the only non valid --field-separator argument\n");
1518 477 return -1;
1519 setup_pager(); 478 }
1520 479
1521 return __cmd_annotate(); 480 return __cmd_annotate();
1522} 481}
diff --git a/tools/perf/builtin-bench.c b/tools/perf/builtin-bench.c
new file mode 100644
index 00000000000..fcb96269852
--- /dev/null
+++ b/tools/perf/builtin-bench.c
@@ -0,0 +1,245 @@
1/*
2 *
3 * builtin-bench.c
4 *
5 * General benchmarking subsystem provided by perf
6 *
7 * Copyright (C) 2009, Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp>
8 *
9 */
10
11/*
12 *
13 * Available subsystem list:
14 * sched ... scheduler and IPC mechanism
15 * mem ... memory access performance
16 *
17 */
18
19#include "perf.h"
20#include "util/util.h"
21#include "util/parse-options.h"
22#include "builtin.h"
23#include "bench/bench.h"
24
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28
29struct bench_suite {
30 const char *name;
31 const char *summary;
32 int (*fn)(int, const char **, const char *);
33};
34 \
35/* sentinel: easy for help */
36#define suite_all { "all", "test all suite (pseudo suite)", NULL }
37
38static struct bench_suite sched_suites[] = {
39 { "messaging",
40 "Benchmark for scheduler and IPC mechanisms",
41 bench_sched_messaging },
42 { "pipe",
43 "Flood of communication over pipe() between two processes",
44 bench_sched_pipe },
45 suite_all,
46 { NULL,
47 NULL,
48 NULL }
49};
50
51static struct bench_suite mem_suites[] = {
52 { "memcpy",
53 "Simple memory copy in various ways",
54 bench_mem_memcpy },
55 suite_all,
56 { NULL,
57 NULL,
58 NULL }
59};
60
61struct bench_subsys {
62 const char *name;
63 const char *summary;
64 struct bench_suite *suites;
65};
66
67static struct bench_subsys subsystems[] = {
68 { "sched",
69 "scheduler and IPC mechanism",
70 sched_suites },
71 { "mem",
72 "memory access performance",
73 mem_suites },
74 { "all", /* sentinel: easy for help */
75 "test all subsystem (pseudo subsystem)",
76 NULL },
77 { NULL,
78 NULL,
79 NULL }
80};
81
82static void dump_suites(int subsys_index)
83{
84 int i;
85
86 printf("# List of available suites for %s...\n\n",
87 subsystems[subsys_index].name);
88
89 for (i = 0; subsystems[subsys_index].suites[i].name; i++)
90 printf("%14s: %s\n",
91 subsystems[subsys_index].suites[i].name,
92 subsystems[subsys_index].suites[i].summary);
93
94 printf("\n");
95 return;
96}
97
98static const char *bench_format_str;
99int bench_format = BENCH_FORMAT_DEFAULT;
100
101static const struct option bench_options[] = {
102 OPT_STRING('f', "format", &bench_format_str, "default",
103 "Specify format style"),
104 OPT_END()
105};
106
107static const char * const bench_usage[] = {
108 "perf bench [<common options>] <subsystem> <suite> [<options>]",
109 NULL
110};
111
112static void print_usage(void)
113{
114 int i;
115
116 printf("Usage: \n");
117 for (i = 0; bench_usage[i]; i++)
118 printf("\t%s\n", bench_usage[i]);
119 printf("\n");
120
121 printf("# List of available subsystems...\n\n");
122
123 for (i = 0; subsystems[i].name; i++)
124 printf("%14s: %s\n",
125 subsystems[i].name, subsystems[i].summary);
126 printf("\n");
127}
128
129static int bench_str2int(const char *str)
130{
131 if (!str)
132 return BENCH_FORMAT_DEFAULT;
133
134 if (!strcmp(str, BENCH_FORMAT_DEFAULT_STR))
135 return BENCH_FORMAT_DEFAULT;
136 else if (!strcmp(str, BENCH_FORMAT_SIMPLE_STR))
137 return BENCH_FORMAT_SIMPLE;
138
139 return BENCH_FORMAT_UNKNOWN;
140}
141
142static void all_suite(struct bench_subsys *subsys) /* FROM HERE */
143{
144 int i;
145 const char *argv[2];
146 struct bench_suite *suites = subsys->suites;
147
148 argv[1] = NULL;
149 /*
150 * TODO:
151 * preparing preset parameters for
152 * embedded, ordinary PC, HPC, etc...
153 * will be helpful
154 */
155 for (i = 0; suites[i].fn; i++) {
156 printf("# Running %s/%s benchmark...\n",
157 subsys->name,
158 suites[i].name);
159
160 argv[1] = suites[i].name;
161 suites[i].fn(1, argv, NULL);
162 printf("\n");
163 }
164}
165
166static void all_subsystem(void)
167{
168 int i;
169 for (i = 0; subsystems[i].suites; i++)
170 all_suite(&subsystems[i]);
171}
172
173int cmd_bench(int argc, const char **argv, const char *prefix __used)
174{
175 int i, j, status = 0;
176
177 if (argc < 2) {
178 /* No subsystem specified. */
179 print_usage();
180 goto end;
181 }
182
183 argc = parse_options(argc, argv, bench_options, bench_usage,
184 PARSE_OPT_STOP_AT_NON_OPTION);
185
186 bench_format = bench_str2int(bench_format_str);
187 if (bench_format == BENCH_FORMAT_UNKNOWN) {
188 printf("Unknown format descriptor:%s\n", bench_format_str);
189 goto end;
190 }
191
192 if (argc < 1) {
193 print_usage();
194 goto end;
195 }
196
197 if (!strcmp(argv[0], "all")) {
198 all_subsystem();
199 goto end;
200 }
201
202 for (i = 0; subsystems[i].name; i++) {
203 if (strcmp(subsystems[i].name, argv[0]))
204 continue;
205
206 if (argc < 2) {
207 /* No suite specified. */
208 dump_suites(i);
209 goto end;
210 }
211
212 if (!strcmp(argv[1], "all")) {
213 all_suite(&subsystems[i]);
214 goto end;
215 }
216
217 for (j = 0; subsystems[i].suites[j].name; j++) {
218 if (strcmp(subsystems[i].suites[j].name, argv[1]))
219 continue;
220
221 if (bench_format == BENCH_FORMAT_DEFAULT)
222 printf("# Running %s/%s benchmark...\n",
223 subsystems[i].name,
224 subsystems[i].suites[j].name);
225 status = subsystems[i].suites[j].fn(argc - 1,
226 argv + 1, prefix);
227 goto end;
228 }
229
230 if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
231 dump_suites(i);
232 goto end;
233 }
234
235 printf("Unknown suite:%s for %s\n", argv[1], argv[0]);
236 status = 1;
237 goto end;
238 }
239
240 printf("Unknown subsystem:%s\n", argv[0]);
241 status = 1;
242
243end:
244 return status;
245}
diff --git a/tools/perf/builtin-buildid-cache.c b/tools/perf/builtin-buildid-cache.c
new file mode 100644
index 00000000000..29ad20e6791
--- /dev/null
+++ b/tools/perf/builtin-buildid-cache.c
@@ -0,0 +1,132 @@
1/*
2 * builtin-buildid-cache.c
3 *
4 * Builtin buildid-cache command: Manages build-id cache
5 *
6 * Copyright (C) 2010, Red Hat Inc.
7 * Copyright (C) 2010, Arnaldo Carvalho de Melo <acme@redhat.com>
8 */
9#include "builtin.h"
10#include "perf.h"
11#include "util/cache.h"
12#include "util/debug.h"
13#include "util/header.h"
14#include "util/parse-options.h"
15#include "util/strlist.h"
16#include "util/symbol.h"
17
18static char const *add_name_list_str, *remove_name_list_str;
19
20static const char * const buildid_cache_usage[] = {
21 "perf buildid-cache [<options>]",
22 NULL
23};
24
25static const struct option buildid_cache_options[] = {
26 OPT_STRING('a', "add", &add_name_list_str,
27 "file list", "file(s) to add"),
28 OPT_STRING('r', "remove", &remove_name_list_str, "file list",
29 "file(s) to remove"),
30 OPT_INCR('v', "verbose", &verbose, "be more verbose"),
31 OPT_END()
32};
33
34static int build_id_cache__add_file(const char *filename, const char *debugdir)
35{
36 char sbuild_id[BUILD_ID_SIZE * 2 + 1];
37 u8 build_id[BUILD_ID_SIZE];
38 int err;
39
40 if (filename__read_build_id(filename, &build_id, sizeof(build_id)) < 0) {
41 pr_debug("Couldn't read a build-id in %s\n", filename);
42 return -1;
43 }
44
45 build_id__sprintf(build_id, sizeof(build_id), sbuild_id);
46 err = build_id_cache__add_s(sbuild_id, debugdir, filename, false);
47 if (verbose)
48 pr_info("Adding %s %s: %s\n", sbuild_id, filename,
49 err ? "FAIL" : "Ok");
50 return err;
51}
52
53static int build_id_cache__remove_file(const char *filename __used,
54 const char *debugdir __used)
55{
56 u8 build_id[BUILD_ID_SIZE];
57 char sbuild_id[BUILD_ID_SIZE * 2 + 1];
58
59 int err;
60
61 if (filename__read_build_id(filename, &build_id, sizeof(build_id)) < 0) {
62 pr_debug("Couldn't read a build-id in %s\n", filename);
63 return -1;
64 }
65
66 build_id__sprintf(build_id, sizeof(build_id), sbuild_id);
67 err = build_id_cache__remove_s(sbuild_id, debugdir);
68 if (verbose)
69 pr_info("Removing %s %s: %s\n", sbuild_id, filename,
70 err ? "FAIL" : "Ok");
71
72 return err;
73}
74
75static int __cmd_buildid_cache(void)
76{
77 struct strlist *list;
78 struct str_node *pos;
79 char debugdir[PATH_MAX];
80
81 snprintf(debugdir, sizeof(debugdir), "%s", buildid_dir);
82
83 if (add_name_list_str) {
84 list = strlist__new(true, add_name_list_str);
85 if (list) {
86 strlist__for_each(pos, list)
87 if (build_id_cache__add_file(pos->s, debugdir)) {
88 if (errno == EEXIST) {
89 pr_debug("%s already in the cache\n",
90 pos->s);
91 continue;
92 }
93 pr_warning("Couldn't add %s: %s\n",
94 pos->s, strerror(errno));
95 }
96
97 strlist__delete(list);
98 }
99 }
100
101 if (remove_name_list_str) {
102 list = strlist__new(true, remove_name_list_str);
103 if (list) {
104 strlist__for_each(pos, list)
105 if (build_id_cache__remove_file(pos->s, debugdir)) {
106 if (errno == ENOENT) {
107 pr_debug("%s wasn't in the cache\n",
108 pos->s);
109 continue;
110 }
111 pr_warning("Couldn't remove %s: %s\n",
112 pos->s, strerror(errno));
113 }
114
115 strlist__delete(list);
116 }
117 }
118
119 return 0;
120}
121
122int cmd_buildid_cache(int argc, const char **argv, const char *prefix __used)
123{
124 argc = parse_options(argc, argv, buildid_cache_options,
125 buildid_cache_usage, 0);
126
127 if (symbol__init() < 0)
128 return -1;
129
130 setup_pager();
131 return __cmd_buildid_cache();
132}
diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c
new file mode 100644
index 00000000000..5af32ae9031
--- /dev/null
+++ b/tools/perf/builtin-buildid-list.c
@@ -0,0 +1,60 @@
1/*
2 * builtin-buildid-list.c
3 *
4 * Builtin buildid-list command: list buildids in perf.data
5 *
6 * Copyright (C) 2009, Red Hat Inc.
7 * Copyright (C) 2009, Arnaldo Carvalho de Melo <acme@redhat.com>
8 */
9#include "builtin.h"
10#include "perf.h"
11#include "util/build-id.h"
12#include "util/cache.h"
13#include "util/debug.h"
14#include "util/parse-options.h"
15#include "util/session.h"
16#include "util/symbol.h"
17
18static char const *input_name = "perf.data";
19static bool force;
20static bool with_hits;
21
22static const char * const buildid_list_usage[] = {
23 "perf buildid-list [<options>]",
24 NULL
25};
26
27static const struct option options[] = {
28 OPT_BOOLEAN('H', "with-hits", &with_hits, "Show only DSOs with hits"),
29 OPT_STRING('i', "input", &input_name, "file",
30 "input file name"),
31 OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
32 OPT_INCR('v', "verbose", &verbose,
33 "be more verbose"),
34 OPT_END()
35};
36
37static int __cmd_buildid_list(void)
38{
39 struct perf_session *session;
40
41 session = perf_session__new(input_name, O_RDONLY, force, false,
42 &build_id__mark_dso_hit_ops);
43 if (session == NULL)
44 return -1;
45
46 if (with_hits)
47 perf_session__process_events(session, &build_id__mark_dso_hit_ops);
48
49 perf_session__fprintf_dsos_buildid(session, stdout, with_hits);
50
51 perf_session__delete(session);
52 return 0;
53}
54
55int cmd_buildid_list(int argc, const char **argv, const char *prefix __used)
56{
57 argc = parse_options(argc, argv, options, buildid_list_usage, 0);
58 setup_pager();
59 return __cmd_buildid_list();
60}
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c
new file mode 100644
index 00000000000..3153e492dbc
--- /dev/null
+++ b/tools/perf/builtin-diff.c
@@ -0,0 +1,232 @@
1/*
2 * builtin-diff.c
3 *
4 * Builtin diff command: Analyze two perf.data input files, look up and read
5 * DSOs and symbol information, sort them and produce a diff.
6 */
7#include "builtin.h"
8
9#include "util/debug.h"
10#include "util/event.h"
11#include "util/hist.h"
12#include "util/session.h"
13#include "util/sort.h"
14#include "util/symbol.h"
15#include "util/util.h"
16
17#include <stdlib.h>
18
19static char const *input_old = "perf.data.old",
20 *input_new = "perf.data";
21static char diff__default_sort_order[] = "dso,symbol";
22static bool force;
23static bool show_displacement;
24
25static int hists__add_entry(struct hists *self,
26 struct addr_location *al, u64 period)
27{
28 if (__hists__add_entry(self, al, NULL, period) != NULL)
29 return 0;
30 return -ENOMEM;
31}
32
33static int diff__process_sample_event(event_t *event,
34 struct sample_data *sample,
35 struct perf_session *session)
36{
37 struct addr_location al;
38
39 if (event__preprocess_sample(event, session, &al, sample, NULL) < 0) {
40 pr_warning("problem processing %d event, skipping it.\n",
41 event->header.type);
42 return -1;
43 }
44
45 if (al.filtered || al.sym == NULL)
46 return 0;
47
48 if (hists__add_entry(&session->hists, &al, sample->period)) {
49 pr_warning("problem incrementing symbol period, skipping event\n");
50 return -1;
51 }
52
53 session->hists.stats.total_period += sample->period;
54 return 0;
55}
56
57static struct perf_event_ops event_ops = {
58 .sample = diff__process_sample_event,
59 .mmap = event__process_mmap,
60 .comm = event__process_comm,
61 .exit = event__process_task,
62 .fork = event__process_task,
63 .lost = event__process_lost,
64 .ordered_samples = true,
65 .ordering_requires_timestamps = true,
66};
67
68static void perf_session__insert_hist_entry_by_name(struct rb_root *root,
69 struct hist_entry *he)
70{
71 struct rb_node **p = &root->rb_node;
72 struct rb_node *parent = NULL;
73 struct hist_entry *iter;
74
75 while (*p != NULL) {
76 parent = *p;
77 iter = rb_entry(parent, struct hist_entry, rb_node);
78 if (hist_entry__cmp(he, iter) < 0)
79 p = &(*p)->rb_left;
80 else
81 p = &(*p)->rb_right;
82 }
83
84 rb_link_node(&he->rb_node, parent, p);
85 rb_insert_color(&he->rb_node, root);
86}
87
88static void hists__resort_entries(struct hists *self)
89{
90 unsigned long position = 1;
91 struct rb_root tmp = RB_ROOT;
92 struct rb_node *next = rb_first(&self->entries);
93
94 while (next != NULL) {
95 struct hist_entry *n = rb_entry(next, struct hist_entry, rb_node);
96
97 next = rb_next(&n->rb_node);
98 rb_erase(&n->rb_node, &self->entries);
99 n->position = position++;
100 perf_session__insert_hist_entry_by_name(&tmp, n);
101 }
102
103 self->entries = tmp;
104}
105
106static void hists__set_positions(struct hists *self)
107{
108 hists__output_resort(self);
109 hists__resort_entries(self);
110}
111
112static struct hist_entry *hists__find_entry(struct hists *self,
113 struct hist_entry *he)
114{
115 struct rb_node *n = self->entries.rb_node;
116
117 while (n) {
118 struct hist_entry *iter = rb_entry(n, struct hist_entry, rb_node);
119 int64_t cmp = hist_entry__cmp(he, iter);
120
121 if (cmp < 0)
122 n = n->rb_left;
123 else if (cmp > 0)
124 n = n->rb_right;
125 else
126 return iter;
127 }
128
129 return NULL;
130}
131
132static void hists__match(struct hists *older, struct hists *newer)
133{
134 struct rb_node *nd;
135
136 for (nd = rb_first(&newer->entries); nd; nd = rb_next(nd)) {
137 struct hist_entry *pos = rb_entry(nd, struct hist_entry, rb_node);
138 pos->pair = hists__find_entry(older, pos);
139 }
140}
141
142static int __cmd_diff(void)
143{
144 int ret, i;
145 struct perf_session *session[2];
146
147 session[0] = perf_session__new(input_old, O_RDONLY, force, false, &event_ops);
148 session[1] = perf_session__new(input_new, O_RDONLY, force, false, &event_ops);
149 if (session[0] == NULL || session[1] == NULL)
150 return -ENOMEM;
151
152 for (i = 0; i < 2; ++i) {
153 ret = perf_session__process_events(session[i], &event_ops);
154 if (ret)
155 goto out_delete;
156 }
157
158 hists__output_resort(&session[1]->hists);
159 if (show_displacement)
160 hists__set_positions(&session[0]->hists);
161
162 hists__match(&session[0]->hists, &session[1]->hists);
163 hists__fprintf(&session[1]->hists, &session[0]->hists,
164 show_displacement, stdout);
165out_delete:
166 for (i = 0; i < 2; ++i)
167 perf_session__delete(session[i]);
168 return ret;
169}
170
171static const char * const diff_usage[] = {
172 "perf diff [<options>] [old_file] [new_file]",
173 NULL,
174};
175
176static const struct option options[] = {
177 OPT_INCR('v', "verbose", &verbose,
178 "be more verbose (show symbol address, etc)"),
179 OPT_BOOLEAN('M', "displacement", &show_displacement,
180 "Show position displacement relative to baseline"),
181 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
182 "dump raw trace in ASCII"),
183 OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
184 OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules,
185 "load module symbols - WARNING: use only with -k and LIVE kernel"),
186 OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]",
187 "only consider symbols in these dsos"),
188 OPT_STRING('C', "comms", &symbol_conf.comm_list_str, "comm[,comm...]",
189 "only consider symbols in these comms"),
190 OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]",
191 "only consider these symbols"),
192 OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
193 "sort by key(s): pid, comm, dso, symbol, parent"),
194 OPT_STRING('t', "field-separator", &symbol_conf.field_sep, "separator",
195 "separator for columns, no spaces will be added between "
196 "columns '.' is reserved."),
197 OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
198 "Look for files with symbols relative to this directory"),
199 OPT_END()
200};
201
202int cmd_diff(int argc, const char **argv, const char *prefix __used)
203{
204 sort_order = diff__default_sort_order;
205 argc = parse_options(argc, argv, options, diff_usage, 0);
206 if (argc) {
207 if (argc > 2)
208 usage_with_options(diff_usage, options);
209 if (argc == 2) {
210 input_old = argv[0];
211 input_new = argv[1];
212 } else
213 input_new = argv[0];
214 } else if (symbol_conf.default_guest_vmlinux_name ||
215 symbol_conf.default_guest_kallsyms) {
216 input_old = "perf.data.host";
217 input_new = "perf.data.guest";
218 }
219
220 symbol_conf.exclude_other = false;
221 if (symbol__init() < 0)
222 return -1;
223
224 setup_sorting(diff_usage, options);
225 setup_pager();
226
227 sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso", NULL);
228 sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, "comm", NULL);
229 sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list, "symbol", NULL);
230
231 return __cmd_diff();
232}
diff --git a/tools/perf/builtin-help.c b/tools/perf/builtin-help.c
index 0f32dc3f3c4..6d5a8a7faf4 100644
--- a/tools/perf/builtin-help.c
+++ b/tools/perf/builtin-help.c
@@ -3,6 +3,7 @@
3 * 3 *
4 * Builtin help command 4 * Builtin help command
5 */ 5 */
6#include "perf.h"
6#include "util/cache.h" 7#include "util/cache.h"
7#include "builtin.h" 8#include "builtin.h"
8#include "util/exec_cmd.h" 9#include "util/exec_cmd.h"
@@ -28,14 +29,14 @@ enum help_format {
28 HELP_FORMAT_WEB, 29 HELP_FORMAT_WEB,
29}; 30};
30 31
31static int show_all = 0; 32static bool show_all = false;
32static enum help_format help_format = HELP_FORMAT_MAN; 33static enum help_format help_format = HELP_FORMAT_MAN;
33static struct option builtin_help_options[] = { 34static struct option builtin_help_options[] = {
34 OPT_BOOLEAN('a', "all", &show_all, "print all available commands"), 35 OPT_BOOLEAN('a', "all", &show_all, "print all available commands"),
35 OPT_SET_INT('m', "man", &help_format, "show man page", HELP_FORMAT_MAN), 36 OPT_SET_UINT('m', "man", &help_format, "show man page", HELP_FORMAT_MAN),
36 OPT_SET_INT('w', "web", &help_format, "show manual in web browser", 37 OPT_SET_UINT('w', "web", &help_format, "show manual in web browser",
37 HELP_FORMAT_WEB), 38 HELP_FORMAT_WEB),
38 OPT_SET_INT('i', "info", &help_format, "show info page", 39 OPT_SET_UINT('i', "info", &help_format, "show info page",
39 HELP_FORMAT_INFO), 40 HELP_FORMAT_INFO),
40 OPT_END(), 41 OPT_END(),
41}; 42};
@@ -60,8 +61,7 @@ static const char *get_man_viewer_info(const char *name)
60{ 61{
61 struct man_viewer_info_list *viewer; 62 struct man_viewer_info_list *viewer;
62 63
63 for (viewer = man_viewer_info_list; viewer; viewer = viewer->next) 64 for (viewer = man_viewer_info_list; viewer; viewer = viewer->next) {
64 {
65 if (!strcasecmp(name, viewer->name)) 65 if (!strcasecmp(name, viewer->name))
66 return viewer->info; 66 return viewer->info;
67 } 67 }
@@ -114,7 +114,7 @@ static int check_emacsclient_version(void)
114 return 0; 114 return 0;
115} 115}
116 116
117static void exec_woman_emacs(const char* path, const char *page) 117static void exec_woman_emacs(const char *path, const char *page)
118{ 118{
119 if (!check_emacsclient_version()) { 119 if (!check_emacsclient_version()) {
120 /* This works only with emacsclient version >= 22. */ 120 /* This works only with emacsclient version >= 22. */
@@ -128,7 +128,7 @@ static void exec_woman_emacs(const char* path, const char *page)
128 } 128 }
129} 129}
130 130
131static void exec_man_konqueror(const char* path, const char *page) 131static void exec_man_konqueror(const char *path, const char *page)
132{ 132{
133 const char *display = getenv("DISPLAY"); 133 const char *display = getenv("DISPLAY");
134 if (display && *display) { 134 if (display && *display) {
@@ -156,7 +156,7 @@ static void exec_man_konqueror(const char* path, const char *page)
156 } 156 }
157} 157}
158 158
159static void exec_man_man(const char* path, const char *page) 159static void exec_man_man(const char *path, const char *page)
160{ 160{
161 if (!path) 161 if (!path)
162 path = "man"; 162 path = "man";
@@ -179,7 +179,7 @@ static void add_man_viewer(const char *name)
179 179
180 while (*p) 180 while (*p)
181 p = &((*p)->next); 181 p = &((*p)->next);
182 *p = calloc(1, (sizeof(**p) + len + 1)); 182 *p = zalloc(sizeof(**p) + len + 1);
183 strncpy((*p)->name, name, len); 183 strncpy((*p)->name, name, len);
184} 184}
185 185
@@ -194,7 +194,7 @@ static void do_add_man_viewer_info(const char *name,
194 size_t len, 194 size_t len,
195 const char *value) 195 const char *value)
196{ 196{
197 struct man_viewer_info_list *new = calloc(1, sizeof(*new) + len + 1); 197 struct man_viewer_info_list *new = zalloc(sizeof(*new) + len + 1);
198 198
199 strncpy(new->name, name, len); 199 strncpy(new->name, name, len);
200 new->info = strdup(value); 200 new->info = strdup(value);
@@ -277,7 +277,7 @@ static struct cmdnames main_cmds, other_cmds;
277 277
278void list_common_cmds_help(void) 278void list_common_cmds_help(void)
279{ 279{
280 int i, longest = 0; 280 unsigned int i, longest = 0;
281 281
282 for (i = 0; i < ARRAY_SIZE(common_cmds); i++) { 282 for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
283 if (longest < strlen(common_cmds[i].name)) 283 if (longest < strlen(common_cmds[i].name))
@@ -286,8 +286,7 @@ void list_common_cmds_help(void)
286 286
287 puts(" The most commonly used perf commands are:"); 287 puts(" The most commonly used perf commands are:");
288 for (i = 0; i < ARRAY_SIZE(common_cmds); i++) { 288 for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
289 printf(" %s ", common_cmds[i].name); 289 printf(" %-*s ", longest, common_cmds[i].name);
290 mput_char(' ', longest - strlen(common_cmds[i].name));
291 puts(common_cmds[i].help); 290 puts(common_cmds[i].help);
292 } 291 }
293} 292}
@@ -314,8 +313,6 @@ static const char *cmd_to_page(const char *perf_cmd)
314 return "perf"; 313 return "perf";
315 else if (!prefixcmp(perf_cmd, "perf")) 314 else if (!prefixcmp(perf_cmd, "perf"))
316 return perf_cmd; 315 return perf_cmd;
317 else if (is_perf_command(perf_cmd))
318 return prepend("perf-", perf_cmd);
319 else 316 else
320 return prepend("perf-", perf_cmd); 317 return prepend("perf-", perf_cmd);
321} 318}
@@ -363,9 +360,8 @@ static void show_man_page(const char *perf_cmd)
363 360
364 setup_man_path(); 361 setup_man_path();
365 for (viewer = man_viewer_list; viewer; viewer = viewer->next) 362 for (viewer = man_viewer_list; viewer; viewer = viewer->next)
366 {
367 exec_viewer(viewer->name, page); /* will return when unable */ 363 exec_viewer(viewer->name, page); /* will return when unable */
368 } 364
369 if (fallback) 365 if (fallback)
370 exec_viewer(fallback, page); 366 exec_viewer(fallback, page);
371 exec_viewer("man", page); 367 exec_viewer("man", page);
@@ -415,9 +411,10 @@ static void show_html_page(const char *perf_cmd)
415 open_html(page_path.buf); 411 open_html(page_path.buf);
416} 412}
417 413
418int cmd_help(int argc, const char **argv, const char *prefix) 414int cmd_help(int argc, const char **argv, const char *prefix __used)
419{ 415{
420 const char *alias; 416 const char *alias;
417
421 load_command_list("perf-", &main_cmds, &other_cmds); 418 load_command_list("perf-", &main_cmds, &other_cmds);
422 419
423 perf_config(perf_help_config, NULL); 420 perf_config(perf_help_config, NULL);
@@ -454,6 +451,7 @@ int cmd_help(int argc, const char **argv, const char *prefix)
454 break; 451 break;
455 case HELP_FORMAT_WEB: 452 case HELP_FORMAT_WEB:
456 show_html_page(argv[0]); 453 show_html_page(argv[0]);
454 default:
457 break; 455 break;
458 } 456 }
459 457
diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c
new file mode 100644
index 00000000000..0c78ffa7bf6
--- /dev/null
+++ b/tools/perf/builtin-inject.c
@@ -0,0 +1,237 @@
1/*
2 * builtin-inject.c
3 *
4 * Builtin inject command: Examine the live mode (stdin) event stream
5 * and repipe it to stdout while optionally injecting additional
6 * events into it.
7 */
8#include "builtin.h"
9
10#include "perf.h"
11#include "util/session.h"
12#include "util/debug.h"
13
14#include "util/parse-options.h"
15
16static char const *input_name = "-";
17static bool inject_build_ids;
18
19static int event__repipe_synth(event_t *event,
20 struct perf_session *session __used)
21{
22 uint32_t size;
23 void *buf = event;
24
25 size = event->header.size;
26
27 while (size) {
28 int ret = write(STDOUT_FILENO, buf, size);
29 if (ret < 0)
30 return -errno;
31
32 size -= ret;
33 buf += ret;
34 }
35
36 return 0;
37}
38
39static int event__repipe(event_t *event, struct sample_data *sample __used,
40 struct perf_session *session)
41{
42 return event__repipe_synth(event, session);
43}
44
45static int event__repipe_mmap(event_t *self, struct sample_data *sample,
46 struct perf_session *session)
47{
48 int err;
49
50 err = event__process_mmap(self, sample, session);
51 event__repipe(self, sample, session);
52
53 return err;
54}
55
56static int event__repipe_task(event_t *self, struct sample_data *sample,
57 struct perf_session *session)
58{
59 int err;
60
61 err = event__process_task(self, sample, session);
62 event__repipe(self, sample, session);
63
64 return err;
65}
66
67static int event__repipe_tracing_data(event_t *self,
68 struct perf_session *session)
69{
70 int err;
71
72 event__repipe_synth(self, session);
73 err = event__process_tracing_data(self, session);
74
75 return err;
76}
77
78static int dso__read_build_id(struct dso *self)
79{
80 if (self->has_build_id)
81 return 0;
82
83 if (filename__read_build_id(self->long_name, self->build_id,
84 sizeof(self->build_id)) > 0) {
85 self->has_build_id = true;
86 return 0;
87 }
88
89 return -1;
90}
91
92static int dso__inject_build_id(struct dso *self, struct perf_session *session)
93{
94 u16 misc = PERF_RECORD_MISC_USER;
95 struct machine *machine;
96 int err;
97
98 if (dso__read_build_id(self) < 0) {
99 pr_debug("no build_id found for %s\n", self->long_name);
100 return -1;
101 }
102
103 machine = perf_session__find_host_machine(session);
104 if (machine == NULL) {
105 pr_err("Can't find machine for session\n");
106 return -1;
107 }
108
109 if (self->kernel)
110 misc = PERF_RECORD_MISC_KERNEL;
111
112 err = event__synthesize_build_id(self, misc, event__repipe,
113 machine, session);
114 if (err) {
115 pr_err("Can't synthesize build_id event for %s\n", self->long_name);
116 return -1;
117 }
118
119 return 0;
120}
121
122static int event__inject_buildid(event_t *event, struct sample_data *sample,
123 struct perf_session *session)
124{
125 struct addr_location al;
126 struct thread *thread;
127 u8 cpumode;
128
129 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
130
131 thread = perf_session__findnew(session, event->ip.pid);
132 if (thread == NULL) {
133 pr_err("problem processing %d event, skipping it.\n",
134 event->header.type);
135 goto repipe;
136 }
137
138 thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION,
139 event->ip.pid, event->ip.ip, &al);
140
141 if (al.map != NULL) {
142 if (!al.map->dso->hit) {
143 al.map->dso->hit = 1;
144 if (map__load(al.map, NULL) >= 0) {
145 dso__inject_build_id(al.map->dso, session);
146 /*
147 * If this fails, too bad, let the other side
148 * account this as unresolved.
149 */
150 } else
151 pr_warning("no symbols found in %s, maybe "
152 "install a debug package?\n",
153 al.map->dso->long_name);
154 }
155 }
156
157repipe:
158 event__repipe(event, sample, session);
159 return 0;
160}
161
162struct perf_event_ops inject_ops = {
163 .sample = event__repipe,
164 .mmap = event__repipe,
165 .comm = event__repipe,
166 .fork = event__repipe,
167 .exit = event__repipe,
168 .lost = event__repipe,
169 .read = event__repipe,
170 .throttle = event__repipe,
171 .unthrottle = event__repipe,
172 .attr = event__repipe_synth,
173 .event_type = event__repipe_synth,
174 .tracing_data = event__repipe_synth,
175 .build_id = event__repipe_synth,
176};
177
178extern volatile int session_done;
179
180static void sig_handler(int sig __attribute__((__unused__)))
181{
182 session_done = 1;
183}
184
185static int __cmd_inject(void)
186{
187 struct perf_session *session;
188 int ret = -EINVAL;
189
190 signal(SIGINT, sig_handler);
191
192 if (inject_build_ids) {
193 inject_ops.sample = event__inject_buildid;
194 inject_ops.mmap = event__repipe_mmap;
195 inject_ops.fork = event__repipe_task;
196 inject_ops.tracing_data = event__repipe_tracing_data;
197 }
198
199 session = perf_session__new(input_name, O_RDONLY, false, true, &inject_ops);
200 if (session == NULL)
201 return -ENOMEM;
202
203 ret = perf_session__process_events(session, &inject_ops);
204
205 perf_session__delete(session);
206
207 return ret;
208}
209
210static const char * const report_usage[] = {
211 "perf inject [<options>]",
212 NULL
213};
214
215static const struct option options[] = {
216 OPT_BOOLEAN('b', "build-ids", &inject_build_ids,
217 "Inject build-ids into the output stream"),
218 OPT_INCR('v', "verbose", &verbose,
219 "be more verbose (show build ids, etc)"),
220 OPT_END()
221};
222
223int cmd_inject(int argc, const char **argv, const char *prefix __used)
224{
225 argc = parse_options(argc, argv, options, report_usage, 0);
226
227 /*
228 * Any (unrecognized) arguments left?
229 */
230 if (argc)
231 usage_with_options(report_usage, options);
232
233 if (symbol__init() < 0)
234 return -1;
235
236 return __cmd_inject();
237}
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c
new file mode 100644
index 00000000000..def7ddc2fd4
--- /dev/null
+++ b/tools/perf/builtin-kmem.c
@@ -0,0 +1,777 @@
1#include "builtin.h"
2#include "perf.h"
3
4#include "util/util.h"
5#include "util/cache.h"
6#include "util/symbol.h"
7#include "util/thread.h"
8#include "util/header.h"
9#include "util/session.h"
10
11#include "util/parse-options.h"
12#include "util/trace-event.h"
13
14#include "util/debug.h"
15
16#include <linux/rbtree.h>
17
18struct alloc_stat;
19typedef int (*sort_fn_t)(struct alloc_stat *, struct alloc_stat *);
20
21static char const *input_name = "perf.data";
22
23static int alloc_flag;
24static int caller_flag;
25
26static int alloc_lines = -1;
27static int caller_lines = -1;
28
29static bool raw_ip;
30
31static char default_sort_order[] = "frag,hit,bytes";
32
33static int *cpunode_map;
34static int max_cpu_num;
35
36struct alloc_stat {
37 u64 call_site;
38 u64 ptr;
39 u64 bytes_req;
40 u64 bytes_alloc;
41 u32 hit;
42 u32 pingpong;
43
44 short alloc_cpu;
45
46 struct rb_node node;
47};
48
49static struct rb_root root_alloc_stat;
50static struct rb_root root_alloc_sorted;
51static struct rb_root root_caller_stat;
52static struct rb_root root_caller_sorted;
53
54static unsigned long total_requested, total_allocated;
55static unsigned long nr_allocs, nr_cross_allocs;
56
57#define PATH_SYS_NODE "/sys/devices/system/node"
58
59static void init_cpunode_map(void)
60{
61 FILE *fp;
62 int i;
63
64 fp = fopen("/sys/devices/system/cpu/kernel_max", "r");
65 if (!fp) {
66 max_cpu_num = 4096;
67 return;
68 }
69
70 if (fscanf(fp, "%d", &max_cpu_num) < 1)
71 die("Failed to read 'kernel_max' from sysfs");
72 max_cpu_num++;
73
74 cpunode_map = calloc(max_cpu_num, sizeof(int));
75 if (!cpunode_map)
76 die("calloc");
77 for (i = 0; i < max_cpu_num; i++)
78 cpunode_map[i] = -1;
79 fclose(fp);
80}
81
82static void setup_cpunode_map(void)
83{
84 struct dirent *dent1, *dent2;
85 DIR *dir1, *dir2;
86 unsigned int cpu, mem;
87 char buf[PATH_MAX];
88
89 init_cpunode_map();
90
91 dir1 = opendir(PATH_SYS_NODE);
92 if (!dir1)
93 return;
94
95 while ((dent1 = readdir(dir1)) != NULL) {
96 if (dent1->d_type != DT_DIR ||
97 sscanf(dent1->d_name, "node%u", &mem) < 1)
98 continue;
99
100 snprintf(buf, PATH_MAX, "%s/%s", PATH_SYS_NODE, dent1->d_name);
101 dir2 = opendir(buf);
102 if (!dir2)
103 continue;
104 while ((dent2 = readdir(dir2)) != NULL) {
105 if (dent2->d_type != DT_LNK ||
106 sscanf(dent2->d_name, "cpu%u", &cpu) < 1)
107 continue;
108 cpunode_map[cpu] = mem;
109 }
110 }
111}
112
113static void insert_alloc_stat(unsigned long call_site, unsigned long ptr,
114 int bytes_req, int bytes_alloc, int cpu)
115{
116 struct rb_node **node = &root_alloc_stat.rb_node;
117 struct rb_node *parent = NULL;
118 struct alloc_stat *data = NULL;
119
120 while (*node) {
121 parent = *node;
122 data = rb_entry(*node, struct alloc_stat, node);
123
124 if (ptr > data->ptr)
125 node = &(*node)->rb_right;
126 else if (ptr < data->ptr)
127 node = &(*node)->rb_left;
128 else
129 break;
130 }
131
132 if (data && data->ptr == ptr) {
133 data->hit++;
134 data->bytes_req += bytes_req;
135 data->bytes_alloc += bytes_alloc;
136 } else {
137 data = malloc(sizeof(*data));
138 if (!data)
139 die("malloc");
140 data->ptr = ptr;
141 data->pingpong = 0;
142 data->hit = 1;
143 data->bytes_req = bytes_req;
144 data->bytes_alloc = bytes_alloc;
145
146 rb_link_node(&data->node, parent, node);
147 rb_insert_color(&data->node, &root_alloc_stat);
148 }
149 data->call_site = call_site;
150 data->alloc_cpu = cpu;
151}
152
153static void insert_caller_stat(unsigned long call_site,
154 int bytes_req, int bytes_alloc)
155{
156 struct rb_node **node = &root_caller_stat.rb_node;
157 struct rb_node *parent = NULL;
158 struct alloc_stat *data = NULL;
159
160 while (*node) {
161 parent = *node;
162 data = rb_entry(*node, struct alloc_stat, node);
163
164 if (call_site > data->call_site)
165 node = &(*node)->rb_right;
166 else if (call_site < data->call_site)
167 node = &(*node)->rb_left;
168 else
169 break;
170 }
171
172 if (data && data->call_site == call_site) {
173 data->hit++;
174 data->bytes_req += bytes_req;
175 data->bytes_alloc += bytes_alloc;
176 } else {
177 data = malloc(sizeof(*data));
178 if (!data)
179 die("malloc");
180 data->call_site = call_site;
181 data->pingpong = 0;
182 data->hit = 1;
183 data->bytes_req = bytes_req;
184 data->bytes_alloc = bytes_alloc;
185
186 rb_link_node(&data->node, parent, node);
187 rb_insert_color(&data->node, &root_caller_stat);
188 }
189}
190
191static void process_alloc_event(void *data,
192 struct event *event,
193 int cpu,
194 u64 timestamp __used,
195 struct thread *thread __used,
196 int node)
197{
198 unsigned long call_site;
199 unsigned long ptr;
200 int bytes_req;
201 int bytes_alloc;
202 int node1, node2;
203
204 ptr = raw_field_value(event, "ptr", data);
205 call_site = raw_field_value(event, "call_site", data);
206 bytes_req = raw_field_value(event, "bytes_req", data);
207 bytes_alloc = raw_field_value(event, "bytes_alloc", data);
208
209 insert_alloc_stat(call_site, ptr, bytes_req, bytes_alloc, cpu);
210 insert_caller_stat(call_site, bytes_req, bytes_alloc);
211
212 total_requested += bytes_req;
213 total_allocated += bytes_alloc;
214
215 if (node) {
216 node1 = cpunode_map[cpu];
217 node2 = raw_field_value(event, "node", data);
218 if (node1 != node2)
219 nr_cross_allocs++;
220 }
221 nr_allocs++;
222}
223
224static int ptr_cmp(struct alloc_stat *, struct alloc_stat *);
225static int callsite_cmp(struct alloc_stat *, struct alloc_stat *);
226
227static struct alloc_stat *search_alloc_stat(unsigned long ptr,
228 unsigned long call_site,
229 struct rb_root *root,
230 sort_fn_t sort_fn)
231{
232 struct rb_node *node = root->rb_node;
233 struct alloc_stat key = { .ptr = ptr, .call_site = call_site };
234
235 while (node) {
236 struct alloc_stat *data;
237 int cmp;
238
239 data = rb_entry(node, struct alloc_stat, node);
240
241 cmp = sort_fn(&key, data);
242 if (cmp < 0)
243 node = node->rb_left;
244 else if (cmp > 0)
245 node = node->rb_right;
246 else
247 return data;
248 }
249 return NULL;
250}
251
252static void process_free_event(void *data,
253 struct event *event,
254 int cpu,
255 u64 timestamp __used,
256 struct thread *thread __used)
257{
258 unsigned long ptr;
259 struct alloc_stat *s_alloc, *s_caller;
260
261 ptr = raw_field_value(event, "ptr", data);
262
263 s_alloc = search_alloc_stat(ptr, 0, &root_alloc_stat, ptr_cmp);
264 if (!s_alloc)
265 return;
266
267 if (cpu != s_alloc->alloc_cpu) {
268 s_alloc->pingpong++;
269
270 s_caller = search_alloc_stat(0, s_alloc->call_site,
271 &root_caller_stat, callsite_cmp);
272 assert(s_caller);
273 s_caller->pingpong++;
274 }
275 s_alloc->alloc_cpu = -1;
276}
277
278static void
279process_raw_event(event_t *raw_event __used, void *data,
280 int cpu, u64 timestamp, struct thread *thread)
281{
282 struct event *event;
283 int type;
284
285 type = trace_parse_common_type(data);
286 event = trace_find_event(type);
287
288 if (!strcmp(event->name, "kmalloc") ||
289 !strcmp(event->name, "kmem_cache_alloc")) {
290 process_alloc_event(data, event, cpu, timestamp, thread, 0);
291 return;
292 }
293
294 if (!strcmp(event->name, "kmalloc_node") ||
295 !strcmp(event->name, "kmem_cache_alloc_node")) {
296 process_alloc_event(data, event, cpu, timestamp, thread, 1);
297 return;
298 }
299
300 if (!strcmp(event->name, "kfree") ||
301 !strcmp(event->name, "kmem_cache_free")) {
302 process_free_event(data, event, cpu, timestamp, thread);
303 return;
304 }
305}
306
307static int process_sample_event(event_t *event, struct sample_data *sample,
308 struct perf_session *session)
309{
310 struct thread *thread = perf_session__findnew(session, event->ip.pid);
311
312 if (thread == NULL) {
313 pr_debug("problem processing %d event, skipping it.\n",
314 event->header.type);
315 return -1;
316 }
317
318 dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid);
319
320 process_raw_event(event, sample->raw_data, sample->cpu,
321 sample->time, thread);
322
323 return 0;
324}
325
326static struct perf_event_ops event_ops = {
327 .sample = process_sample_event,
328 .comm = event__process_comm,
329 .ordered_samples = true,
330};
331
332static double fragmentation(unsigned long n_req, unsigned long n_alloc)
333{
334 if (n_alloc == 0)
335 return 0.0;
336 else
337 return 100.0 - (100.0 * n_req / n_alloc);
338}
339
340static void __print_result(struct rb_root *root, struct perf_session *session,
341 int n_lines, int is_caller)
342{
343 struct rb_node *next;
344 struct machine *machine;
345
346 printf("%.102s\n", graph_dotted_line);
347 printf(" %-34s |", is_caller ? "Callsite": "Alloc Ptr");
348 printf(" Total_alloc/Per | Total_req/Per | Hit | Ping-pong | Frag\n");
349 printf("%.102s\n", graph_dotted_line);
350
351 next = rb_first(root);
352
353 machine = perf_session__find_host_machine(session);
354 if (!machine) {
355 pr_err("__print_result: couldn't find kernel information\n");
356 return;
357 }
358 while (next && n_lines--) {
359 struct alloc_stat *data = rb_entry(next, struct alloc_stat,
360 node);
361 struct symbol *sym = NULL;
362 struct map *map;
363 char buf[BUFSIZ];
364 u64 addr;
365
366 if (is_caller) {
367 addr = data->call_site;
368 if (!raw_ip)
369 sym = machine__find_kernel_function(machine, addr, &map, NULL);
370 } else
371 addr = data->ptr;
372
373 if (sym != NULL)
374 snprintf(buf, sizeof(buf), "%s+%Lx", sym->name,
375 addr - map->unmap_ip(map, sym->start));
376 else
377 snprintf(buf, sizeof(buf), "%#Lx", addr);
378 printf(" %-34s |", buf);
379
380 printf(" %9llu/%-5lu | %9llu/%-5lu | %8lu | %8lu | %6.3f%%\n",
381 (unsigned long long)data->bytes_alloc,
382 (unsigned long)data->bytes_alloc / data->hit,
383 (unsigned long long)data->bytes_req,
384 (unsigned long)data->bytes_req / data->hit,
385 (unsigned long)data->hit,
386 (unsigned long)data->pingpong,
387 fragmentation(data->bytes_req, data->bytes_alloc));
388
389 next = rb_next(next);
390 }
391
392 if (n_lines == -1)
393 printf(" ... | ... | ... | ... | ... | ... \n");
394
395 printf("%.102s\n", graph_dotted_line);
396}
397
398static void print_summary(void)
399{
400 printf("\nSUMMARY\n=======\n");
401 printf("Total bytes requested: %lu\n", total_requested);
402 printf("Total bytes allocated: %lu\n", total_allocated);
403 printf("Total bytes wasted on internal fragmentation: %lu\n",
404 total_allocated - total_requested);
405 printf("Internal fragmentation: %f%%\n",
406 fragmentation(total_requested, total_allocated));
407 printf("Cross CPU allocations: %lu/%lu\n", nr_cross_allocs, nr_allocs);
408}
409
410static void print_result(struct perf_session *session)
411{
412 if (caller_flag)
413 __print_result(&root_caller_sorted, session, caller_lines, 1);
414 if (alloc_flag)
415 __print_result(&root_alloc_sorted, session, alloc_lines, 0);
416 print_summary();
417}
418
419struct sort_dimension {
420 const char name[20];
421 sort_fn_t cmp;
422 struct list_head list;
423};
424
425static LIST_HEAD(caller_sort);
426static LIST_HEAD(alloc_sort);
427
428static void sort_insert(struct rb_root *root, struct alloc_stat *data,
429 struct list_head *sort_list)
430{
431 struct rb_node **new = &(root->rb_node);
432 struct rb_node *parent = NULL;
433 struct sort_dimension *sort;
434
435 while (*new) {
436 struct alloc_stat *this;
437 int cmp = 0;
438
439 this = rb_entry(*new, struct alloc_stat, node);
440 parent = *new;
441
442 list_for_each_entry(sort, sort_list, list) {
443 cmp = sort->cmp(data, this);
444 if (cmp)
445 break;
446 }
447
448 if (cmp > 0)
449 new = &((*new)->rb_left);
450 else
451 new = &((*new)->rb_right);
452 }
453
454 rb_link_node(&data->node, parent, new);
455 rb_insert_color(&data->node, root);
456}
457
458static void __sort_result(struct rb_root *root, struct rb_root *root_sorted,
459 struct list_head *sort_list)
460{
461 struct rb_node *node;
462 struct alloc_stat *data;
463
464 for (;;) {
465 node = rb_first(root);
466 if (!node)
467 break;
468
469 rb_erase(node, root);
470 data = rb_entry(node, struct alloc_stat, node);
471 sort_insert(root_sorted, data, sort_list);
472 }
473}
474
475static void sort_result(void)
476{
477 __sort_result(&root_alloc_stat, &root_alloc_sorted, &alloc_sort);
478 __sort_result(&root_caller_stat, &root_caller_sorted, &caller_sort);
479}
480
481static int __cmd_kmem(void)
482{
483 int err = -EINVAL;
484 struct perf_session *session = perf_session__new(input_name, O_RDONLY,
485 0, false, &event_ops);
486 if (session == NULL)
487 return -ENOMEM;
488
489 if (perf_session__create_kernel_maps(session) < 0)
490 goto out_delete;
491
492 if (!perf_session__has_traces(session, "kmem record"))
493 goto out_delete;
494
495 setup_pager();
496 err = perf_session__process_events(session, &event_ops);
497 if (err != 0)
498 goto out_delete;
499 sort_result();
500 print_result(session);
501out_delete:
502 perf_session__delete(session);
503 return err;
504}
505
506static const char * const kmem_usage[] = {
507 "perf kmem [<options>] {record|stat}",
508 NULL
509};
510
511static int ptr_cmp(struct alloc_stat *l, struct alloc_stat *r)
512{
513 if (l->ptr < r->ptr)
514 return -1;
515 else if (l->ptr > r->ptr)
516 return 1;
517 return 0;
518}
519
520static struct sort_dimension ptr_sort_dimension = {
521 .name = "ptr",
522 .cmp = ptr_cmp,
523};
524
525static int callsite_cmp(struct alloc_stat *l, struct alloc_stat *r)
526{
527 if (l->call_site < r->call_site)
528 return -1;
529 else if (l->call_site > r->call_site)
530 return 1;
531 return 0;
532}
533
534static struct sort_dimension callsite_sort_dimension = {
535 .name = "callsite",
536 .cmp = callsite_cmp,
537};
538
539static int hit_cmp(struct alloc_stat *l, struct alloc_stat *r)
540{
541 if (l->hit < r->hit)
542 return -1;
543 else if (l->hit > r->hit)
544 return 1;
545 return 0;
546}
547
548static struct sort_dimension hit_sort_dimension = {
549 .name = "hit",
550 .cmp = hit_cmp,
551};
552
553static int bytes_cmp(struct alloc_stat *l, struct alloc_stat *r)
554{
555 if (l->bytes_alloc < r->bytes_alloc)
556 return -1;
557 else if (l->bytes_alloc > r->bytes_alloc)
558 return 1;
559 return 0;
560}
561
562static struct sort_dimension bytes_sort_dimension = {
563 .name = "bytes",
564 .cmp = bytes_cmp,
565};
566
567static int frag_cmp(struct alloc_stat *l, struct alloc_stat *r)
568{
569 double x, y;
570
571 x = fragmentation(l->bytes_req, l->bytes_alloc);
572 y = fragmentation(r->bytes_req, r->bytes_alloc);
573
574 if (x < y)
575 return -1;
576 else if (x > y)
577 return 1;
578 return 0;
579}
580
581static struct sort_dimension frag_sort_dimension = {
582 .name = "frag",
583 .cmp = frag_cmp,
584};
585
586static int pingpong_cmp(struct alloc_stat *l, struct alloc_stat *r)
587{
588 if (l->pingpong < r->pingpong)
589 return -1;
590 else if (l->pingpong > r->pingpong)
591 return 1;
592 return 0;
593}
594
595static struct sort_dimension pingpong_sort_dimension = {
596 .name = "pingpong",
597 .cmp = pingpong_cmp,
598};
599
600static struct sort_dimension *avail_sorts[] = {
601 &ptr_sort_dimension,
602 &callsite_sort_dimension,
603 &hit_sort_dimension,
604 &bytes_sort_dimension,
605 &frag_sort_dimension,
606 &pingpong_sort_dimension,
607};
608
609#define NUM_AVAIL_SORTS \
610 (int)(sizeof(avail_sorts) / sizeof(struct sort_dimension *))
611
612static int sort_dimension__add(const char *tok, struct list_head *list)
613{
614 struct sort_dimension *sort;
615 int i;
616
617 for (i = 0; i < NUM_AVAIL_SORTS; i++) {
618 if (!strcmp(avail_sorts[i]->name, tok)) {
619 sort = malloc(sizeof(*sort));
620 if (!sort)
621 die("malloc");
622 memcpy(sort, avail_sorts[i], sizeof(*sort));
623 list_add_tail(&sort->list, list);
624 return 0;
625 }
626 }
627
628 return -1;
629}
630
631static int setup_sorting(struct list_head *sort_list, const char *arg)
632{
633 char *tok;
634 char *str = strdup(arg);
635
636 if (!str)
637 die("strdup");
638
639 while (true) {
640 tok = strsep(&str, ",");
641 if (!tok)
642 break;
643 if (sort_dimension__add(tok, sort_list) < 0) {
644 error("Unknown --sort key: '%s'", tok);
645 return -1;
646 }
647 }
648
649 free(str);
650 return 0;
651}
652
653static int parse_sort_opt(const struct option *opt __used,
654 const char *arg, int unset __used)
655{
656 if (!arg)
657 return -1;
658
659 if (caller_flag > alloc_flag)
660 return setup_sorting(&caller_sort, arg);
661 else
662 return setup_sorting(&alloc_sort, arg);
663
664 return 0;
665}
666
667static int parse_caller_opt(const struct option *opt __used,
668 const char *arg __used, int unset __used)
669{
670 caller_flag = (alloc_flag + 1);
671 return 0;
672}
673
674static int parse_alloc_opt(const struct option *opt __used,
675 const char *arg __used, int unset __used)
676{
677 alloc_flag = (caller_flag + 1);
678 return 0;
679}
680
681static int parse_line_opt(const struct option *opt __used,
682 const char *arg, int unset __used)
683{
684 int lines;
685
686 if (!arg)
687 return -1;
688
689 lines = strtoul(arg, NULL, 10);
690
691 if (caller_flag > alloc_flag)
692 caller_lines = lines;
693 else
694 alloc_lines = lines;
695
696 return 0;
697}
698
699static const struct option kmem_options[] = {
700 OPT_STRING('i', "input", &input_name, "file",
701 "input file name"),
702 OPT_CALLBACK_NOOPT(0, "caller", NULL, NULL,
703 "show per-callsite statistics",
704 parse_caller_opt),
705 OPT_CALLBACK_NOOPT(0, "alloc", NULL, NULL,
706 "show per-allocation statistics",
707 parse_alloc_opt),
708 OPT_CALLBACK('s', "sort", NULL, "key[,key2...]",
709 "sort by keys: ptr, call_site, bytes, hit, pingpong, frag",
710 parse_sort_opt),
711 OPT_CALLBACK('l', "line", NULL, "num",
712 "show n lines",
713 parse_line_opt),
714 OPT_BOOLEAN(0, "raw-ip", &raw_ip, "show raw ip instead of symbol"),
715 OPT_END()
716};
717
718static const char *record_args[] = {
719 "record",
720 "-a",
721 "-R",
722 "-f",
723 "-c", "1",
724 "-e", "kmem:kmalloc",
725 "-e", "kmem:kmalloc_node",
726 "-e", "kmem:kfree",
727 "-e", "kmem:kmem_cache_alloc",
728 "-e", "kmem:kmem_cache_alloc_node",
729 "-e", "kmem:kmem_cache_free",
730};
731
732static int __cmd_record(int argc, const char **argv)
733{
734 unsigned int rec_argc, i, j;
735 const char **rec_argv;
736
737 rec_argc = ARRAY_SIZE(record_args) + argc - 1;
738 rec_argv = calloc(rec_argc + 1, sizeof(char *));
739
740 if (rec_argv == NULL)
741 return -ENOMEM;
742
743 for (i = 0; i < ARRAY_SIZE(record_args); i++)
744 rec_argv[i] = strdup(record_args[i]);
745
746 for (j = 1; j < (unsigned int)argc; j++, i++)
747 rec_argv[i] = argv[j];
748
749 return cmd_record(i, rec_argv, NULL);
750}
751
752int cmd_kmem(int argc, const char **argv, const char *prefix __used)
753{
754 argc = parse_options(argc, argv, kmem_options, kmem_usage, 0);
755
756 if (!argc)
757 usage_with_options(kmem_usage, kmem_options);
758
759 symbol__init();
760
761 if (!strncmp(argv[0], "rec", 3)) {
762 return __cmd_record(argc, argv);
763 } else if (!strcmp(argv[0], "stat")) {
764 setup_cpunode_map();
765
766 if (list_empty(&caller_sort))
767 setup_sorting(&caller_sort, default_sort_order);
768 if (list_empty(&alloc_sort))
769 setup_sorting(&alloc_sort, default_sort_order);
770
771 return __cmd_kmem();
772 } else
773 usage_with_options(kmem_usage, kmem_options);
774
775 return 0;
776}
777
diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c
new file mode 100644
index 00000000000..34d1e853829
--- /dev/null
+++ b/tools/perf/builtin-kvm.c
@@ -0,0 +1,144 @@
1#include "builtin.h"
2#include "perf.h"
3
4#include "util/util.h"
5#include "util/cache.h"
6#include "util/symbol.h"
7#include "util/thread.h"
8#include "util/header.h"
9#include "util/session.h"
10
11#include "util/parse-options.h"
12#include "util/trace-event.h"
13
14#include "util/debug.h"
15
16#include <sys/prctl.h>
17
18#include <semaphore.h>
19#include <pthread.h>
20#include <math.h>
21
22static const char *file_name;
23static char name_buffer[256];
24
25bool perf_host = 1;
26bool perf_guest;
27
28static const char * const kvm_usage[] = {
29 "perf kvm [<options>] {top|record|report|diff|buildid-list}",
30 NULL
31};
32
33static const struct option kvm_options[] = {
34 OPT_STRING('i', "input", &file_name, "file",
35 "Input file name"),
36 OPT_STRING('o', "output", &file_name, "file",
37 "Output file name"),
38 OPT_BOOLEAN(0, "guest", &perf_guest,
39 "Collect guest os data"),
40 OPT_BOOLEAN(0, "host", &perf_host,
41 "Collect guest os data"),
42 OPT_STRING(0, "guestmount", &symbol_conf.guestmount, "directory",
43 "guest mount directory under which every guest os"
44 " instance has a subdir"),
45 OPT_STRING(0, "guestvmlinux", &symbol_conf.default_guest_vmlinux_name,
46 "file", "file saving guest os vmlinux"),
47 OPT_STRING(0, "guestkallsyms", &symbol_conf.default_guest_kallsyms,
48 "file", "file saving guest os /proc/kallsyms"),
49 OPT_STRING(0, "guestmodules", &symbol_conf.default_guest_modules,
50 "file", "file saving guest os /proc/modules"),
51 OPT_END()
52};
53
54static int __cmd_record(int argc, const char **argv)
55{
56 int rec_argc, i = 0, j;
57 const char **rec_argv;
58
59 rec_argc = argc + 2;
60 rec_argv = calloc(rec_argc + 1, sizeof(char *));
61 rec_argv[i++] = strdup("record");
62 rec_argv[i++] = strdup("-o");
63 rec_argv[i++] = strdup(file_name);
64 for (j = 1; j < argc; j++, i++)
65 rec_argv[i] = argv[j];
66
67 BUG_ON(i != rec_argc);
68
69 return cmd_record(i, rec_argv, NULL);
70}
71
72static int __cmd_report(int argc, const char **argv)
73{
74 int rec_argc, i = 0, j;
75 const char **rec_argv;
76
77 rec_argc = argc + 2;
78 rec_argv = calloc(rec_argc + 1, sizeof(char *));
79 rec_argv[i++] = strdup("report");
80 rec_argv[i++] = strdup("-i");
81 rec_argv[i++] = strdup(file_name);
82 for (j = 1; j < argc; j++, i++)
83 rec_argv[i] = argv[j];
84
85 BUG_ON(i != rec_argc);
86
87 return cmd_report(i, rec_argv, NULL);
88}
89
90static int __cmd_buildid_list(int argc, const char **argv)
91{
92 int rec_argc, i = 0, j;
93 const char **rec_argv;
94
95 rec_argc = argc + 2;
96 rec_argv = calloc(rec_argc + 1, sizeof(char *));
97 rec_argv[i++] = strdup("buildid-list");
98 rec_argv[i++] = strdup("-i");
99 rec_argv[i++] = strdup(file_name);
100 for (j = 1; j < argc; j++, i++)
101 rec_argv[i] = argv[j];
102
103 BUG_ON(i != rec_argc);
104
105 return cmd_buildid_list(i, rec_argv, NULL);
106}
107
108int cmd_kvm(int argc, const char **argv, const char *prefix __used)
109{
110 perf_host = perf_guest = 0;
111
112 argc = parse_options(argc, argv, kvm_options, kvm_usage,
113 PARSE_OPT_STOP_AT_NON_OPTION);
114 if (!argc)
115 usage_with_options(kvm_usage, kvm_options);
116
117 if (!perf_host)
118 perf_guest = 1;
119
120 if (!file_name) {
121 if (perf_host && !perf_guest)
122 sprintf(name_buffer, "perf.data.host");
123 else if (!perf_host && perf_guest)
124 sprintf(name_buffer, "perf.data.guest");
125 else
126 sprintf(name_buffer, "perf.data.kvm");
127 file_name = name_buffer;
128 }
129
130 if (!strncmp(argv[0], "rec", 3))
131 return __cmd_record(argc, argv);
132 else if (!strncmp(argv[0], "rep", 3))
133 return __cmd_report(argc, argv);
134 else if (!strncmp(argv[0], "diff", 4))
135 return cmd_diff(argc, argv, NULL);
136 else if (!strncmp(argv[0], "top", 3))
137 return cmd_top(argc, argv, NULL);
138 else if (!strncmp(argv[0], "buildid-list", 12))
139 return __cmd_buildid_list(argc, argv);
140 else
141 usage_with_options(kvm_usage, kvm_options);
142
143 return 0;
144}
diff --git a/tools/perf/builtin-list.c b/tools/perf/builtin-list.c
index fe60e37c96e..d88c6961274 100644
--- a/tools/perf/builtin-list.c
+++ b/tools/perf/builtin-list.c
@@ -10,11 +10,12 @@
10 10
11#include "perf.h" 11#include "perf.h"
12 12
13#include "util/parse-options.h"
14#include "util/parse-events.h" 13#include "util/parse-events.h"
14#include "util/cache.h"
15 15
16int cmd_list(int argc, const char **argv, const char *prefix) 16int cmd_list(int argc __used, const char **argv __used, const char *prefix __used)
17{ 17{
18 setup_pager();
18 print_events(); 19 print_events();
19 return 0; 20 return 0;
20} 21}
diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c
new file mode 100644
index 00000000000..b9c6e543297
--- /dev/null
+++ b/tools/perf/builtin-lock.c
@@ -0,0 +1,1004 @@
1#include "builtin.h"
2#include "perf.h"
3
4#include "util/util.h"
5#include "util/cache.h"
6#include "util/symbol.h"
7#include "util/thread.h"
8#include "util/header.h"
9
10#include "util/parse-options.h"
11#include "util/trace-event.h"
12
13#include "util/debug.h"
14#include "util/session.h"
15
16#include <sys/types.h>
17#include <sys/prctl.h>
18#include <semaphore.h>
19#include <pthread.h>
20#include <math.h>
21#include <limits.h>
22
23#include <linux/list.h>
24#include <linux/hash.h>
25
26static struct perf_session *session;
27
28/* based on kernel/lockdep.c */
29#define LOCKHASH_BITS 12
30#define LOCKHASH_SIZE (1UL << LOCKHASH_BITS)
31
32static struct list_head lockhash_table[LOCKHASH_SIZE];
33
34#define __lockhashfn(key) hash_long((unsigned long)key, LOCKHASH_BITS)
35#define lockhashentry(key) (lockhash_table + __lockhashfn((key)))
36
37struct lock_stat {
38 struct list_head hash_entry;
39 struct rb_node rb; /* used for sorting */
40
41 /*
42 * FIXME: raw_field_value() returns unsigned long long,
43 * so address of lockdep_map should be dealed as 64bit.
44 * Is there more better solution?
45 */
46 void *addr; /* address of lockdep_map, used as ID */
47 char *name; /* for strcpy(), we cannot use const */
48
49 unsigned int nr_acquire;
50 unsigned int nr_acquired;
51 unsigned int nr_contended;
52 unsigned int nr_release;
53
54 unsigned int nr_readlock;
55 unsigned int nr_trylock;
56 /* these times are in nano sec. */
57 u64 wait_time_total;
58 u64 wait_time_min;
59 u64 wait_time_max;
60
61 int discard; /* flag of blacklist */
62};
63
64/*
65 * States of lock_seq_stat
66 *
67 * UNINITIALIZED is required for detecting first event of acquire.
68 * As the nature of lock events, there is no guarantee
69 * that the first event for the locks are acquire,
70 * it can be acquired, contended or release.
71 */
72#define SEQ_STATE_UNINITIALIZED 0 /* initial state */
73#define SEQ_STATE_RELEASED 1
74#define SEQ_STATE_ACQUIRING 2
75#define SEQ_STATE_ACQUIRED 3
76#define SEQ_STATE_READ_ACQUIRED 4
77#define SEQ_STATE_CONTENDED 5
78
79/*
80 * MAX_LOCK_DEPTH
81 * Imported from include/linux/sched.h.
82 * Should this be synchronized?
83 */
84#define MAX_LOCK_DEPTH 48
85
86/*
87 * struct lock_seq_stat:
88 * Place to put on state of one lock sequence
89 * 1) acquire -> acquired -> release
90 * 2) acquire -> contended -> acquired -> release
91 * 3) acquire (with read or try) -> release
92 * 4) Are there other patterns?
93 */
94struct lock_seq_stat {
95 struct list_head list;
96 int state;
97 u64 prev_event_time;
98 void *addr;
99
100 int read_count;
101};
102
103struct thread_stat {
104 struct rb_node rb;
105
106 u32 tid;
107 struct list_head seq_list;
108};
109
110static struct rb_root thread_stats;
111
112static struct thread_stat *thread_stat_find(u32 tid)
113{
114 struct rb_node *node;
115 struct thread_stat *st;
116
117 node = thread_stats.rb_node;
118 while (node) {
119 st = container_of(node, struct thread_stat, rb);
120 if (st->tid == tid)
121 return st;
122 else if (tid < st->tid)
123 node = node->rb_left;
124 else
125 node = node->rb_right;
126 }
127
128 return NULL;
129}
130
131static void thread_stat_insert(struct thread_stat *new)
132{
133 struct rb_node **rb = &thread_stats.rb_node;
134 struct rb_node *parent = NULL;
135 struct thread_stat *p;
136
137 while (*rb) {
138 p = container_of(*rb, struct thread_stat, rb);
139 parent = *rb;
140
141 if (new->tid < p->tid)
142 rb = &(*rb)->rb_left;
143 else if (new->tid > p->tid)
144 rb = &(*rb)->rb_right;
145 else
146 BUG_ON("inserting invalid thread_stat\n");
147 }
148
149 rb_link_node(&new->rb, parent, rb);
150 rb_insert_color(&new->rb, &thread_stats);
151}
152
153static struct thread_stat *thread_stat_findnew_after_first(u32 tid)
154{
155 struct thread_stat *st;
156
157 st = thread_stat_find(tid);
158 if (st)
159 return st;
160
161 st = zalloc(sizeof(struct thread_stat));
162 if (!st)
163 die("memory allocation failed\n");
164
165 st->tid = tid;
166 INIT_LIST_HEAD(&st->seq_list);
167
168 thread_stat_insert(st);
169
170 return st;
171}
172
173static struct thread_stat *thread_stat_findnew_first(u32 tid);
174static struct thread_stat *(*thread_stat_findnew)(u32 tid) =
175 thread_stat_findnew_first;
176
177static struct thread_stat *thread_stat_findnew_first(u32 tid)
178{
179 struct thread_stat *st;
180
181 st = zalloc(sizeof(struct thread_stat));
182 if (!st)
183 die("memory allocation failed\n");
184 st->tid = tid;
185 INIT_LIST_HEAD(&st->seq_list);
186
187 rb_link_node(&st->rb, NULL, &thread_stats.rb_node);
188 rb_insert_color(&st->rb, &thread_stats);
189
190 thread_stat_findnew = thread_stat_findnew_after_first;
191 return st;
192}
193
194/* build simple key function one is bigger than two */
195#define SINGLE_KEY(member) \
196 static int lock_stat_key_ ## member(struct lock_stat *one, \
197 struct lock_stat *two) \
198 { \
199 return one->member > two->member; \
200 }
201
202SINGLE_KEY(nr_acquired)
203SINGLE_KEY(nr_contended)
204SINGLE_KEY(wait_time_total)
205SINGLE_KEY(wait_time_min)
206SINGLE_KEY(wait_time_max)
207
208struct lock_key {
209 /*
210 * name: the value for specify by user
211 * this should be simpler than raw name of member
212 * e.g. nr_acquired -> acquired, wait_time_total -> wait_total
213 */
214 const char *name;
215 int (*key)(struct lock_stat*, struct lock_stat*);
216};
217
218static const char *sort_key = "acquired";
219
220static int (*compare)(struct lock_stat *, struct lock_stat *);
221
222static struct rb_root result; /* place to store sorted data */
223
224#define DEF_KEY_LOCK(name, fn_suffix) \
225 { #name, lock_stat_key_ ## fn_suffix }
226struct lock_key keys[] = {
227 DEF_KEY_LOCK(acquired, nr_acquired),
228 DEF_KEY_LOCK(contended, nr_contended),
229 DEF_KEY_LOCK(wait_total, wait_time_total),
230 DEF_KEY_LOCK(wait_min, wait_time_min),
231 DEF_KEY_LOCK(wait_max, wait_time_max),
232
233 /* extra comparisons much complicated should be here */
234
235 { NULL, NULL }
236};
237
238static void select_key(void)
239{
240 int i;
241
242 for (i = 0; keys[i].name; i++) {
243 if (!strcmp(keys[i].name, sort_key)) {
244 compare = keys[i].key;
245 return;
246 }
247 }
248
249 die("Unknown compare key:%s\n", sort_key);
250}
251
252static void insert_to_result(struct lock_stat *st,
253 int (*bigger)(struct lock_stat *, struct lock_stat *))
254{
255 struct rb_node **rb = &result.rb_node;
256 struct rb_node *parent = NULL;
257 struct lock_stat *p;
258
259 while (*rb) {
260 p = container_of(*rb, struct lock_stat, rb);
261 parent = *rb;
262
263 if (bigger(st, p))
264 rb = &(*rb)->rb_left;
265 else
266 rb = &(*rb)->rb_right;
267 }
268
269 rb_link_node(&st->rb, parent, rb);
270 rb_insert_color(&st->rb, &result);
271}
272
273/* returns left most element of result, and erase it */
274static struct lock_stat *pop_from_result(void)
275{
276 struct rb_node *node = result.rb_node;
277
278 if (!node)
279 return NULL;
280
281 while (node->rb_left)
282 node = node->rb_left;
283
284 rb_erase(node, &result);
285 return container_of(node, struct lock_stat, rb);
286}
287
288static struct lock_stat *lock_stat_findnew(void *addr, const char *name)
289{
290 struct list_head *entry = lockhashentry(addr);
291 struct lock_stat *ret, *new;
292
293 list_for_each_entry(ret, entry, hash_entry) {
294 if (ret->addr == addr)
295 return ret;
296 }
297
298 new = zalloc(sizeof(struct lock_stat));
299 if (!new)
300 goto alloc_failed;
301
302 new->addr = addr;
303 new->name = zalloc(sizeof(char) * strlen(name) + 1);
304 if (!new->name)
305 goto alloc_failed;
306 strcpy(new->name, name);
307
308 new->wait_time_min = ULLONG_MAX;
309
310 list_add(&new->hash_entry, entry);
311 return new;
312
313alloc_failed:
314 die("memory allocation failed\n");
315}
316
317static char const *input_name = "perf.data";
318
319struct raw_event_sample {
320 u32 size;
321 char data[0];
322};
323
324struct trace_acquire_event {
325 void *addr;
326 const char *name;
327 int flag;
328};
329
330struct trace_acquired_event {
331 void *addr;
332 const char *name;
333};
334
335struct trace_contended_event {
336 void *addr;
337 const char *name;
338};
339
340struct trace_release_event {
341 void *addr;
342 const char *name;
343};
344
345struct trace_lock_handler {
346 void (*acquire_event)(struct trace_acquire_event *,
347 struct event *,
348 int cpu,
349 u64 timestamp,
350 struct thread *thread);
351
352 void (*acquired_event)(struct trace_acquired_event *,
353 struct event *,
354 int cpu,
355 u64 timestamp,
356 struct thread *thread);
357
358 void (*contended_event)(struct trace_contended_event *,
359 struct event *,
360 int cpu,
361 u64 timestamp,
362 struct thread *thread);
363
364 void (*release_event)(struct trace_release_event *,
365 struct event *,
366 int cpu,
367 u64 timestamp,
368 struct thread *thread);
369};
370
371static struct lock_seq_stat *get_seq(struct thread_stat *ts, void *addr)
372{
373 struct lock_seq_stat *seq;
374
375 list_for_each_entry(seq, &ts->seq_list, list) {
376 if (seq->addr == addr)
377 return seq;
378 }
379
380 seq = zalloc(sizeof(struct lock_seq_stat));
381 if (!seq)
382 die("Not enough memory\n");
383 seq->state = SEQ_STATE_UNINITIALIZED;
384 seq->addr = addr;
385
386 list_add(&seq->list, &ts->seq_list);
387 return seq;
388}
389
390enum broken_state {
391 BROKEN_ACQUIRE,
392 BROKEN_ACQUIRED,
393 BROKEN_CONTENDED,
394 BROKEN_RELEASE,
395 BROKEN_MAX,
396};
397
398static int bad_hist[BROKEN_MAX];
399
400enum acquire_flags {
401 TRY_LOCK = 1,
402 READ_LOCK = 2,
403};
404
405static void
406report_lock_acquire_event(struct trace_acquire_event *acquire_event,
407 struct event *__event __used,
408 int cpu __used,
409 u64 timestamp __used,
410 struct thread *thread __used)
411{
412 struct lock_stat *ls;
413 struct thread_stat *ts;
414 struct lock_seq_stat *seq;
415
416 ls = lock_stat_findnew(acquire_event->addr, acquire_event->name);
417 if (ls->discard)
418 return;
419
420 ts = thread_stat_findnew(thread->pid);
421 seq = get_seq(ts, acquire_event->addr);
422
423 switch (seq->state) {
424 case SEQ_STATE_UNINITIALIZED:
425 case SEQ_STATE_RELEASED:
426 if (!acquire_event->flag) {
427 seq->state = SEQ_STATE_ACQUIRING;
428 } else {
429 if (acquire_event->flag & TRY_LOCK)
430 ls->nr_trylock++;
431 if (acquire_event->flag & READ_LOCK)
432 ls->nr_readlock++;
433 seq->state = SEQ_STATE_READ_ACQUIRED;
434 seq->read_count = 1;
435 ls->nr_acquired++;
436 }
437 break;
438 case SEQ_STATE_READ_ACQUIRED:
439 if (acquire_event->flag & READ_LOCK) {
440 seq->read_count++;
441 ls->nr_acquired++;
442 goto end;
443 } else {
444 goto broken;
445 }
446 break;
447 case SEQ_STATE_ACQUIRED:
448 case SEQ_STATE_ACQUIRING:
449 case SEQ_STATE_CONTENDED:
450broken:
451 /* broken lock sequence, discard it */
452 ls->discard = 1;
453 bad_hist[BROKEN_ACQUIRE]++;
454 list_del(&seq->list);
455 free(seq);
456 goto end;
457 break;
458 default:
459 BUG_ON("Unknown state of lock sequence found!\n");
460 break;
461 }
462
463 ls->nr_acquire++;
464 seq->prev_event_time = timestamp;
465end:
466 return;
467}
468
469static void
470report_lock_acquired_event(struct trace_acquired_event *acquired_event,
471 struct event *__event __used,
472 int cpu __used,
473 u64 timestamp __used,
474 struct thread *thread __used)
475{
476 struct lock_stat *ls;
477 struct thread_stat *ts;
478 struct lock_seq_stat *seq;
479 u64 contended_term;
480
481 ls = lock_stat_findnew(acquired_event->addr, acquired_event->name);
482 if (ls->discard)
483 return;
484
485 ts = thread_stat_findnew(thread->pid);
486 seq = get_seq(ts, acquired_event->addr);
487
488 switch (seq->state) {
489 case SEQ_STATE_UNINITIALIZED:
490 /* orphan event, do nothing */
491 return;
492 case SEQ_STATE_ACQUIRING:
493 break;
494 case SEQ_STATE_CONTENDED:
495 contended_term = timestamp - seq->prev_event_time;
496 ls->wait_time_total += contended_term;
497 if (contended_term < ls->wait_time_min)
498 ls->wait_time_min = contended_term;
499 if (ls->wait_time_max < contended_term)
500 ls->wait_time_max = contended_term;
501 break;
502 case SEQ_STATE_RELEASED:
503 case SEQ_STATE_ACQUIRED:
504 case SEQ_STATE_READ_ACQUIRED:
505 /* broken lock sequence, discard it */
506 ls->discard = 1;
507 bad_hist[BROKEN_ACQUIRED]++;
508 list_del(&seq->list);
509 free(seq);
510 goto end;
511 break;
512
513 default:
514 BUG_ON("Unknown state of lock sequence found!\n");
515 break;
516 }
517
518 seq->state = SEQ_STATE_ACQUIRED;
519 ls->nr_acquired++;
520 seq->prev_event_time = timestamp;
521end:
522 return;
523}
524
525static void
526report_lock_contended_event(struct trace_contended_event *contended_event,
527 struct event *__event __used,
528 int cpu __used,
529 u64 timestamp __used,
530 struct thread *thread __used)
531{
532 struct lock_stat *ls;
533 struct thread_stat *ts;
534 struct lock_seq_stat *seq;
535
536 ls = lock_stat_findnew(contended_event->addr, contended_event->name);
537 if (ls->discard)
538 return;
539
540 ts = thread_stat_findnew(thread->pid);
541 seq = get_seq(ts, contended_event->addr);
542
543 switch (seq->state) {
544 case SEQ_STATE_UNINITIALIZED:
545 /* orphan event, do nothing */
546 return;
547 case SEQ_STATE_ACQUIRING:
548 break;
549 case SEQ_STATE_RELEASED:
550 case SEQ_STATE_ACQUIRED:
551 case SEQ_STATE_READ_ACQUIRED:
552 case SEQ_STATE_CONTENDED:
553 /* broken lock sequence, discard it */
554 ls->discard = 1;
555 bad_hist[BROKEN_CONTENDED]++;
556 list_del(&seq->list);
557 free(seq);
558 goto end;
559 break;
560 default:
561 BUG_ON("Unknown state of lock sequence found!\n");
562 break;
563 }
564
565 seq->state = SEQ_STATE_CONTENDED;
566 ls->nr_contended++;
567 seq->prev_event_time = timestamp;
568end:
569 return;
570}
571
572static void
573report_lock_release_event(struct trace_release_event *release_event,
574 struct event *__event __used,
575 int cpu __used,
576 u64 timestamp __used,
577 struct thread *thread __used)
578{
579 struct lock_stat *ls;
580 struct thread_stat *ts;
581 struct lock_seq_stat *seq;
582
583 ls = lock_stat_findnew(release_event->addr, release_event->name);
584 if (ls->discard)
585 return;
586
587 ts = thread_stat_findnew(thread->pid);
588 seq = get_seq(ts, release_event->addr);
589
590 switch (seq->state) {
591 case SEQ_STATE_UNINITIALIZED:
592 goto end;
593 break;
594 case SEQ_STATE_ACQUIRED:
595 break;
596 case SEQ_STATE_READ_ACQUIRED:
597 seq->read_count--;
598 BUG_ON(seq->read_count < 0);
599 if (!seq->read_count) {
600 ls->nr_release++;
601 goto end;
602 }
603 break;
604 case SEQ_STATE_ACQUIRING:
605 case SEQ_STATE_CONTENDED:
606 case SEQ_STATE_RELEASED:
607 /* broken lock sequence, discard it */
608 ls->discard = 1;
609 bad_hist[BROKEN_RELEASE]++;
610 goto free_seq;
611 break;
612 default:
613 BUG_ON("Unknown state of lock sequence found!\n");
614 break;
615 }
616
617 ls->nr_release++;
618free_seq:
619 list_del(&seq->list);
620 free(seq);
621end:
622 return;
623}
624
625/* lock oriented handlers */
626/* TODO: handlers for CPU oriented, thread oriented */
627static struct trace_lock_handler report_lock_ops = {
628 .acquire_event = report_lock_acquire_event,
629 .acquired_event = report_lock_acquired_event,
630 .contended_event = report_lock_contended_event,
631 .release_event = report_lock_release_event,
632};
633
634static struct trace_lock_handler *trace_handler;
635
636static void
637process_lock_acquire_event(void *data,
638 struct event *event __used,
639 int cpu __used,
640 u64 timestamp __used,
641 struct thread *thread __used)
642{
643 struct trace_acquire_event acquire_event;
644 u64 tmp; /* this is required for casting... */
645
646 tmp = raw_field_value(event, "lockdep_addr", data);
647 memcpy(&acquire_event.addr, &tmp, sizeof(void *));
648 acquire_event.name = (char *)raw_field_ptr(event, "name", data);
649 acquire_event.flag = (int)raw_field_value(event, "flag", data);
650
651 if (trace_handler->acquire_event)
652 trace_handler->acquire_event(&acquire_event, event, cpu, timestamp, thread);
653}
654
655static void
656process_lock_acquired_event(void *data,
657 struct event *event __used,
658 int cpu __used,
659 u64 timestamp __used,
660 struct thread *thread __used)
661{
662 struct trace_acquired_event acquired_event;
663 u64 tmp; /* this is required for casting... */
664
665 tmp = raw_field_value(event, "lockdep_addr", data);
666 memcpy(&acquired_event.addr, &tmp, sizeof(void *));
667 acquired_event.name = (char *)raw_field_ptr(event, "name", data);
668
669 if (trace_handler->acquire_event)
670 trace_handler->acquired_event(&acquired_event, event, cpu, timestamp, thread);
671}
672
673static void
674process_lock_contended_event(void *data,
675 struct event *event __used,
676 int cpu __used,
677 u64 timestamp __used,
678 struct thread *thread __used)
679{
680 struct trace_contended_event contended_event;
681 u64 tmp; /* this is required for casting... */
682
683 tmp = raw_field_value(event, "lockdep_addr", data);
684 memcpy(&contended_event.addr, &tmp, sizeof(void *));
685 contended_event.name = (char *)raw_field_ptr(event, "name", data);
686
687 if (trace_handler->acquire_event)
688 trace_handler->contended_event(&contended_event, event, cpu, timestamp, thread);
689}
690
691static void
692process_lock_release_event(void *data,
693 struct event *event __used,
694 int cpu __used,
695 u64 timestamp __used,
696 struct thread *thread __used)
697{
698 struct trace_release_event release_event;
699 u64 tmp; /* this is required for casting... */
700
701 tmp = raw_field_value(event, "lockdep_addr", data);
702 memcpy(&release_event.addr, &tmp, sizeof(void *));
703 release_event.name = (char *)raw_field_ptr(event, "name", data);
704
705 if (trace_handler->acquire_event)
706 trace_handler->release_event(&release_event, event, cpu, timestamp, thread);
707}
708
709static void
710process_raw_event(void *data, int cpu, u64 timestamp, struct thread *thread)
711{
712 struct event *event;
713 int type;
714
715 type = trace_parse_common_type(data);
716 event = trace_find_event(type);
717
718 if (!strcmp(event->name, "lock_acquire"))
719 process_lock_acquire_event(data, event, cpu, timestamp, thread);
720 if (!strcmp(event->name, "lock_acquired"))
721 process_lock_acquired_event(data, event, cpu, timestamp, thread);
722 if (!strcmp(event->name, "lock_contended"))
723 process_lock_contended_event(data, event, cpu, timestamp, thread);
724 if (!strcmp(event->name, "lock_release"))
725 process_lock_release_event(data, event, cpu, timestamp, thread);
726}
727
728static void print_bad_events(int bad, int total)
729{
730 /* Output for debug, this have to be removed */
731 int i;
732 const char *name[4] =
733 { "acquire", "acquired", "contended", "release" };
734
735 pr_info("\n=== output for debug===\n\n");
736 pr_info("bad: %d, total: %d\n", bad, total);
737 pr_info("bad rate: %f %%\n", (double)bad / (double)total * 100);
738 pr_info("histogram of events caused bad sequence\n");
739 for (i = 0; i < BROKEN_MAX; i++)
740 pr_info(" %10s: %d\n", name[i], bad_hist[i]);
741}
742
743/* TODO: various way to print, coloring, nano or milli sec */
744static void print_result(void)
745{
746 struct lock_stat *st;
747 char cut_name[20];
748 int bad, total;
749
750 pr_info("%20s ", "Name");
751 pr_info("%10s ", "acquired");
752 pr_info("%10s ", "contended");
753
754 pr_info("%15s ", "total wait (ns)");
755 pr_info("%15s ", "max wait (ns)");
756 pr_info("%15s ", "min wait (ns)");
757
758 pr_info("\n\n");
759
760 bad = total = 0;
761 while ((st = pop_from_result())) {
762 total++;
763 if (st->discard) {
764 bad++;
765 continue;
766 }
767 bzero(cut_name, 20);
768
769 if (strlen(st->name) < 16) {
770 /* output raw name */
771 pr_info("%20s ", st->name);
772 } else {
773 strncpy(cut_name, st->name, 16);
774 cut_name[16] = '.';
775 cut_name[17] = '.';
776 cut_name[18] = '.';
777 cut_name[19] = '\0';
778 /* cut off name for saving output style */
779 pr_info("%20s ", cut_name);
780 }
781
782 pr_info("%10u ", st->nr_acquired);
783 pr_info("%10u ", st->nr_contended);
784
785 pr_info("%15llu ", st->wait_time_total);
786 pr_info("%15llu ", st->wait_time_max);
787 pr_info("%15llu ", st->wait_time_min == ULLONG_MAX ?
788 0 : st->wait_time_min);
789 pr_info("\n");
790 }
791
792 print_bad_events(bad, total);
793}
794
795static bool info_threads, info_map;
796
797static void dump_threads(void)
798{
799 struct thread_stat *st;
800 struct rb_node *node;
801 struct thread *t;
802
803 pr_info("%10s: comm\n", "Thread ID");
804
805 node = rb_first(&thread_stats);
806 while (node) {
807 st = container_of(node, struct thread_stat, rb);
808 t = perf_session__findnew(session, st->tid);
809 pr_info("%10d: %s\n", st->tid, t->comm);
810 node = rb_next(node);
811 };
812}
813
814static void dump_map(void)
815{
816 unsigned int i;
817 struct lock_stat *st;
818
819 pr_info("Address of instance: name of class\n");
820 for (i = 0; i < LOCKHASH_SIZE; i++) {
821 list_for_each_entry(st, &lockhash_table[i], hash_entry) {
822 pr_info(" %p: %s\n", st->addr, st->name);
823 }
824 }
825}
826
827static void dump_info(void)
828{
829 if (info_threads)
830 dump_threads();
831 else if (info_map)
832 dump_map();
833 else
834 die("Unknown type of information\n");
835}
836
837static int process_sample_event(event_t *self, struct sample_data *sample,
838 struct perf_session *s)
839{
840 struct thread *thread = perf_session__findnew(s, sample->tid);
841
842 if (thread == NULL) {
843 pr_debug("problem processing %d event, skipping it.\n",
844 self->header.type);
845 return -1;
846 }
847
848 process_raw_event(sample->raw_data, sample->cpu, sample->time, thread);
849
850 return 0;
851}
852
853static struct perf_event_ops eops = {
854 .sample = process_sample_event,
855 .comm = event__process_comm,
856 .ordered_samples = true,
857};
858
859static int read_events(void)
860{
861 session = perf_session__new(input_name, O_RDONLY, 0, false, &eops);
862 if (!session)
863 die("Initializing perf session failed\n");
864
865 return perf_session__process_events(session, &eops);
866}
867
868static void sort_result(void)
869{
870 unsigned int i;
871 struct lock_stat *st;
872
873 for (i = 0; i < LOCKHASH_SIZE; i++) {
874 list_for_each_entry(st, &lockhash_table[i], hash_entry) {
875 insert_to_result(st, compare);
876 }
877 }
878}
879
880static void __cmd_report(void)
881{
882 setup_pager();
883 select_key();
884 read_events();
885 sort_result();
886 print_result();
887}
888
889static const char * const report_usage[] = {
890 "perf lock report [<options>]",
891 NULL
892};
893
894static const struct option report_options[] = {
895 OPT_STRING('k', "key", &sort_key, "acquired",
896 "key for sorting"),
897 /* TODO: type */
898 OPT_END()
899};
900
901static const char * const info_usage[] = {
902 "perf lock info [<options>]",
903 NULL
904};
905
906static const struct option info_options[] = {
907 OPT_BOOLEAN('t', "threads", &info_threads,
908 "dump thread list in perf.data"),
909 OPT_BOOLEAN('m', "map", &info_map,
910 "map of lock instances (name:address table)"),
911 OPT_END()
912};
913
914static const char * const lock_usage[] = {
915 "perf lock [<options>] {record|trace|report}",
916 NULL
917};
918
919static const struct option lock_options[] = {
920 OPT_STRING('i', "input", &input_name, "file", "input file name"),
921 OPT_INCR('v', "verbose", &verbose, "be more verbose (show symbol address, etc)"),
922 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, "dump raw trace in ASCII"),
923 OPT_END()
924};
925
926static const char *record_args[] = {
927 "record",
928 "-R",
929 "-f",
930 "-m", "1024",
931 "-c", "1",
932 "-e", "lock:lock_acquire:r",
933 "-e", "lock:lock_acquired:r",
934 "-e", "lock:lock_contended:r",
935 "-e", "lock:lock_release:r",
936};
937
938static int __cmd_record(int argc, const char **argv)
939{
940 unsigned int rec_argc, i, j;
941 const char **rec_argv;
942
943 rec_argc = ARRAY_SIZE(record_args) + argc - 1;
944 rec_argv = calloc(rec_argc + 1, sizeof(char *));
945
946 if (rec_argv == NULL)
947 return -ENOMEM;
948
949 for (i = 0; i < ARRAY_SIZE(record_args); i++)
950 rec_argv[i] = strdup(record_args[i]);
951
952 for (j = 1; j < (unsigned int)argc; j++, i++)
953 rec_argv[i] = argv[j];
954
955 BUG_ON(i != rec_argc);
956
957 return cmd_record(i, rec_argv, NULL);
958}
959
960int cmd_lock(int argc, const char **argv, const char *prefix __used)
961{
962 unsigned int i;
963
964 symbol__init();
965 for (i = 0; i < LOCKHASH_SIZE; i++)
966 INIT_LIST_HEAD(lockhash_table + i);
967
968 argc = parse_options(argc, argv, lock_options, lock_usage,
969 PARSE_OPT_STOP_AT_NON_OPTION);
970 if (!argc)
971 usage_with_options(lock_usage, lock_options);
972
973 if (!strncmp(argv[0], "rec", 3)) {
974 return __cmd_record(argc, argv);
975 } else if (!strncmp(argv[0], "report", 6)) {
976 trace_handler = &report_lock_ops;
977 if (argc) {
978 argc = parse_options(argc, argv,
979 report_options, report_usage, 0);
980 if (argc)
981 usage_with_options(report_usage, report_options);
982 }
983 __cmd_report();
984 } else if (!strcmp(argv[0], "script")) {
985 /* Aliased to 'perf script' */
986 return cmd_script(argc, argv, prefix);
987 } else if (!strcmp(argv[0], "info")) {
988 if (argc) {
989 argc = parse_options(argc, argv,
990 info_options, info_usage, 0);
991 if (argc)
992 usage_with_options(info_usage, info_options);
993 }
994 /* recycling report_lock_ops */
995 trace_handler = &report_lock_ops;
996 setup_pager();
997 read_events();
998 dump_info();
999 } else {
1000 usage_with_options(lock_usage, lock_options);
1001 }
1002
1003 return 0;
1004}
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
new file mode 100644
index 00000000000..add163c9f0e
--- /dev/null
+++ b/tools/perf/builtin-probe.c
@@ -0,0 +1,330 @@
1/*
2 * builtin-probe.c
3 *
4 * Builtin probe command: Set up probe events by C expression
5 *
6 * Written by Masami Hiramatsu <mhiramat@redhat.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 *
22 */
23#define _GNU_SOURCE
24#include <sys/utsname.h>
25#include <sys/types.h>
26#include <sys/stat.h>
27#include <fcntl.h>
28#include <errno.h>
29#include <stdio.h>
30#include <unistd.h>
31#include <stdlib.h>
32#include <string.h>
33
34#undef _GNU_SOURCE
35#include "perf.h"
36#include "builtin.h"
37#include "util/util.h"
38#include "util/strlist.h"
39#include "util/symbol.h"
40#include "util/debug.h"
41#include "util/debugfs.h"
42#include "util/parse-options.h"
43#include "util/probe-finder.h"
44#include "util/probe-event.h"
45
46#define MAX_PATH_LEN 256
47
48/* Session management structure */
49static struct {
50 bool list_events;
51 bool force_add;
52 bool show_lines;
53 bool show_vars;
54 bool show_ext_vars;
55 bool mod_events;
56 int nevents;
57 struct perf_probe_event events[MAX_PROBES];
58 struct strlist *dellist;
59 struct line_range line_range;
60 const char *target_module;
61 int max_probe_points;
62} params;
63
64/* Parse an event definition. Note that any error must die. */
65static int parse_probe_event(const char *str)
66{
67 struct perf_probe_event *pev = &params.events[params.nevents];
68 int ret;
69
70 pr_debug("probe-definition(%d): %s\n", params.nevents, str);
71 if (++params.nevents == MAX_PROBES) {
72 pr_err("Too many probes (> %d) were specified.", MAX_PROBES);
73 return -1;
74 }
75
76 /* Parse a perf-probe command into event */
77 ret = parse_perf_probe_command(str, pev);
78 pr_debug("%d arguments\n", pev->nargs);
79
80 return ret;
81}
82
83static int parse_probe_event_argv(int argc, const char **argv)
84{
85 int i, len, ret;
86 char *buf;
87
88 /* Bind up rest arguments */
89 len = 0;
90 for (i = 0; i < argc; i++)
91 len += strlen(argv[i]) + 1;
92 buf = zalloc(len + 1);
93 if (buf == NULL)
94 return -ENOMEM;
95 len = 0;
96 for (i = 0; i < argc; i++)
97 len += sprintf(&buf[len], "%s ", argv[i]);
98 params.mod_events = true;
99 ret = parse_probe_event(buf);
100 free(buf);
101 return ret;
102}
103
104static int opt_add_probe_event(const struct option *opt __used,
105 const char *str, int unset __used)
106{
107 if (str) {
108 params.mod_events = true;
109 return parse_probe_event(str);
110 } else
111 return 0;
112}
113
114static int opt_del_probe_event(const struct option *opt __used,
115 const char *str, int unset __used)
116{
117 if (str) {
118 params.mod_events = true;
119 if (!params.dellist)
120 params.dellist = strlist__new(true, NULL);
121 strlist__add(params.dellist, str);
122 }
123 return 0;
124}
125
126#ifdef DWARF_SUPPORT
127static int opt_show_lines(const struct option *opt __used,
128 const char *str, int unset __used)
129{
130 int ret = 0;
131
132 if (str)
133 ret = parse_line_range_desc(str, &params.line_range);
134 INIT_LIST_HEAD(&params.line_range.line_list);
135 params.show_lines = true;
136
137 return ret;
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}
158#endif
159
160static const char * const probe_usage[] = {
161 "perf probe [<options>] 'PROBEDEF' ['PROBEDEF' ...]",
162 "perf probe [<options>] --add 'PROBEDEF' [--add 'PROBEDEF' ...]",
163 "perf probe [<options>] --del '[GROUP:]EVENT' ...",
164 "perf probe --list",
165#ifdef DWARF_SUPPORT
166 "perf probe [<options>] --line 'LINEDESC'",
167 "perf probe [<options>] --vars 'PROBEPOINT'",
168#endif
169 NULL
170};
171
172static const struct option options[] = {
173 OPT_INCR('v', "verbose", &verbose,
174 "be more verbose (show parsed arguments, etc)"),
175 OPT_BOOLEAN('l', "list", &params.list_events,
176 "list up current probe events"),
177 OPT_CALLBACK('d', "del", NULL, "[GROUP:]EVENT", "delete a probe event.",
178 opt_del_probe_event),
179 OPT_CALLBACK('a', "add", NULL,
180#ifdef DWARF_SUPPORT
181 "[EVENT=]FUNC[@SRC][+OFF|%return|:RL|;PT]|SRC:AL|SRC;PT"
182 " [[NAME=]ARG ...]",
183#else
184 "[EVENT=]FUNC[+OFF|%return] [[NAME=]ARG ...]",
185#endif
186 "probe point definition, where\n"
187 "\t\tGROUP:\tGroup name (optional)\n"
188 "\t\tEVENT:\tEvent name\n"
189 "\t\tFUNC:\tFunction name\n"
190 "\t\tOFF:\tOffset from function entry (in byte)\n"
191 "\t\t%return:\tPut the probe at function return\n"
192#ifdef DWARF_SUPPORT
193 "\t\tSRC:\tSource code path\n"
194 "\t\tRL:\tRelative line number from function entry.\n"
195 "\t\tAL:\tAbsolute line number in file.\n"
196 "\t\tPT:\tLazy expression of line code.\n"
197 "\t\tARG:\tProbe argument (local variable name or\n"
198 "\t\t\tkprobe-tracer argument format.)\n",
199#else
200 "\t\tARG:\tProbe argument (kprobe-tracer argument format.)\n",
201#endif
202 opt_add_probe_event),
203 OPT_BOOLEAN('f', "force", &params.force_add, "forcibly add events"
204 " with existing name"),
205#ifdef DWARF_SUPPORT
206 OPT_CALLBACK('L', "line", NULL,
207 "FUNC[:RLN[+NUM|-RLN2]]|SRC:ALN[+NUM|-ALN2]",
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)"),
214 OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
215 "file", "vmlinux pathname"),
216 OPT_STRING('s', "source", &symbol_conf.source_prefix,
217 "directory", "path to kernel source"),
218 OPT_STRING('m', "module", &params.target_module,
219 "modname", "target module name"),
220#endif
221 OPT__DRY_RUN(&probe_event_dry_run),
222 OPT_INTEGER('\0', "max-probes", &params.max_probe_points,
223 "Set how many probe points can be found for a probe."),
224 OPT_END()
225};
226
227int cmd_probe(int argc, const char **argv, const char *prefix __used)
228{
229 int ret;
230
231 argc = parse_options(argc, argv, options, probe_usage,
232 PARSE_OPT_STOP_AT_NON_OPTION);
233 if (argc > 0) {
234 if (strcmp(argv[0], "-") == 0) {
235 pr_warning(" Error: '-' is not supported.\n");
236 usage_with_options(probe_usage, options);
237 }
238 ret = parse_probe_event_argv(argc, argv);
239 if (ret < 0) {
240 pr_err(" Error: Parse Error. (%d)\n", ret);
241 return ret;
242 }
243 }
244
245 if (params.max_probe_points == 0)
246 params.max_probe_points = MAX_PROBES;
247
248 if ((!params.nevents && !params.dellist && !params.list_events &&
249 !params.show_lines))
250 usage_with_options(probe_usage, options);
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
257 if (params.list_events) {
258 if (params.mod_events) {
259 pr_err(" Error: Don't use --list with --add/--del.\n");
260 usage_with_options(probe_usage, options);
261 }
262 if (params.show_lines) {
263 pr_err(" Error: Don't use --list with --line.\n");
264 usage_with_options(probe_usage, options);
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 }
270 ret = show_perf_probe_events();
271 if (ret < 0)
272 pr_err(" Error: Failed to show event list. (%d)\n",
273 ret);
274 return ret;
275 }
276
277#ifdef DWARF_SUPPORT
278 if (params.show_lines) {
279 if (params.mod_events) {
280 pr_err(" Error: Don't use --line with"
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");
286 usage_with_options(probe_usage, options);
287 }
288
289 ret = show_line_range(&params.line_range, params.target_module);
290 if (ret < 0)
291 pr_err(" Error: Failed to show lines. (%d)\n", ret);
292 return ret;
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 }
308#endif
309
310 if (params.dellist) {
311 ret = del_perf_probe_events(params.dellist);
312 strlist__delete(params.dellist);
313 if (ret < 0) {
314 pr_err(" Error: Failed to delete events. (%d)\n", ret);
315 return ret;
316 }
317 }
318
319 if (params.nevents) {
320 ret = add_perf_probe_events(params.events, params.nevents,
321 params.max_probe_points,
322 params.target_module,
323 params.force_add);
324 if (ret < 0) {
325 pr_err(" Error: Failed to add events. (%d)\n", ret);
326 return ret;
327 }
328 }
329 return 0;
330}
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index d7ebbd75754..7069bd3e90b 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -5,85 +5,92 @@
5 * (or a CPU, or a PID) into the perf.data output file - for 5 * (or a CPU, or a PID) into the perf.data output file - for
6 * later analysis via perf report. 6 * later analysis via perf report.
7 */ 7 */
8#define _FILE_OFFSET_BITS 64
9
8#include "builtin.h" 10#include "builtin.h"
9 11
10#include "perf.h" 12#include "perf.h"
11 13
14#include "util/build-id.h"
12#include "util/util.h" 15#include "util/util.h"
13#include "util/parse-options.h" 16#include "util/parse-options.h"
14#include "util/parse-events.h" 17#include "util/parse-events.h"
15#include "util/string.h" 18
19#include "util/header.h"
20#include "util/event.h"
21#include "util/evsel.h"
22#include "util/debug.h"
23#include "util/session.h"
24#include "util/symbol.h"
25#include "util/cpumap.h"
16 26
17#include <unistd.h> 27#include <unistd.h>
18#include <sched.h> 28#include <sched.h>
29#include <sys/mman.h>
19 30
20#define ALIGN(x, a) __ALIGN_MASK(x, (typeof(x))(a)-1) 31#define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y))
21#define __ALIGN_MASK(x, mask) (((x)+(mask))&~(mask))
22 32
23static int fd[MAX_NR_CPUS][MAX_COUNTERS]; 33enum write_mode_t {
34 WRITE_FORCE,
35 WRITE_APPEND
36};
24 37
25static long default_interval = 100000; 38static u64 user_interval = ULLONG_MAX;
39static u64 default_interval = 0;
40static u64 sample_type;
26 41
27static int nr_cpus = 0; 42static struct cpu_map *cpus;
28static unsigned int page_size; 43static unsigned int page_size;
29static unsigned int mmap_pages = 128; 44static unsigned int mmap_pages = 128;
30static int freq = 0; 45static unsigned int user_freq = UINT_MAX;
46static int freq = 1000;
31static int output; 47static int output;
48static int pipe_output = 0;
32static const char *output_name = "perf.data"; 49static const char *output_name = "perf.data";
33static int group = 0; 50static int group = 0;
34static unsigned int realtime_prio = 0; 51static int realtime_prio = 0;
35static int system_wide = 0; 52static bool raw_samples = false;
36static pid_t target_pid = -1; 53static bool sample_id_all_avail = true;
37static int inherit = 1; 54static bool system_wide = false;
38static int force = 0; 55static pid_t target_pid = -1;
39static int append_file = 0; 56static pid_t target_tid = -1;
40static int call_graph = 0; 57static struct thread_map *threads;
41static int verbose = 0; 58static pid_t child_pid = -1;
42 59static bool no_inherit = false;
43static long samples; 60static enum write_mode_t write_mode = WRITE_FORCE;
44static struct timeval last_read; 61static bool call_graph = false;
45static struct timeval this_read; 62static bool inherit_stat = false;
46 63static bool no_samples = false;
47static u64 bytes_written; 64static bool sample_address = false;
48 65static bool sample_time = false;
49static struct pollfd event_array[MAX_NR_CPUS * MAX_COUNTERS]; 66static bool no_buildid = false;
50 67static bool no_buildid_cache = false;
51static int nr_poll; 68
52static int nr_cpu; 69static long samples = 0;
53 70static u64 bytes_written = 0;
54static int file_new = 1; 71
55static struct perf_file_header file_header; 72static struct pollfd *event_array;
56 73
57struct mmap_event { 74static int nr_poll = 0;
58 struct perf_event_header header; 75static int nr_cpu = 0;
59 u32 pid; 76
60 u32 tid; 77static int file_new = 1;
61 u64 start; 78static off_t post_processing_offset;
62 u64 len; 79
63 u64 pgoff; 80static struct perf_session *session;
64 char filename[PATH_MAX]; 81static const char *cpu_list;
65};
66
67struct comm_event {
68 struct perf_event_header header;
69 u32 pid;
70 u32 tid;
71 char comm[16];
72};
73
74 82
75struct mmap_data { 83struct mmap_data {
76 int counter;
77 void *base; 84 void *base;
78 unsigned int mask; 85 unsigned int mask;
79 unsigned int prev; 86 unsigned int prev;
80}; 87};
81 88
82static struct mmap_data mmap_array[MAX_NR_CPUS][MAX_COUNTERS]; 89static struct mmap_data mmap_array[MAX_NR_CPUS];
83 90
84static unsigned long mmap_read_head(struct mmap_data *md) 91static unsigned long mmap_read_head(struct mmap_data *md)
85{ 92{
86 struct perf_counter_mmap_page *pc = md->base; 93 struct perf_event_mmap_page *pc = md->base;
87 long head; 94 long head;
88 95
89 head = pc->data_head; 96 head = pc->data_head;
@@ -94,7 +101,7 @@ static unsigned long mmap_read_head(struct mmap_data *md)
94 101
95static void mmap_write_tail(struct mmap_data *md, unsigned long tail) 102static void mmap_write_tail(struct mmap_data *md, unsigned long tail)
96{ 103{
97 struct perf_counter_mmap_page *pc = md->base; 104 struct perf_event_mmap_page *pc = md->base;
98 105
99 /* 106 /*
100 * ensure all reads are done before we write the tail out. 107 * ensure all reads are done before we write the tail out.
@@ -103,6 +110,11 @@ static void mmap_write_tail(struct mmap_data *md, unsigned long tail)
103 pc->data_tail = tail; 110 pc->data_tail = tail;
104} 111}
105 112
113static void advance_output(size_t size)
114{
115 bytes_written += size;
116}
117
106static void write_output(void *buf, size_t size) 118static void write_output(void *buf, size_t size)
107{ 119{
108 while (size) { 120 while (size) {
@@ -118,6 +130,14 @@ static void write_output(void *buf, size_t size)
118 } 130 }
119} 131}
120 132
133static int process_synthesized_event(event_t *event,
134 struct sample_data *sample __used,
135 struct perf_session *self __used)
136{
137 write_output(event, event->header.size);
138 return 0;
139}
140
121static void mmap_read(struct mmap_data *md) 141static void mmap_read(struct mmap_data *md)
122{ 142{
123 unsigned int head = mmap_read_head(md); 143 unsigned int head = mmap_read_head(md);
@@ -127,8 +147,6 @@ static void mmap_read(struct mmap_data *md)
127 void *buf; 147 void *buf;
128 int diff; 148 int diff;
129 149
130 gettimeofday(&this_read, NULL);
131
132 /* 150 /*
133 * If we're further behind than half the buffer, there's a chance 151 * If we're further behind than half the buffer, there's a chance
134 * the writer will bite our tail and mess up the samples under us. 152 * the writer will bite our tail and mess up the samples under us.
@@ -139,23 +157,13 @@ static void mmap_read(struct mmap_data *md)
139 */ 157 */
140 diff = head - old; 158 diff = head - old;
141 if (diff < 0) { 159 if (diff < 0) {
142 struct timeval iv; 160 fprintf(stderr, "WARNING: failed to keep up with mmap data\n");
143 unsigned long msecs;
144
145 timersub(&this_read, &last_read, &iv);
146 msecs = iv.tv_sec*1000 + iv.tv_usec/1000;
147
148 fprintf(stderr, "WARNING: failed to keep up with mmap data."
149 " Last read %lu msecs ago.\n", msecs);
150
151 /* 161 /*
152 * head points to a known good entry, start there. 162 * head points to a known good entry, start there.
153 */ 163 */
154 old = head; 164 old = head;
155 } 165 }
156 166
157 last_read = this_read;
158
159 if (old != head) 167 if (old != head)
160 samples++; 168 samples++;
161 169
@@ -190,370 +198,617 @@ static void sig_handler(int sig)
190 198
191static void sig_atexit(void) 199static void sig_atexit(void)
192{ 200{
193 if (signr == -1) 201 if (child_pid > 0)
202 kill(child_pid, SIGTERM);
203
204 if (signr == -1 || signr == SIGUSR1)
194 return; 205 return;
195 206
196 signal(signr, SIG_DFL); 207 signal(signr, SIG_DFL);
197 kill(getpid(), signr); 208 kill(getpid(), signr);
198} 209}
199 210
200static void pid_synthesize_comm_event(pid_t pid, int full) 211static int group_fd;
201{
202 struct comm_event comm_ev;
203 char filename[PATH_MAX];
204 char bf[BUFSIZ];
205 int fd;
206 size_t size;
207 char *field, *sep;
208 DIR *tasks;
209 struct dirent dirent, *next;
210
211 snprintf(filename, sizeof(filename), "/proc/%d/stat", pid);
212
213 fd = open(filename, O_RDONLY);
214 if (fd < 0) {
215 /*
216 * We raced with a task exiting - just return:
217 */
218 if (verbose)
219 fprintf(stderr, "couldn't open %s\n", filename);
220 return;
221 }
222 if (read(fd, bf, sizeof(bf)) < 0) {
223 fprintf(stderr, "couldn't read %s\n", filename);
224 exit(EXIT_FAILURE);
225 }
226 close(fd);
227
228 /* 9027 (cat) R 6747 9027 6747 34816 9027 ... */
229 memset(&comm_ev, 0, sizeof(comm_ev));
230 field = strchr(bf, '(');
231 if (field == NULL)
232 goto out_failure;
233 sep = strchr(++field, ')');
234 if (sep == NULL)
235 goto out_failure;
236 size = sep - field;
237 memcpy(comm_ev.comm, field, size++);
238
239 comm_ev.pid = pid;
240 comm_ev.header.type = PERF_EVENT_COMM;
241 size = ALIGN(size, sizeof(u64));
242 comm_ev.header.size = sizeof(comm_ev) - (sizeof(comm_ev.comm) - size);
243
244 if (!full) {
245 comm_ev.tid = pid;
246
247 write_output(&comm_ev, comm_ev.header.size);
248 return;
249 }
250
251 snprintf(filename, sizeof(filename), "/proc/%d/task", pid);
252
253 tasks = opendir(filename);
254 while (!readdir_r(tasks, &dirent, &next) && next) {
255 char *end;
256 pid = strtol(dirent.d_name, &end, 10);
257 if (*end)
258 continue;
259 212
260 comm_ev.tid = pid; 213static struct perf_header_attr *get_header_attr(struct perf_event_attr *a, int nr)
214{
215 struct perf_header_attr *h_attr;
261 216
262 write_output(&comm_ev, comm_ev.header.size); 217 if (nr < session->header.attrs) {
218 h_attr = session->header.attr[nr];
219 } else {
220 h_attr = perf_header_attr__new(a);
221 if (h_attr != NULL)
222 if (perf_header__add_attr(&session->header, h_attr) < 0) {
223 perf_header_attr__delete(h_attr);
224 h_attr = NULL;
225 }
263 } 226 }
264 closedir(tasks);
265 return;
266 227
267out_failure: 228 return h_attr;
268 fprintf(stderr, "couldn't get COMM and pgid, malformed %s\n",
269 filename);
270 exit(EXIT_FAILURE);
271} 229}
272 230
273static void pid_synthesize_mmap_samples(pid_t pid) 231static void create_counter(struct perf_evsel *evsel, int cpu)
274{ 232{
275 char filename[PATH_MAX]; 233 char *filter = evsel->filter;
276 FILE *fp; 234 struct perf_event_attr *attr = &evsel->attr;
277 235 struct perf_header_attr *h_attr;
278 snprintf(filename, sizeof(filename), "/proc/%d/maps", pid); 236 int track = !evsel->idx; /* only the first counter needs these */
237 int thread_index;
238 int ret;
239 struct {
240 u64 count;
241 u64 time_enabled;
242 u64 time_running;
243 u64 id;
244 } read_data;
245 /*
246 * Check if parse_single_tracepoint_event has already asked for
247 * PERF_SAMPLE_TIME.
248 *
249 * XXX this is kludgy but short term fix for problems introduced by
250 * eac23d1c that broke 'perf script' by having different sample_types
251 * when using multiple tracepoint events when we use a perf binary
252 * that tries to use sample_id_all on an older kernel.
253 *
254 * We need to move counter creation to perf_session, support
255 * different sample_types, etc.
256 */
257 bool time_needed = attr->sample_type & PERF_SAMPLE_TIME;
258
259 attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED |
260 PERF_FORMAT_TOTAL_TIME_RUNNING |
261 PERF_FORMAT_ID;
262
263 attr->sample_type |= PERF_SAMPLE_IP | PERF_SAMPLE_TID;
264
265 if (nr_counters > 1)
266 attr->sample_type |= PERF_SAMPLE_ID;
279 267
280 fp = fopen(filename, "r"); 268 /*
281 if (fp == NULL) { 269 * We default some events to a 1 default interval. But keep
282 /* 270 * it a weak assumption overridable by the user.
283 * We raced with a task exiting - just return: 271 */
284 */ 272 if (!attr->sample_period || (user_freq != UINT_MAX &&
285 if (verbose) 273 user_interval != ULLONG_MAX)) {
286 fprintf(stderr, "couldn't open %s\n", filename); 274 if (freq) {
287 return; 275 attr->sample_type |= PERF_SAMPLE_PERIOD;
288 } 276 attr->freq = 1;
289 while (1) { 277 attr->sample_freq = freq;
290 char bf[BUFSIZ], *pbf = bf; 278 } else {
291 struct mmap_event mmap_ev = { 279 attr->sample_period = default_interval;
292 .header.type = PERF_EVENT_MMAP,
293 };
294 int n;
295 size_t size;
296 if (fgets(bf, sizeof(bf), fp) == NULL)
297 break;
298
299 /* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */
300 n = hex2u64(pbf, &mmap_ev.start);
301 if (n < 0)
302 continue;
303 pbf += n + 1;
304 n = hex2u64(pbf, &mmap_ev.len);
305 if (n < 0)
306 continue;
307 pbf += n + 3;
308 if (*pbf == 'x') { /* vm_exec */
309 char *execname = strrchr(bf, ' ');
310
311 if (execname == NULL || execname[1] != '/')
312 continue;
313
314 execname += 1;
315 size = strlen(execname);
316 execname[size - 1] = '\0'; /* Remove \n */
317 memcpy(mmap_ev.filename, execname, size);
318 size = ALIGN(size, sizeof(u64));
319 mmap_ev.len -= mmap_ev.start;
320 mmap_ev.header.size = (sizeof(mmap_ev) -
321 (sizeof(mmap_ev.filename) - size));
322 mmap_ev.pid = pid;
323 mmap_ev.tid = pid;
324
325 write_output(&mmap_ev, mmap_ev.header.size);
326 } 280 }
327 } 281 }
328 282
329 fclose(fp); 283 if (no_samples)
330} 284 attr->sample_freq = 0;
331
332static void synthesize_samples(void)
333{
334 DIR *proc;
335 struct dirent dirent, *next;
336
337 proc = opendir("/proc");
338
339 while (!readdir_r(proc, &dirent, &next) && next) {
340 char *end;
341 pid_t pid;
342 285
343 pid = strtol(dirent.d_name, &end, 10); 286 if (inherit_stat)
344 if (*end) /* only interested in proper numerical dirents */ 287 attr->inherit_stat = 1;
345 continue;
346 288
347 pid_synthesize_comm_event(pid, 1); 289 if (sample_address) {
348 pid_synthesize_mmap_samples(pid); 290 attr->sample_type |= PERF_SAMPLE_ADDR;
291 attr->mmap_data = track;
349 } 292 }
350 293
351 closedir(proc); 294 if (call_graph)
352} 295 attr->sample_type |= PERF_SAMPLE_CALLCHAIN;
353 296
354static int group_fd; 297 if (system_wide)
298 attr->sample_type |= PERF_SAMPLE_CPU;
355 299
356static void create_counter(int counter, int cpu, pid_t pid) 300 if (sample_id_all_avail &&
357{ 301 (sample_time || system_wide || !no_inherit || cpu_list))
358 struct perf_counter_attr *attr = attrs + counter; 302 attr->sample_type |= PERF_SAMPLE_TIME;
359 int track = 1;
360 303
361 attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID; 304 if (raw_samples) {
305 attr->sample_type |= PERF_SAMPLE_TIME;
306 attr->sample_type |= PERF_SAMPLE_RAW;
307 attr->sample_type |= PERF_SAMPLE_CPU;
308 }
362 309
363 if (freq) { 310 attr->mmap = track;
364 attr->sample_type |= PERF_SAMPLE_PERIOD; 311 attr->comm = track;
365 attr->freq = 1; 312 attr->inherit = !no_inherit;
366 attr->sample_freq = freq; 313 if (target_pid == -1 && target_tid == -1 && !system_wide) {
314 attr->disabled = 1;
315 attr->enable_on_exec = 1;
367 } 316 }
317retry_sample_id:
318 attr->sample_id_all = sample_id_all_avail ? 1 : 0;
368 319
369 if (call_graph) 320 for (thread_index = 0; thread_index < threads->nr; thread_index++) {
370 attr->sample_type |= PERF_SAMPLE_CALLCHAIN; 321try_again:
322 FD(evsel, nr_cpu, thread_index) = sys_perf_event_open(attr, threads->map[thread_index], cpu, group_fd, 0);
323
324 if (FD(evsel, nr_cpu, thread_index) < 0) {
325 int err = errno;
326
327 if (err == EPERM || err == EACCES)
328 die("Permission error - are you root?\n"
329 "\t Consider tweaking"
330 " /proc/sys/kernel/perf_event_paranoid.\n");
331 else if (err == ENODEV && cpu_list) {
332 die("No such device - did you specify"
333 " an out-of-range profile CPU?\n");
334 } else if (err == ENOENT) {
335 die("%s event is not supported. ",
336 event_name(evsel));
337 } else if (err == EINVAL && sample_id_all_avail) {
338 /*
339 * Old kernel, no attr->sample_id_type_all field
340 */
341 sample_id_all_avail = false;
342 if (!sample_time && !raw_samples && !time_needed)
343 attr->sample_type &= ~PERF_SAMPLE_TIME;
344
345 goto retry_sample_id;
346 }
371 347
372 if (file_new) { 348 /*
373 file_header.sample_type = attr->sample_type; 349 * If it's cycles then fall back to hrtimer
374 } else { 350 * based cpu-clock-tick sw counter, which
375 if (file_header.sample_type != attr->sample_type) { 351 * is always available even if no PMU support:
376 fprintf(stderr, "incompatible append\n"); 352 */
353 if (attr->type == PERF_TYPE_HARDWARE
354 && attr->config == PERF_COUNT_HW_CPU_CYCLES) {
355
356 if (verbose)
357 warning(" ... trying to fall back to cpu-clock-ticks\n");
358 attr->type = PERF_TYPE_SOFTWARE;
359 attr->config = PERF_COUNT_SW_CPU_CLOCK;
360 goto try_again;
361 }
362 printf("\n");
363 error("sys_perf_event_open() syscall returned with %d (%s). /bin/dmesg may provide additional information.\n",
364 FD(evsel, nr_cpu, thread_index), strerror(err));
365
366#if defined(__i386__) || defined(__x86_64__)
367 if (attr->type == PERF_TYPE_HARDWARE && err == EOPNOTSUPP)
368 die("No hardware sampling interrupt available."
369 " No APIC? If so then you can boot the kernel"
370 " with the \"lapic\" boot parameter to"
371 " force-enable it.\n");
372#endif
373
374 die("No CONFIG_PERF_EVENTS=y kernel support configured?\n");
377 exit(-1); 375 exit(-1);
378 } 376 }
379 }
380 377
381 attr->mmap = track; 378 h_attr = get_header_attr(attr, evsel->idx);
382 attr->comm = track; 379 if (h_attr == NULL)
383 attr->inherit = (cpu < 0) && inherit; 380 die("nomem\n");
384 attr->disabled = 1;
385 381
386 track = 0; /* only the first counter needs these */ 382 if (!file_new) {
383 if (memcmp(&h_attr->attr, attr, sizeof(*attr))) {
384 fprintf(stderr, "incompatible append\n");
385 exit(-1);
386 }
387 }
387 388
388try_again: 389 if (read(FD(evsel, nr_cpu, thread_index), &read_data, sizeof(read_data)) == -1) {
389 fd[nr_cpu][counter] = sys_perf_counter_open(attr, pid, cpu, group_fd, 0); 390 perror("Unable to read perf file descriptor");
391 exit(-1);
392 }
390 393
391 if (fd[nr_cpu][counter] < 0) { 394 if (perf_header_attr__add_id(h_attr, read_data.id) < 0) {
392 int err = errno; 395 pr_warning("Not enough memory to add id\n");
396 exit(-1);
397 }
393 398
394 if (err == EPERM) 399 assert(FD(evsel, nr_cpu, thread_index) >= 0);
395 die("Permission error - are you root?\n"); 400 fcntl(FD(evsel, nr_cpu, thread_index), F_SETFL, O_NONBLOCK);
396 401
397 /* 402 /*
398 * If it's cycles then fall back to hrtimer 403 * First counter acts as the group leader:
399 * based cpu-clock-tick sw counter, which
400 * is always available even if no PMU support:
401 */ 404 */
402 if (attr->type == PERF_TYPE_HARDWARE 405 if (group && group_fd == -1)
403 && attr->config == PERF_COUNT_HW_CPU_CYCLES) { 406 group_fd = FD(evsel, nr_cpu, thread_index);
404 407
405 if (verbose) 408 if (evsel->idx || thread_index) {
406 warning(" ... trying to fall back to cpu-clock-ticks\n"); 409 struct perf_evsel *first;
407 attr->type = PERF_TYPE_SOFTWARE; 410 first = list_entry(evsel_list.next, struct perf_evsel, node);
408 attr->config = PERF_COUNT_SW_CPU_CLOCK; 411 ret = ioctl(FD(evsel, nr_cpu, thread_index),
409 goto try_again; 412 PERF_EVENT_IOC_SET_OUTPUT,
410 } 413 FD(first, nr_cpu, 0));
411 printf("\n"); 414 if (ret) {
412 error("perfcounter syscall returned with %d (%s)\n", 415 error("failed to set output: %d (%s)\n", errno,
413 fd[nr_cpu][counter], strerror(err)); 416 strerror(errno));
414 die("No CONFIG_PERF_COUNTERS=y kernel support configured?\n"); 417 exit(-1);
415 exit(-1); 418 }
416 } 419 } else {
420 mmap_array[nr_cpu].prev = 0;
421 mmap_array[nr_cpu].mask = mmap_pages*page_size - 1;
422 mmap_array[nr_cpu].base = mmap(NULL, (mmap_pages+1)*page_size,
423 PROT_READ | PROT_WRITE, MAP_SHARED, FD(evsel, nr_cpu, thread_index), 0);
424 if (mmap_array[nr_cpu].base == MAP_FAILED) {
425 error("failed to mmap with %d (%s)\n", errno, strerror(errno));
426 exit(-1);
427 }
417 428
418 assert(fd[nr_cpu][counter] >= 0); 429 event_array[nr_poll].fd = FD(evsel, nr_cpu, thread_index);
419 fcntl(fd[nr_cpu][counter], F_SETFL, O_NONBLOCK); 430 event_array[nr_poll].events = POLLIN;
431 nr_poll++;
432 }
420 433
421 /* 434 if (filter != NULL) {
422 * First counter acts as the group leader: 435 ret = ioctl(FD(evsel, nr_cpu, thread_index),
423 */ 436 PERF_EVENT_IOC_SET_FILTER, filter);
424 if (group && group_fd == -1) 437 if (ret) {
425 group_fd = fd[nr_cpu][counter]; 438 error("failed to set filter with %d (%s)\n", errno,
426 439 strerror(errno));
427 event_array[nr_poll].fd = fd[nr_cpu][counter]; 440 exit(-1);
428 event_array[nr_poll].events = POLLIN; 441 }
429 nr_poll++; 442 }
430
431 mmap_array[nr_cpu][counter].counter = counter;
432 mmap_array[nr_cpu][counter].prev = 0;
433 mmap_array[nr_cpu][counter].mask = mmap_pages*page_size - 1;
434 mmap_array[nr_cpu][counter].base = mmap(NULL, (mmap_pages+1)*page_size,
435 PROT_READ|PROT_WRITE, MAP_SHARED, fd[nr_cpu][counter], 0);
436 if (mmap_array[nr_cpu][counter].base == MAP_FAILED) {
437 error("failed to mmap with %d (%s)\n", errno, strerror(errno));
438 exit(-1);
439 } 443 }
440 444
441 ioctl(fd[nr_cpu][counter], PERF_COUNTER_IOC_ENABLE); 445 if (!sample_type)
446 sample_type = attr->sample_type;
442} 447}
443 448
444static void open_counters(int cpu, pid_t pid) 449static void open_counters(int cpu)
445{ 450{
446 int counter; 451 struct perf_evsel *pos;
447
448 if (pid > 0) {
449 pid_synthesize_comm_event(pid, 0);
450 pid_synthesize_mmap_samples(pid);
451 }
452 452
453 group_fd = -1; 453 group_fd = -1;
454 for (counter = 0; counter < nr_counters; counter++) 454
455 create_counter(counter, cpu, pid); 455 list_for_each_entry(pos, &evsel_list, node)
456 create_counter(pos, cpu);
456 457
457 nr_cpu++; 458 nr_cpu++;
458} 459}
459 460
461static int process_buildids(void)
462{
463 u64 size = lseek(output, 0, SEEK_CUR);
464
465 if (size == 0)
466 return 0;
467
468 session->fd = output;
469 return __perf_session__process_events(session, post_processing_offset,
470 size - post_processing_offset,
471 size, &build_id__mark_dso_hit_ops);
472}
473
460static void atexit_header(void) 474static void atexit_header(void)
461{ 475{
462 file_header.data_size += bytes_written; 476 if (!pipe_output) {
477 session->header.data_size += bytes_written;
478
479 if (!no_buildid)
480 process_buildids();
481 perf_header__write(&session->header, output, true);
482 perf_session__delete(session);
483 symbol__exit();
484 }
485}
486
487static void event__synthesize_guest_os(struct machine *machine, void *data)
488{
489 int err;
490 struct perf_session *psession = data;
491
492 if (machine__is_host(machine))
493 return;
463 494
464 if (pwrite(output, &file_header, sizeof(file_header), 0) == -1) 495 /*
465 perror("failed to write on file headers"); 496 *As for guest kernel when processing subcommand record&report,
497 *we arrange module mmap prior to guest kernel mmap and trigger
498 *a preload dso because default guest module symbols are loaded
499 *from guest kallsyms instead of /lib/modules/XXX/XXX. This
500 *method is used to avoid symbol missing when the first addr is
501 *in module instead of in guest kernel.
502 */
503 err = event__synthesize_modules(process_synthesized_event,
504 psession, machine);
505 if (err < 0)
506 pr_err("Couldn't record guest kernel [%d]'s reference"
507 " relocation symbol.\n", machine->pid);
508
509 /*
510 * We use _stext for guest kernel because guest kernel's /proc/kallsyms
511 * have no _text sometimes.
512 */
513 err = event__synthesize_kernel_mmap(process_synthesized_event,
514 psession, machine, "_text");
515 if (err < 0)
516 err = event__synthesize_kernel_mmap(process_synthesized_event,
517 psession, machine, "_stext");
518 if (err < 0)
519 pr_err("Couldn't record guest kernel [%d]'s reference"
520 " relocation symbol.\n", machine->pid);
521}
522
523static struct perf_event_header finished_round_event = {
524 .size = sizeof(struct perf_event_header),
525 .type = PERF_RECORD_FINISHED_ROUND,
526};
527
528static void mmap_read_all(void)
529{
530 int i;
531
532 for (i = 0; i < nr_cpu; i++) {
533 if (mmap_array[i].base)
534 mmap_read(&mmap_array[i]);
535 }
536
537 if (perf_header__has_feat(&session->header, HEADER_TRACE_INFO))
538 write_output(&finished_round_event, sizeof(finished_round_event));
466} 539}
467 540
468static int __cmd_record(int argc, const char **argv) 541static int __cmd_record(int argc, const char **argv)
469{ 542{
470 int i, counter; 543 int i;
471 struct stat st; 544 struct stat st;
472 pid_t pid;
473 int flags; 545 int flags;
474 int ret; 546 int err;
547 unsigned long waking = 0;
548 int child_ready_pipe[2], go_pipe[2];
549 const bool forks = argc > 0;
550 char buf;
551 struct machine *machine;
475 552
476 page_size = sysconf(_SC_PAGE_SIZE); 553 page_size = sysconf(_SC_PAGE_SIZE);
477 nr_cpus = sysconf(_SC_NPROCESSORS_ONLN);
478 assert(nr_cpus <= MAX_NR_CPUS);
479 assert(nr_cpus >= 0);
480 554
481 atexit(sig_atexit); 555 atexit(sig_atexit);
482 signal(SIGCHLD, sig_handler); 556 signal(SIGCHLD, sig_handler);
483 signal(SIGINT, sig_handler); 557 signal(SIGINT, sig_handler);
558 signal(SIGUSR1, sig_handler);
484 559
485 if (!stat(output_name, &st) && !force && !append_file) { 560 if (forks && (pipe(child_ready_pipe) < 0 || pipe(go_pipe) < 0)) {
486 fprintf(stderr, "Error, output file %s exists, use -A to append or -f to overwrite.\n", 561 perror("failed to create pipes");
487 output_name);
488 exit(-1); 562 exit(-1);
489 } 563 }
490 564
565 if (!strcmp(output_name, "-"))
566 pipe_output = 1;
567 else if (!stat(output_name, &st) && st.st_size) {
568 if (write_mode == WRITE_FORCE) {
569 char oldname[PATH_MAX];
570 snprintf(oldname, sizeof(oldname), "%s.old",
571 output_name);
572 unlink(oldname);
573 rename(output_name, oldname);
574 }
575 } else if (write_mode == WRITE_APPEND) {
576 write_mode = WRITE_FORCE;
577 }
578
491 flags = O_CREAT|O_RDWR; 579 flags = O_CREAT|O_RDWR;
492 if (append_file) 580 if (write_mode == WRITE_APPEND)
493 file_new = 0; 581 file_new = 0;
494 else 582 else
495 flags |= O_TRUNC; 583 flags |= O_TRUNC;
496 584
497 output = open(output_name, flags, S_IRUSR|S_IWUSR); 585 if (pipe_output)
586 output = STDOUT_FILENO;
587 else
588 output = open(output_name, flags, S_IRUSR | S_IWUSR);
498 if (output < 0) { 589 if (output < 0) {
499 perror("failed to create output file"); 590 perror("failed to create output file");
500 exit(-1); 591 exit(-1);
501 } 592 }
502 593
594 session = perf_session__new(output_name, O_WRONLY,
595 write_mode == WRITE_FORCE, false, NULL);
596 if (session == NULL) {
597 pr_err("Not enough memory for reading perf file header\n");
598 return -1;
599 }
600
601 if (!no_buildid)
602 perf_header__set_feat(&session->header, HEADER_BUILD_ID);
603
503 if (!file_new) { 604 if (!file_new) {
504 if (read(output, &file_header, sizeof(file_header)) == -1) { 605 err = perf_header__read(session, output);
505 perror("failed to read file headers"); 606 if (err < 0)
607 goto out_delete_session;
608 }
609
610 if (have_tracepoints(&evsel_list))
611 perf_header__set_feat(&session->header, HEADER_TRACE_INFO);
612
613 /*
614 * perf_session__delete(session) will be called at atexit_header()
615 */
616 atexit(atexit_header);
617
618 if (forks) {
619 child_pid = fork();
620 if (child_pid < 0) {
621 perror("failed to fork");
506 exit(-1); 622 exit(-1);
507 } 623 }
508 624
509 lseek(output, file_header.data_size, SEEK_CUR); 625 if (!child_pid) {
626 if (pipe_output)
627 dup2(2, 1);
628 close(child_ready_pipe[0]);
629 close(go_pipe[1]);
630 fcntl(go_pipe[0], F_SETFD, FD_CLOEXEC);
631
632 /*
633 * Do a dummy execvp to get the PLT entry resolved,
634 * so we avoid the resolver overhead on the real
635 * execvp call.
636 */
637 execvp("", (char **)argv);
638
639 /*
640 * Tell the parent we're ready to go
641 */
642 close(child_ready_pipe[1]);
643
644 /*
645 * Wait until the parent tells us to go.
646 */
647 if (read(go_pipe[0], &buf, 1) == -1)
648 perror("unable to read pipe");
649
650 execvp(argv[0], (char **)argv);
651
652 perror(argv[0]);
653 kill(getppid(), SIGUSR1);
654 exit(-1);
655 }
656
657 if (!system_wide && target_tid == -1 && target_pid == -1)
658 threads->map[0] = child_pid;
659
660 close(child_ready_pipe[1]);
661 close(go_pipe[0]);
662 /*
663 * wait for child to settle
664 */
665 if (read(child_ready_pipe[0], &buf, 1) == -1) {
666 perror("unable to read pipe");
667 exit(-1);
668 }
669 close(child_ready_pipe[0]);
510 } 670 }
511 671
512 atexit(atexit_header); 672 if (!system_wide && no_inherit && !cpu_list) {
673 open_counters(-1);
674 } else {
675 for (i = 0; i < cpus->nr; i++)
676 open_counters(cpus->map[i]);
677 }
513 678
514 if (!system_wide) { 679 perf_session__set_sample_type(session, sample_type);
515 open_counters(-1, target_pid != -1 ? target_pid : getpid());
516 } else for (i = 0; i < nr_cpus; i++)
517 open_counters(i, target_pid);
518 680
519 if (target_pid == -1 && argc) { 681 if (pipe_output) {
520 pid = fork(); 682 err = perf_header__write_pipe(output);
521 if (pid < 0) 683 if (err < 0)
522 perror("failed to fork"); 684 return err;
685 } else if (file_new) {
686 err = perf_header__write(&session->header, output, false);
687 if (err < 0)
688 return err;
689 }
523 690
524 if (!pid) { 691 post_processing_offset = lseek(output, 0, SEEK_CUR);
525 if (execvp(argv[0], (char **)argv)) { 692
526 perror(argv[0]); 693 perf_session__set_sample_id_all(session, sample_id_all_avail);
527 exit(-1); 694
695 if (pipe_output) {
696 err = event__synthesize_attrs(&session->header,
697 process_synthesized_event,
698 session);
699 if (err < 0) {
700 pr_err("Couldn't synthesize attrs.\n");
701 return err;
702 }
703
704 err = event__synthesize_event_types(process_synthesized_event,
705 session);
706 if (err < 0) {
707 pr_err("Couldn't synthesize event_types.\n");
708 return err;
709 }
710
711 if (have_tracepoints(&evsel_list)) {
712 /*
713 * FIXME err <= 0 here actually means that
714 * there were no tracepoints so its not really
715 * an error, just that we don't need to
716 * synthesize anything. We really have to
717 * return this more properly and also
718 * propagate errors that now are calling die()
719 */
720 err = event__synthesize_tracing_data(output, &evsel_list,
721 process_synthesized_event,
722 session);
723 if (err <= 0) {
724 pr_err("Couldn't record tracing data.\n");
725 return err;
528 } 726 }
727 advance_output(err);
529 } 728 }
530 } 729 }
531 730
731 machine = perf_session__find_host_machine(session);
732 if (!machine) {
733 pr_err("Couldn't find native kernel information.\n");
734 return -1;
735 }
736
737 err = event__synthesize_kernel_mmap(process_synthesized_event,
738 session, machine, "_text");
739 if (err < 0)
740 err = event__synthesize_kernel_mmap(process_synthesized_event,
741 session, machine, "_stext");
742 if (err < 0)
743 pr_err("Couldn't record kernel reference relocation symbol\n"
744 "Symbol resolution may be skewed if relocation was used (e.g. kexec).\n"
745 "Check /proc/kallsyms permission or run as root.\n");
746
747 err = event__synthesize_modules(process_synthesized_event,
748 session, machine);
749 if (err < 0)
750 pr_err("Couldn't record kernel module information.\n"
751 "Symbol resolution may be skewed if relocation was used (e.g. kexec).\n"
752 "Check /proc/modules permission or run as root.\n");
753
754 if (perf_guest)
755 perf_session__process_machines(session, event__synthesize_guest_os);
756
757 if (!system_wide)
758 event__synthesize_thread(target_tid, process_synthesized_event,
759 session);
760 else
761 event__synthesize_threads(process_synthesized_event, session);
762
532 if (realtime_prio) { 763 if (realtime_prio) {
533 struct sched_param param; 764 struct sched_param param;
534 765
535 param.sched_priority = realtime_prio; 766 param.sched_priority = realtime_prio;
536 if (sched_setscheduler(0, SCHED_FIFO, &param)) { 767 if (sched_setscheduler(0, SCHED_FIFO, &param)) {
537 printf("Could not set realtime priority.\n"); 768 pr_err("Could not set realtime priority.\n");
538 exit(-1); 769 exit(-1);
539 } 770 }
540 } 771 }
541 772
542 if (system_wide) 773 /*
543 synthesize_samples(); 774 * Let the child rip
775 */
776 if (forks)
777 close(go_pipe[1]);
544 778
545 while (!done) { 779 for (;;) {
546 int hits = samples; 780 int hits = samples;
781 int thread;
782
783 mmap_read_all();
547 784
548 for (i = 0; i < nr_cpu; i++) { 785 if (hits == samples) {
549 for (counter = 0; counter < nr_counters; counter++) 786 if (done)
550 mmap_read(&mmap_array[i][counter]); 787 break;
788 err = poll(event_array, nr_poll, -1);
789 waking++;
551 } 790 }
552 791
553 if (hits == samples) 792 if (done) {
554 ret = poll(event_array, nr_poll, 100); 793 for (i = 0; i < nr_cpu; i++) {
794 struct perf_evsel *pos;
795
796 list_for_each_entry(pos, &evsel_list, node) {
797 for (thread = 0;
798 thread < threads->nr;
799 thread++)
800 ioctl(FD(pos, i, thread),
801 PERF_EVENT_IOC_DISABLE);
802 }
803 }
804 }
555 } 805 }
556 806
807 if (quiet || signr == SIGUSR1)
808 return 0;
809
810 fprintf(stderr, "[ perf record: Woken up %ld times to write data ]\n", waking);
811
557 /* 812 /*
558 * Approximate RIP event size: 24 bytes. 813 * Approximate RIP event size: 24 bytes.
559 */ 814 */
@@ -564,6 +819,10 @@ static int __cmd_record(int argc, const char **argv)
564 bytes_written / 24); 819 bytes_written / 24);
565 820
566 return 0; 821 return 0;
822
823out_delete_session:
824 perf_session__delete(session);
825 return err;
567} 826}
568 827
569static const char * const record_usage[] = { 828static const char * const record_usage[] = {
@@ -572,57 +831,137 @@ static const char * const record_usage[] = {
572 NULL 831 NULL
573}; 832};
574 833
575static const struct option options[] = { 834static bool force, append_file;
835
836const struct option record_options[] = {
576 OPT_CALLBACK('e', "event", NULL, "event", 837 OPT_CALLBACK('e', "event", NULL, "event",
577 "event selector. use 'perf list' to list available events", 838 "event selector. use 'perf list' to list available events",
578 parse_events), 839 parse_events),
840 OPT_CALLBACK(0, "filter", NULL, "filter",
841 "event filter", parse_filter),
579 OPT_INTEGER('p', "pid", &target_pid, 842 OPT_INTEGER('p', "pid", &target_pid,
580 "record events on existing pid"), 843 "record events on existing process id"),
844 OPT_INTEGER('t', "tid", &target_tid,
845 "record events on existing thread id"),
581 OPT_INTEGER('r', "realtime", &realtime_prio, 846 OPT_INTEGER('r', "realtime", &realtime_prio,
582 "collect data with this RT SCHED_FIFO priority"), 847 "collect data with this RT SCHED_FIFO priority"),
848 OPT_BOOLEAN('R', "raw-samples", &raw_samples,
849 "collect raw sample records from all opened counters"),
583 OPT_BOOLEAN('a', "all-cpus", &system_wide, 850 OPT_BOOLEAN('a', "all-cpus", &system_wide,
584 "system-wide collection from all CPUs"), 851 "system-wide collection from all CPUs"),
585 OPT_BOOLEAN('A', "append", &append_file, 852 OPT_BOOLEAN('A', "append", &append_file,
586 "append to the output file to do incremental profiling"), 853 "append to the output file to do incremental profiling"),
854 OPT_STRING('C', "cpu", &cpu_list, "cpu",
855 "list of cpus to monitor"),
587 OPT_BOOLEAN('f', "force", &force, 856 OPT_BOOLEAN('f', "force", &force,
588 "overwrite existing data file"), 857 "overwrite existing data file (deprecated)"),
589 OPT_LONG('c', "count", &default_interval, 858 OPT_U64('c', "count", &user_interval, "event period to sample"),
590 "event period to sample"),
591 OPT_STRING('o', "output", &output_name, "file", 859 OPT_STRING('o', "output", &output_name, "file",
592 "output file name"), 860 "output file name"),
593 OPT_BOOLEAN('i', "inherit", &inherit, 861 OPT_BOOLEAN('i', "no-inherit", &no_inherit,
594 "child tasks inherit counters"), 862 "child tasks do not inherit counters"),
595 OPT_INTEGER('F', "freq", &freq, 863 OPT_UINTEGER('F', "freq", &user_freq, "profile at this frequency"),
596 "profile at this frequency"), 864 OPT_UINTEGER('m', "mmap-pages", &mmap_pages, "number of mmap data pages"),
597 OPT_INTEGER('m', "mmap-pages", &mmap_pages,
598 "number of mmap data pages"),
599 OPT_BOOLEAN('g', "call-graph", &call_graph, 865 OPT_BOOLEAN('g', "call-graph", &call_graph,
600 "do call-graph (stack chain/backtrace) recording"), 866 "do call-graph (stack chain/backtrace) recording"),
601 OPT_BOOLEAN('v', "verbose", &verbose, 867 OPT_INCR('v', "verbose", &verbose,
602 "be more verbose (show counter open errors, etc)"), 868 "be more verbose (show counter open errors, etc)"),
869 OPT_BOOLEAN('q', "quiet", &quiet, "don't print any message"),
870 OPT_BOOLEAN('s', "stat", &inherit_stat,
871 "per thread counts"),
872 OPT_BOOLEAN('d', "data", &sample_address,
873 "Sample addresses"),
874 OPT_BOOLEAN('T', "timestamp", &sample_time, "Sample timestamps"),
875 OPT_BOOLEAN('n', "no-samples", &no_samples,
876 "don't sample"),
877 OPT_BOOLEAN('N', "no-buildid-cache", &no_buildid_cache,
878 "do not update the buildid cache"),
879 OPT_BOOLEAN('B', "no-buildid", &no_buildid,
880 "do not collect buildids in perf.data"),
603 OPT_END() 881 OPT_END()
604}; 882};
605 883
606int cmd_record(int argc, const char **argv, const char *prefix) 884int cmd_record(int argc, const char **argv, const char *prefix __used)
607{ 885{
608 int counter; 886 int err = -ENOMEM;
887 struct perf_evsel *pos;
888
889 argc = parse_options(argc, argv, record_options, record_usage,
890 PARSE_OPT_STOP_AT_NON_OPTION);
891 if (!argc && target_pid == -1 && target_tid == -1 &&
892 !system_wide && !cpu_list)
893 usage_with_options(record_usage, record_options);
894
895 if (force && append_file) {
896 fprintf(stderr, "Can't overwrite and append at the same time."
897 " You need to choose between -f and -A");
898 usage_with_options(record_usage, record_options);
899 } else if (append_file) {
900 write_mode = WRITE_APPEND;
901 } else {
902 write_mode = WRITE_FORCE;
903 }
904
905 symbol__init();
609 906
610 argc = parse_options(argc, argv, options, record_usage, 0); 907 if (no_buildid_cache || no_buildid)
611 if (!argc && target_pid == -1 && !system_wide) 908 disable_buildid_cache();
612 usage_with_options(record_usage, options);
613 909
614 if (!nr_counters) { 910 if (list_empty(&evsel_list) && perf_evsel_list__create_default() < 0) {
615 nr_counters = 1; 911 pr_err("Not enough memory for event selector list\n");
616 attrs[0].type = PERF_TYPE_HARDWARE; 912 goto out_symbol_exit;
617 attrs[0].config = PERF_COUNT_HW_CPU_CYCLES;
618 } 913 }
619 914
620 for (counter = 0; counter < nr_counters; counter++) { 915 if (target_pid != -1)
621 if (attrs[counter].sample_period) 916 target_tid = target_pid;
622 continue;
623 917
624 attrs[counter].sample_period = default_interval; 918 threads = thread_map__new(target_pid, target_tid);
919 if (threads == NULL) {
920 pr_err("Problems finding threads of monitor\n");
921 usage_with_options(record_usage, record_options);
625 } 922 }
626 923
627 return __cmd_record(argc, argv); 924 cpus = cpu_map__new(cpu_list);
925 if (cpus == NULL) {
926 perror("failed to parse CPUs map");
927 return -1;
928 }
929
930 list_for_each_entry(pos, &evsel_list, node) {
931 if (perf_evsel__alloc_fd(pos, cpus->nr, threads->nr) < 0)
932 goto out_free_fd;
933 }
934 event_array = malloc((sizeof(struct pollfd) * MAX_NR_CPUS *
935 MAX_COUNTERS * threads->nr));
936 if (!event_array)
937 goto out_free_fd;
938
939 if (user_interval != ULLONG_MAX)
940 default_interval = user_interval;
941 if (user_freq != UINT_MAX)
942 freq = user_freq;
943
944 /*
945 * User specified count overrides default frequency.
946 */
947 if (default_interval)
948 freq = 0;
949 else if (freq) {
950 default_interval = freq;
951 } else {
952 fprintf(stderr, "frequency and count are zero, aborting\n");
953 err = -EINVAL;
954 goto out_free_event_array;
955 }
956
957 err = __cmd_record(argc, argv);
958
959out_free_event_array:
960 free(event_array);
961out_free_fd:
962 thread_map__delete(threads);
963 threads = NULL;
964out_symbol_exit:
965 symbol__exit();
966 return err;
628} 967}
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 5eb5566f0c9..75183a4518e 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -10,1509 +10,425 @@
10#include "util/util.h" 10#include "util/util.h"
11 11
12#include "util/color.h" 12#include "util/color.h"
13#include "util/list.h" 13#include <linux/list.h>
14#include "util/cache.h" 14#include "util/cache.h"
15#include "util/rbtree.h" 15#include <linux/rbtree.h>
16#include "util/symbol.h" 16#include "util/symbol.h"
17#include "util/string.h" 17#include "util/callchain.h"
18#include "util/strlist.h"
19#include "util/values.h"
18 20
19#include "perf.h" 21#include "perf.h"
22#include "util/debug.h"
23#include "util/header.h"
24#include "util/session.h"
20 25
21#include "util/parse-options.h" 26#include "util/parse-options.h"
22#include "util/parse-events.h" 27#include "util/parse-events.h"
23 28
24#define SHOW_KERNEL 1 29#include "util/thread.h"
25#define SHOW_USER 2 30#include "util/sort.h"
26#define SHOW_HV 4 31#include "util/hist.h"
27 32
28static char const *input_name = "perf.data"; 33static char const *input_name = "perf.data";
29static char *vmlinux = NULL;
30 34
31static char default_sort_order[] = "comm,dso"; 35static bool force, use_tui, use_stdio;
32static char *sort_order = default_sort_order; 36static bool hide_unresolved;
37static bool dont_use_callchains;
33 38
34static int input; 39static bool show_threads;
35static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV; 40static struct perf_read_values show_threads_values;
36 41
37static int dump_trace = 0; 42static const char default_pretty_printing_style[] = "normal";
38#define dprintf(x...) do { if (dump_trace) printf(x); } while (0) 43static const char *pretty_printing_style = default_pretty_printing_style;
39#define cdprintf(x...) do { if (dump_trace) color_fprintf(stdout, color, x); } while (0)
40 44
41static int verbose; 45static char callchain_default_opt[] = "fractal,0.5";
42#define eprintf(x...) do { if (verbose) fprintf(stderr, x); } while (0)
43 46
44static int full_paths; 47static struct hists *perf_session__hists_findnew(struct perf_session *self,
45 48 u64 event_stream, u32 type,
46static unsigned long page_size; 49 u64 config)
47static unsigned long mmap_window = 32;
48
49static char default_parent_pattern[] = "^sys_|^do_page_fault";
50static char *parent_pattern = default_parent_pattern;
51static regex_t parent_regex;
52
53static int exclude_other = 1;
54
55struct ip_event {
56 struct perf_event_header header;
57 u64 ip;
58 u32 pid, tid;
59 unsigned char __more_data[];
60};
61
62struct ip_callchain {
63 u64 nr;
64 u64 ips[0];
65};
66
67struct mmap_event {
68 struct perf_event_header header;
69 u32 pid, tid;
70 u64 start;
71 u64 len;
72 u64 pgoff;
73 char filename[PATH_MAX];
74};
75
76struct comm_event {
77 struct perf_event_header header;
78 u32 pid, tid;
79 char comm[16];
80};
81
82struct fork_event {
83 struct perf_event_header header;
84 u32 pid, ppid;
85};
86
87struct period_event {
88 struct perf_event_header header;
89 u64 time;
90 u64 id;
91 u64 sample_period;
92};
93
94struct lost_event {
95 struct perf_event_header header;
96 u64 id;
97 u64 lost;
98};
99
100typedef union event_union {
101 struct perf_event_header header;
102 struct ip_event ip;
103 struct mmap_event mmap;
104 struct comm_event comm;
105 struct fork_event fork;
106 struct period_event period;
107 struct lost_event lost;
108} event_t;
109
110static LIST_HEAD(dsos);
111static struct dso *kernel_dso;
112static struct dso *vdso;
113
114static void dsos__add(struct dso *dso)
115{
116 list_add_tail(&dso->node, &dsos);
117}
118
119static struct dso *dsos__find(const char *name)
120{
121 struct dso *pos;
122
123 list_for_each_entry(pos, &dsos, node)
124 if (strcmp(pos->name, name) == 0)
125 return pos;
126 return NULL;
127}
128
129static struct dso *dsos__findnew(const char *name)
130{
131 struct dso *dso = dsos__find(name);
132 int nr;
133
134 if (dso)
135 return dso;
136
137 dso = dso__new(name, 0);
138 if (!dso)
139 goto out_delete_dso;
140
141 nr = dso__load(dso, NULL, verbose);
142 if (nr < 0) {
143 eprintf("Failed to open: %s\n", name);
144 goto out_delete_dso;
145 }
146 if (!nr)
147 eprintf("No symbols found in: %s, maybe install a debug package?\n", name);
148
149 dsos__add(dso);
150
151 return dso;
152
153out_delete_dso:
154 dso__delete(dso);
155 return NULL;
156}
157
158static void dsos__fprintf(FILE *fp)
159{ 50{
160 struct dso *pos; 51 struct rb_node **p = &self->hists_tree.rb_node;
161 52 struct rb_node *parent = NULL;
162 list_for_each_entry(pos, &dsos, node) 53 struct hists *iter, *new;
163 dso__fprintf(pos, fp);
164}
165
166static struct symbol *vdso__find_symbol(struct dso *dso, u64 ip)
167{
168 return dso__find_symbol(kernel_dso, ip);
169}
170
171static int load_kernel(void)
172{
173 int err;
174
175 kernel_dso = dso__new("[kernel]", 0);
176 if (!kernel_dso)
177 return -1;
178
179 err = dso__load_kernel(kernel_dso, vmlinux, NULL, verbose);
180 if (err) {
181 dso__delete(kernel_dso);
182 kernel_dso = NULL;
183 } else
184 dsos__add(kernel_dso);
185
186 vdso = dso__new("[vdso]", 0);
187 if (!vdso)
188 return -1;
189
190 vdso->find_symbol = vdso__find_symbol;
191
192 dsos__add(vdso);
193
194 return err;
195}
196
197static char __cwd[PATH_MAX];
198static char *cwd = __cwd;
199static int cwdlen;
200
201static int strcommon(const char *pathname)
202{
203 int n = 0;
204
205 while (pathname[n] == cwd[n] && n < cwdlen)
206 ++n;
207
208 return n;
209}
210
211struct map {
212 struct list_head node;
213 u64 start;
214 u64 end;
215 u64 pgoff;
216 u64 (*map_ip)(struct map *, u64);
217 struct dso *dso;
218};
219
220static u64 map__map_ip(struct map *map, u64 ip)
221{
222 return ip - map->start + map->pgoff;
223}
224
225static u64 vdso__map_ip(struct map *map, u64 ip)
226{
227 return ip;
228}
229
230static inline int is_anon_memory(const char *filename)
231{
232 return strcmp(filename, "//anon") == 0;
233}
234
235static struct map *map__new(struct mmap_event *event)
236{
237 struct map *self = malloc(sizeof(*self));
238
239 if (self != NULL) {
240 const char *filename = event->filename;
241 char newfilename[PATH_MAX];
242 int anon;
243
244 if (cwd) {
245 int n = strcommon(filename);
246
247 if (n == cwdlen) {
248 snprintf(newfilename, sizeof(newfilename),
249 ".%s", filename + n);
250 filename = newfilename;
251 }
252 }
253
254 anon = is_anon_memory(filename);
255
256 if (anon) {
257 snprintf(newfilename, sizeof(newfilename), "/tmp/perf-%d.map", event->pid);
258 filename = newfilename;
259 }
260 54
261 self->start = event->start; 55 while (*p != NULL) {
262 self->end = event->start + event->len; 56 parent = *p;
263 self->pgoff = event->pgoff; 57 iter = rb_entry(parent, struct hists, rb_node);
58 if (iter->config == config)
59 return iter;
264 60
265 self->dso = dsos__findnew(filename);
266 if (self->dso == NULL)
267 goto out_delete;
268 61
269 if (self->dso == vdso || anon) 62 if (config > iter->config)
270 self->map_ip = vdso__map_ip; 63 p = &(*p)->rb_right;
271 else 64 else
272 self->map_ip = map__map_ip; 65 p = &(*p)->rb_left;
273 } 66 }
274 return self;
275out_delete:
276 free(self);
277 return NULL;
278}
279 67
280static struct map *map__clone(struct map *self) 68 new = malloc(sizeof(struct hists));
281{ 69 if (new == NULL)
282 struct map *map = malloc(sizeof(*self));
283
284 if (!map)
285 return NULL; 70 return NULL;
71 memset(new, 0, sizeof(struct hists));
72 new->event_stream = event_stream;
73 new->config = config;
74 new->type = type;
75 rb_link_node(&new->rb_node, parent, p);
76 rb_insert_color(&new->rb_node, &self->hists_tree);
77 return new;
78}
79
80static int perf_session__add_hist_entry(struct perf_session *self,
81 struct addr_location *al,
82 struct sample_data *data)
83{
84 struct map_symbol *syms = NULL;
85 struct symbol *parent = NULL;
86 int err = -ENOMEM;
87 struct hist_entry *he;
88 struct hists *hists;
89 struct perf_event_attr *attr;
286 90
287 memcpy(map, self, sizeof(*self)); 91 if ((sort__has_parent || symbol_conf.use_callchain) && data->callchain) {
288 92 syms = perf_session__resolve_callchain(self, al->thread,
289 return map; 93 data->callchain, &parent);
290} 94 if (syms == NULL)
291 95 return -ENOMEM;
292static int map__overlap(struct map *l, struct map *r)
293{
294 if (l->start > r->start) {
295 struct map *t = l;
296 l = r;
297 r = t;
298 } 96 }
299 97
300 if (l->end > r->start) 98 attr = perf_header__find_attr(data->id, &self->header);
301 return 1; 99 if (attr)
302 100 hists = perf_session__hists_findnew(self, data->id, attr->type, attr->config);
303 return 0; 101 else
304} 102 hists = perf_session__hists_findnew(self, data->id, 0, 0);
305 103 if (hists == NULL)
306static size_t map__fprintf(struct map *self, FILE *fp) 104 goto out_free_syms;
307{ 105 he = __hists__add_entry(hists, al, parent, data->period);
308 return fprintf(fp, " %Lx-%Lx %Lx %s\n", 106 if (he == NULL)
309 self->start, self->end, self->pgoff, self->dso->name); 107 goto out_free_syms;
310} 108 err = 0;
311 109 if (symbol_conf.use_callchain) {
312 110 err = callchain_append(he->callchain, data->callchain, syms,
313struct thread { 111 data->period);
314 struct rb_node rb_node; 112 if (err)
315 struct list_head maps; 113 goto out_free_syms;
316 pid_t pid;
317 char *comm;
318};
319
320static struct thread *thread__new(pid_t pid)
321{
322 struct thread *self = malloc(sizeof(*self));
323
324 if (self != NULL) {
325 self->pid = pid;
326 self->comm = malloc(32);
327 if (self->comm)
328 snprintf(self->comm, 32, ":%d", self->pid);
329 INIT_LIST_HEAD(&self->maps);
330 } 114 }
331
332 return self;
333}
334
335static int thread__set_comm(struct thread *self, const char *comm)
336{
337 if (self->comm)
338 free(self->comm);
339 self->comm = strdup(comm);
340 return self->comm ? 0 : -ENOMEM;
341}
342
343static size_t thread__fprintf(struct thread *self, FILE *fp)
344{
345 struct map *pos;
346 size_t ret = fprintf(fp, "Thread %d %s\n", self->pid, self->comm);
347
348 list_for_each_entry(pos, &self->maps, node)
349 ret += map__fprintf(pos, fp);
350
351 return ret;
352}
353
354
355static struct rb_root threads;
356static struct thread *last_match;
357
358static struct thread *threads__findnew(pid_t pid)
359{
360 struct rb_node **p = &threads.rb_node;
361 struct rb_node *parent = NULL;
362 struct thread *th;
363
364 /* 115 /*
365 * Font-end cache - PID lookups come in blocks, 116 * Only in the newt browser we are doing integrated annotation,
366 * so most of the time we dont have to look up 117 * so we don't allocated the extra space needed because the stdio
367 * the full rbtree: 118 * code will not use it.
368 */ 119 */
369 if (last_match && last_match->pid == pid) 120 if (use_browser > 0)
370 return last_match; 121 err = hist_entry__inc_addr_samples(he, al->addr);
371 122out_free_syms:
372 while (*p != NULL) { 123 free(syms);
373 parent = *p; 124 return err;
374 th = rb_entry(parent, struct thread, rb_node);
375
376 if (th->pid == pid) {
377 last_match = th;
378 return th;
379 }
380
381 if (pid < th->pid)
382 p = &(*p)->rb_left;
383 else
384 p = &(*p)->rb_right;
385 }
386
387 th = thread__new(pid);
388 if (th != NULL) {
389 rb_link_node(&th->rb_node, parent, p);
390 rb_insert_color(&th->rb_node, &threads);
391 last_match = th;
392 }
393
394 return th;
395} 125}
396 126
397static void thread__insert_map(struct thread *self, struct map *map) 127static int add_event_total(struct perf_session *session,
128 struct sample_data *data,
129 struct perf_event_attr *attr)
398{ 130{
399 struct map *pos, *tmp; 131 struct hists *hists;
400 132
401 list_for_each_entry_safe(pos, tmp, &self->maps, node) { 133 if (attr)
402 if (map__overlap(pos, map)) { 134 hists = perf_session__hists_findnew(session, data->id,
403 list_del_init(&pos->node); 135 attr->type, attr->config);
404 /* XXX leaks dsos */ 136 else
405 free(pos); 137 hists = perf_session__hists_findnew(session, data->id, 0, 0);
406 }
407 }
408
409 list_add_tail(&map->node, &self->maps);
410}
411
412static int thread__fork(struct thread *self, struct thread *parent)
413{
414 struct map *map;
415 138
416 if (self->comm) 139 if (!hists)
417 free(self->comm);
418 self->comm = strdup(parent->comm);
419 if (!self->comm)
420 return -ENOMEM; 140 return -ENOMEM;
421 141
422 list_for_each_entry(map, &parent->maps, node) { 142 hists->stats.total_period += data->period;
423 struct map *new = map__clone(map); 143 /*
424 if (!new) 144 * FIXME: add_event_total should be moved from here to
425 return -ENOMEM; 145 * perf_session__process_event so that the proper hist is passed to
426 thread__insert_map(self, new); 146 * the event_op methods.
427 } 147 */
428 148 hists__inc_nr_events(hists, PERF_RECORD_SAMPLE);
149 session->hists.stats.total_period += data->period;
429 return 0; 150 return 0;
430} 151}
431 152
432static struct map *thread__find_map(struct thread *self, u64 ip) 153static int process_sample_event(event_t *event, struct sample_data *sample,
433{ 154 struct perf_session *session)
434 struct map *pos;
435
436 if (self == NULL)
437 return NULL;
438
439 list_for_each_entry(pos, &self->maps, node)
440 if (ip >= pos->start && ip <= pos->end)
441 return pos;
442
443 return NULL;
444}
445
446static size_t threads__fprintf(FILE *fp)
447{ 155{
448 size_t ret = 0; 156 struct addr_location al;
449 struct rb_node *nd; 157 struct perf_event_attr *attr;
450
451 for (nd = rb_first(&threads); nd; nd = rb_next(nd)) {
452 struct thread *pos = rb_entry(nd, struct thread, rb_node);
453
454 ret += thread__fprintf(pos, fp);
455 }
456
457 return ret;
458}
459
460/*
461 * histogram, sorted on item, collects counts
462 */
463
464static struct rb_root hist;
465
466struct hist_entry {
467 struct rb_node rb_node;
468
469 struct thread *thread;
470 struct map *map;
471 struct dso *dso;
472 struct symbol *sym;
473 struct symbol *parent;
474 u64 ip;
475 char level;
476
477 u64 count;
478};
479
480/*
481 * configurable sorting bits
482 */
483 158
484struct sort_entry { 159 if (event__preprocess_sample(event, session, &al, sample, NULL) < 0) {
485 struct list_head list; 160 fprintf(stderr, "problem processing %d event, skipping it.\n",
486 161 event->header.type);
487 char *header;
488
489 int64_t (*cmp)(struct hist_entry *, struct hist_entry *);
490 int64_t (*collapse)(struct hist_entry *, struct hist_entry *);
491 size_t (*print)(FILE *fp, struct hist_entry *);
492};
493
494static int64_t cmp_null(void *l, void *r)
495{
496 if (!l && !r)
497 return 0;
498 else if (!l)
499 return -1; 162 return -1;
500 else
501 return 1;
502}
503
504/* --sort pid */
505
506static int64_t
507sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)
508{
509 return right->thread->pid - left->thread->pid;
510}
511
512static size_t
513sort__thread_print(FILE *fp, struct hist_entry *self)
514{
515 return fprintf(fp, "%16s:%5d", self->thread->comm ?: "", self->thread->pid);
516}
517
518static struct sort_entry sort_thread = {
519 .header = " Command: Pid",
520 .cmp = sort__thread_cmp,
521 .print = sort__thread_print,
522};
523
524/* --sort comm */
525
526static int64_t
527sort__comm_cmp(struct hist_entry *left, struct hist_entry *right)
528{
529 return right->thread->pid - left->thread->pid;
530}
531
532static int64_t
533sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)
534{
535 char *comm_l = left->thread->comm;
536 char *comm_r = right->thread->comm;
537
538 if (!comm_l || !comm_r)
539 return cmp_null(comm_l, comm_r);
540
541 return strcmp(comm_l, comm_r);
542}
543
544static size_t
545sort__comm_print(FILE *fp, struct hist_entry *self)
546{
547 return fprintf(fp, "%16s", self->thread->comm);
548}
549
550static struct sort_entry sort_comm = {
551 .header = " Command",
552 .cmp = sort__comm_cmp,
553 .collapse = sort__comm_collapse,
554 .print = sort__comm_print,
555};
556
557/* --sort dso */
558
559static int64_t
560sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
561{
562 struct dso *dso_l = left->dso;
563 struct dso *dso_r = right->dso;
564
565 if (!dso_l || !dso_r)
566 return cmp_null(dso_l, dso_r);
567
568 return strcmp(dso_l->name, dso_r->name);
569}
570
571static size_t
572sort__dso_print(FILE *fp, struct hist_entry *self)
573{
574 if (self->dso)
575 return fprintf(fp, "%-25s", self->dso->name);
576
577 return fprintf(fp, "%016llx ", (u64)self->ip);
578}
579
580static struct sort_entry sort_dso = {
581 .header = "Shared Object ",
582 .cmp = sort__dso_cmp,
583 .print = sort__dso_print,
584};
585
586/* --sort symbol */
587
588static int64_t
589sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
590{
591 u64 ip_l, ip_r;
592
593 if (left->sym == right->sym)
594 return 0;
595
596 ip_l = left->sym ? left->sym->start : left->ip;
597 ip_r = right->sym ? right->sym->start : right->ip;
598
599 return (int64_t)(ip_r - ip_l);
600}
601
602static size_t
603sort__sym_print(FILE *fp, struct hist_entry *self)
604{
605 size_t ret = 0;
606
607 if (verbose)
608 ret += fprintf(fp, "%#018llx ", (u64)self->ip);
609
610 if (self->sym) {
611 ret += fprintf(fp, "[%c] %s",
612 self->dso == kernel_dso ? 'k' : '.', self->sym->name);
613 } else {
614 ret += fprintf(fp, "%#016llx", (u64)self->ip);
615 } 163 }
616 164
617 return ret; 165 if (al.filtered || (hide_unresolved && al.sym == NULL))
618}
619
620static struct sort_entry sort_sym = {
621 .header = "Symbol",
622 .cmp = sort__sym_cmp,
623 .print = sort__sym_print,
624};
625
626/* --sort parent */
627
628static int64_t
629sort__parent_cmp(struct hist_entry *left, struct hist_entry *right)
630{
631 struct symbol *sym_l = left->parent;
632 struct symbol *sym_r = right->parent;
633
634 if (!sym_l || !sym_r)
635 return cmp_null(sym_l, sym_r);
636
637 return strcmp(sym_l->name, sym_r->name);
638}
639
640static size_t
641sort__parent_print(FILE *fp, struct hist_entry *self)
642{
643 size_t ret = 0;
644
645 ret += fprintf(fp, "%-20s", self->parent ? self->parent->name : "[other]");
646
647 return ret;
648}
649
650static struct sort_entry sort_parent = {
651 .header = "Parent symbol ",
652 .cmp = sort__parent_cmp,
653 .print = sort__parent_print,
654};
655
656static int sort__need_collapse = 0;
657static int sort__has_parent = 0;
658
659struct sort_dimension {
660 char *name;
661 struct sort_entry *entry;
662 int taken;
663};
664
665static struct sort_dimension sort_dimensions[] = {
666 { .name = "pid", .entry = &sort_thread, },
667 { .name = "comm", .entry = &sort_comm, },
668 { .name = "dso", .entry = &sort_dso, },
669 { .name = "symbol", .entry = &sort_sym, },
670 { .name = "parent", .entry = &sort_parent, },
671};
672
673static LIST_HEAD(hist_entry__sort_list);
674
675static int sort_dimension__add(char *tok)
676{
677 int i;
678
679 for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) {
680 struct sort_dimension *sd = &sort_dimensions[i];
681
682 if (sd->taken)
683 continue;
684
685 if (strncasecmp(tok, sd->name, strlen(tok)))
686 continue;
687
688 if (sd->entry->collapse)
689 sort__need_collapse = 1;
690
691 if (sd->entry == &sort_parent) {
692 int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED);
693 if (ret) {
694 char err[BUFSIZ];
695
696 regerror(ret, &parent_regex, err, sizeof(err));
697 fprintf(stderr, "Invalid regex: %s\n%s",
698 parent_pattern, err);
699 exit(-1);
700 }
701 sort__has_parent = 1;
702 }
703
704 list_add_tail(&sd->entry->list, &hist_entry__sort_list);
705 sd->taken = 1;
706
707 return 0; 166 return 0;
708 }
709
710 return -ESRCH;
711}
712
713static int64_t
714hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)
715{
716 struct sort_entry *se;
717 int64_t cmp = 0;
718
719 list_for_each_entry(se, &hist_entry__sort_list, list) {
720 cmp = se->cmp(left, right);
721 if (cmp)
722 break;
723 }
724
725 return cmp;
726}
727
728static int64_t
729hist_entry__collapse(struct hist_entry *left, struct hist_entry *right)
730{
731 struct sort_entry *se;
732 int64_t cmp = 0;
733 167
734 list_for_each_entry(se, &hist_entry__sort_list, list) { 168 if (perf_session__add_hist_entry(session, &al, sample)) {
735 int64_t (*f)(struct hist_entry *, struct hist_entry *); 169 pr_debug("problem incrementing symbol period, skipping event\n");
736 170 return -1;
737 f = se->collapse ?: se->cmp;
738
739 cmp = f(left, right);
740 if (cmp)
741 break;
742 } 171 }
743 172
744 return cmp; 173 attr = perf_header__find_attr(sample->id, &session->header);
745}
746
747static size_t
748hist_entry__fprintf(FILE *fp, struct hist_entry *self, u64 total_samples)
749{
750 struct sort_entry *se;
751 size_t ret;
752
753 if (exclude_other && !self->parent)
754 return 0;
755
756 if (total_samples) {
757 double percent = self->count * 100.0 / total_samples;
758 char *color = PERF_COLOR_NORMAL;
759 174
760 /* 175 if (add_event_total(session, sample, attr)) {
761 * We color high-overhead entries in red, mid-overhead 176 pr_debug("problem adding event period\n");
762 * entries in green - and keep the low overhead places 177 return -1;
763 * normal:
764 */
765 if (percent >= 5.0) {
766 color = PERF_COLOR_RED;
767 } else {
768 if (percent >= 0.5)
769 color = PERF_COLOR_GREEN;
770 }
771
772 ret = color_fprintf(fp, color, " %6.2f%%",
773 (self->count * 100.0) / total_samples);
774 } else
775 ret = fprintf(fp, "%12Ld ", self->count);
776
777 list_for_each_entry(se, &hist_entry__sort_list, list) {
778 if (exclude_other && (se == &sort_parent))
779 continue;
780
781 fprintf(fp, " ");
782 ret += se->print(fp, self);
783 } 178 }
784 179
785 ret += fprintf(fp, "\n"); 180 return 0;
786
787 return ret;
788} 181}
789 182
790/* 183static int process_read_event(event_t *event, struct sample_data *sample __used,
791 * 184 struct perf_session *session __used)
792 */
793
794static struct symbol *
795resolve_symbol(struct thread *thread, struct map **mapp,
796 struct dso **dsop, u64 *ipp)
797{ 185{
798 struct dso *dso = dsop ? *dsop : NULL; 186 struct perf_event_attr *attr;
799 struct map *map = mapp ? *mapp : NULL;
800 uint64_t ip = *ipp;
801
802 if (!thread)
803 return NULL;
804
805 if (dso)
806 goto got_dso;
807 187
808 if (map) 188 attr = perf_header__find_attr(event->read.id, &session->header);
809 goto got_map;
810 189
811 map = thread__find_map(thread, ip); 190 if (show_threads) {
812 if (map != NULL) { 191 const char *name = attr ? __event_name(attr->type, attr->config)
813 if (mapp) 192 : "unknown";
814 *mapp = map; 193 perf_read_values_add_value(&show_threads_values,
815got_map: 194 event->read.pid, event->read.tid,
816 ip = map->map_ip(map, ip); 195 event->read.id,
817 *ipp = ip; 196 name,
818 197 event->read.value);
819 dso = map->dso;
820 } else {
821 /*
822 * If this is outside of all known maps,
823 * and is a negative address, try to look it
824 * up in the kernel dso, as it might be a
825 * vsyscall (which executes in user-mode):
826 */
827 if ((long long)ip < 0)
828 dso = kernel_dso;
829 } 198 }
830 dprintf(" ...... dso: %s\n", dso ? dso->name : "<not found>");
831
832 if (dsop)
833 *dsop = dso;
834
835 if (!dso)
836 return NULL;
837got_dso:
838 return dso->find_symbol(dso, ip);
839}
840 199
841static int call__match(struct symbol *sym) 200 dump_printf(": %d %d %s %Lu\n", event->read.pid, event->read.tid,
842{ 201 attr ? __event_name(attr->type, attr->config) : "FAIL",
843 if (sym->name && !regexec(&parent_regex, sym->name, 0, NULL, 0)) 202 event->read.value);
844 return 1;
845 203
846 return 0; 204 return 0;
847} 205}
848 206
849/* 207static int perf_session__setup_sample_type(struct perf_session *self)
850 * collect histogram counts
851 */
852
853static int
854hist_entry__add(struct thread *thread, struct map *map, struct dso *dso,
855 struct symbol *sym, u64 ip, struct ip_callchain *chain,
856 char level, u64 count)
857{ 208{
858 struct rb_node **p = &hist.rb_node; 209 if (!(self->sample_type & PERF_SAMPLE_CALLCHAIN)) {
859 struct rb_node *parent = NULL; 210 if (sort__has_parent) {
860 struct hist_entry *he; 211 fprintf(stderr, "selected --sort parent, but no"
861 struct hist_entry entry = { 212 " callchain data. Did you call"
862 .thread = thread, 213 " perf record without -g?\n");
863 .map = map, 214 return -EINVAL;
864 .dso = dso,
865 .sym = sym,
866 .ip = ip,
867 .level = level,
868 .count = count,
869 .parent = NULL,
870 };
871 int cmp;
872
873 if (sort__has_parent && chain) {
874 u64 context = PERF_CONTEXT_MAX;
875 int i;
876
877 for (i = 0; i < chain->nr; i++) {
878 u64 ip = chain->ips[i];
879 struct dso *dso = NULL;
880 struct symbol *sym;
881
882 if (ip >= PERF_CONTEXT_MAX) {
883 context = ip;
884 continue;
885 }
886
887 switch (context) {
888 case PERF_CONTEXT_KERNEL:
889 dso = kernel_dso;
890 break;
891 default:
892 break;
893 }
894
895 sym = resolve_symbol(thread, NULL, &dso, &ip);
896
897 if (sym && call__match(sym)) {
898 entry.parent = sym;
899 break;
900 }
901 } 215 }
902 } 216 if (symbol_conf.use_callchain) {
903 217 fprintf(stderr, "selected -g but no callchain data."
904 while (*p != NULL) { 218 " Did you call perf record without"
905 parent = *p; 219 " -g?\n");
906 he = rb_entry(parent, struct hist_entry, rb_node); 220 return -1;
907
908 cmp = hist_entry__cmp(&entry, he);
909
910 if (!cmp) {
911 he->count += count;
912 return 0;
913 } 221 }
914 222 } else if (!dont_use_callchains && callchain_param.mode != CHAIN_NONE &&
915 if (cmp < 0) 223 !symbol_conf.use_callchain) {
916 p = &(*p)->rb_left; 224 symbol_conf.use_callchain = true;
917 else 225 if (register_callchain_param(&callchain_param) < 0) {
918 p = &(*p)->rb_right; 226 fprintf(stderr, "Can't register callchain"
227 " params\n");
228 return -EINVAL;
229 }
919 } 230 }
920 231
921 he = malloc(sizeof(*he));
922 if (!he)
923 return -ENOMEM;
924 *he = entry;
925 rb_link_node(&he->rb_node, parent, p);
926 rb_insert_color(&he->rb_node, &hist);
927
928 return 0; 232 return 0;
929} 233}
930 234
931static void hist_entry__free(struct hist_entry *he) 235static struct perf_event_ops event_ops = {
932{ 236 .sample = process_sample_event,
933 free(he); 237 .mmap = event__process_mmap,
934} 238 .comm = event__process_comm,
935 239 .exit = event__process_task,
936/* 240 .fork = event__process_task,
937 * collapse the histogram 241 .lost = event__process_lost,
938 */ 242 .read = process_read_event,
939 243 .attr = event__process_attr,
940static struct rb_root collapse_hists; 244 .event_type = event__process_event_type,
941 245 .tracing_data = event__process_tracing_data,
942static void collapse__insert_entry(struct hist_entry *he) 246 .build_id = event__process_build_id,
943{ 247 .ordered_samples = true,
944 struct rb_node **p = &collapse_hists.rb_node; 248 .ordering_requires_timestamps = true,
945 struct rb_node *parent = NULL; 249};
946 struct hist_entry *iter;
947 int64_t cmp;
948
949 while (*p != NULL) {
950 parent = *p;
951 iter = rb_entry(parent, struct hist_entry, rb_node);
952
953 cmp = hist_entry__collapse(iter, he);
954
955 if (!cmp) {
956 iter->count += he->count;
957 hist_entry__free(he);
958 return;
959 }
960
961 if (cmp < 0)
962 p = &(*p)->rb_left;
963 else
964 p = &(*p)->rb_right;
965 }
966 250
967 rb_link_node(&he->rb_node, parent, p); 251extern volatile int session_done;
968 rb_insert_color(&he->rb_node, &collapse_hists);
969}
970 252
971static void collapse__resort(void) 253static void sig_handler(int sig __used)
972{ 254{
973 struct rb_node *next; 255 session_done = 1;
974 struct hist_entry *n;
975
976 if (!sort__need_collapse)
977 return;
978
979 next = rb_first(&hist);
980 while (next) {
981 n = rb_entry(next, struct hist_entry, rb_node);
982 next = rb_next(&n->rb_node);
983
984 rb_erase(&n->rb_node, &hist);
985 collapse__insert_entry(n);
986 }
987} 256}
988 257
989/* 258static size_t hists__fprintf_nr_sample_events(struct hists *self,
990 * reverse the map, sort on count. 259 const char *evname, FILE *fp)
991 */
992
993static struct rb_root output_hists;
994
995static void output__insert_entry(struct hist_entry *he)
996{ 260{
997 struct rb_node **p = &output_hists.rb_node; 261 size_t ret;
998 struct rb_node *parent = NULL; 262 char unit;
999 struct hist_entry *iter; 263 unsigned long nr_events = self->stats.nr_events[PERF_RECORD_SAMPLE];
1000
1001 while (*p != NULL) {
1002 parent = *p;
1003 iter = rb_entry(parent, struct hist_entry, rb_node);
1004
1005 if (he->count > iter->count)
1006 p = &(*p)->rb_left;
1007 else
1008 p = &(*p)->rb_right;
1009 }
1010 264
1011 rb_link_node(&he->rb_node, parent, p); 265 nr_events = convert_unit(nr_events, &unit);
1012 rb_insert_color(&he->rb_node, &output_hists); 266 ret = fprintf(fp, "# Events: %lu%c", nr_events, unit);
267 if (evname != NULL)
268 ret += fprintf(fp, " %s", evname);
269 return ret + fprintf(fp, "\n#\n");
1013} 270}
1014 271
1015static void output__resort(void) 272static int hists__tty_browse_tree(struct rb_root *tree, const char *help)
1016{ 273{
1017 struct rb_node *next; 274 struct rb_node *next = rb_first(tree);
1018 struct hist_entry *n;
1019 struct rb_root *tree = &hist;
1020
1021 if (sort__need_collapse)
1022 tree = &collapse_hists;
1023
1024 next = rb_first(tree);
1025 275
1026 while (next) { 276 while (next) {
1027 n = rb_entry(next, struct hist_entry, rb_node); 277 struct hists *hists = rb_entry(next, struct hists, rb_node);
1028 next = rb_next(&n->rb_node); 278 const char *evname = NULL;
1029 279
1030 rb_erase(&n->rb_node, tree); 280 if (rb_first(&hists->entries) != rb_last(&hists->entries))
1031 output__insert_entry(n); 281 evname = __event_name(hists->type, hists->config);
1032 }
1033}
1034 282
1035static size_t output__fprintf(FILE *fp, u64 total_samples) 283 hists__fprintf_nr_sample_events(hists, evname, stdout);
1036{ 284 hists__fprintf(hists, NULL, false, stdout);
1037 struct hist_entry *pos; 285 fprintf(stdout, "\n\n");
1038 struct sort_entry *se; 286 next = rb_next(&hists->rb_node);
1039 struct rb_node *nd;
1040 size_t ret = 0;
1041
1042 fprintf(fp, "\n");
1043 fprintf(fp, "#\n");
1044 fprintf(fp, "# (%Ld samples)\n", (u64)total_samples);
1045 fprintf(fp, "#\n");
1046
1047 fprintf(fp, "# Overhead");
1048 list_for_each_entry(se, &hist_entry__sort_list, list) {
1049 if (exclude_other && (se == &sort_parent))
1050 continue;
1051 fprintf(fp, " %s", se->header);
1052 }
1053 fprintf(fp, "\n");
1054
1055 fprintf(fp, "# ........");
1056 list_for_each_entry(se, &hist_entry__sort_list, list) {
1057 int i;
1058
1059 if (exclude_other && (se == &sort_parent))
1060 continue;
1061
1062 fprintf(fp, " ");
1063 for (i = 0; i < strlen(se->header); i++)
1064 fprintf(fp, ".");
1065 }
1066 fprintf(fp, "\n");
1067
1068 fprintf(fp, "#\n");
1069
1070 for (nd = rb_first(&output_hists); nd; nd = rb_next(nd)) {
1071 pos = rb_entry(nd, struct hist_entry, rb_node);
1072 ret += hist_entry__fprintf(fp, pos, total_samples);
1073 } 287 }
1074 288
1075 if (sort_order == default_sort_order && 289 if (sort_order == default_sort_order &&
1076 parent_pattern == default_parent_pattern) { 290 parent_pattern == default_parent_pattern) {
1077 fprintf(fp, "#\n"); 291 fprintf(stdout, "#\n# (%s)\n#\n", help);
1078 fprintf(fp, "# (For more details, try: perf report --sort comm,dso,symbol)\n"); 292
1079 fprintf(fp, "#\n"); 293 if (show_threads) {
1080 } 294 bool style = !strcmp(pretty_printing_style, "raw");
1081 fprintf(fp, "\n"); 295 perf_read_values_display(stdout, &show_threads_values,
1082 296 style);
1083 return ret; 297 perf_read_values_destroy(&show_threads_values);
1084}
1085
1086static void register_idle_thread(void)
1087{
1088 struct thread *thread = threads__findnew(0);
1089
1090 if (thread == NULL ||
1091 thread__set_comm(thread, "[idle]")) {
1092 fprintf(stderr, "problem inserting idle task.\n");
1093 exit(-1);
1094 }
1095}
1096
1097static unsigned long total = 0,
1098 total_mmap = 0,
1099 total_comm = 0,
1100 total_fork = 0,
1101 total_unknown = 0,
1102 total_lost = 0;
1103
1104static int validate_chain(struct ip_callchain *chain, event_t *event)
1105{
1106 unsigned int chain_size;
1107
1108 chain_size = event->header.size;
1109 chain_size -= (unsigned long)&event->ip.__more_data - (unsigned long)event;
1110
1111 if (chain->nr*sizeof(u64) > chain_size)
1112 return -1;
1113
1114 return 0;
1115}
1116
1117static int
1118process_overflow_event(event_t *event, unsigned long offset, unsigned long head)
1119{
1120 char level;
1121 int show = 0;
1122 struct dso *dso = NULL;
1123 struct thread *thread = threads__findnew(event->ip.pid);
1124 u64 ip = event->ip.ip;
1125 u64 period = 1;
1126 struct map *map = NULL;
1127 void *more_data = event->ip.__more_data;
1128 struct ip_callchain *chain = NULL;
1129
1130 if (event->header.type & PERF_SAMPLE_PERIOD) {
1131 period = *(u64 *)more_data;
1132 more_data += sizeof(u64);
1133 }
1134
1135 dprintf("%p [%p]: PERF_EVENT (IP, %d): %d: %p period: %Ld\n",
1136 (void *)(offset + head),
1137 (void *)(long)(event->header.size),
1138 event->header.misc,
1139 event->ip.pid,
1140 (void *)(long)ip,
1141 (long long)period);
1142
1143 if (event->header.type & PERF_SAMPLE_CALLCHAIN) {
1144 int i;
1145
1146 chain = (void *)more_data;
1147
1148 dprintf("... chain: nr:%Lu\n", chain->nr);
1149
1150 if (validate_chain(chain, event) < 0) {
1151 eprintf("call-chain problem with event, skipping it.\n");
1152 return 0;
1153 }
1154
1155 if (dump_trace) {
1156 for (i = 0; i < chain->nr; i++)
1157 dprintf("..... %2d: %016Lx\n", i, chain->ips[i]);
1158 } 298 }
1159 } 299 }
1160 300
1161 dprintf(" ... thread: %s:%d\n", thread->comm, thread->pid);
1162
1163 if (thread == NULL) {
1164 eprintf("problem processing %d event, skipping it.\n",
1165 event->header.type);
1166 return -1;
1167 }
1168
1169 if (event->header.misc & PERF_EVENT_MISC_KERNEL) {
1170 show = SHOW_KERNEL;
1171 level = 'k';
1172
1173 dso = kernel_dso;
1174
1175 dprintf(" ...... dso: %s\n", dso->name);
1176
1177 } else if (event->header.misc & PERF_EVENT_MISC_USER) {
1178
1179 show = SHOW_USER;
1180 level = '.';
1181
1182 } else {
1183 show = SHOW_HV;
1184 level = 'H';
1185 dprintf(" ...... dso: [hypervisor]\n");
1186 }
1187
1188 if (show & show_mask) {
1189 struct symbol *sym = resolve_symbol(thread, &map, &dso, &ip);
1190
1191 if (hist_entry__add(thread, map, dso, sym, ip, chain, level, period)) {
1192 eprintf("problem incrementing symbol count, skipping event\n");
1193 return -1;
1194 }
1195 }
1196 total += period;
1197
1198 return 0; 301 return 0;
1199} 302}
1200 303
1201static int 304static int __cmd_report(void)
1202process_mmap_event(event_t *event, unsigned long offset, unsigned long head)
1203{
1204 struct thread *thread = threads__findnew(event->mmap.pid);
1205 struct map *map = map__new(&event->mmap);
1206
1207 dprintf("%p [%p]: PERF_EVENT_MMAP %d: [%p(%p) @ %p]: %s\n",
1208 (void *)(offset + head),
1209 (void *)(long)(event->header.size),
1210 event->mmap.pid,
1211 (void *)(long)event->mmap.start,
1212 (void *)(long)event->mmap.len,
1213 (void *)(long)event->mmap.pgoff,
1214 event->mmap.filename);
1215
1216 if (thread == NULL || map == NULL) {
1217 dprintf("problem processing PERF_EVENT_MMAP, skipping event.\n");
1218 return 0;
1219 }
1220
1221 thread__insert_map(thread, map);
1222 total_mmap++;
1223
1224 return 0;
1225}
1226
1227static int
1228process_comm_event(event_t *event, unsigned long offset, unsigned long head)
1229{ 305{
1230 struct thread *thread = threads__findnew(event->comm.pid); 306 int ret = -EINVAL;
307 struct perf_session *session;
308 struct rb_node *next;
309 const char *help = "For a higher level overview, try: perf report --sort comm,dso";
1231 310
1232 dprintf("%p [%p]: PERF_EVENT_COMM: %s:%d\n", 311 signal(SIGINT, sig_handler);
1233 (void *)(offset + head),
1234 (void *)(long)(event->header.size),
1235 event->comm.comm, event->comm.pid);
1236 312
1237 if (thread == NULL || 313 session = perf_session__new(input_name, O_RDONLY, force, false, &event_ops);
1238 thread__set_comm(thread, event->comm.comm)) { 314 if (session == NULL)
1239 dprintf("problem processing PERF_EVENT_COMM, skipping event.\n"); 315 return -ENOMEM;
1240 return -1;
1241 }
1242 total_comm++;
1243 316
1244 return 0; 317 if (show_threads)
1245} 318 perf_read_values_init(&show_threads_values);
1246 319
1247static int 320 ret = perf_session__setup_sample_type(session);
1248process_fork_event(event_t *event, unsigned long offset, unsigned long head) 321 if (ret)
1249{ 322 goto out_delete;
1250 struct thread *thread = threads__findnew(event->fork.pid);
1251 struct thread *parent = threads__findnew(event->fork.ppid);
1252 323
1253 dprintf("%p [%p]: PERF_EVENT_FORK: %d:%d\n", 324 ret = perf_session__process_events(session, &event_ops);
1254 (void *)(offset + head), 325 if (ret)
1255 (void *)(long)(event->header.size), 326 goto out_delete;
1256 event->fork.pid, event->fork.ppid);
1257 327
1258 if (!thread || !parent || thread__fork(thread, parent)) { 328 if (dump_trace) {
1259 dprintf("problem processing PERF_EVENT_FORK, skipping event.\n"); 329 perf_session__fprintf_nr_events(session, stdout);
1260 return -1; 330 goto out_delete;
1261 } 331 }
1262 total_fork++;
1263
1264 return 0;
1265}
1266
1267static int
1268process_period_event(event_t *event, unsigned long offset, unsigned long head)
1269{
1270 dprintf("%p [%p]: PERF_EVENT_PERIOD: time:%Ld, id:%Ld: period:%Ld\n",
1271 (void *)(offset + head),
1272 (void *)(long)(event->header.size),
1273 event->period.time,
1274 event->period.id,
1275 event->period.sample_period);
1276
1277 return 0;
1278}
1279
1280static int
1281process_lost_event(event_t *event, unsigned long offset, unsigned long head)
1282{
1283 dprintf("%p [%p]: PERF_EVENT_LOST: id:%Ld: lost:%Ld\n",
1284 (void *)(offset + head),
1285 (void *)(long)(event->header.size),
1286 event->lost.id,
1287 event->lost.lost);
1288 332
1289 total_lost += event->lost.lost; 333 if (verbose > 3)
334 perf_session__fprintf(session, stdout);
1290 335
1291 return 0; 336 if (verbose > 2)
1292} 337 perf_session__fprintf_dsos(session, stdout);
1293 338
1294static void trace_event(event_t *event) 339 next = rb_first(&session->hists_tree);
1295{ 340 while (next) {
1296 unsigned char *raw_event = (void *)event; 341 struct hists *hists;
1297 char *color = PERF_COLOR_BLUE;
1298 int i, j;
1299
1300 if (!dump_trace)
1301 return;
1302 342
1303 dprintf("."); 343 hists = rb_entry(next, struct hists, rb_node);
1304 cdprintf("\n. ... raw event: size %d bytes\n", event->header.size); 344 hists__collapse_resort(hists);
345 hists__output_resort(hists);
346 next = rb_next(&hists->rb_node);
347 }
1305 348
1306 for (i = 0; i < event->header.size; i++) { 349 if (use_browser > 0)
1307 if ((i & 15) == 0) { 350 hists__tui_browse_tree(&session->hists_tree, help);
1308 dprintf("."); 351 else
1309 cdprintf(" %04x: ", i); 352 hists__tty_browse_tree(&session->hists_tree, help);
1310 }
1311 353
1312 cdprintf(" %02x", raw_event[i]); 354out_delete:
1313 355 /*
1314 if (((i & 15) == 15) || i == event->header.size-1) { 356 * Speed up the exit process, for large files this can
1315 cdprintf(" "); 357 * take quite a while.
1316 for (j = 0; j < 15-(i & 15); j++) 358 *
1317 cdprintf(" "); 359 * XXX Enable this when using valgrind or if we ever
1318 for (j = 0; j < (i & 15); j++) { 360 * librarize this command.
1319 if (isprint(raw_event[i-15+j])) 361 *
1320 cdprintf("%c", raw_event[i-15+j]); 362 * Also experiment with obstacks to see how much speed
1321 else 363 * up we'll get here.
1322 cdprintf("."); 364 *
1323 } 365 * perf_session__delete(session);
1324 cdprintf("\n"); 366 */
1325 } 367 return ret;
1326 }
1327 dprintf(".\n");
1328} 368}
1329 369
1330static int 370static int
1331process_event(event_t *event, unsigned long offset, unsigned long head) 371parse_callchain_opt(const struct option *opt __used, const char *arg,
372 int unset)
1332{ 373{
1333 trace_event(event); 374 char *tok, *tok2;
1334 375 char *endptr;
1335 if (event->header.misc & PERF_EVENT_MISC_OVERFLOW)
1336 return process_overflow_event(event, offset, head);
1337
1338 switch (event->header.type) {
1339 case PERF_EVENT_MMAP:
1340 return process_mmap_event(event, offset, head);
1341
1342 case PERF_EVENT_COMM:
1343 return process_comm_event(event, offset, head);
1344
1345 case PERF_EVENT_FORK:
1346 return process_fork_event(event, offset, head);
1347
1348 case PERF_EVENT_PERIOD:
1349 return process_period_event(event, offset, head);
1350
1351 case PERF_EVENT_LOST:
1352 return process_lost_event(event, offset, head);
1353 376
1354 /* 377 /*
1355 * We dont process them right now but they are fine: 378 * --no-call-graph
1356 */ 379 */
1357 380 if (unset) {
1358 case PERF_EVENT_THROTTLE: 381 dont_use_callchains = true;
1359 case PERF_EVENT_UNTHROTTLE:
1360 return 0; 382 return 0;
1361
1362 default:
1363 return -1;
1364 }
1365
1366 return 0;
1367}
1368
1369static struct perf_file_header file_header;
1370
1371static int __cmd_report(void)
1372{
1373 int ret, rc = EXIT_FAILURE;
1374 unsigned long offset = 0;
1375 unsigned long head = sizeof(file_header);
1376 struct stat stat;
1377 event_t *event;
1378 uint32_t size;
1379 char *buf;
1380
1381 register_idle_thread();
1382
1383 input = open(input_name, O_RDONLY);
1384 if (input < 0) {
1385 fprintf(stderr, " failed to open file: %s", input_name);
1386 if (!strcmp(input_name, "perf.data"))
1387 fprintf(stderr, " (try 'perf record' first)");
1388 fprintf(stderr, "\n");
1389 exit(-1);
1390 } 383 }
1391 384
1392 ret = fstat(input, &stat); 385 symbol_conf.use_callchain = true;
1393 if (ret < 0) {
1394 perror("failed to stat file");
1395 exit(-1);
1396 }
1397 386
1398 if (!stat.st_size) { 387 if (!arg)
1399 fprintf(stderr, "zero-sized file, nothing to do!\n"); 388 return 0;
1400 exit(0);
1401 }
1402
1403 if (read(input, &file_header, sizeof(file_header)) == -1) {
1404 perror("failed to read file headers");
1405 exit(-1);
1406 }
1407
1408 if (sort__has_parent &&
1409 !(file_header.sample_type & PERF_SAMPLE_CALLCHAIN)) {
1410 fprintf(stderr, "selected --sort parent, but no callchain data\n");
1411 exit(-1);
1412 }
1413
1414 if (load_kernel() < 0) {
1415 perror("failed to load kernel symbols");
1416 return EXIT_FAILURE;
1417 }
1418 389
1419 if (!full_paths) { 390 tok = strtok((char *)arg, ",");
1420 if (getcwd(__cwd, sizeof(__cwd)) == NULL) { 391 if (!tok)
1421 perror("failed to get the current directory"); 392 return -1;
1422 return EXIT_FAILURE;
1423 }
1424 cwdlen = strlen(cwd);
1425 } else {
1426 cwd = NULL;
1427 cwdlen = 0;
1428 }
1429remap:
1430 buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ,
1431 MAP_SHARED, input, offset);
1432 if (buf == MAP_FAILED) {
1433 perror("failed to mmap file");
1434 exit(-1);
1435 }
1436 393
1437more: 394 /* get the output mode */
1438 event = (event_t *)(buf + head); 395 if (!strncmp(tok, "graph", strlen(arg)))
396 callchain_param.mode = CHAIN_GRAPH_ABS;
1439 397
1440 size = event->header.size; 398 else if (!strncmp(tok, "flat", strlen(arg)))
1441 if (!size) 399 callchain_param.mode = CHAIN_FLAT;
1442 size = 8;
1443 400
1444 if (head + event->header.size >= page_size * mmap_window) { 401 else if (!strncmp(tok, "fractal", strlen(arg)))
1445 unsigned long shift = page_size * (head / page_size); 402 callchain_param.mode = CHAIN_GRAPH_REL;
1446 int ret;
1447 403
1448 ret = munmap(buf, page_size * mmap_window); 404 else if (!strncmp(tok, "none", strlen(arg))) {
1449 assert(ret == 0); 405 callchain_param.mode = CHAIN_NONE;
406 symbol_conf.use_callchain = false;
1450 407
1451 offset += shift; 408 return 0;
1452 head -= shift;
1453 goto remap;
1454 } 409 }
1455 410
1456 size = event->header.size; 411 else
1457 412 return -1;
1458 dprintf("\n%p [%p]: event: %d\n",
1459 (void *)(offset + head),
1460 (void *)(long)event->header.size,
1461 event->header.type);
1462
1463 if (!size || process_event(event, offset, head) < 0) {
1464
1465 dprintf("%p [%p]: skipping unknown header type: %d\n",
1466 (void *)(offset + head),
1467 (void *)(long)(event->header.size),
1468 event->header.type);
1469 413
1470 total_unknown++; 414 /* get the min percentage */
415 tok = strtok(NULL, ",");
416 if (!tok)
417 goto setup;
1471 418
1472 /* 419 tok2 = strtok(NULL, ",");
1473 * assume we lost track of the stream, check alignment, and 420 callchain_param.min_percent = strtod(tok, &endptr);
1474 * increment a single u64 in the hope to catch on again 'soon'. 421 if (tok == endptr)
1475 */ 422 return -1;
1476
1477 if (unlikely(head & 7))
1478 head &= ~7ULL;
1479 423
1480 size = 8; 424 if (tok2)
425 callchain_param.print_limit = strtod(tok2, &endptr);
426setup:
427 if (register_callchain_param(&callchain_param) < 0) {
428 fprintf(stderr, "Can't register callchain params\n");
429 return -1;
1481 } 430 }
1482 431 return 0;
1483 head += size;
1484
1485 if (offset + head >= sizeof(file_header) + file_header.data_size)
1486 goto done;
1487
1488 if (offset + head < stat.st_size)
1489 goto more;
1490
1491done:
1492 rc = EXIT_SUCCESS;
1493 close(input);
1494
1495 dprintf(" IP events: %10ld\n", total);
1496 dprintf(" mmap events: %10ld\n", total_mmap);
1497 dprintf(" comm events: %10ld\n", total_comm);
1498 dprintf(" fork events: %10ld\n", total_fork);
1499 dprintf(" lost events: %10ld\n", total_lost);
1500 dprintf(" unknown events: %10ld\n", total_unknown);
1501
1502 if (dump_trace)
1503 return 0;
1504
1505 if (verbose >= 3)
1506 threads__fprintf(stdout);
1507
1508 if (verbose >= 2)
1509 dsos__fprintf(stdout);
1510
1511 collapse__resort();
1512 output__resort();
1513 output__fprintf(stdout, total);
1514
1515 return rc;
1516} 432}
1517 433
1518static const char * const report_usage[] = { 434static const char * const report_usage[] = {
@@ -1523,51 +439,103 @@ static const char * const report_usage[] = {
1523static const struct option options[] = { 439static const struct option options[] = {
1524 OPT_STRING('i', "input", &input_name, "file", 440 OPT_STRING('i', "input", &input_name, "file",
1525 "input file name"), 441 "input file name"),
1526 OPT_BOOLEAN('v', "verbose", &verbose, 442 OPT_INCR('v', "verbose", &verbose,
1527 "be more verbose (show symbol address, etc)"), 443 "be more verbose (show symbol address, etc)"),
1528 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, 444 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
1529 "dump raw trace in ASCII"), 445 "dump raw trace in ASCII"),
1530 OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"), 446 OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
447 "file", "vmlinux pathname"),
448 OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name,
449 "file", "kallsyms pathname"),
450 OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
451 OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules,
452 "load module symbols - WARNING: use only with -k and LIVE kernel"),
453 OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples,
454 "Show a column with the number of samples"),
455 OPT_BOOLEAN('T', "threads", &show_threads,
456 "Show per-thread event counters"),
457 OPT_STRING(0, "pretty", &pretty_printing_style, "key",
458 "pretty printing style key: normal raw"),
459 OPT_BOOLEAN(0, "tui", &use_tui, "Use the TUI interface"),
460 OPT_BOOLEAN(0, "stdio", &use_stdio, "Use the stdio interface"),
1531 OPT_STRING('s', "sort", &sort_order, "key[,key2...]", 461 OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
1532 "sort by key(s): pid, comm, dso, symbol, parent"), 462 "sort by key(s): pid, comm, dso, symbol, parent"),
1533 OPT_BOOLEAN('P', "full-paths", &full_paths, 463 OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization,
1534 "Don't shorten the pathnames taking into account the cwd"), 464 "Show sample percentage for different cpu modes"),
1535 OPT_STRING('p', "parent", &parent_pattern, "regex", 465 OPT_STRING('p', "parent", &parent_pattern, "regex",
1536 "regex filter to identify parent, see: '--sort parent'"), 466 "regex filter to identify parent, see: '--sort parent'"),
1537 OPT_BOOLEAN('x', "exclude-other", &exclude_other, 467 OPT_BOOLEAN('x', "exclude-other", &symbol_conf.exclude_other,
1538 "Only display entries with parent-match"), 468 "Only display entries with parent-match"),
469 OPT_CALLBACK_DEFAULT('g', "call-graph", NULL, "output_type,min_percent",
470 "Display callchains using output_type (graph, flat, fractal, or none) and min percent threshold. "
471 "Default: fractal,0.5", &parse_callchain_opt, callchain_default_opt),
472 OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]",
473 "only consider symbols in these dsos"),
474 OPT_STRING('C', "comms", &symbol_conf.comm_list_str, "comm[,comm...]",
475 "only consider symbols in these comms"),
476 OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]",
477 "only consider these symbols"),
478 OPT_STRING('w', "column-widths", &symbol_conf.col_width_list_str,
479 "width[,width...]",
480 "don't try to adjust column width, use these fixed values"),
481 OPT_STRING('t', "field-separator", &symbol_conf.field_sep, "separator",
482 "separator for columns, no spaces will be added between "
483 "columns '.' is reserved."),
484 OPT_BOOLEAN('U', "hide-unresolved", &hide_unresolved,
485 "Only display entries resolved to a symbol"),
486 OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
487 "Look for files with symbols relative to this directory"),
1539 OPT_END() 488 OPT_END()
1540}; 489};
1541 490
1542static void setup_sorting(void) 491int cmd_report(int argc, const char **argv, const char *prefix __used)
1543{ 492{
1544 char *tmp, *tok, *str = strdup(sort_order); 493 argc = parse_options(argc, argv, options, report_usage, 0);
494
495 if (use_stdio)
496 use_browser = 0;
497 else if (use_tui)
498 use_browser = 1;
1545 499
1546 for (tok = strtok_r(str, ", ", &tmp); 500 if (strcmp(input_name, "-") != 0)
1547 tok; tok = strtok_r(NULL, ", ", &tmp)) { 501 setup_browser();
1548 if (sort_dimension__add(tok) < 0) { 502 else
1549 error("Unknown --sort key: `%s'", tok); 503 use_browser = 0;
1550 usage_with_options(report_usage, options); 504 /*
505 * Only in the newt browser we are doing integrated annotation,
506 * so don't allocate extra space that won't be used in the stdio
507 * implementation.
508 */
509 if (use_browser > 0) {
510 symbol_conf.priv_size = sizeof(struct sym_priv);
511 /*
512 * For searching by name on the "Browse map details".
513 * providing it only in verbose mode not to bloat too
514 * much struct symbol.
515 */
516 if (verbose) {
517 /*
518 * XXX: Need to provide a less kludgy way to ask for
519 * more space per symbol, the u32 is for the index on
520 * the ui browser.
521 * See symbol__browser_index.
522 */
523 symbol_conf.priv_size += sizeof(u32);
524 symbol_conf.sort_by_name = true;
1551 } 525 }
1552 } 526 }
1553 527
1554 free(str); 528 if (symbol__init() < 0)
1555} 529 return -1;
1556
1557int cmd_report(int argc, const char **argv, const char *prefix)
1558{
1559 symbol__init();
1560
1561 page_size = getpagesize();
1562
1563 argc = parse_options(argc, argv, options, report_usage, 0);
1564 530
1565 setup_sorting(); 531 setup_sorting(report_usage, options);
1566 532
1567 if (parent_pattern != default_parent_pattern) 533 if (parent_pattern != default_parent_pattern) {
1568 sort_dimension__add("parent"); 534 if (sort_dimension__add("parent") < 0)
1569 else 535 return -1;
1570 exclude_other = 0; 536 sort_parent.elide = 1;
537 } else
538 symbol_conf.exclude_other = false;
1571 539
1572 /* 540 /*
1573 * Any (unrecognized) arguments left? 541 * Any (unrecognized) arguments left?
@@ -1575,7 +543,9 @@ int cmd_report(int argc, const char **argv, const char *prefix)
1575 if (argc) 543 if (argc)
1576 usage_with_options(report_usage, options); 544 usage_with_options(report_usage, options);
1577 545
1578 setup_pager(); 546 sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso", stdout);
547 sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, "comm", stdout);
548 sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list, "symbol", stdout);
1579 549
1580 return __cmd_report(); 550 return __cmd_report();
1581} 551}
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c
new file mode 100644
index 00000000000..abd4b8497bc
--- /dev/null
+++ b/tools/perf/builtin-sched.c
@@ -0,0 +1,1921 @@
1#include "builtin.h"
2#include "perf.h"
3
4#include "util/util.h"
5#include "util/cache.h"
6#include "util/symbol.h"
7#include "util/thread.h"
8#include "util/header.h"
9#include "util/session.h"
10
11#include "util/parse-options.h"
12#include "util/trace-event.h"
13
14#include "util/debug.h"
15
16#include <sys/prctl.h>
17
18#include <semaphore.h>
19#include <pthread.h>
20#include <math.h>
21
22static char const *input_name = "perf.data";
23
24static char default_sort_order[] = "avg, max, switch, runtime";
25static const char *sort_order = default_sort_order;
26
27static int profile_cpu = -1;
28
29#define PR_SET_NAME 15 /* Set process name */
30#define MAX_CPUS 4096
31
32static u64 run_measurement_overhead;
33static u64 sleep_measurement_overhead;
34
35#define COMM_LEN 20
36#define SYM_LEN 129
37
38#define MAX_PID 65536
39
40static unsigned long nr_tasks;
41
42struct sched_atom;
43
44struct task_desc {
45 unsigned long nr;
46 unsigned long pid;
47 char comm[COMM_LEN];
48
49 unsigned long nr_events;
50 unsigned long curr_event;
51 struct sched_atom **atoms;
52
53 pthread_t thread;
54 sem_t sleep_sem;
55
56 sem_t ready_for_work;
57 sem_t work_done_sem;
58
59 u64 cpu_usage;
60};
61
62enum sched_event_type {
63 SCHED_EVENT_RUN,
64 SCHED_EVENT_SLEEP,
65 SCHED_EVENT_WAKEUP,
66 SCHED_EVENT_MIGRATION,
67};
68
69struct sched_atom {
70 enum sched_event_type type;
71 int specific_wait;
72 u64 timestamp;
73 u64 duration;
74 unsigned long nr;
75 sem_t *wait_sem;
76 struct task_desc *wakee;
77};
78
79static struct task_desc *pid_to_task[MAX_PID];
80
81static struct task_desc **tasks;
82
83static pthread_mutex_t start_work_mutex = PTHREAD_MUTEX_INITIALIZER;
84static u64 start_time;
85
86static pthread_mutex_t work_done_wait_mutex = PTHREAD_MUTEX_INITIALIZER;
87
88static unsigned long nr_run_events;
89static unsigned long nr_sleep_events;
90static unsigned long nr_wakeup_events;
91
92static unsigned long nr_sleep_corrections;
93static unsigned long nr_run_events_optimized;
94
95static unsigned long targetless_wakeups;
96static unsigned long multitarget_wakeups;
97
98static u64 cpu_usage;
99static u64 runavg_cpu_usage;
100static u64 parent_cpu_usage;
101static u64 runavg_parent_cpu_usage;
102
103static unsigned long nr_runs;
104static u64 sum_runtime;
105static u64 sum_fluct;
106static u64 run_avg;
107
108static unsigned int replay_repeat = 10;
109static unsigned long nr_timestamps;
110static unsigned long nr_unordered_timestamps;
111static unsigned long nr_state_machine_bugs;
112static unsigned long nr_context_switch_bugs;
113static unsigned long nr_events;
114static unsigned long nr_lost_chunks;
115static unsigned long nr_lost_events;
116
117#define TASK_STATE_TO_CHAR_STR "RSDTtZX"
118
119enum thread_state {
120 THREAD_SLEEPING = 0,
121 THREAD_WAIT_CPU,
122 THREAD_SCHED_IN,
123 THREAD_IGNORE
124};
125
126struct work_atom {
127 struct list_head list;
128 enum thread_state state;
129 u64 sched_out_time;
130 u64 wake_up_time;
131 u64 sched_in_time;
132 u64 runtime;
133};
134
135struct work_atoms {
136 struct list_head work_list;
137 struct thread *thread;
138 struct rb_node node;
139 u64 max_lat;
140 u64 max_lat_at;
141 u64 total_lat;
142 u64 nb_atoms;
143 u64 total_runtime;
144};
145
146typedef int (*sort_fn_t)(struct work_atoms *, struct work_atoms *);
147
148static struct rb_root atom_root, sorted_atom_root;
149
150static u64 all_runtime;
151static u64 all_count;
152
153
154static u64 get_nsecs(void)
155{
156 struct timespec ts;
157
158 clock_gettime(CLOCK_MONOTONIC, &ts);
159
160 return ts.tv_sec * 1000000000ULL + ts.tv_nsec;
161}
162
163static void burn_nsecs(u64 nsecs)
164{
165 u64 T0 = get_nsecs(), T1;
166
167 do {
168 T1 = get_nsecs();
169 } while (T1 + run_measurement_overhead < T0 + nsecs);
170}
171
172static void sleep_nsecs(u64 nsecs)
173{
174 struct timespec ts;
175
176 ts.tv_nsec = nsecs % 999999999;
177 ts.tv_sec = nsecs / 999999999;
178
179 nanosleep(&ts, NULL);
180}
181
182static void calibrate_run_measurement_overhead(void)
183{
184 u64 T0, T1, delta, min_delta = 1000000000ULL;
185 int i;
186
187 for (i = 0; i < 10; i++) {
188 T0 = get_nsecs();
189 burn_nsecs(0);
190 T1 = get_nsecs();
191 delta = T1-T0;
192 min_delta = min(min_delta, delta);
193 }
194 run_measurement_overhead = min_delta;
195
196 printf("run measurement overhead: %Ld nsecs\n", min_delta);
197}
198
199static void calibrate_sleep_measurement_overhead(void)
200{
201 u64 T0, T1, delta, min_delta = 1000000000ULL;
202 int i;
203
204 for (i = 0; i < 10; i++) {
205 T0 = get_nsecs();
206 sleep_nsecs(10000);
207 T1 = get_nsecs();
208 delta = T1-T0;
209 min_delta = min(min_delta, delta);
210 }
211 min_delta -= 10000;
212 sleep_measurement_overhead = min_delta;
213
214 printf("sleep measurement overhead: %Ld nsecs\n", min_delta);
215}
216
217static struct sched_atom *
218get_new_event(struct task_desc *task, u64 timestamp)
219{
220 struct sched_atom *event = zalloc(sizeof(*event));
221 unsigned long idx = task->nr_events;
222 size_t size;
223
224 event->timestamp = timestamp;
225 event->nr = idx;
226
227 task->nr_events++;
228 size = sizeof(struct sched_atom *) * task->nr_events;
229 task->atoms = realloc(task->atoms, size);
230 BUG_ON(!task->atoms);
231
232 task->atoms[idx] = event;
233
234 return event;
235}
236
237static struct sched_atom *last_event(struct task_desc *task)
238{
239 if (!task->nr_events)
240 return NULL;
241
242 return task->atoms[task->nr_events - 1];
243}
244
245static void
246add_sched_event_run(struct task_desc *task, u64 timestamp, u64 duration)
247{
248 struct sched_atom *event, *curr_event = last_event(task);
249
250 /*
251 * optimize an existing RUN event by merging this one
252 * to it:
253 */
254 if (curr_event && curr_event->type == SCHED_EVENT_RUN) {
255 nr_run_events_optimized++;
256 curr_event->duration += duration;
257 return;
258 }
259
260 event = get_new_event(task, timestamp);
261
262 event->type = SCHED_EVENT_RUN;
263 event->duration = duration;
264
265 nr_run_events++;
266}
267
268static void
269add_sched_event_wakeup(struct task_desc *task, u64 timestamp,
270 struct task_desc *wakee)
271{
272 struct sched_atom *event, *wakee_event;
273
274 event = get_new_event(task, timestamp);
275 event->type = SCHED_EVENT_WAKEUP;
276 event->wakee = wakee;
277
278 wakee_event = last_event(wakee);
279 if (!wakee_event || wakee_event->type != SCHED_EVENT_SLEEP) {
280 targetless_wakeups++;
281 return;
282 }
283 if (wakee_event->wait_sem) {
284 multitarget_wakeups++;
285 return;
286 }
287
288 wakee_event->wait_sem = zalloc(sizeof(*wakee_event->wait_sem));
289 sem_init(wakee_event->wait_sem, 0, 0);
290 wakee_event->specific_wait = 1;
291 event->wait_sem = wakee_event->wait_sem;
292
293 nr_wakeup_events++;
294}
295
296static void
297add_sched_event_sleep(struct task_desc *task, u64 timestamp,
298 u64 task_state __used)
299{
300 struct sched_atom *event = get_new_event(task, timestamp);
301
302 event->type = SCHED_EVENT_SLEEP;
303
304 nr_sleep_events++;
305}
306
307static struct task_desc *register_pid(unsigned long pid, const char *comm)
308{
309 struct task_desc *task;
310
311 BUG_ON(pid >= MAX_PID);
312
313 task = pid_to_task[pid];
314
315 if (task)
316 return task;
317
318 task = zalloc(sizeof(*task));
319 task->pid = pid;
320 task->nr = nr_tasks;
321 strcpy(task->comm, comm);
322 /*
323 * every task starts in sleeping state - this gets ignored
324 * if there's no wakeup pointing to this sleep state:
325 */
326 add_sched_event_sleep(task, 0, 0);
327
328 pid_to_task[pid] = task;
329 nr_tasks++;
330 tasks = realloc(tasks, nr_tasks*sizeof(struct task_task *));
331 BUG_ON(!tasks);
332 tasks[task->nr] = task;
333
334 if (verbose)
335 printf("registered task #%ld, PID %ld (%s)\n", nr_tasks, pid, comm);
336
337 return task;
338}
339
340
341static void print_task_traces(void)
342{
343 struct task_desc *task;
344 unsigned long i;
345
346 for (i = 0; i < nr_tasks; i++) {
347 task = tasks[i];
348 printf("task %6ld (%20s:%10ld), nr_events: %ld\n",
349 task->nr, task->comm, task->pid, task->nr_events);
350 }
351}
352
353static void add_cross_task_wakeups(void)
354{
355 struct task_desc *task1, *task2;
356 unsigned long i, j;
357
358 for (i = 0; i < nr_tasks; i++) {
359 task1 = tasks[i];
360 j = i + 1;
361 if (j == nr_tasks)
362 j = 0;
363 task2 = tasks[j];
364 add_sched_event_wakeup(task1, 0, task2);
365 }
366}
367
368static void
369process_sched_event(struct task_desc *this_task __used, struct sched_atom *atom)
370{
371 int ret = 0;
372 u64 now;
373 long long delta;
374
375 now = get_nsecs();
376 delta = start_time + atom->timestamp - now;
377
378 switch (atom->type) {
379 case SCHED_EVENT_RUN:
380 burn_nsecs(atom->duration);
381 break;
382 case SCHED_EVENT_SLEEP:
383 if (atom->wait_sem)
384 ret = sem_wait(atom->wait_sem);
385 BUG_ON(ret);
386 break;
387 case SCHED_EVENT_WAKEUP:
388 if (atom->wait_sem)
389 ret = sem_post(atom->wait_sem);
390 BUG_ON(ret);
391 break;
392 case SCHED_EVENT_MIGRATION:
393 break;
394 default:
395 BUG_ON(1);
396 }
397}
398
399static u64 get_cpu_usage_nsec_parent(void)
400{
401 struct rusage ru;
402 u64 sum;
403 int err;
404
405 err = getrusage(RUSAGE_SELF, &ru);
406 BUG_ON(err);
407
408 sum = ru.ru_utime.tv_sec*1e9 + ru.ru_utime.tv_usec*1e3;
409 sum += ru.ru_stime.tv_sec*1e9 + ru.ru_stime.tv_usec*1e3;
410
411 return sum;
412}
413
414static int self_open_counters(void)
415{
416 struct perf_event_attr attr;
417 int fd;
418
419 memset(&attr, 0, sizeof(attr));
420
421 attr.type = PERF_TYPE_SOFTWARE;
422 attr.config = PERF_COUNT_SW_TASK_CLOCK;
423
424 fd = sys_perf_event_open(&attr, 0, -1, -1, 0);
425
426 if (fd < 0)
427 die("Error: sys_perf_event_open() syscall returned"
428 "with %d (%s)\n", fd, strerror(errno));
429 return fd;
430}
431
432static u64 get_cpu_usage_nsec_self(int fd)
433{
434 u64 runtime;
435 int ret;
436
437 ret = read(fd, &runtime, sizeof(runtime));
438 BUG_ON(ret != sizeof(runtime));
439
440 return runtime;
441}
442
443static void *thread_func(void *ctx)
444{
445 struct task_desc *this_task = ctx;
446 u64 cpu_usage_0, cpu_usage_1;
447 unsigned long i, ret;
448 char comm2[22];
449 int fd;
450
451 sprintf(comm2, ":%s", this_task->comm);
452 prctl(PR_SET_NAME, comm2);
453 fd = self_open_counters();
454
455again:
456 ret = sem_post(&this_task->ready_for_work);
457 BUG_ON(ret);
458 ret = pthread_mutex_lock(&start_work_mutex);
459 BUG_ON(ret);
460 ret = pthread_mutex_unlock(&start_work_mutex);
461 BUG_ON(ret);
462
463 cpu_usage_0 = get_cpu_usage_nsec_self(fd);
464
465 for (i = 0; i < this_task->nr_events; i++) {
466 this_task->curr_event = i;
467 process_sched_event(this_task, this_task->atoms[i]);
468 }
469
470 cpu_usage_1 = get_cpu_usage_nsec_self(fd);
471 this_task->cpu_usage = cpu_usage_1 - cpu_usage_0;
472 ret = sem_post(&this_task->work_done_sem);
473 BUG_ON(ret);
474
475 ret = pthread_mutex_lock(&work_done_wait_mutex);
476 BUG_ON(ret);
477 ret = pthread_mutex_unlock(&work_done_wait_mutex);
478 BUG_ON(ret);
479
480 goto again;
481}
482
483static void create_tasks(void)
484{
485 struct task_desc *task;
486 pthread_attr_t attr;
487 unsigned long i;
488 int err;
489
490 err = pthread_attr_init(&attr);
491 BUG_ON(err);
492 err = pthread_attr_setstacksize(&attr,
493 (size_t) max(16 * 1024, PTHREAD_STACK_MIN));
494 BUG_ON(err);
495 err = pthread_mutex_lock(&start_work_mutex);
496 BUG_ON(err);
497 err = pthread_mutex_lock(&work_done_wait_mutex);
498 BUG_ON(err);
499 for (i = 0; i < nr_tasks; i++) {
500 task = tasks[i];
501 sem_init(&task->sleep_sem, 0, 0);
502 sem_init(&task->ready_for_work, 0, 0);
503 sem_init(&task->work_done_sem, 0, 0);
504 task->curr_event = 0;
505 err = pthread_create(&task->thread, &attr, thread_func, task);
506 BUG_ON(err);
507 }
508}
509
510static void wait_for_tasks(void)
511{
512 u64 cpu_usage_0, cpu_usage_1;
513 struct task_desc *task;
514 unsigned long i, ret;
515
516 start_time = get_nsecs();
517 cpu_usage = 0;
518 pthread_mutex_unlock(&work_done_wait_mutex);
519
520 for (i = 0; i < nr_tasks; i++) {
521 task = tasks[i];
522 ret = sem_wait(&task->ready_for_work);
523 BUG_ON(ret);
524 sem_init(&task->ready_for_work, 0, 0);
525 }
526 ret = pthread_mutex_lock(&work_done_wait_mutex);
527 BUG_ON(ret);
528
529 cpu_usage_0 = get_cpu_usage_nsec_parent();
530
531 pthread_mutex_unlock(&start_work_mutex);
532
533 for (i = 0; i < nr_tasks; i++) {
534 task = tasks[i];
535 ret = sem_wait(&task->work_done_sem);
536 BUG_ON(ret);
537 sem_init(&task->work_done_sem, 0, 0);
538 cpu_usage += task->cpu_usage;
539 task->cpu_usage = 0;
540 }
541
542 cpu_usage_1 = get_cpu_usage_nsec_parent();
543 if (!runavg_cpu_usage)
544 runavg_cpu_usage = cpu_usage;
545 runavg_cpu_usage = (runavg_cpu_usage*9 + cpu_usage)/10;
546
547 parent_cpu_usage = cpu_usage_1 - cpu_usage_0;
548 if (!runavg_parent_cpu_usage)
549 runavg_parent_cpu_usage = parent_cpu_usage;
550 runavg_parent_cpu_usage = (runavg_parent_cpu_usage*9 +
551 parent_cpu_usage)/10;
552
553 ret = pthread_mutex_lock(&start_work_mutex);
554 BUG_ON(ret);
555
556 for (i = 0; i < nr_tasks; i++) {
557 task = tasks[i];
558 sem_init(&task->sleep_sem, 0, 0);
559 task->curr_event = 0;
560 }
561}
562
563static void run_one_test(void)
564{
565 u64 T0, T1, delta, avg_delta, fluct, std_dev;
566
567 T0 = get_nsecs();
568 wait_for_tasks();
569 T1 = get_nsecs();
570
571 delta = T1 - T0;
572 sum_runtime += delta;
573 nr_runs++;
574
575 avg_delta = sum_runtime / nr_runs;
576 if (delta < avg_delta)
577 fluct = avg_delta - delta;
578 else
579 fluct = delta - avg_delta;
580 sum_fluct += fluct;
581 std_dev = sum_fluct / nr_runs / sqrt(nr_runs);
582 if (!run_avg)
583 run_avg = delta;
584 run_avg = (run_avg*9 + delta)/10;
585
586 printf("#%-3ld: %0.3f, ",
587 nr_runs, (double)delta/1000000.0);
588
589 printf("ravg: %0.2f, ",
590 (double)run_avg/1e6);
591
592 printf("cpu: %0.2f / %0.2f",
593 (double)cpu_usage/1e6, (double)runavg_cpu_usage/1e6);
594
595#if 0
596 /*
597 * rusage statistics done by the parent, these are less
598 * accurate than the sum_exec_runtime based statistics:
599 */
600 printf(" [%0.2f / %0.2f]",
601 (double)parent_cpu_usage/1e6,
602 (double)runavg_parent_cpu_usage/1e6);
603#endif
604
605 printf("\n");
606
607 if (nr_sleep_corrections)
608 printf(" (%ld sleep corrections)\n", nr_sleep_corrections);
609 nr_sleep_corrections = 0;
610}
611
612static void test_calibrations(void)
613{
614 u64 T0, T1;
615
616 T0 = get_nsecs();
617 burn_nsecs(1e6);
618 T1 = get_nsecs();
619
620 printf("the run test took %Ld nsecs\n", T1-T0);
621
622 T0 = get_nsecs();
623 sleep_nsecs(1e6);
624 T1 = get_nsecs();
625
626 printf("the sleep test took %Ld nsecs\n", T1-T0);
627}
628
629#define FILL_FIELD(ptr, field, event, data) \
630 ptr.field = (typeof(ptr.field)) raw_field_value(event, #field, data)
631
632#define FILL_ARRAY(ptr, array, event, data) \
633do { \
634 void *__array = raw_field_ptr(event, #array, data); \
635 memcpy(ptr.array, __array, sizeof(ptr.array)); \
636} while(0)
637
638#define FILL_COMMON_FIELDS(ptr, event, data) \
639do { \
640 FILL_FIELD(ptr, common_type, event, data); \
641 FILL_FIELD(ptr, common_flags, event, data); \
642 FILL_FIELD(ptr, common_preempt_count, event, data); \
643 FILL_FIELD(ptr, common_pid, event, data); \
644 FILL_FIELD(ptr, common_tgid, event, data); \
645} while (0)
646
647
648
649struct trace_switch_event {
650 u32 size;
651
652 u16 common_type;
653 u8 common_flags;
654 u8 common_preempt_count;
655 u32 common_pid;
656 u32 common_tgid;
657
658 char prev_comm[16];
659 u32 prev_pid;
660 u32 prev_prio;
661 u64 prev_state;
662 char next_comm[16];
663 u32 next_pid;
664 u32 next_prio;
665};
666
667struct trace_runtime_event {
668 u32 size;
669
670 u16 common_type;
671 u8 common_flags;
672 u8 common_preempt_count;
673 u32 common_pid;
674 u32 common_tgid;
675
676 char comm[16];
677 u32 pid;
678 u64 runtime;
679 u64 vruntime;
680};
681
682struct trace_wakeup_event {
683 u32 size;
684
685 u16 common_type;
686 u8 common_flags;
687 u8 common_preempt_count;
688 u32 common_pid;
689 u32 common_tgid;
690
691 char comm[16];
692 u32 pid;
693
694 u32 prio;
695 u32 success;
696 u32 cpu;
697};
698
699struct trace_fork_event {
700 u32 size;
701
702 u16 common_type;
703 u8 common_flags;
704 u8 common_preempt_count;
705 u32 common_pid;
706 u32 common_tgid;
707
708 char parent_comm[16];
709 u32 parent_pid;
710 char child_comm[16];
711 u32 child_pid;
712};
713
714struct trace_migrate_task_event {
715 u32 size;
716
717 u16 common_type;
718 u8 common_flags;
719 u8 common_preempt_count;
720 u32 common_pid;
721 u32 common_tgid;
722
723 char comm[16];
724 u32 pid;
725
726 u32 prio;
727 u32 cpu;
728};
729
730struct trace_sched_handler {
731 void (*switch_event)(struct trace_switch_event *,
732 struct perf_session *,
733 struct event *,
734 int cpu,
735 u64 timestamp,
736 struct thread *thread);
737
738 void (*runtime_event)(struct trace_runtime_event *,
739 struct perf_session *,
740 struct event *,
741 int cpu,
742 u64 timestamp,
743 struct thread *thread);
744
745 void (*wakeup_event)(struct trace_wakeup_event *,
746 struct perf_session *,
747 struct event *,
748 int cpu,
749 u64 timestamp,
750 struct thread *thread);
751
752 void (*fork_event)(struct trace_fork_event *,
753 struct event *,
754 int cpu,
755 u64 timestamp,
756 struct thread *thread);
757
758 void (*migrate_task_event)(struct trace_migrate_task_event *,
759 struct perf_session *session,
760 struct event *,
761 int cpu,
762 u64 timestamp,
763 struct thread *thread);
764};
765
766
767static void
768replay_wakeup_event(struct trace_wakeup_event *wakeup_event,
769 struct perf_session *session __used,
770 struct event *event,
771 int cpu __used,
772 u64 timestamp __used,
773 struct thread *thread __used)
774{
775 struct task_desc *waker, *wakee;
776
777 if (verbose) {
778 printf("sched_wakeup event %p\n", event);
779
780 printf(" ... pid %d woke up %s/%d\n",
781 wakeup_event->common_pid,
782 wakeup_event->comm,
783 wakeup_event->pid);
784 }
785
786 waker = register_pid(wakeup_event->common_pid, "<unknown>");
787 wakee = register_pid(wakeup_event->pid, wakeup_event->comm);
788
789 add_sched_event_wakeup(waker, timestamp, wakee);
790}
791
792static u64 cpu_last_switched[MAX_CPUS];
793
794static void
795replay_switch_event(struct trace_switch_event *switch_event,
796 struct perf_session *session __used,
797 struct event *event,
798 int cpu,
799 u64 timestamp,
800 struct thread *thread __used)
801{
802 struct task_desc *prev, *next;
803 u64 timestamp0;
804 s64 delta;
805
806 if (verbose)
807 printf("sched_switch event %p\n", event);
808
809 if (cpu >= MAX_CPUS || cpu < 0)
810 return;
811
812 timestamp0 = cpu_last_switched[cpu];
813 if (timestamp0)
814 delta = timestamp - timestamp0;
815 else
816 delta = 0;
817
818 if (delta < 0)
819 die("hm, delta: %Ld < 0 ?\n", delta);
820
821 if (verbose) {
822 printf(" ... switch from %s/%d to %s/%d [ran %Ld nsecs]\n",
823 switch_event->prev_comm, switch_event->prev_pid,
824 switch_event->next_comm, switch_event->next_pid,
825 delta);
826 }
827
828 prev = register_pid(switch_event->prev_pid, switch_event->prev_comm);
829 next = register_pid(switch_event->next_pid, switch_event->next_comm);
830
831 cpu_last_switched[cpu] = timestamp;
832
833 add_sched_event_run(prev, timestamp, delta);
834 add_sched_event_sleep(prev, timestamp, switch_event->prev_state);
835}
836
837
838static void
839replay_fork_event(struct trace_fork_event *fork_event,
840 struct event *event,
841 int cpu __used,
842 u64 timestamp __used,
843 struct thread *thread __used)
844{
845 if (verbose) {
846 printf("sched_fork event %p\n", event);
847 printf("... parent: %s/%d\n", fork_event->parent_comm, fork_event->parent_pid);
848 printf("... child: %s/%d\n", fork_event->child_comm, fork_event->child_pid);
849 }
850 register_pid(fork_event->parent_pid, fork_event->parent_comm);
851 register_pid(fork_event->child_pid, fork_event->child_comm);
852}
853
854static struct trace_sched_handler replay_ops = {
855 .wakeup_event = replay_wakeup_event,
856 .switch_event = replay_switch_event,
857 .fork_event = replay_fork_event,
858};
859
860struct sort_dimension {
861 const char *name;
862 sort_fn_t cmp;
863 struct list_head list;
864};
865
866static LIST_HEAD(cmp_pid);
867
868static int
869thread_lat_cmp(struct list_head *list, struct work_atoms *l, struct work_atoms *r)
870{
871 struct sort_dimension *sort;
872 int ret = 0;
873
874 BUG_ON(list_empty(list));
875
876 list_for_each_entry(sort, list, list) {
877 ret = sort->cmp(l, r);
878 if (ret)
879 return ret;
880 }
881
882 return ret;
883}
884
885static struct work_atoms *
886thread_atoms_search(struct rb_root *root, struct thread *thread,
887 struct list_head *sort_list)
888{
889 struct rb_node *node = root->rb_node;
890 struct work_atoms key = { .thread = thread };
891
892 while (node) {
893 struct work_atoms *atoms;
894 int cmp;
895
896 atoms = container_of(node, struct work_atoms, node);
897
898 cmp = thread_lat_cmp(sort_list, &key, atoms);
899 if (cmp > 0)
900 node = node->rb_left;
901 else if (cmp < 0)
902 node = node->rb_right;
903 else {
904 BUG_ON(thread != atoms->thread);
905 return atoms;
906 }
907 }
908 return NULL;
909}
910
911static void
912__thread_latency_insert(struct rb_root *root, struct work_atoms *data,
913 struct list_head *sort_list)
914{
915 struct rb_node **new = &(root->rb_node), *parent = NULL;
916
917 while (*new) {
918 struct work_atoms *this;
919 int cmp;
920
921 this = container_of(*new, struct work_atoms, node);
922 parent = *new;
923
924 cmp = thread_lat_cmp(sort_list, data, this);
925
926 if (cmp > 0)
927 new = &((*new)->rb_left);
928 else
929 new = &((*new)->rb_right);
930 }
931
932 rb_link_node(&data->node, parent, new);
933 rb_insert_color(&data->node, root);
934}
935
936static void thread_atoms_insert(struct thread *thread)
937{
938 struct work_atoms *atoms = zalloc(sizeof(*atoms));
939 if (!atoms)
940 die("No memory");
941
942 atoms->thread = thread;
943 INIT_LIST_HEAD(&atoms->work_list);
944 __thread_latency_insert(&atom_root, atoms, &cmp_pid);
945}
946
947static void
948latency_fork_event(struct trace_fork_event *fork_event __used,
949 struct event *event __used,
950 int cpu __used,
951 u64 timestamp __used,
952 struct thread *thread __used)
953{
954 /* should insert the newcomer */
955}
956
957__used
958static char sched_out_state(struct trace_switch_event *switch_event)
959{
960 const char *str = TASK_STATE_TO_CHAR_STR;
961
962 return str[switch_event->prev_state];
963}
964
965static void
966add_sched_out_event(struct work_atoms *atoms,
967 char run_state,
968 u64 timestamp)
969{
970 struct work_atom *atom = zalloc(sizeof(*atom));
971 if (!atom)
972 die("Non memory");
973
974 atom->sched_out_time = timestamp;
975
976 if (run_state == 'R') {
977 atom->state = THREAD_WAIT_CPU;
978 atom->wake_up_time = atom->sched_out_time;
979 }
980
981 list_add_tail(&atom->list, &atoms->work_list);
982}
983
984static void
985add_runtime_event(struct work_atoms *atoms, u64 delta, u64 timestamp __used)
986{
987 struct work_atom *atom;
988
989 BUG_ON(list_empty(&atoms->work_list));
990
991 atom = list_entry(atoms->work_list.prev, struct work_atom, list);
992
993 atom->runtime += delta;
994 atoms->total_runtime += delta;
995}
996
997static void
998add_sched_in_event(struct work_atoms *atoms, u64 timestamp)
999{
1000 struct work_atom *atom;
1001 u64 delta;
1002
1003 if (list_empty(&atoms->work_list))
1004 return;
1005
1006 atom = list_entry(atoms->work_list.prev, struct work_atom, list);
1007
1008 if (atom->state != THREAD_WAIT_CPU)
1009 return;
1010
1011 if (timestamp < atom->wake_up_time) {
1012 atom->state = THREAD_IGNORE;
1013 return;
1014 }
1015
1016 atom->state = THREAD_SCHED_IN;
1017 atom->sched_in_time = timestamp;
1018
1019 delta = atom->sched_in_time - atom->wake_up_time;
1020 atoms->total_lat += delta;
1021 if (delta > atoms->max_lat) {
1022 atoms->max_lat = delta;
1023 atoms->max_lat_at = timestamp;
1024 }
1025 atoms->nb_atoms++;
1026}
1027
1028static void
1029latency_switch_event(struct trace_switch_event *switch_event,
1030 struct perf_session *session,
1031 struct event *event __used,
1032 int cpu,
1033 u64 timestamp,
1034 struct thread *thread __used)
1035{
1036 struct work_atoms *out_events, *in_events;
1037 struct thread *sched_out, *sched_in;
1038 u64 timestamp0;
1039 s64 delta;
1040
1041 BUG_ON(cpu >= MAX_CPUS || cpu < 0);
1042
1043 timestamp0 = cpu_last_switched[cpu];
1044 cpu_last_switched[cpu] = timestamp;
1045 if (timestamp0)
1046 delta = timestamp - timestamp0;
1047 else
1048 delta = 0;
1049
1050 if (delta < 0)
1051 die("hm, delta: %Ld < 0 ?\n", delta);
1052
1053
1054 sched_out = perf_session__findnew(session, switch_event->prev_pid);
1055 sched_in = perf_session__findnew(session, switch_event->next_pid);
1056
1057 out_events = thread_atoms_search(&atom_root, sched_out, &cmp_pid);
1058 if (!out_events) {
1059 thread_atoms_insert(sched_out);
1060 out_events = thread_atoms_search(&atom_root, sched_out, &cmp_pid);
1061 if (!out_events)
1062 die("out-event: Internal tree error");
1063 }
1064 add_sched_out_event(out_events, sched_out_state(switch_event), timestamp);
1065
1066 in_events = thread_atoms_search(&atom_root, sched_in, &cmp_pid);
1067 if (!in_events) {
1068 thread_atoms_insert(sched_in);
1069 in_events = thread_atoms_search(&atom_root, sched_in, &cmp_pid);
1070 if (!in_events)
1071 die("in-event: Internal tree error");
1072 /*
1073 * Take came in we have not heard about yet,
1074 * add in an initial atom in runnable state:
1075 */
1076 add_sched_out_event(in_events, 'R', timestamp);
1077 }
1078 add_sched_in_event(in_events, timestamp);
1079}
1080
1081static void
1082latency_runtime_event(struct trace_runtime_event *runtime_event,
1083 struct perf_session *session,
1084 struct event *event __used,
1085 int cpu,
1086 u64 timestamp,
1087 struct thread *this_thread __used)
1088{
1089 struct thread *thread = perf_session__findnew(session, runtime_event->pid);
1090 struct work_atoms *atoms = thread_atoms_search(&atom_root, thread, &cmp_pid);
1091
1092 BUG_ON(cpu >= MAX_CPUS || cpu < 0);
1093 if (!atoms) {
1094 thread_atoms_insert(thread);
1095 atoms = thread_atoms_search(&atom_root, thread, &cmp_pid);
1096 if (!atoms)
1097 die("in-event: Internal tree error");
1098 add_sched_out_event(atoms, 'R', timestamp);
1099 }
1100
1101 add_runtime_event(atoms, runtime_event->runtime, timestamp);
1102}
1103
1104static void
1105latency_wakeup_event(struct trace_wakeup_event *wakeup_event,
1106 struct perf_session *session,
1107 struct event *__event __used,
1108 int cpu __used,
1109 u64 timestamp,
1110 struct thread *thread __used)
1111{
1112 struct work_atoms *atoms;
1113 struct work_atom *atom;
1114 struct thread *wakee;
1115
1116 /* Note for later, it may be interesting to observe the failing cases */
1117 if (!wakeup_event->success)
1118 return;
1119
1120 wakee = perf_session__findnew(session, wakeup_event->pid);
1121 atoms = thread_atoms_search(&atom_root, wakee, &cmp_pid);
1122 if (!atoms) {
1123 thread_atoms_insert(wakee);
1124 atoms = thread_atoms_search(&atom_root, wakee, &cmp_pid);
1125 if (!atoms)
1126 die("wakeup-event: Internal tree error");
1127 add_sched_out_event(atoms, 'S', timestamp);
1128 }
1129
1130 BUG_ON(list_empty(&atoms->work_list));
1131
1132 atom = list_entry(atoms->work_list.prev, struct work_atom, list);
1133
1134 /*
1135 * You WILL be missing events if you've recorded only
1136 * one CPU, or are only looking at only one, so don't
1137 * make useless noise.
1138 */
1139 if (profile_cpu == -1 && atom->state != THREAD_SLEEPING)
1140 nr_state_machine_bugs++;
1141
1142 nr_timestamps++;
1143 if (atom->sched_out_time > timestamp) {
1144 nr_unordered_timestamps++;
1145 return;
1146 }
1147
1148 atom->state = THREAD_WAIT_CPU;
1149 atom->wake_up_time = timestamp;
1150}
1151
1152static void
1153latency_migrate_task_event(struct trace_migrate_task_event *migrate_task_event,
1154 struct perf_session *session,
1155 struct event *__event __used,
1156 int cpu __used,
1157 u64 timestamp,
1158 struct thread *thread __used)
1159{
1160 struct work_atoms *atoms;
1161 struct work_atom *atom;
1162 struct thread *migrant;
1163
1164 /*
1165 * Only need to worry about migration when profiling one CPU.
1166 */
1167 if (profile_cpu == -1)
1168 return;
1169
1170 migrant = perf_session__findnew(session, migrate_task_event->pid);
1171 atoms = thread_atoms_search(&atom_root, migrant, &cmp_pid);
1172 if (!atoms) {
1173 thread_atoms_insert(migrant);
1174 register_pid(migrant->pid, migrant->comm);
1175 atoms = thread_atoms_search(&atom_root, migrant, &cmp_pid);
1176 if (!atoms)
1177 die("migration-event: Internal tree error");
1178 add_sched_out_event(atoms, 'R', timestamp);
1179 }
1180
1181 BUG_ON(list_empty(&atoms->work_list));
1182
1183 atom = list_entry(atoms->work_list.prev, struct work_atom, list);
1184 atom->sched_in_time = atom->sched_out_time = atom->wake_up_time = timestamp;
1185
1186 nr_timestamps++;
1187
1188 if (atom->sched_out_time > timestamp)
1189 nr_unordered_timestamps++;
1190}
1191
1192static struct trace_sched_handler lat_ops = {
1193 .wakeup_event = latency_wakeup_event,
1194 .switch_event = latency_switch_event,
1195 .runtime_event = latency_runtime_event,
1196 .fork_event = latency_fork_event,
1197 .migrate_task_event = latency_migrate_task_event,
1198};
1199
1200static void output_lat_thread(struct work_atoms *work_list)
1201{
1202 int i;
1203 int ret;
1204 u64 avg;
1205
1206 if (!work_list->nb_atoms)
1207 return;
1208 /*
1209 * Ignore idle threads:
1210 */
1211 if (!strcmp(work_list->thread->comm, "swapper"))
1212 return;
1213
1214 all_runtime += work_list->total_runtime;
1215 all_count += work_list->nb_atoms;
1216
1217 ret = printf(" %s:%d ", work_list->thread->comm, work_list->thread->pid);
1218
1219 for (i = 0; i < 24 - ret; i++)
1220 printf(" ");
1221
1222 avg = work_list->total_lat / work_list->nb_atoms;
1223
1224 printf("|%11.3f ms |%9llu | avg:%9.3f ms | max:%9.3f ms | max at: %9.6f s\n",
1225 (double)work_list->total_runtime / 1e6,
1226 work_list->nb_atoms, (double)avg / 1e6,
1227 (double)work_list->max_lat / 1e6,
1228 (double)work_list->max_lat_at / 1e9);
1229}
1230
1231static int pid_cmp(struct work_atoms *l, struct work_atoms *r)
1232{
1233 if (l->thread->pid < r->thread->pid)
1234 return -1;
1235 if (l->thread->pid > r->thread->pid)
1236 return 1;
1237
1238 return 0;
1239}
1240
1241static struct sort_dimension pid_sort_dimension = {
1242 .name = "pid",
1243 .cmp = pid_cmp,
1244};
1245
1246static int avg_cmp(struct work_atoms *l, struct work_atoms *r)
1247{
1248 u64 avgl, avgr;
1249
1250 if (!l->nb_atoms)
1251 return -1;
1252
1253 if (!r->nb_atoms)
1254 return 1;
1255
1256 avgl = l->total_lat / l->nb_atoms;
1257 avgr = r->total_lat / r->nb_atoms;
1258
1259 if (avgl < avgr)
1260 return -1;
1261 if (avgl > avgr)
1262 return 1;
1263
1264 return 0;
1265}
1266
1267static struct sort_dimension avg_sort_dimension = {
1268 .name = "avg",
1269 .cmp = avg_cmp,
1270};
1271
1272static int max_cmp(struct work_atoms *l, struct work_atoms *r)
1273{
1274 if (l->max_lat < r->max_lat)
1275 return -1;
1276 if (l->max_lat > r->max_lat)
1277 return 1;
1278
1279 return 0;
1280}
1281
1282static struct sort_dimension max_sort_dimension = {
1283 .name = "max",
1284 .cmp = max_cmp,
1285};
1286
1287static int switch_cmp(struct work_atoms *l, struct work_atoms *r)
1288{
1289 if (l->nb_atoms < r->nb_atoms)
1290 return -1;
1291 if (l->nb_atoms > r->nb_atoms)
1292 return 1;
1293
1294 return 0;
1295}
1296
1297static struct sort_dimension switch_sort_dimension = {
1298 .name = "switch",
1299 .cmp = switch_cmp,
1300};
1301
1302static int runtime_cmp(struct work_atoms *l, struct work_atoms *r)
1303{
1304 if (l->total_runtime < r->total_runtime)
1305 return -1;
1306 if (l->total_runtime > r->total_runtime)
1307 return 1;
1308
1309 return 0;
1310}
1311
1312static struct sort_dimension runtime_sort_dimension = {
1313 .name = "runtime",
1314 .cmp = runtime_cmp,
1315};
1316
1317static struct sort_dimension *available_sorts[] = {
1318 &pid_sort_dimension,
1319 &avg_sort_dimension,
1320 &max_sort_dimension,
1321 &switch_sort_dimension,
1322 &runtime_sort_dimension,
1323};
1324
1325#define NB_AVAILABLE_SORTS (int)(sizeof(available_sorts) / sizeof(struct sort_dimension *))
1326
1327static LIST_HEAD(sort_list);
1328
1329static int sort_dimension__add(const char *tok, struct list_head *list)
1330{
1331 int i;
1332
1333 for (i = 0; i < NB_AVAILABLE_SORTS; i++) {
1334 if (!strcmp(available_sorts[i]->name, tok)) {
1335 list_add_tail(&available_sorts[i]->list, list);
1336
1337 return 0;
1338 }
1339 }
1340
1341 return -1;
1342}
1343
1344static void setup_sorting(void);
1345
1346static void sort_lat(void)
1347{
1348 struct rb_node *node;
1349
1350 for (;;) {
1351 struct work_atoms *data;
1352 node = rb_first(&atom_root);
1353 if (!node)
1354 break;
1355
1356 rb_erase(node, &atom_root);
1357 data = rb_entry(node, struct work_atoms, node);
1358 __thread_latency_insert(&sorted_atom_root, data, &sort_list);
1359 }
1360}
1361
1362static struct trace_sched_handler *trace_handler;
1363
1364static void
1365process_sched_wakeup_event(void *data, struct perf_session *session,
1366 struct event *event,
1367 int cpu __used,
1368 u64 timestamp __used,
1369 struct thread *thread __used)
1370{
1371 struct trace_wakeup_event wakeup_event;
1372
1373 FILL_COMMON_FIELDS(wakeup_event, event, data);
1374
1375 FILL_ARRAY(wakeup_event, comm, event, data);
1376 FILL_FIELD(wakeup_event, pid, event, data);
1377 FILL_FIELD(wakeup_event, prio, event, data);
1378 FILL_FIELD(wakeup_event, success, event, data);
1379 FILL_FIELD(wakeup_event, cpu, event, data);
1380
1381 if (trace_handler->wakeup_event)
1382 trace_handler->wakeup_event(&wakeup_event, session, event,
1383 cpu, timestamp, thread);
1384}
1385
1386/*
1387 * Track the current task - that way we can know whether there's any
1388 * weird events, such as a task being switched away that is not current.
1389 */
1390static int max_cpu;
1391
1392static u32 curr_pid[MAX_CPUS] = { [0 ... MAX_CPUS-1] = -1 };
1393
1394static struct thread *curr_thread[MAX_CPUS];
1395
1396static char next_shortname1 = 'A';
1397static char next_shortname2 = '0';
1398
1399static void
1400map_switch_event(struct trace_switch_event *switch_event,
1401 struct perf_session *session,
1402 struct event *event __used,
1403 int this_cpu,
1404 u64 timestamp,
1405 struct thread *thread __used)
1406{
1407 struct thread *sched_out, *sched_in;
1408 int new_shortname;
1409 u64 timestamp0;
1410 s64 delta;
1411 int cpu;
1412
1413 BUG_ON(this_cpu >= MAX_CPUS || this_cpu < 0);
1414
1415 if (this_cpu > max_cpu)
1416 max_cpu = this_cpu;
1417
1418 timestamp0 = cpu_last_switched[this_cpu];
1419 cpu_last_switched[this_cpu] = timestamp;
1420 if (timestamp0)
1421 delta = timestamp - timestamp0;
1422 else
1423 delta = 0;
1424
1425 if (delta < 0)
1426 die("hm, delta: %Ld < 0 ?\n", delta);
1427
1428
1429 sched_out = perf_session__findnew(session, switch_event->prev_pid);
1430 sched_in = perf_session__findnew(session, switch_event->next_pid);
1431
1432 curr_thread[this_cpu] = sched_in;
1433
1434 printf(" ");
1435
1436 new_shortname = 0;
1437 if (!sched_in->shortname[0]) {
1438 sched_in->shortname[0] = next_shortname1;
1439 sched_in->shortname[1] = next_shortname2;
1440
1441 if (next_shortname1 < 'Z') {
1442 next_shortname1++;
1443 } else {
1444 next_shortname1='A';
1445 if (next_shortname2 < '9') {
1446 next_shortname2++;
1447 } else {
1448 next_shortname2='0';
1449 }
1450 }
1451 new_shortname = 1;
1452 }
1453
1454 for (cpu = 0; cpu <= max_cpu; cpu++) {
1455 if (cpu != this_cpu)
1456 printf(" ");
1457 else
1458 printf("*");
1459
1460 if (curr_thread[cpu]) {
1461 if (curr_thread[cpu]->pid)
1462 printf("%2s ", curr_thread[cpu]->shortname);
1463 else
1464 printf(". ");
1465 } else
1466 printf(" ");
1467 }
1468
1469 printf(" %12.6f secs ", (double)timestamp/1e9);
1470 if (new_shortname) {
1471 printf("%s => %s:%d\n",
1472 sched_in->shortname, sched_in->comm, sched_in->pid);
1473 } else {
1474 printf("\n");
1475 }
1476}
1477
1478
1479static void
1480process_sched_switch_event(void *data, struct perf_session *session,
1481 struct event *event,
1482 int this_cpu,
1483 u64 timestamp __used,
1484 struct thread *thread __used)
1485{
1486 struct trace_switch_event switch_event;
1487
1488 FILL_COMMON_FIELDS(switch_event, event, data);
1489
1490 FILL_ARRAY(switch_event, prev_comm, event, data);
1491 FILL_FIELD(switch_event, prev_pid, event, data);
1492 FILL_FIELD(switch_event, prev_prio, event, data);
1493 FILL_FIELD(switch_event, prev_state, event, data);
1494 FILL_ARRAY(switch_event, next_comm, event, data);
1495 FILL_FIELD(switch_event, next_pid, event, data);
1496 FILL_FIELD(switch_event, next_prio, event, data);
1497
1498 if (curr_pid[this_cpu] != (u32)-1) {
1499 /*
1500 * Are we trying to switch away a PID that is
1501 * not current?
1502 */
1503 if (curr_pid[this_cpu] != switch_event.prev_pid)
1504 nr_context_switch_bugs++;
1505 }
1506 if (trace_handler->switch_event)
1507 trace_handler->switch_event(&switch_event, session, event,
1508 this_cpu, timestamp, thread);
1509
1510 curr_pid[this_cpu] = switch_event.next_pid;
1511}
1512
1513static void
1514process_sched_runtime_event(void *data, struct perf_session *session,
1515 struct event *event,
1516 int cpu __used,
1517 u64 timestamp __used,
1518 struct thread *thread __used)
1519{
1520 struct trace_runtime_event runtime_event;
1521
1522 FILL_ARRAY(runtime_event, comm, event, data);
1523 FILL_FIELD(runtime_event, pid, event, data);
1524 FILL_FIELD(runtime_event, runtime, event, data);
1525 FILL_FIELD(runtime_event, vruntime, event, data);
1526
1527 if (trace_handler->runtime_event)
1528 trace_handler->runtime_event(&runtime_event, session, event, cpu, timestamp, thread);
1529}
1530
1531static void
1532process_sched_fork_event(void *data,
1533 struct event *event,
1534 int cpu __used,
1535 u64 timestamp __used,
1536 struct thread *thread __used)
1537{
1538 struct trace_fork_event fork_event;
1539
1540 FILL_COMMON_FIELDS(fork_event, event, data);
1541
1542 FILL_ARRAY(fork_event, parent_comm, event, data);
1543 FILL_FIELD(fork_event, parent_pid, event, data);
1544 FILL_ARRAY(fork_event, child_comm, event, data);
1545 FILL_FIELD(fork_event, child_pid, event, data);
1546
1547 if (trace_handler->fork_event)
1548 trace_handler->fork_event(&fork_event, event,
1549 cpu, timestamp, thread);
1550}
1551
1552static void
1553process_sched_exit_event(struct event *event,
1554 int cpu __used,
1555 u64 timestamp __used,
1556 struct thread *thread __used)
1557{
1558 if (verbose)
1559 printf("sched_exit event %p\n", event);
1560}
1561
1562static void
1563process_sched_migrate_task_event(void *data, struct perf_session *session,
1564 struct event *event,
1565 int cpu __used,
1566 u64 timestamp __used,
1567 struct thread *thread __used)
1568{
1569 struct trace_migrate_task_event migrate_task_event;
1570
1571 FILL_COMMON_FIELDS(migrate_task_event, event, data);
1572
1573 FILL_ARRAY(migrate_task_event, comm, event, data);
1574 FILL_FIELD(migrate_task_event, pid, event, data);
1575 FILL_FIELD(migrate_task_event, prio, event, data);
1576 FILL_FIELD(migrate_task_event, cpu, event, data);
1577
1578 if (trace_handler->migrate_task_event)
1579 trace_handler->migrate_task_event(&migrate_task_event, session,
1580 event, cpu, timestamp, thread);
1581}
1582
1583static void
1584process_raw_event(event_t *raw_event __used, struct perf_session *session,
1585 void *data, int cpu, u64 timestamp, struct thread *thread)
1586{
1587 struct event *event;
1588 int type;
1589
1590
1591 type = trace_parse_common_type(data);
1592 event = trace_find_event(type);
1593
1594 if (!strcmp(event->name, "sched_switch"))
1595 process_sched_switch_event(data, session, event, cpu, timestamp, thread);
1596 if (!strcmp(event->name, "sched_stat_runtime"))
1597 process_sched_runtime_event(data, session, event, cpu, timestamp, thread);
1598 if (!strcmp(event->name, "sched_wakeup"))
1599 process_sched_wakeup_event(data, session, event, cpu, timestamp, thread);
1600 if (!strcmp(event->name, "sched_wakeup_new"))
1601 process_sched_wakeup_event(data, session, event, cpu, timestamp, thread);
1602 if (!strcmp(event->name, "sched_process_fork"))
1603 process_sched_fork_event(data, event, cpu, timestamp, thread);
1604 if (!strcmp(event->name, "sched_process_exit"))
1605 process_sched_exit_event(event, cpu, timestamp, thread);
1606 if (!strcmp(event->name, "sched_migrate_task"))
1607 process_sched_migrate_task_event(data, session, event, cpu, timestamp, thread);
1608}
1609
1610static int process_sample_event(event_t *event, struct sample_data *sample,
1611 struct perf_session *session)
1612{
1613 struct thread *thread;
1614
1615 if (!(session->sample_type & PERF_SAMPLE_RAW))
1616 return 0;
1617
1618 thread = perf_session__findnew(session, sample->pid);
1619 if (thread == NULL) {
1620 pr_debug("problem processing %d event, skipping it.\n",
1621 event->header.type);
1622 return -1;
1623 }
1624
1625 dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid);
1626
1627 if (profile_cpu != -1 && profile_cpu != (int)sample->cpu)
1628 return 0;
1629
1630 process_raw_event(event, session, sample->raw_data, sample->cpu,
1631 sample->time, thread);
1632
1633 return 0;
1634}
1635
1636static struct perf_event_ops event_ops = {
1637 .sample = process_sample_event,
1638 .comm = event__process_comm,
1639 .lost = event__process_lost,
1640 .fork = event__process_task,
1641 .ordered_samples = true,
1642};
1643
1644static int read_events(void)
1645{
1646 int err = -EINVAL;
1647 struct perf_session *session = perf_session__new(input_name, O_RDONLY,
1648 0, false, &event_ops);
1649 if (session == NULL)
1650 return -ENOMEM;
1651
1652 if (perf_session__has_traces(session, "record -R")) {
1653 err = perf_session__process_events(session, &event_ops);
1654 nr_events = session->hists.stats.nr_events[0];
1655 nr_lost_events = session->hists.stats.total_lost;
1656 nr_lost_chunks = session->hists.stats.nr_events[PERF_RECORD_LOST];
1657 }
1658
1659 perf_session__delete(session);
1660 return err;
1661}
1662
1663static void print_bad_events(void)
1664{
1665 if (nr_unordered_timestamps && nr_timestamps) {
1666 printf(" INFO: %.3f%% unordered timestamps (%ld out of %ld)\n",
1667 (double)nr_unordered_timestamps/(double)nr_timestamps*100.0,
1668 nr_unordered_timestamps, nr_timestamps);
1669 }
1670 if (nr_lost_events && nr_events) {
1671 printf(" INFO: %.3f%% lost events (%ld out of %ld, in %ld chunks)\n",
1672 (double)nr_lost_events/(double)nr_events*100.0,
1673 nr_lost_events, nr_events, nr_lost_chunks);
1674 }
1675 if (nr_state_machine_bugs && nr_timestamps) {
1676 printf(" INFO: %.3f%% state machine bugs (%ld out of %ld)",
1677 (double)nr_state_machine_bugs/(double)nr_timestamps*100.0,
1678 nr_state_machine_bugs, nr_timestamps);
1679 if (nr_lost_events)
1680 printf(" (due to lost events?)");
1681 printf("\n");
1682 }
1683 if (nr_context_switch_bugs && nr_timestamps) {
1684 printf(" INFO: %.3f%% context switch bugs (%ld out of %ld)",
1685 (double)nr_context_switch_bugs/(double)nr_timestamps*100.0,
1686 nr_context_switch_bugs, nr_timestamps);
1687 if (nr_lost_events)
1688 printf(" (due to lost events?)");
1689 printf("\n");
1690 }
1691}
1692
1693static void __cmd_lat(void)
1694{
1695 struct rb_node *next;
1696
1697 setup_pager();
1698 read_events();
1699 sort_lat();
1700
1701 printf("\n ---------------------------------------------------------------------------------------------------------------\n");
1702 printf(" Task | Runtime ms | Switches | Average delay ms | Maximum delay ms | Maximum delay at |\n");
1703 printf(" ---------------------------------------------------------------------------------------------------------------\n");
1704
1705 next = rb_first(&sorted_atom_root);
1706
1707 while (next) {
1708 struct work_atoms *work_list;
1709
1710 work_list = rb_entry(next, struct work_atoms, node);
1711 output_lat_thread(work_list);
1712 next = rb_next(next);
1713 }
1714
1715 printf(" -----------------------------------------------------------------------------------------\n");
1716 printf(" TOTAL: |%11.3f ms |%9Ld |\n",
1717 (double)all_runtime/1e6, all_count);
1718
1719 printf(" ---------------------------------------------------\n");
1720
1721 print_bad_events();
1722 printf("\n");
1723
1724}
1725
1726static struct trace_sched_handler map_ops = {
1727 .wakeup_event = NULL,
1728 .switch_event = map_switch_event,
1729 .runtime_event = NULL,
1730 .fork_event = NULL,
1731};
1732
1733static void __cmd_map(void)
1734{
1735 max_cpu = sysconf(_SC_NPROCESSORS_CONF);
1736
1737 setup_pager();
1738 read_events();
1739 print_bad_events();
1740}
1741
1742static void __cmd_replay(void)
1743{
1744 unsigned long i;
1745
1746 calibrate_run_measurement_overhead();
1747 calibrate_sleep_measurement_overhead();
1748
1749 test_calibrations();
1750
1751 read_events();
1752
1753 printf("nr_run_events: %ld\n", nr_run_events);
1754 printf("nr_sleep_events: %ld\n", nr_sleep_events);
1755 printf("nr_wakeup_events: %ld\n", nr_wakeup_events);
1756
1757 if (targetless_wakeups)
1758 printf("target-less wakeups: %ld\n", targetless_wakeups);
1759 if (multitarget_wakeups)
1760 printf("multi-target wakeups: %ld\n", multitarget_wakeups);
1761 if (nr_run_events_optimized)
1762 printf("run atoms optimized: %ld\n",
1763 nr_run_events_optimized);
1764
1765 print_task_traces();
1766 add_cross_task_wakeups();
1767
1768 create_tasks();
1769 printf("------------------------------------------------------------\n");
1770 for (i = 0; i < replay_repeat; i++)
1771 run_one_test();
1772}
1773
1774
1775static const char * const sched_usage[] = {
1776 "perf sched [<options>] {record|latency|map|replay|trace}",
1777 NULL
1778};
1779
1780static const struct option sched_options[] = {
1781 OPT_STRING('i', "input", &input_name, "file",
1782 "input file name"),
1783 OPT_INCR('v', "verbose", &verbose,
1784 "be more verbose (show symbol address, etc)"),
1785 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
1786 "dump raw trace in ASCII"),
1787 OPT_END()
1788};
1789
1790static const char * const latency_usage[] = {
1791 "perf sched latency [<options>]",
1792 NULL
1793};
1794
1795static const struct option latency_options[] = {
1796 OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
1797 "sort by key(s): runtime, switch, avg, max"),
1798 OPT_INCR('v', "verbose", &verbose,
1799 "be more verbose (show symbol address, etc)"),
1800 OPT_INTEGER('C', "CPU", &profile_cpu,
1801 "CPU to profile on"),
1802 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
1803 "dump raw trace in ASCII"),
1804 OPT_END()
1805};
1806
1807static const char * const replay_usage[] = {
1808 "perf sched replay [<options>]",
1809 NULL
1810};
1811
1812static const struct option replay_options[] = {
1813 OPT_UINTEGER('r', "repeat", &replay_repeat,
1814 "repeat the workload replay N times (-1: infinite)"),
1815 OPT_INCR('v', "verbose", &verbose,
1816 "be more verbose (show symbol address, etc)"),
1817 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
1818 "dump raw trace in ASCII"),
1819 OPT_END()
1820};
1821
1822static void setup_sorting(void)
1823{
1824 char *tmp, *tok, *str = strdup(sort_order);
1825
1826 for (tok = strtok_r(str, ", ", &tmp);
1827 tok; tok = strtok_r(NULL, ", ", &tmp)) {
1828 if (sort_dimension__add(tok, &sort_list) < 0) {
1829 error("Unknown --sort key: `%s'", tok);
1830 usage_with_options(latency_usage, latency_options);
1831 }
1832 }
1833
1834 free(str);
1835
1836 sort_dimension__add("pid", &cmp_pid);
1837}
1838
1839static const char *record_args[] = {
1840 "record",
1841 "-a",
1842 "-R",
1843 "-f",
1844 "-m", "1024",
1845 "-c", "1",
1846 "-e", "sched:sched_switch:r",
1847 "-e", "sched:sched_stat_wait:r",
1848 "-e", "sched:sched_stat_sleep:r",
1849 "-e", "sched:sched_stat_iowait:r",
1850 "-e", "sched:sched_stat_runtime:r",
1851 "-e", "sched:sched_process_exit:r",
1852 "-e", "sched:sched_process_fork:r",
1853 "-e", "sched:sched_wakeup:r",
1854 "-e", "sched:sched_migrate_task:r",
1855};
1856
1857static int __cmd_record(int argc, const char **argv)
1858{
1859 unsigned int rec_argc, i, j;
1860 const char **rec_argv;
1861
1862 rec_argc = ARRAY_SIZE(record_args) + argc - 1;
1863 rec_argv = calloc(rec_argc + 1, sizeof(char *));
1864
1865 if (rec_argv == NULL)
1866 return -ENOMEM;
1867
1868 for (i = 0; i < ARRAY_SIZE(record_args); i++)
1869 rec_argv[i] = strdup(record_args[i]);
1870
1871 for (j = 1; j < (unsigned int)argc; j++, i++)
1872 rec_argv[i] = argv[j];
1873
1874 BUG_ON(i != rec_argc);
1875
1876 return cmd_record(i, rec_argv, NULL);
1877}
1878
1879int cmd_sched(int argc, const char **argv, const char *prefix __used)
1880{
1881 argc = parse_options(argc, argv, sched_options, sched_usage,
1882 PARSE_OPT_STOP_AT_NON_OPTION);
1883 if (!argc)
1884 usage_with_options(sched_usage, sched_options);
1885
1886 /*
1887 * Aliased to 'perf script' for now:
1888 */
1889 if (!strcmp(argv[0], "script"))
1890 return cmd_script(argc, argv, prefix);
1891
1892 symbol__init();
1893 if (!strncmp(argv[0], "rec", 3)) {
1894 return __cmd_record(argc, argv);
1895 } else if (!strncmp(argv[0], "lat", 3)) {
1896 trace_handler = &lat_ops;
1897 if (argc > 1) {
1898 argc = parse_options(argc, argv, latency_options, latency_usage, 0);
1899 if (argc)
1900 usage_with_options(latency_usage, latency_options);
1901 }
1902 setup_sorting();
1903 __cmd_lat();
1904 } else if (!strcmp(argv[0], "map")) {
1905 trace_handler = &map_ops;
1906 setup_sorting();
1907 __cmd_map();
1908 } else if (!strncmp(argv[0], "rep", 3)) {
1909 trace_handler = &replay_ops;
1910 if (argc) {
1911 argc = parse_options(argc, argv, replay_options, replay_usage, 0);
1912 if (argc)
1913 usage_with_options(replay_usage, replay_options);
1914 }
1915 __cmd_replay();
1916 } else {
1917 usage_with_options(sched_usage, sched_options);
1918 }
1919
1920 return 0;
1921}
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c
new file mode 100644
index 00000000000..150a606002e
--- /dev/null
+++ b/tools/perf/builtin-script.c
@@ -0,0 +1,821 @@
1#include "builtin.h"
2
3#include "perf.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"
10#include "util/symbol.h"
11#include "util/thread.h"
12#include "util/trace-event.h"
13#include "util/parse-options.h"
14#include "util/util.h"
15
16static char const *script_name;
17static char const *generate_script_lang;
18static bool debug_mode;
19static u64 last_timestamp;
20static u64 nr_unordered;
21extern const struct option record_options[];
22
23static int default_start_script(const char *script __unused,
24 int argc __unused,
25 const char **argv __unused)
26{
27 return 0;
28}
29
30static int default_stop_script(void)
31{
32 return 0;
33}
34
35static int default_generate_script(const char *outfile __unused)
36{
37 return 0;
38}
39
40static struct scripting_ops default_scripting_ops = {
41 .start_script = default_start_script,
42 .stop_script = default_stop_script,
43 .process_event = print_event,
44 .generate_script = default_generate_script,
45};
46
47static struct scripting_ops *scripting_ops;
48
49static void setup_scripting(void)
50{
51 setup_perl_scripting();
52 setup_python_scripting();
53
54 scripting_ops = &default_scripting_ops;
55}
56
57static int cleanup_scripting(void)
58{
59 pr_debug("\nperf script stopped\n");
60
61 return scripting_ops->stop_script();
62}
63
64static char const *input_name = "perf.data";
65
66static int process_sample_event(event_t *event, struct sample_data *sample,
67 struct perf_session *session)
68{
69 struct thread *thread = perf_session__findnew(session, event->ip.pid);
70
71 if (thread == NULL) {
72 pr_debug("problem processing %d event, skipping it.\n",
73 event->header.type);
74 return -1;
75 }
76
77 if (session->sample_type & PERF_SAMPLE_RAW) {
78 if (debug_mode) {
79 if (sample->time < last_timestamp) {
80 pr_err("Samples misordered, previous: %llu "
81 "this: %llu\n", last_timestamp,
82 sample->time);
83 nr_unordered++;
84 }
85 last_timestamp = sample->time;
86 return 0;
87 }
88 /*
89 * FIXME: better resolve from pid from the struct trace_entry
90 * field, although it should be the same than this perf
91 * event pid
92 */
93 scripting_ops->process_event(sample->cpu, sample->raw_data,
94 sample->raw_size,
95 sample->time, thread->comm);
96 }
97
98 session->hists.stats.total_period += sample->period;
99 return 0;
100}
101
102static struct perf_event_ops event_ops = {
103 .sample = process_sample_event,
104 .comm = event__process_comm,
105 .attr = event__process_attr,
106 .event_type = event__process_event_type,
107 .tracing_data = event__process_tracing_data,
108 .build_id = event__process_build_id,
109 .ordering_requires_timestamps = true,
110 .ordered_samples = true,
111};
112
113extern volatile int session_done;
114
115static void sig_handler(int sig __unused)
116{
117 session_done = 1;
118}
119
120static int __cmd_script(struct perf_session *session)
121{
122 int ret;
123
124 signal(SIGINT, sig_handler);
125
126 ret = perf_session__process_events(session, &event_ops);
127
128 if (debug_mode)
129 pr_err("Misordered timestamps: %llu\n", nr_unordered);
130
131 return ret;
132}
133
134struct script_spec {
135 struct list_head node;
136 struct scripting_ops *ops;
137 char spec[0];
138};
139
140static LIST_HEAD(script_specs);
141
142static struct script_spec *script_spec__new(const char *spec,
143 struct scripting_ops *ops)
144{
145 struct script_spec *s = malloc(sizeof(*s) + strlen(spec) + 1);
146
147 if (s != NULL) {
148 strcpy(s->spec, spec);
149 s->ops = ops;
150 }
151
152 return s;
153}
154
155static void script_spec__delete(struct script_spec *s)
156{
157 free(s->spec);
158 free(s);
159}
160
161static void script_spec__add(struct script_spec *s)
162{
163 list_add_tail(&s->node, &script_specs);
164}
165
166static struct script_spec *script_spec__find(const char *spec)
167{
168 struct script_spec *s;
169
170 list_for_each_entry(s, &script_specs, node)
171 if (strcasecmp(s->spec, spec) == 0)
172 return s;
173 return NULL;
174}
175
176static struct script_spec *script_spec__findnew(const char *spec,
177 struct scripting_ops *ops)
178{
179 struct script_spec *s = script_spec__find(spec);
180
181 if (s)
182 return s;
183
184 s = script_spec__new(spec, ops);
185 if (!s)
186 goto out_delete_spec;
187
188 script_spec__add(s);
189
190 return s;
191
192out_delete_spec:
193 script_spec__delete(s);
194
195 return NULL;
196}
197
198int script_spec_register(const char *spec, struct scripting_ops *ops)
199{
200 struct script_spec *s;
201
202 s = script_spec__find(spec);
203 if (s)
204 return -1;
205
206 s = script_spec__findnew(spec, ops);
207 if (!s)
208 return -1;
209
210 return 0;
211}
212
213static struct scripting_ops *script_spec__lookup(const char *spec)
214{
215 struct script_spec *s = script_spec__find(spec);
216 if (!s)
217 return NULL;
218
219 return s->ops;
220}
221
222static void list_available_languages(void)
223{
224 struct script_spec *s;
225
226 fprintf(stderr, "\n");
227 fprintf(stderr, "Scripting language extensions (used in "
228 "perf script -s [spec:]script.[spec]):\n\n");
229
230 list_for_each_entry(s, &script_specs, node)
231 fprintf(stderr, " %-42s [%s]\n", s->spec, s->ops->name);
232
233 fprintf(stderr, "\n");
234}
235
236static int parse_scriptname(const struct option *opt __used,
237 const char *str, int unset __used)
238{
239 char spec[PATH_MAX];
240 const char *script, *ext;
241 int len;
242
243 if (strcmp(str, "lang") == 0) {
244 list_available_languages();
245 exit(0);
246 }
247
248 script = strchr(str, ':');
249 if (script) {
250 len = script - str;
251 if (len >= PATH_MAX) {
252 fprintf(stderr, "invalid language specifier");
253 return -1;
254 }
255 strncpy(spec, str, len);
256 spec[len] = '\0';
257 scripting_ops = script_spec__lookup(spec);
258 if (!scripting_ops) {
259 fprintf(stderr, "invalid language specifier");
260 return -1;
261 }
262 script++;
263 } else {
264 script = str;
265 ext = strrchr(script, '.');
266 if (!ext) {
267 fprintf(stderr, "invalid script extension");
268 return -1;
269 }
270 scripting_ops = script_spec__lookup(++ext);
271 if (!scripting_ops) {
272 fprintf(stderr, "invalid script extension");
273 return -1;
274 }
275 }
276
277 script_name = strdup(script);
278
279 return 0;
280}
281
282/* Helper function for filesystems that return a dent->d_type DT_UNKNOWN */
283static int is_directory(const char *base_path, const struct dirent *dent)
284{
285 char path[PATH_MAX];
286 struct stat st;
287
288 sprintf(path, "%s/%s", base_path, dent->d_name);
289 if (stat(path, &st))
290 return 0;
291
292 return S_ISDIR(st.st_mode);
293}
294
295#define for_each_lang(scripts_path, scripts_dir, lang_dirent, lang_next)\
296 while (!readdir_r(scripts_dir, &lang_dirent, &lang_next) && \
297 lang_next) \
298 if ((lang_dirent.d_type == DT_DIR || \
299 (lang_dirent.d_type == DT_UNKNOWN && \
300 is_directory(scripts_path, &lang_dirent))) && \
301 (strcmp(lang_dirent.d_name, ".")) && \
302 (strcmp(lang_dirent.d_name, "..")))
303
304#define for_each_script(lang_path, lang_dir, script_dirent, script_next)\
305 while (!readdir_r(lang_dir, &script_dirent, &script_next) && \
306 script_next) \
307 if (script_dirent.d_type != DT_DIR && \
308 (script_dirent.d_type != DT_UNKNOWN || \
309 !is_directory(lang_path, &script_dirent)))
310
311
312#define RECORD_SUFFIX "-record"
313#define REPORT_SUFFIX "-report"
314
315struct script_desc {
316 struct list_head node;
317 char *name;
318 char *half_liner;
319 char *args;
320};
321
322static LIST_HEAD(script_descs);
323
324static struct script_desc *script_desc__new(const char *name)
325{
326 struct script_desc *s = zalloc(sizeof(*s));
327
328 if (s != NULL && name)
329 s->name = strdup(name);
330
331 return s;
332}
333
334static void script_desc__delete(struct script_desc *s)
335{
336 free(s->name);
337 free(s->half_liner);
338 free(s->args);
339 free(s);
340}
341
342static void script_desc__add(struct script_desc *s)
343{
344 list_add_tail(&s->node, &script_descs);
345}
346
347static struct script_desc *script_desc__find(const char *name)
348{
349 struct script_desc *s;
350
351 list_for_each_entry(s, &script_descs, node)
352 if (strcasecmp(s->name, name) == 0)
353 return s;
354 return NULL;
355}
356
357static struct script_desc *script_desc__findnew(const char *name)
358{
359 struct script_desc *s = script_desc__find(name);
360
361 if (s)
362 return s;
363
364 s = script_desc__new(name);
365 if (!s)
366 goto out_delete_desc;
367
368 script_desc__add(s);
369
370 return s;
371
372out_delete_desc:
373 script_desc__delete(s);
374
375 return NULL;
376}
377
378static const char *ends_with(const char *str, const char *suffix)
379{
380 size_t suffix_len = strlen(suffix);
381 const char *p = str;
382
383 if (strlen(str) > suffix_len) {
384 p = str + strlen(str) - suffix_len;
385 if (!strncmp(p, suffix, suffix_len))
386 return p;
387 }
388
389 return NULL;
390}
391
392static char *ltrim(char *str)
393{
394 int len = strlen(str);
395
396 while (len && isspace(*str)) {
397 len--;
398 str++;
399 }
400
401 return str;
402}
403
404static int read_script_info(struct script_desc *desc, const char *filename)
405{
406 char line[BUFSIZ], *p;
407 FILE *fp;
408
409 fp = fopen(filename, "r");
410 if (!fp)
411 return -1;
412
413 while (fgets(line, sizeof(line), fp)) {
414 p = ltrim(line);
415 if (strlen(p) == 0)
416 continue;
417 if (*p != '#')
418 continue;
419 p++;
420 if (strlen(p) && *p == '!')
421 continue;
422
423 p = ltrim(p);
424 if (strlen(p) && p[strlen(p) - 1] == '\n')
425 p[strlen(p) - 1] = '\0';
426
427 if (!strncmp(p, "description:", strlen("description:"))) {
428 p += strlen("description:");
429 desc->half_liner = strdup(ltrim(p));
430 continue;
431 }
432
433 if (!strncmp(p, "args:", strlen("args:"))) {
434 p += strlen("args:");
435 desc->args = strdup(ltrim(p));
436 continue;
437 }
438 }
439
440 fclose(fp);
441
442 return 0;
443}
444
445static int list_available_scripts(const struct option *opt __used,
446 const char *s __used, int unset __used)
447{
448 struct dirent *script_next, *lang_next, script_dirent, lang_dirent;
449 char scripts_path[MAXPATHLEN];
450 DIR *scripts_dir, *lang_dir;
451 char script_path[MAXPATHLEN];
452 char lang_path[MAXPATHLEN];
453 struct script_desc *desc;
454 char first_half[BUFSIZ];
455 char *script_root;
456 char *str;
457
458 snprintf(scripts_path, MAXPATHLEN, "%s/scripts", perf_exec_path());
459
460 scripts_dir = opendir(scripts_path);
461 if (!scripts_dir)
462 return -1;
463
464 for_each_lang(scripts_path, scripts_dir, lang_dirent, lang_next) {
465 snprintf(lang_path, MAXPATHLEN, "%s/%s/bin", scripts_path,
466 lang_dirent.d_name);
467 lang_dir = opendir(lang_path);
468 if (!lang_dir)
469 continue;
470
471 for_each_script(lang_path, lang_dir, script_dirent, script_next) {
472 script_root = strdup(script_dirent.d_name);
473 str = (char *)ends_with(script_root, REPORT_SUFFIX);
474 if (str) {
475 *str = '\0';
476 desc = script_desc__findnew(script_root);
477 snprintf(script_path, MAXPATHLEN, "%s/%s",
478 lang_path, script_dirent.d_name);
479 read_script_info(desc, script_path);
480 }
481 free(script_root);
482 }
483 }
484
485 fprintf(stdout, "List of available trace scripts:\n");
486 list_for_each_entry(desc, &script_descs, node) {
487 sprintf(first_half, "%s %s", desc->name,
488 desc->args ? desc->args : "");
489 fprintf(stdout, " %-36s %s\n", first_half,
490 desc->half_liner ? desc->half_liner : "");
491 }
492
493 exit(0);
494}
495
496static char *get_script_path(const char *script_root, const char *suffix)
497{
498 struct dirent *script_next, *lang_next, script_dirent, lang_dirent;
499 char scripts_path[MAXPATHLEN];
500 char script_path[MAXPATHLEN];
501 DIR *scripts_dir, *lang_dir;
502 char lang_path[MAXPATHLEN];
503 char *str, *__script_root;
504 char *path = NULL;
505
506 snprintf(scripts_path, MAXPATHLEN, "%s/scripts", perf_exec_path());
507
508 scripts_dir = opendir(scripts_path);
509 if (!scripts_dir)
510 return NULL;
511
512 for_each_lang(scripts_path, scripts_dir, lang_dirent, lang_next) {
513 snprintf(lang_path, MAXPATHLEN, "%s/%s/bin", scripts_path,
514 lang_dirent.d_name);
515 lang_dir = opendir(lang_path);
516 if (!lang_dir)
517 continue;
518
519 for_each_script(lang_path, lang_dir, script_dirent, script_next) {
520 __script_root = strdup(script_dirent.d_name);
521 str = (char *)ends_with(__script_root, suffix);
522 if (str) {
523 *str = '\0';
524 if (strcmp(__script_root, script_root))
525 continue;
526 snprintf(script_path, MAXPATHLEN, "%s/%s",
527 lang_path, script_dirent.d_name);
528 path = strdup(script_path);
529 free(__script_root);
530 break;
531 }
532 free(__script_root);
533 }
534 }
535
536 return path;
537}
538
539static bool is_top_script(const char *script_path)
540{
541 return ends_with(script_path, "top") == NULL ? false : true;
542}
543
544static int has_required_arg(char *script_path)
545{
546 struct script_desc *desc;
547 int n_args = 0;
548 char *p;
549
550 desc = script_desc__new(NULL);
551
552 if (read_script_info(desc, script_path))
553 goto out;
554
555 if (!desc->args)
556 goto out;
557
558 for (p = desc->args; *p; p++)
559 if (*p == '<')
560 n_args++;
561out:
562 script_desc__delete(desc);
563
564 return n_args;
565}
566
567static const char * const script_usage[] = {
568 "perf script [<options>]",
569 "perf script [<options>] record <script> [<record-options>] <command>",
570 "perf script [<options>] report <script> [script-args]",
571 "perf script [<options>] <script> [<record-options>] <command>",
572 "perf script [<options>] <top-script> [script-args]",
573 NULL
574};
575
576static const struct option options[] = {
577 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
578 "dump raw trace in ASCII"),
579 OPT_INCR('v', "verbose", &verbose,
580 "be more verbose (show symbol address, etc)"),
581 OPT_BOOLEAN('L', "Latency", &latency_format,
582 "show latency attributes (irqs/preemption disabled, etc)"),
583 OPT_CALLBACK_NOOPT('l', "list", NULL, NULL, "list available scripts",
584 list_available_scripts),
585 OPT_CALLBACK('s', "script", NULL, "name",
586 "script file name (lang:script name, script name, or *)",
587 parse_scriptname),
588 OPT_STRING('g', "gen-script", &generate_script_lang, "lang",
589 "generate perf-script.xx script in specified language"),
590 OPT_STRING('i', "input", &input_name, "file",
591 "input file name"),
592 OPT_BOOLEAN('d', "debug-mode", &debug_mode,
593 "do various checks like samples ordering and lost events"),
594
595 OPT_END()
596};
597
598static bool have_cmd(int argc, const char **argv)
599{
600 char **__argv = malloc(sizeof(const char *) * argc);
601
602 if (!__argv)
603 die("malloc");
604 memcpy(__argv, argv, sizeof(const char *) * argc);
605 argc = parse_options(argc, (const char **)__argv, record_options,
606 NULL, PARSE_OPT_STOP_AT_NON_OPTION);
607 free(__argv);
608
609 return argc != 0;
610}
611
612int cmd_script(int argc, const char **argv, const char *prefix __used)
613{
614 char *rec_script_path = NULL;
615 char *rep_script_path = NULL;
616 struct perf_session *session;
617 char *script_path = NULL;
618 const char **__argv;
619 bool system_wide;
620 int i, j, err;
621
622 setup_scripting();
623
624 argc = parse_options(argc, argv, options, script_usage,
625 PARSE_OPT_STOP_AT_NON_OPTION);
626
627 if (argc > 1 && !strncmp(argv[0], "rec", strlen("rec"))) {
628 rec_script_path = get_script_path(argv[1], RECORD_SUFFIX);
629 if (!rec_script_path)
630 return cmd_record(argc, argv, NULL);
631 }
632
633 if (argc > 1 && !strncmp(argv[0], "rep", strlen("rep"))) {
634 rep_script_path = get_script_path(argv[1], REPORT_SUFFIX);
635 if (!rep_script_path) {
636 fprintf(stderr,
637 "Please specify a valid report script"
638 "(see 'perf script -l' for listing)\n");
639 return -1;
640 }
641 }
642
643 /* make sure PERF_EXEC_PATH is set for scripts */
644 perf_set_argv_exec_path(perf_exec_path());
645
646 if (argc && !script_name && !rec_script_path && !rep_script_path) {
647 int live_pipe[2];
648 int rep_args;
649 pid_t pid;
650
651 rec_script_path = get_script_path(argv[0], RECORD_SUFFIX);
652 rep_script_path = get_script_path(argv[0], REPORT_SUFFIX);
653
654 if (!rec_script_path && !rep_script_path) {
655 fprintf(stderr, " Couldn't find script %s\n\n See perf"
656 " script -l for available scripts.\n", argv[0]);
657 usage_with_options(script_usage, options);
658 }
659
660 if (is_top_script(argv[0])) {
661 rep_args = argc - 1;
662 } else {
663 int rec_args;
664
665 rep_args = has_required_arg(rep_script_path);
666 rec_args = (argc - 1) - rep_args;
667 if (rec_args < 0) {
668 fprintf(stderr, " %s script requires options."
669 "\n\n See perf script -l for available "
670 "scripts and options.\n", argv[0]);
671 usage_with_options(script_usage, options);
672 }
673 }
674
675 if (pipe(live_pipe) < 0) {
676 perror("failed to create pipe");
677 exit(-1);
678 }
679
680 pid = fork();
681 if (pid < 0) {
682 perror("failed to fork");
683 exit(-1);
684 }
685
686 if (!pid) {
687 system_wide = true;
688 j = 0;
689
690 dup2(live_pipe[1], 1);
691 close(live_pipe[0]);
692
693 if (!is_top_script(argv[0]))
694 system_wide = !have_cmd(argc - rep_args,
695 &argv[rep_args]);
696
697 __argv = malloc((argc + 6) * sizeof(const char *));
698 if (!__argv)
699 die("malloc");
700
701 __argv[j++] = "/bin/sh";
702 __argv[j++] = rec_script_path;
703 if (system_wide)
704 __argv[j++] = "-a";
705 __argv[j++] = "-q";
706 __argv[j++] = "-o";
707 __argv[j++] = "-";
708 for (i = rep_args + 1; i < argc; i++)
709 __argv[j++] = argv[i];
710 __argv[j++] = NULL;
711
712 execvp("/bin/sh", (char **)__argv);
713 free(__argv);
714 exit(-1);
715 }
716
717 dup2(live_pipe[0], 0);
718 close(live_pipe[1]);
719
720 __argv = malloc((argc + 4) * sizeof(const char *));
721 if (!__argv)
722 die("malloc");
723 j = 0;
724 __argv[j++] = "/bin/sh";
725 __argv[j++] = rep_script_path;
726 for (i = 1; i < rep_args + 1; i++)
727 __argv[j++] = argv[i];
728 __argv[j++] = "-i";
729 __argv[j++] = "-";
730 __argv[j++] = NULL;
731
732 execvp("/bin/sh", (char **)__argv);
733 free(__argv);
734 exit(-1);
735 }
736
737 if (rec_script_path)
738 script_path = rec_script_path;
739 if (rep_script_path)
740 script_path = rep_script_path;
741
742 if (script_path) {
743 system_wide = false;
744 j = 0;
745
746 if (rec_script_path)
747 system_wide = !have_cmd(argc - 1, &argv[1]);
748
749 __argv = malloc((argc + 2) * sizeof(const char *));
750 if (!__argv)
751 die("malloc");
752 __argv[j++] = "/bin/sh";
753 __argv[j++] = script_path;
754 if (system_wide)
755 __argv[j++] = "-a";
756 for (i = 2; i < argc; i++)
757 __argv[j++] = argv[i];
758 __argv[j++] = NULL;
759
760 execvp("/bin/sh", (char **)__argv);
761 free(__argv);
762 exit(-1);
763 }
764
765 if (symbol__init() < 0)
766 return -1;
767 if (!script_name)
768 setup_pager();
769
770 session = perf_session__new(input_name, O_RDONLY, 0, false, &event_ops);
771 if (session == NULL)
772 return -ENOMEM;
773
774 if (strcmp(input_name, "-") &&
775 !perf_session__has_traces(session, "record -R"))
776 return -EINVAL;
777
778 if (generate_script_lang) {
779 struct stat perf_stat;
780
781 int input = open(input_name, O_RDONLY);
782 if (input < 0) {
783 perror("failed to open file");
784 exit(-1);
785 }
786
787 err = fstat(input, &perf_stat);
788 if (err < 0) {
789 perror("failed to stat file");
790 exit(-1);
791 }
792
793 if (!perf_stat.st_size) {
794 fprintf(stderr, "zero-sized file, nothing to do!\n");
795 exit(0);
796 }
797
798 scripting_ops = script_spec__lookup(generate_script_lang);
799 if (!scripting_ops) {
800 fprintf(stderr, "invalid language specifier");
801 return -1;
802 }
803
804 err = scripting_ops->generate_script("perf-script");
805 goto out;
806 }
807
808 if (script_name) {
809 err = scripting_ops->start_script(script_name, argc, argv);
810 if (err)
811 goto out;
812 pr_debug("perf script started with script %s\n\n", script_name);
813 }
814
815 err = __cmd_script(session);
816
817 perf_session__delete(session);
818 cleanup_scripting();
819out:
820 return err;
821}
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c
index 6d3eeac1ea2..c385a63ebfd 100644
--- a/tools/perf/builtin-stat.c
+++ b/tools/perf/builtin-stat.c
@@ -32,6 +32,7 @@
32 * Wu Fengguang <fengguang.wu@intel.com> 32 * Wu Fengguang <fengguang.wu@intel.com>
33 * Mike Galbraith <efault@gmx.de> 33 * Mike Galbraith <efault@gmx.de>
34 * Paul Mackerras <paulus@samba.org> 34 * Paul Mackerras <paulus@samba.org>
35 * Jaswinder Singh Rajput <jaswinder@kernel.org>
35 * 36 *
36 * Released under the GPL v2. (and only v2, not any later version) 37 * Released under the GPL v2. (and only v2, not any later version)
37 */ 38 */
@@ -41,113 +42,148 @@
41#include "util/util.h" 42#include "util/util.h"
42#include "util/parse-options.h" 43#include "util/parse-options.h"
43#include "util/parse-events.h" 44#include "util/parse-events.h"
45#include "util/event.h"
46#include "util/evsel.h"
47#include "util/debug.h"
48#include "util/header.h"
49#include "util/cpumap.h"
50#include "util/thread.h"
44 51
45#include <sys/prctl.h> 52#include <sys/prctl.h>
46#include <math.h> 53#include <math.h>
54#include <locale.h>
47 55
48static struct perf_counter_attr default_attrs[MAX_COUNTERS] = { 56#define DEFAULT_SEPARATOR " "
49 57
50 { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_TASK_CLOCK }, 58static struct perf_event_attr default_attrs[] = {
51 { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_CONTEXT_SWITCHES},
52 { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_CPU_MIGRATIONS },
53 { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_PAGE_FAULTS },
54 59
55 { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_CPU_CYCLES }, 60 { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_TASK_CLOCK },
56 { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_INSTRUCTIONS }, 61 { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_CONTEXT_SWITCHES },
57 { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_CACHE_REFERENCES}, 62 { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_CPU_MIGRATIONS },
58 { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_CACHE_MISSES }, 63 { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_PAGE_FAULTS },
59 64
60}; 65 { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_CPU_CYCLES },
61 66 { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_INSTRUCTIONS },
62static int system_wide = 0; 67 { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS },
63static int inherit = 1; 68 { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_BRANCH_MISSES },
64static int verbose = 0; 69 { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_CACHE_REFERENCES },
65 70 { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_CACHE_MISSES },
66static int fd[MAX_NR_CPUS][MAX_COUNTERS];
67 71
68static int target_pid = -1;
69static int nr_cpus = 0;
70static unsigned int page_size;
71
72static int scale = 1;
73
74static const unsigned int default_count[] = {
75 1000000,
76 1000000,
77 10000,
78 10000,
79 1000000,
80 10000,
81}; 72};
82 73
83#define MAX_RUN 100 74static bool system_wide = false;
84 75static struct cpu_map *cpus;
85static int run_count = 1; 76static int run_idx = 0;
86static int run_idx = 0; 77
78static int run_count = 1;
79static bool no_inherit = false;
80static bool scale = true;
81static bool no_aggr = false;
82static pid_t target_pid = -1;
83static pid_t target_tid = -1;
84static struct thread_map *threads;
85static pid_t child_pid = -1;
86static bool null_run = false;
87static bool big_num = true;
88static int big_num_opt = -1;
89static const char *cpu_list;
90static const char *csv_sep = NULL;
91static bool csv_output = false;
92
93static volatile int done = 0;
94
95struct stats
96{
97 double n, mean, M2;
98};
87 99
88static u64 event_res[MAX_RUN][MAX_COUNTERS][3]; 100struct perf_stat {
89static u64 event_scaled[MAX_RUN][MAX_COUNTERS]; 101 struct stats res_stats[3];
102};
90 103
91//static u64 event_hist[MAX_RUN][MAX_COUNTERS][3]; 104static int perf_evsel__alloc_stat_priv(struct perf_evsel *evsel)
105{
106 evsel->priv = zalloc(sizeof(struct perf_stat));
107 return evsel->priv == NULL ? -ENOMEM : 0;
108}
92 109
110static void perf_evsel__free_stat_priv(struct perf_evsel *evsel)
111{
112 free(evsel->priv);
113 evsel->priv = NULL;
114}
93 115
94static u64 runtime_nsecs[MAX_RUN]; 116static void update_stats(struct stats *stats, u64 val)
95static u64 walltime_nsecs[MAX_RUN]; 117{
96static u64 runtime_cycles[MAX_RUN]; 118 double delta;
97 119
98static u64 event_res_avg[MAX_COUNTERS][3]; 120 stats->n++;
99static u64 event_res_noise[MAX_COUNTERS][3]; 121 delta = val - stats->mean;
122 stats->mean += delta / stats->n;
123 stats->M2 += delta*(val - stats->mean);
124}
100 125
101static u64 event_scaled_avg[MAX_COUNTERS]; 126static double avg_stats(struct stats *stats)
127{
128 return stats->mean;
129}
102 130
103static u64 runtime_nsecs_avg; 131/*
104static u64 runtime_nsecs_noise; 132 * http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance
133 *
134 * (\Sum n_i^2) - ((\Sum n_i)^2)/n
135 * s^2 = -------------------------------
136 * n - 1
137 *
138 * http://en.wikipedia.org/wiki/Stddev
139 *
140 * The std dev of the mean is related to the std dev by:
141 *
142 * s
143 * s_mean = -------
144 * sqrt(n)
145 *
146 */
147static double stddev_stats(struct stats *stats)
148{
149 double variance = stats->M2 / (stats->n - 1);
150 double variance_mean = variance / stats->n;
105 151
106static u64 walltime_nsecs_avg; 152 return sqrt(variance_mean);
107static u64 walltime_nsecs_noise; 153}
108 154
109static u64 runtime_cycles_avg; 155struct stats runtime_nsecs_stats[MAX_NR_CPUS];
110static u64 runtime_cycles_noise; 156struct stats runtime_cycles_stats[MAX_NR_CPUS];
157struct stats runtime_branches_stats[MAX_NR_CPUS];
158struct stats walltime_nsecs_stats;
111 159
112static void create_perf_stat_counter(int counter) 160static int create_perf_stat_counter(struct perf_evsel *evsel)
113{ 161{
114 struct perf_counter_attr *attr = attrs + counter; 162 struct perf_event_attr *attr = &evsel->attr;
115 163
116 if (scale) 164 if (scale)
117 attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | 165 attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED |
118 PERF_FORMAT_TOTAL_TIME_RUNNING; 166 PERF_FORMAT_TOTAL_TIME_RUNNING;
119 167
120 if (system_wide) { 168 if (system_wide)
121 int cpu; 169 return perf_evsel__open_per_cpu(evsel, cpus);
122 for (cpu = 0; cpu < nr_cpus; cpu ++) {
123 fd[cpu][counter] = sys_perf_counter_open(attr, -1, cpu, -1, 0);
124 if (fd[cpu][counter] < 0 && verbose) {
125 printf("Error: counter %d, sys_perf_counter_open() syscall returned with %d (%s)\n", counter, fd[cpu][counter], strerror(errno));
126 }
127 }
128 } else {
129 attr->inherit = inherit;
130 attr->disabled = 1;
131 170
132 fd[0][counter] = sys_perf_counter_open(attr, 0, -1, -1, 0); 171 attr->inherit = !no_inherit;
133 if (fd[0][counter] < 0 && verbose) { 172 if (target_pid == -1 && target_tid == -1) {
134 printf("Error: counter %d, sys_perf_counter_open() syscall returned with %d (%s)\n", counter, fd[0][counter], strerror(errno)); 173 attr->disabled = 1;
135 } 174 attr->enable_on_exec = 1;
136 } 175 }
176
177 return perf_evsel__open_per_thread(evsel, threads);
137} 178}
138 179
139/* 180/*
140 * Does the counter have nsecs as a unit? 181 * Does the counter have nsecs as a unit?
141 */ 182 */
142static inline int nsec_counter(int counter) 183static inline int nsec_counter(struct perf_evsel *evsel)
143{ 184{
144 if (attrs[counter].type != PERF_TYPE_SOFTWARE) 185 if (perf_evsel__match(evsel, SOFTWARE, SW_CPU_CLOCK) ||
145 return 0; 186 perf_evsel__match(evsel, SOFTWARE, SW_TASK_CLOCK))
146
147 if (attrs[counter].config == PERF_COUNT_SW_CPU_CLOCK)
148 return 1;
149
150 if (attrs[counter].config == PERF_COUNT_SW_TASK_CLOCK)
151 return 1; 187 return 1;
152 188
153 return 0; 189 return 0;
@@ -155,296 +191,405 @@ static inline int nsec_counter(int counter)
155 191
156/* 192/*
157 * Read out the results of a single counter: 193 * Read out the results of a single counter:
194 * aggregate counts across CPUs in system-wide mode
158 */ 195 */
159static void read_counter(int counter) 196static int read_counter_aggr(struct perf_evsel *counter)
160{ 197{
161 u64 *count, single_count[3]; 198 struct perf_stat *ps = counter->priv;
162 ssize_t res; 199 u64 *count = counter->counts->aggr.values;
163 int cpu, nv; 200 int i;
164 int scaled;
165
166 count = event_res[run_idx][counter];
167 201
168 count[0] = count[1] = count[2] = 0; 202 if (__perf_evsel__read(counter, cpus->nr, threads->nr, scale) < 0)
203 return -1;
169 204
170 nv = scale ? 3 : 1; 205 for (i = 0; i < 3; i++)
171 for (cpu = 0; cpu < nr_cpus; cpu ++) { 206 update_stats(&ps->res_stats[i], count[i]);
172 if (fd[cpu][counter] < 0)
173 continue;
174 207
175 res = read(fd[cpu][counter], single_count, nv * sizeof(u64)); 208 if (verbose) {
176 assert(res == nv * sizeof(u64)); 209 fprintf(stderr, "%s: %Ld %Ld %Ld\n", event_name(counter),
177 close(fd[cpu][counter]); 210 count[0], count[1], count[2]);
178 fd[cpu][counter] = -1;
179
180 count[0] += single_count[0];
181 if (scale) {
182 count[1] += single_count[1];
183 count[2] += single_count[2];
184 }
185 } 211 }
186 212
187 scaled = 0;
188 if (scale) {
189 if (count[2] == 0) {
190 event_scaled[run_idx][counter] = -1;
191 count[0] = 0;
192 return;
193 }
194
195 if (count[2] < count[1]) {
196 event_scaled[run_idx][counter] = 1;
197 count[0] = (unsigned long long)
198 ((double)count[0] * count[1] / count[2] + 0.5);
199 }
200 }
201 /* 213 /*
202 * Save the full runtime - to allow normalization during printout: 214 * Save the full runtime - to allow normalization during printout:
203 */ 215 */
204 if (attrs[counter].type == PERF_TYPE_SOFTWARE && 216 if (perf_evsel__match(counter, SOFTWARE, SW_TASK_CLOCK))
205 attrs[counter].config == PERF_COUNT_SW_TASK_CLOCK) 217 update_stats(&runtime_nsecs_stats[0], count[0]);
206 runtime_nsecs[run_idx] = count[0]; 218 if (perf_evsel__match(counter, HARDWARE, HW_CPU_CYCLES))
207 if (attrs[counter].type == PERF_TYPE_HARDWARE && 219 update_stats(&runtime_cycles_stats[0], count[0]);
208 attrs[counter].config == PERF_COUNT_HW_CPU_CYCLES) 220 if (perf_evsel__match(counter, HARDWARE, HW_BRANCH_INSTRUCTIONS))
209 runtime_cycles[run_idx] = count[0]; 221 update_stats(&runtime_branches_stats[0], count[0]);
222
223 return 0;
210} 224}
211 225
212static int run_perf_stat(int argc, const char **argv) 226/*
227 * Read out the results of a single counter:
228 * do not aggregate counts across CPUs in system-wide mode
229 */
230static int read_counter(struct perf_evsel *counter)
231{
232 u64 *count;
233 int cpu;
234
235 for (cpu = 0; cpu < cpus->nr; cpu++) {
236 if (__perf_evsel__read_on_cpu(counter, cpu, 0, scale) < 0)
237 return -1;
238
239 count = counter->counts->cpu[cpu].values;
240
241 if (perf_evsel__match(counter, SOFTWARE, SW_TASK_CLOCK))
242 update_stats(&runtime_nsecs_stats[cpu], count[0]);
243 if (perf_evsel__match(counter, HARDWARE, HW_CPU_CYCLES))
244 update_stats(&runtime_cycles_stats[cpu], count[0]);
245 if (perf_evsel__match(counter, HARDWARE, HW_BRANCH_INSTRUCTIONS))
246 update_stats(&runtime_branches_stats[cpu], count[0]);
247 }
248
249 return 0;
250}
251
252static int run_perf_stat(int argc __used, const char **argv)
213{ 253{
214 unsigned long long t0, t1; 254 unsigned long long t0, t1;
255 struct perf_evsel *counter;
215 int status = 0; 256 int status = 0;
216 int counter; 257 int child_ready_pipe[2], go_pipe[2];
217 int pid; 258 const bool forks = (argc > 0);
259 char buf;
218 260
219 if (!system_wide) 261 if (forks && (pipe(child_ready_pipe) < 0 || pipe(go_pipe) < 0)) {
220 nr_cpus = 1; 262 perror("failed to create pipes");
263 exit(1);
264 }
221 265
222 for (counter = 0; counter < nr_counters; counter++) 266 if (forks) {
223 create_perf_stat_counter(counter); 267 if ((child_pid = fork()) < 0)
268 perror("failed to fork");
224 269
225 /* 270 if (!child_pid) {
226 * Enable counters and exec the command: 271 close(child_ready_pipe[0]);
227 */ 272 close(go_pipe[1]);
228 t0 = rdclock(); 273 fcntl(go_pipe[0], F_SETFD, FD_CLOEXEC);
229 prctl(PR_TASK_PERF_COUNTERS_ENABLE); 274
275 /*
276 * Do a dummy execvp to get the PLT entry resolved,
277 * so we avoid the resolver overhead on the real
278 * execvp call.
279 */
280 execvp("", (char **)argv);
281
282 /*
283 * Tell the parent we're ready to go
284 */
285 close(child_ready_pipe[1]);
286
287 /*
288 * Wait until the parent tells us to go.
289 */
290 if (read(go_pipe[0], &buf, 1) == -1)
291 perror("unable to read pipe");
230 292
231 if ((pid = fork()) < 0) 293 execvp(argv[0], (char **)argv);
232 perror("failed to fork");
233 294
234 if (!pid) {
235 if (execvp(argv[0], (char **)argv)) {
236 perror(argv[0]); 295 perror(argv[0]);
237 exit(-1); 296 exit(-1);
238 } 297 }
298
299 if (target_tid == -1 && target_pid == -1 && !system_wide)
300 threads->map[0] = child_pid;
301
302 /*
303 * Wait for the child to be ready to exec.
304 */
305 close(child_ready_pipe[1]);
306 close(go_pipe[0]);
307 if (read(child_ready_pipe[0], &buf, 1) == -1)
308 perror("unable to read pipe");
309 close(child_ready_pipe[0]);
239 } 310 }
240 311
241 wait(&status); 312 list_for_each_entry(counter, &evsel_list, node) {
313 if (create_perf_stat_counter(counter) < 0) {
314 if (errno == -EPERM || errno == -EACCES) {
315 error("You may not have permission to collect %sstats.\n"
316 "\t Consider tweaking"
317 " /proc/sys/kernel/perf_event_paranoid or running as root.",
318 system_wide ? "system-wide " : "");
319 } else if (errno == ENOENT) {
320 error("%s event is not supported. ", event_name(counter));
321 } else {
322 error("open_counter returned with %d (%s). "
323 "/bin/dmesg may provide additional information.\n",
324 errno, strerror(errno));
325 }
326 if (child_pid != -1)
327 kill(child_pid, SIGTERM);
328 die("Not all events could be opened.\n");
329 return -1;
330 }
331 }
332
333 /*
334 * Enable counters and exec the command:
335 */
336 t0 = rdclock();
337
338 if (forks) {
339 close(go_pipe[1]);
340 wait(&status);
341 } else {
342 while(!done) sleep(1);
343 }
242 344
243 prctl(PR_TASK_PERF_COUNTERS_DISABLE);
244 t1 = rdclock(); 345 t1 = rdclock();
245 346
246 walltime_nsecs[run_idx] = t1 - t0; 347 update_stats(&walltime_nsecs_stats, t1 - t0);
247 348
248 for (counter = 0; counter < nr_counters; counter++) 349 if (no_aggr) {
249 read_counter(counter); 350 list_for_each_entry(counter, &evsel_list, node) {
351 read_counter(counter);
352 perf_evsel__close_fd(counter, cpus->nr, 1);
353 }
354 } else {
355 list_for_each_entry(counter, &evsel_list, node) {
356 read_counter_aggr(counter);
357 perf_evsel__close_fd(counter, cpus->nr, threads->nr);
358 }
359 }
250 360
251 return WEXITSTATUS(status); 361 return WEXITSTATUS(status);
252} 362}
253 363
254static void print_noise(u64 *count, u64 *noise) 364static void print_noise(struct perf_evsel *evsel, double avg)
255{ 365{
256 if (run_count > 1) 366 struct perf_stat *ps;
257 fprintf(stderr, " ( +- %7.3f%% )", 367
258 (double)noise[0]/(count[0]+1)*100.0); 368 if (run_count == 1)
369 return;
370
371 ps = evsel->priv;
372 fprintf(stderr, " ( +- %7.3f%% )",
373 100 * stddev_stats(&ps->res_stats[0]) / avg);
259} 374}
260 375
261static void nsec_printout(int counter, u64 *count, u64 *noise) 376static void nsec_printout(int cpu, struct perf_evsel *evsel, double avg)
262{ 377{
263 double msecs = (double)count[0] / 1000000; 378 double msecs = avg / 1e6;
379 char cpustr[16] = { '\0', };
380 const char *fmt = csv_output ? "%s%.6f%s%s" : "%s%18.6f%s%-24s";
264 381
265 fprintf(stderr, " %14.6f %-20s", msecs, event_name(counter)); 382 if (no_aggr)
383 sprintf(cpustr, "CPU%*d%s",
384 csv_output ? 0 : -4,
385 cpus->map[cpu], csv_sep);
266 386
267 if (attrs[counter].type == PERF_TYPE_SOFTWARE && 387 fprintf(stderr, fmt, cpustr, msecs, csv_sep, event_name(evsel));
268 attrs[counter].config == PERF_COUNT_SW_TASK_CLOCK) {
269 388
270 if (walltime_nsecs_avg) 389 if (csv_output)
271 fprintf(stderr, " # %10.3f CPUs ", 390 return;
272 (double)count[0] / (double)walltime_nsecs_avg); 391
273 } 392 if (perf_evsel__match(evsel, SOFTWARE, SW_TASK_CLOCK))
274 print_noise(count, noise); 393 fprintf(stderr, " # %10.3f CPUs ",
394 avg / avg_stats(&walltime_nsecs_stats));
275} 395}
276 396
277static void abs_printout(int counter, u64 *count, u64 *noise) 397static void abs_printout(int cpu, struct perf_evsel *evsel, double avg)
278{ 398{
279 fprintf(stderr, " %14Ld %-20s", count[0], event_name(counter)); 399 double total, ratio = 0.0;
400 char cpustr[16] = { '\0', };
401 const char *fmt;
402
403 if (csv_output)
404 fmt = "%s%.0f%s%s";
405 else if (big_num)
406 fmt = "%s%'18.0f%s%-24s";
407 else
408 fmt = "%s%18.0f%s%-24s";
409
410 if (no_aggr)
411 sprintf(cpustr, "CPU%*d%s",
412 csv_output ? 0 : -4,
413 cpus->map[cpu], csv_sep);
414 else
415 cpu = 0;
280 416
281 if (runtime_cycles_avg && 417 fprintf(stderr, fmt, cpustr, avg, csv_sep, event_name(evsel));
282 attrs[counter].type == PERF_TYPE_HARDWARE &&
283 attrs[counter].config == PERF_COUNT_HW_INSTRUCTIONS) {
284 418
285 fprintf(stderr, " # %10.3f IPC ", 419 if (csv_output)
286 (double)count[0] / (double)runtime_cycles_avg); 420 return;
287 } else { 421
288 if (runtime_nsecs_avg) { 422 if (perf_evsel__match(evsel, HARDWARE, HW_INSTRUCTIONS)) {
289 fprintf(stderr, " # %10.3f M/sec", 423 total = avg_stats(&runtime_cycles_stats[cpu]);
290 (double)count[0]/runtime_nsecs_avg*1000.0); 424
291 } 425 if (total)
426 ratio = avg / total;
427
428 fprintf(stderr, " # %10.3f IPC ", ratio);
429 } else if (perf_evsel__match(evsel, HARDWARE, HW_BRANCH_MISSES) &&
430 runtime_branches_stats[cpu].n != 0) {
431 total = avg_stats(&runtime_branches_stats[cpu]);
432
433 if (total)
434 ratio = avg * 100 / total;
435
436 fprintf(stderr, " # %10.3f %% ", ratio);
437
438 } else if (runtime_nsecs_stats[cpu].n != 0) {
439 total = avg_stats(&runtime_nsecs_stats[cpu]);
440
441 if (total)
442 ratio = 1000.0 * avg / total;
443
444 fprintf(stderr, " # %10.3f M/sec", ratio);
292 } 445 }
293 print_noise(count, noise);
294} 446}
295 447
296/* 448/*
297 * Print out the results of a single counter: 449 * Print out the results of a single counter:
450 * aggregated counts in system-wide mode
298 */ 451 */
299static void print_counter(int counter) 452static void print_counter_aggr(struct perf_evsel *counter)
300{ 453{
301 u64 *count, *noise; 454 struct perf_stat *ps = counter->priv;
302 int scaled; 455 double avg = avg_stats(&ps->res_stats[0]);
303 456 int scaled = counter->counts->scaled;
304 count = event_res_avg[counter];
305 noise = event_res_noise[counter];
306 scaled = event_scaled_avg[counter];
307 457
308 if (scaled == -1) { 458 if (scaled == -1) {
309 fprintf(stderr, " %14s %-20s\n", 459 fprintf(stderr, "%*s%s%-24s\n",
310 "<not counted>", event_name(counter)); 460 csv_output ? 0 : 18,
461 "<not counted>", csv_sep, event_name(counter));
311 return; 462 return;
312 } 463 }
313 464
314 if (nsec_counter(counter)) 465 if (nsec_counter(counter))
315 nsec_printout(counter, count, noise); 466 nsec_printout(-1, counter, avg);
316 else 467 else
317 abs_printout(counter, count, noise); 468 abs_printout(-1, counter, avg);
318 469
319 if (scaled) 470 if (csv_output) {
320 fprintf(stderr, " (scaled from %.2f%%)", 471 fputc('\n', stderr);
321 (double) count[2] / count[1] * 100); 472 return;
322 473 }
323 fprintf(stderr, "\n");
324}
325 474
326/* 475 print_noise(counter, avg);
327 * normalize_noise noise values down to stddev:
328 */
329static void normalize_noise(u64 *val)
330{
331 double res;
332 476
333 res = (double)*val / (run_count * sqrt((double)run_count)); 477 if (scaled) {
478 double avg_enabled, avg_running;
334 479
335 *val = (u64)res; 480 avg_enabled = avg_stats(&ps->res_stats[1]);
336} 481 avg_running = avg_stats(&ps->res_stats[2]);
337 482
338static void update_avg(const char *name, int idx, u64 *avg, u64 *val) 483 fprintf(stderr, " (scaled from %.2f%%)",
339{ 484 100 * avg_running / avg_enabled);
340 *avg += *val; 485 }
341 486
342 if (verbose > 1) 487 fprintf(stderr, "\n");
343 fprintf(stderr, "debug: %20s[%d]: %Ld\n", name, idx, *val);
344} 488}
489
345/* 490/*
346 * Calculate the averages and noises: 491 * Print out the results of a single counter:
492 * does not use aggregated count in system-wide
347 */ 493 */
348static void calc_avg(void) 494static void print_counter(struct perf_evsel *counter)
349{ 495{
350 int i, j; 496 u64 ena, run, val;
351 497 int cpu;
352 if (verbose > 1) 498
353 fprintf(stderr, "\n"); 499 for (cpu = 0; cpu < cpus->nr; cpu++) {
354 500 val = counter->counts->cpu[cpu].val;
355 for (i = 0; i < run_count; i++) { 501 ena = counter->counts->cpu[cpu].ena;
356 update_avg("runtime", 0, &runtime_nsecs_avg, runtime_nsecs + i); 502 run = counter->counts->cpu[cpu].run;
357 update_avg("walltime", 0, &walltime_nsecs_avg, walltime_nsecs + i); 503 if (run == 0 || ena == 0) {
358 update_avg("runtime_cycles", 0, &runtime_cycles_avg, runtime_cycles + i); 504 fprintf(stderr, "CPU%*d%s%*s%s%-24s",
359 505 csv_output ? 0 : -4,
360 for (j = 0; j < nr_counters; j++) { 506 cpus->map[cpu], csv_sep,
361 update_avg("counter/0", j, 507 csv_output ? 0 : 18,
362 event_res_avg[j]+0, event_res[i][j]+0); 508 "<not counted>", csv_sep,
363 update_avg("counter/1", j, 509 event_name(counter));
364 event_res_avg[j]+1, event_res[i][j]+1); 510
365 update_avg("counter/2", j, 511 fprintf(stderr, "\n");
366 event_res_avg[j]+2, event_res[i][j]+2); 512 continue;
367 update_avg("scaled", j,
368 event_scaled_avg + j, event_scaled[i]+j);
369 } 513 }
370 }
371 runtime_nsecs_avg /= run_count;
372 walltime_nsecs_avg /= run_count;
373 runtime_cycles_avg /= run_count;
374
375 for (j = 0; j < nr_counters; j++) {
376 event_res_avg[j][0] /= run_count;
377 event_res_avg[j][1] /= run_count;
378 event_res_avg[j][2] /= run_count;
379 }
380 514
381 for (i = 0; i < run_count; i++) { 515 if (nsec_counter(counter))
382 runtime_nsecs_noise += 516 nsec_printout(cpu, counter, val);
383 abs((s64)(runtime_nsecs[i] - runtime_nsecs_avg)); 517 else
384 walltime_nsecs_noise += 518 abs_printout(cpu, counter, val);
385 abs((s64)(walltime_nsecs[i] - walltime_nsecs_avg));
386 runtime_cycles_noise +=
387 abs((s64)(runtime_cycles[i] - runtime_cycles_avg));
388
389 for (j = 0; j < nr_counters; j++) {
390 event_res_noise[j][0] +=
391 abs((s64)(event_res[i][j][0] - event_res_avg[j][0]));
392 event_res_noise[j][1] +=
393 abs((s64)(event_res[i][j][1] - event_res_avg[j][1]));
394 event_res_noise[j][2] +=
395 abs((s64)(event_res[i][j][2] - event_res_avg[j][2]));
396 }
397 }
398 519
399 normalize_noise(&runtime_nsecs_noise); 520 if (!csv_output) {
400 normalize_noise(&walltime_nsecs_noise); 521 print_noise(counter, 1.0);
401 normalize_noise(&runtime_cycles_noise);
402 522
403 for (j = 0; j < nr_counters; j++) { 523 if (run != ena) {
404 normalize_noise(&event_res_noise[j][0]); 524 fprintf(stderr, " (scaled from %.2f%%)",
405 normalize_noise(&event_res_noise[j][1]); 525 100.0 * run / ena);
406 normalize_noise(&event_res_noise[j][2]); 526 }
527 }
528 fprintf(stderr, "\n");
407 } 529 }
408} 530}
409 531
410static void print_stat(int argc, const char **argv) 532static void print_stat(int argc, const char **argv)
411{ 533{
412 int i, counter; 534 struct perf_evsel *counter;
413 535 int i;
414 calc_avg();
415 536
416 fflush(stdout); 537 fflush(stdout);
417 538
418 fprintf(stderr, "\n"); 539 if (!csv_output) {
419 fprintf(stderr, " Performance counter stats for \'%s", argv[0]); 540 fprintf(stderr, "\n");
420 541 fprintf(stderr, " Performance counter stats for ");
421 for (i = 1; i < argc; i++) 542 if(target_pid == -1 && target_tid == -1) {
422 fprintf(stderr, " %s", argv[i]); 543 fprintf(stderr, "\'%s", argv[0]);
423 544 for (i = 1; i < argc; i++)
424 fprintf(stderr, "\'"); 545 fprintf(stderr, " %s", argv[i]);
425 if (run_count > 1) 546 } else if (target_pid != -1)
426 fprintf(stderr, " (%d runs)", run_count); 547 fprintf(stderr, "process id \'%d", target_pid);
427 fprintf(stderr, ":\n\n"); 548 else
428 549 fprintf(stderr, "thread id \'%d", target_tid);
429 for (counter = 0; counter < nr_counters; counter++) 550
430 print_counter(counter); 551 fprintf(stderr, "\'");
552 if (run_count > 1)
553 fprintf(stderr, " (%d runs)", run_count);
554 fprintf(stderr, ":\n\n");
555 }
431 556
557 if (no_aggr) {
558 list_for_each_entry(counter, &evsel_list, node)
559 print_counter(counter);
560 } else {
561 list_for_each_entry(counter, &evsel_list, node)
562 print_counter_aggr(counter);
563 }
432 564
433 fprintf(stderr, "\n"); 565 if (!csv_output) {
434 fprintf(stderr, " %14.9f seconds time elapsed.\n", 566 fprintf(stderr, "\n");
435 (double)walltime_nsecs_avg/1e9); 567 fprintf(stderr, " %18.9f seconds time elapsed",
436 fprintf(stderr, "\n"); 568 avg_stats(&walltime_nsecs_stats)/1e9);
569 if (run_count > 1) {
570 fprintf(stderr, " ( +- %7.3f%% )",
571 100*stddev_stats(&walltime_nsecs_stats) /
572 avg_stats(&walltime_nsecs_stats));
573 }
574 fprintf(stderr, "\n\n");
575 }
437} 576}
438 577
439static volatile int signr = -1; 578static volatile int signr = -1;
440 579
441static void skip_signal(int signo) 580static void skip_signal(int signo)
442{ 581{
582 if(child_pid == -1)
583 done = 1;
584
443 signr = signo; 585 signr = signo;
444} 586}
445 587
446static void sig_atexit(void) 588static void sig_atexit(void)
447{ 589{
590 if (child_pid != -1)
591 kill(child_pid, SIGTERM);
592
448 if (signr == -1) 593 if (signr == -1)
449 return; 594 return;
450 595
@@ -453,49 +598,127 @@ static void sig_atexit(void)
453} 598}
454 599
455static const char * const stat_usage[] = { 600static const char * const stat_usage[] = {
456 "perf stat [<options>] <command>", 601 "perf stat [<options>] [<command>]",
457 NULL 602 NULL
458}; 603};
459 604
605static int stat__set_big_num(const struct option *opt __used,
606 const char *s __used, int unset)
607{
608 big_num_opt = unset ? 0 : 1;
609 return 0;
610}
611
460static const struct option options[] = { 612static const struct option options[] = {
461 OPT_CALLBACK('e', "event", NULL, "event", 613 OPT_CALLBACK('e', "event", NULL, "event",
462 "event selector. use 'perf list' to list available events", 614 "event selector. use 'perf list' to list available events",
463 parse_events), 615 parse_events),
464 OPT_BOOLEAN('i', "inherit", &inherit, 616 OPT_BOOLEAN('i', "no-inherit", &no_inherit,
465 "child tasks inherit counters"), 617 "child tasks do not inherit counters"),
466 OPT_INTEGER('p', "pid", &target_pid, 618 OPT_INTEGER('p', "pid", &target_pid,
467 "stat events on existing pid"), 619 "stat events on existing process id"),
620 OPT_INTEGER('t', "tid", &target_tid,
621 "stat events on existing thread id"),
468 OPT_BOOLEAN('a', "all-cpus", &system_wide, 622 OPT_BOOLEAN('a', "all-cpus", &system_wide,
469 "system-wide collection from all CPUs"), 623 "system-wide collection from all CPUs"),
470 OPT_BOOLEAN('S', "scale", &scale, 624 OPT_BOOLEAN('c', "scale", &scale,
471 "scale/normalize counters"), 625 "scale/normalize counters"),
472 OPT_BOOLEAN('v', "verbose", &verbose, 626 OPT_INCR('v', "verbose", &verbose,
473 "be more verbose (show counter open errors, etc)"), 627 "be more verbose (show counter open errors, etc)"),
474 OPT_INTEGER('r', "repeat", &run_count, 628 OPT_INTEGER('r', "repeat", &run_count,
475 "repeat command and print average + stddev (max: 100)"), 629 "repeat command and print average + stddev (max: 100)"),
630 OPT_BOOLEAN('n', "null", &null_run,
631 "null run - dont start any counters"),
632 OPT_CALLBACK_NOOPT('B', "big-num", NULL, NULL,
633 "print large numbers with thousands\' separators",
634 stat__set_big_num),
635 OPT_STRING('C', "cpu", &cpu_list, "cpu",
636 "list of cpus to monitor in system-wide"),
637 OPT_BOOLEAN('A', "no-aggr", &no_aggr,
638 "disable CPU count aggregation"),
639 OPT_STRING('x', "field-separator", &csv_sep, "separator",
640 "print counts with custom separator"),
476 OPT_END() 641 OPT_END()
477}; 642};
478 643
479int cmd_stat(int argc, const char **argv, const char *prefix) 644int cmd_stat(int argc, const char **argv, const char *prefix __used)
480{ 645{
481 int status; 646 struct perf_evsel *pos;
647 int status = -ENOMEM;
482 648
483 page_size = sysconf(_SC_PAGE_SIZE); 649 setlocale(LC_ALL, "");
484 650
485 memcpy(attrs, default_attrs, sizeof(attrs)); 651 argc = parse_options(argc, argv, options, stat_usage,
652 PARSE_OPT_STOP_AT_NON_OPTION);
486 653
487 argc = parse_options(argc, argv, options, stat_usage, 0); 654 if (csv_sep)
488 if (!argc) 655 csv_output = true;
656 else
657 csv_sep = DEFAULT_SEPARATOR;
658
659 /*
660 * let the spreadsheet do the pretty-printing
661 */
662 if (csv_output) {
663 /* User explicitely passed -B? */
664 if (big_num_opt == 1) {
665 fprintf(stderr, "-B option not supported with -x\n");
666 usage_with_options(stat_usage, options);
667 } else /* Nope, so disable big number formatting */
668 big_num = false;
669 } else if (big_num_opt == 0) /* User passed --no-big-num */
670 big_num = false;
671
672 if (!argc && target_pid == -1 && target_tid == -1)
673 usage_with_options(stat_usage, options);
674 if (run_count <= 0)
489 usage_with_options(stat_usage, options); 675 usage_with_options(stat_usage, options);
490 if (run_count <= 0 || run_count > MAX_RUN) 676
677 /* no_aggr is for system-wide only */
678 if (no_aggr && !system_wide)
491 usage_with_options(stat_usage, options); 679 usage_with_options(stat_usage, options);
492 680
493 if (!nr_counters) 681 /* Set attrs and nr_counters if no event is selected and !null_run */
494 nr_counters = 8; 682 if (!null_run && !nr_counters) {
683 size_t c;
495 684
496 nr_cpus = sysconf(_SC_NPROCESSORS_ONLN); 685 nr_counters = ARRAY_SIZE(default_attrs);
497 assert(nr_cpus <= MAX_NR_CPUS); 686
498 assert(nr_cpus >= 0); 687 for (c = 0; c < ARRAY_SIZE(default_attrs); ++c) {
688 pos = perf_evsel__new(&default_attrs[c],
689 nr_counters);
690 if (pos == NULL)
691 goto out;
692 list_add(&pos->node, &evsel_list);
693 }
694 }
695
696 if (target_pid != -1)
697 target_tid = target_pid;
698
699 threads = thread_map__new(target_pid, target_tid);
700 if (threads == NULL) {
701 pr_err("Problems finding threads of monitor\n");
702 usage_with_options(stat_usage, options);
703 }
704
705 if (system_wide)
706 cpus = cpu_map__new(cpu_list);
707 else
708 cpus = cpu_map__dummy_new();
709
710 if (cpus == NULL) {
711 perror("failed to parse CPUs map");
712 usage_with_options(stat_usage, options);
713 return -1;
714 }
715
716 list_for_each_entry(pos, &evsel_list, node) {
717 if (perf_evsel__alloc_stat_priv(pos) < 0 ||
718 perf_evsel__alloc_counts(pos, cpus->nr) < 0 ||
719 perf_evsel__alloc_fd(pos, cpus->nr, threads->nr) < 0)
720 goto out_free_fd;
721 }
499 722
500 /* 723 /*
501 * We dont want to block the signals - that would cause 724 * We dont want to block the signals - that would cause
@@ -511,11 +734,17 @@ int cmd_stat(int argc, const char **argv, const char *prefix)
511 status = 0; 734 status = 0;
512 for (run_idx = 0; run_idx < run_count; run_idx++) { 735 for (run_idx = 0; run_idx < run_count; run_idx++) {
513 if (run_count != 1 && verbose) 736 if (run_count != 1 && verbose)
514 fprintf(stderr, "[ perf stat: executing run #%d ... ]\n", run_idx+1); 737 fprintf(stderr, "[ perf stat: executing run #%d ... ]\n", run_idx + 1);
515 status = run_perf_stat(argc, argv); 738 status = run_perf_stat(argc, argv);
516 } 739 }
517 740
518 print_stat(argc, argv); 741 if (status != -1)
519 742 print_stat(argc, argv);
743out_free_fd:
744 list_for_each_entry(pos, &evsel_list, node)
745 perf_evsel__free_stat_priv(pos);
746out:
747 thread_map__delete(threads);
748 threads = NULL;
520 return status; 749 return status;
521} 750}
diff --git a/tools/perf/builtin-test.c b/tools/perf/builtin-test.c
new file mode 100644
index 00000000000..ed5696198d3
--- /dev/null
+++ b/tools/perf/builtin-test.c
@@ -0,0 +1,497 @@
1/*
2 * builtin-test.c
3 *
4 * Builtin regression testing command: ever growing number of sanity tests
5 */
6#include "builtin.h"
7
8#include "util/cache.h"
9#include "util/debug.h"
10#include "util/parse-options.h"
11#include "util/session.h"
12#include "util/symbol.h"
13#include "util/thread.h"
14
15static long page_size;
16
17static int vmlinux_matches_kallsyms_filter(struct map *map __used, struct symbol *sym)
18{
19 bool *visited = symbol__priv(sym);
20 *visited = true;
21 return 0;
22}
23
24static int test__vmlinux_matches_kallsyms(void)
25{
26 int err = -1;
27 struct rb_node *nd;
28 struct symbol *sym;
29 struct map *kallsyms_map, *vmlinux_map;
30 struct machine kallsyms, vmlinux;
31 enum map_type type = MAP__FUNCTION;
32 struct ref_reloc_sym ref_reloc_sym = { .name = "_stext", };
33
34 /*
35 * Step 1:
36 *
37 * Init the machines that will hold kernel, modules obtained from
38 * both vmlinux + .ko files and from /proc/kallsyms split by modules.
39 */
40 machine__init(&kallsyms, "", HOST_KERNEL_ID);
41 machine__init(&vmlinux, "", HOST_KERNEL_ID);
42
43 /*
44 * Step 2:
45 *
46 * Create the kernel maps for kallsyms and the DSO where we will then
47 * load /proc/kallsyms. Also create the modules maps from /proc/modules
48 * and find the .ko files that match them in /lib/modules/`uname -r`/.
49 */
50 if (machine__create_kernel_maps(&kallsyms) < 0) {
51 pr_debug("machine__create_kernel_maps ");
52 return -1;
53 }
54
55 /*
56 * Step 3:
57 *
58 * Load and split /proc/kallsyms into multiple maps, one per module.
59 */
60 if (machine__load_kallsyms(&kallsyms, "/proc/kallsyms", type, NULL) <= 0) {
61 pr_debug("dso__load_kallsyms ");
62 goto out;
63 }
64
65 /*
66 * Step 4:
67 *
68 * kallsyms will be internally on demand sorted by name so that we can
69 * find the reference relocation * symbol, i.e. the symbol we will use
70 * to see if the running kernel was relocated by checking if it has the
71 * same value in the vmlinux file we load.
72 */
73 kallsyms_map = machine__kernel_map(&kallsyms, type);
74
75 sym = map__find_symbol_by_name(kallsyms_map, ref_reloc_sym.name, NULL);
76 if (sym == NULL) {
77 pr_debug("dso__find_symbol_by_name ");
78 goto out;
79 }
80
81 ref_reloc_sym.addr = sym->start;
82
83 /*
84 * Step 5:
85 *
86 * Now repeat step 2, this time for the vmlinux file we'll auto-locate.
87 */
88 if (machine__create_kernel_maps(&vmlinux) < 0) {
89 pr_debug("machine__create_kernel_maps ");
90 goto out;
91 }
92
93 vmlinux_map = machine__kernel_map(&vmlinux, type);
94 map__kmap(vmlinux_map)->ref_reloc_sym = &ref_reloc_sym;
95
96 /*
97 * Step 6:
98 *
99 * Locate a vmlinux file in the vmlinux path that has a buildid that
100 * matches the one of the running kernel.
101 *
102 * While doing that look if we find the ref reloc symbol, if we find it
103 * we'll have its ref_reloc_symbol.unrelocated_addr and then
104 * maps__reloc_vmlinux will notice and set proper ->[un]map_ip routines
105 * to fixup the symbols.
106 */
107 if (machine__load_vmlinux_path(&vmlinux, type,
108 vmlinux_matches_kallsyms_filter) <= 0) {
109 pr_debug("machine__load_vmlinux_path ");
110 goto out;
111 }
112
113 err = 0;
114 /*
115 * Step 7:
116 *
117 * Now look at the symbols in the vmlinux DSO and check if we find all of them
118 * in the kallsyms dso. For the ones that are in both, check its names and
119 * end addresses too.
120 */
121 for (nd = rb_first(&vmlinux_map->dso->symbols[type]); nd; nd = rb_next(nd)) {
122 struct symbol *pair, *first_pair;
123 bool backwards = true;
124
125 sym = rb_entry(nd, struct symbol, rb_node);
126
127 if (sym->start == sym->end)
128 continue;
129
130 first_pair = machine__find_kernel_symbol(&kallsyms, type, sym->start, NULL, NULL);
131 pair = first_pair;
132
133 if (pair && pair->start == sym->start) {
134next_pair:
135 if (strcmp(sym->name, pair->name) == 0) {
136 /*
137 * kallsyms don't have the symbol end, so we
138 * set that by using the next symbol start - 1,
139 * in some cases we get this up to a page
140 * wrong, trace_kmalloc when I was developing
141 * this code was one such example, 2106 bytes
142 * off the real size. More than that and we
143 * _really_ have a problem.
144 */
145 s64 skew = sym->end - pair->end;
146 if (llabs(skew) < page_size)
147 continue;
148
149 pr_debug("%#Lx: diff end addr for %s v: %#Lx k: %#Lx\n",
150 sym->start, sym->name, sym->end, pair->end);
151 } else {
152 struct rb_node *nnd;
153detour:
154 nnd = backwards ? rb_prev(&pair->rb_node) :
155 rb_next(&pair->rb_node);
156 if (nnd) {
157 struct symbol *next = rb_entry(nnd, struct symbol, rb_node);
158
159 if (next->start == sym->start) {
160 pair = next;
161 goto next_pair;
162 }
163 }
164
165 if (backwards) {
166 backwards = false;
167 pair = first_pair;
168 goto detour;
169 }
170
171 pr_debug("%#Lx: diff name v: %s k: %s\n",
172 sym->start, sym->name, pair->name);
173 }
174 } else
175 pr_debug("%#Lx: %s not on kallsyms\n", sym->start, sym->name);
176
177 err = -1;
178 }
179
180 if (!verbose)
181 goto out;
182
183 pr_info("Maps only in vmlinux:\n");
184
185 for (nd = rb_first(&vmlinux.kmaps.maps[type]); nd; nd = rb_next(nd)) {
186 struct map *pos = rb_entry(nd, struct map, rb_node), *pair;
187 /*
188 * If it is the kernel, kallsyms is always "[kernel.kallsyms]", while
189 * the kernel will have the path for the vmlinux file being used,
190 * so use the short name, less descriptive but the same ("[kernel]" in
191 * both cases.
192 */
193 pair = map_groups__find_by_name(&kallsyms.kmaps, type,
194 (pos->dso->kernel ?
195 pos->dso->short_name :
196 pos->dso->name));
197 if (pair)
198 pair->priv = 1;
199 else
200 map__fprintf(pos, stderr);
201 }
202
203 pr_info("Maps in vmlinux with a different name in kallsyms:\n");
204
205 for (nd = rb_first(&vmlinux.kmaps.maps[type]); nd; nd = rb_next(nd)) {
206 struct map *pos = rb_entry(nd, struct map, rb_node), *pair;
207
208 pair = map_groups__find(&kallsyms.kmaps, type, pos->start);
209 if (pair == NULL || pair->priv)
210 continue;
211
212 if (pair->start == pos->start) {
213 pair->priv = 1;
214 pr_info(" %Lx-%Lx %Lx %s in kallsyms as",
215 pos->start, pos->end, pos->pgoff, pos->dso->name);
216 if (pos->pgoff != pair->pgoff || pos->end != pair->end)
217 pr_info(": \n*%Lx-%Lx %Lx",
218 pair->start, pair->end, pair->pgoff);
219 pr_info(" %s\n", pair->dso->name);
220 pair->priv = 1;
221 }
222 }
223
224 pr_info("Maps only in kallsyms:\n");
225
226 for (nd = rb_first(&kallsyms.kmaps.maps[type]);
227 nd; nd = rb_next(nd)) {
228 struct map *pos = rb_entry(nd, struct map, rb_node);
229
230 if (!pos->priv)
231 map__fprintf(pos, stderr);
232 }
233out:
234 return err;
235}
236
237#include "util/cpumap.h"
238#include "util/evsel.h"
239#include <sys/types.h>
240
241static int trace_event__id(const char *event_name)
242{
243 char *filename;
244 int err = -1, fd;
245
246 if (asprintf(&filename,
247 "/sys/kernel/debug/tracing/events/syscalls/%s/id",
248 event_name) < 0)
249 return -1;
250
251 fd = open(filename, O_RDONLY);
252 if (fd >= 0) {
253 char id[16];
254 if (read(fd, id, sizeof(id)) > 0)
255 err = atoi(id);
256 close(fd);
257 }
258
259 free(filename);
260 return err;
261}
262
263static int test__open_syscall_event(void)
264{
265 int err = -1, fd;
266 struct thread_map *threads;
267 struct perf_evsel *evsel;
268 struct perf_event_attr attr;
269 unsigned int nr_open_calls = 111, i;
270 int id = trace_event__id("sys_enter_open");
271
272 if (id < 0) {
273 pr_debug("is debugfs mounted on /sys/kernel/debug?\n");
274 return -1;
275 }
276
277 threads = thread_map__new(-1, getpid());
278 if (threads == NULL) {
279 pr_debug("thread_map__new\n");
280 return -1;
281 }
282
283 memset(&attr, 0, sizeof(attr));
284 attr.type = PERF_TYPE_TRACEPOINT;
285 attr.config = id;
286 evsel = perf_evsel__new(&attr, 0);
287 if (evsel == NULL) {
288 pr_debug("perf_evsel__new\n");
289 goto out_thread_map_delete;
290 }
291
292 if (perf_evsel__open_per_thread(evsel, threads) < 0) {
293 pr_debug("failed to open counter: %s, "
294 "tweak /proc/sys/kernel/perf_event_paranoid?\n",
295 strerror(errno));
296 goto out_evsel_delete;
297 }
298
299 for (i = 0; i < nr_open_calls; ++i) {
300 fd = open("/etc/passwd", O_RDONLY);
301 close(fd);
302 }
303
304 if (perf_evsel__read_on_cpu(evsel, 0, 0) < 0) {
305 pr_debug("perf_evsel__open_read_on_cpu\n");
306 goto out_close_fd;
307 }
308
309 if (evsel->counts->cpu[0].val != nr_open_calls) {
310 pr_debug("perf_evsel__read_on_cpu: expected to intercept %d calls, got %Ld\n",
311 nr_open_calls, evsel->counts->cpu[0].val);
312 goto out_close_fd;
313 }
314
315 err = 0;
316out_close_fd:
317 perf_evsel__close_fd(evsel, 1, threads->nr);
318out_evsel_delete:
319 perf_evsel__delete(evsel);
320out_thread_map_delete:
321 thread_map__delete(threads);
322 return err;
323}
324
325#include <sched.h>
326
327static int test__open_syscall_event_on_all_cpus(void)
328{
329 int err = -1, fd, cpu;
330 struct thread_map *threads;
331 struct cpu_map *cpus;
332 struct perf_evsel *evsel;
333 struct perf_event_attr attr;
334 unsigned int nr_open_calls = 111, i;
335 cpu_set_t *cpu_set;
336 size_t cpu_set_size;
337 int id = trace_event__id("sys_enter_open");
338
339 if (id < 0) {
340 pr_debug("is debugfs mounted on /sys/kernel/debug?\n");
341 return -1;
342 }
343
344 threads = thread_map__new(-1, getpid());
345 if (threads == NULL) {
346 pr_debug("thread_map__new\n");
347 return -1;
348 }
349
350 cpus = cpu_map__new(NULL);
351 if (threads == NULL) {
352 pr_debug("thread_map__new\n");
353 return -1;
354 }
355
356 cpu_set = CPU_ALLOC(cpus->nr);
357
358 if (cpu_set == NULL)
359 goto out_thread_map_delete;
360
361 cpu_set_size = CPU_ALLOC_SIZE(cpus->nr);
362 CPU_ZERO_S(cpu_set_size, cpu_set);
363
364 memset(&attr, 0, sizeof(attr));
365 attr.type = PERF_TYPE_TRACEPOINT;
366 attr.config = id;
367 evsel = perf_evsel__new(&attr, 0);
368 if (evsel == NULL) {
369 pr_debug("perf_evsel__new\n");
370 goto out_cpu_free;
371 }
372
373 if (perf_evsel__open(evsel, cpus, threads) < 0) {
374 pr_debug("failed to open counter: %s, "
375 "tweak /proc/sys/kernel/perf_event_paranoid?\n",
376 strerror(errno));
377 goto out_evsel_delete;
378 }
379
380 for (cpu = 0; cpu < cpus->nr; ++cpu) {
381 unsigned int ncalls = nr_open_calls + cpu;
382
383 CPU_SET(cpu, cpu_set);
384 sched_setaffinity(0, cpu_set_size, cpu_set);
385 for (i = 0; i < ncalls; ++i) {
386 fd = open("/etc/passwd", O_RDONLY);
387 close(fd);
388 }
389 CPU_CLR(cpu, cpu_set);
390 }
391
392 /*
393 * Here we need to explicitely preallocate the counts, as if
394 * we use the auto allocation it will allocate just for 1 cpu,
395 * as we start by cpu 0.
396 */
397 if (perf_evsel__alloc_counts(evsel, cpus->nr) < 0) {
398 pr_debug("perf_evsel__alloc_counts(ncpus=%d)\n", cpus->nr);
399 goto out_close_fd;
400 }
401
402 for (cpu = 0; cpu < cpus->nr; ++cpu) {
403 unsigned int expected;
404
405 if (perf_evsel__read_on_cpu(evsel, cpu, 0) < 0) {
406 pr_debug("perf_evsel__open_read_on_cpu\n");
407 goto out_close_fd;
408 }
409
410 expected = nr_open_calls + cpu;
411 if (evsel->counts->cpu[cpu].val != expected) {
412 pr_debug("perf_evsel__read_on_cpu: expected to intercept %d calls on cpu %d, got %Ld\n",
413 expected, cpu, evsel->counts->cpu[cpu].val);
414 goto out_close_fd;
415 }
416 }
417
418 err = 0;
419out_close_fd:
420 perf_evsel__close_fd(evsel, 1, threads->nr);
421out_evsel_delete:
422 perf_evsel__delete(evsel);
423out_cpu_free:
424 CPU_FREE(cpu_set);
425out_thread_map_delete:
426 thread_map__delete(threads);
427 return err;
428}
429
430static struct test {
431 const char *desc;
432 int (*func)(void);
433} tests[] = {
434 {
435 .desc = "vmlinux symtab matches kallsyms",
436 .func = test__vmlinux_matches_kallsyms,
437 },
438 {
439 .desc = "detect open syscall event",
440 .func = test__open_syscall_event,
441 },
442 {
443 .desc = "detect open syscall event on all cpus",
444 .func = test__open_syscall_event_on_all_cpus,
445 },
446 {
447 .func = NULL,
448 },
449};
450
451static int __cmd_test(void)
452{
453 int i = 0;
454
455 page_size = sysconf(_SC_PAGE_SIZE);
456
457 while (tests[i].func) {
458 int err;
459 pr_info("%2d: %s:", i + 1, tests[i].desc);
460 pr_debug("\n--- start ---\n");
461 err = tests[i].func();
462 pr_debug("---- end ----\n%s:", tests[i].desc);
463 pr_info(" %s\n", err ? "FAILED!\n" : "Ok");
464 ++i;
465 }
466
467 return 0;
468}
469
470static const char * const test_usage[] = {
471 "perf test [<options>]",
472 NULL,
473};
474
475static const struct option test_options[] = {
476 OPT_INTEGER('v', "verbose", &verbose,
477 "be more verbose (show symbol address, etc)"),
478 OPT_END()
479};
480
481int cmd_test(int argc, const char **argv, const char *prefix __used)
482{
483 argc = parse_options(argc, argv, test_options, test_usage, 0);
484 if (argc)
485 usage_with_options(test_usage, test_options);
486
487 symbol_conf.priv_size = sizeof(int);
488 symbol_conf.sort_by_name = true;
489 symbol_conf.try_vmlinux_path = true;
490
491 if (symbol__init() < 0)
492 return -1;
493
494 setup_pager();
495
496 return __cmd_test();
497}
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c
new file mode 100644
index 00000000000..746cf03cb05
--- /dev/null
+++ b/tools/perf/builtin-timechart.c
@@ -0,0 +1,1104 @@
1/*
2 * builtin-timechart.c - make an svg timechart of system activity
3 *
4 * (C) Copyright 2009 Intel Corporation
5 *
6 * Authors:
7 * Arjan van de Ven <arjan@linux.intel.com>
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; version 2
12 * of the License.
13 */
14
15#include "builtin.h"
16
17#include "util/util.h"
18
19#include "util/color.h"
20#include <linux/list.h>
21#include "util/cache.h"
22#include <linux/rbtree.h>
23#include "util/symbol.h"
24#include "util/callchain.h"
25#include "util/strlist.h"
26
27#include "perf.h"
28#include "util/header.h"
29#include "util/parse-options.h"
30#include "util/parse-events.h"
31#include "util/event.h"
32#include "util/session.h"
33#include "util/svghelper.h"
34
35#define SUPPORT_OLD_POWER_EVENTS 1
36#define PWR_EVENT_EXIT -1
37
38
39static char const *input_name = "perf.data";
40static char const *output_name = "output.svg";
41
42static unsigned int numcpus;
43static u64 min_freq; /* Lowest CPU frequency seen */
44static u64 max_freq; /* Highest CPU frequency seen */
45static u64 turbo_frequency;
46
47static u64 first_time, last_time;
48
49static bool power_only;
50
51
52struct per_pid;
53struct per_pidcomm;
54
55struct cpu_sample;
56struct power_event;
57struct wake_event;
58
59struct sample_wrapper;
60
61/*
62 * Datastructure layout:
63 * We keep an list of "pid"s, matching the kernels notion of a task struct.
64 * Each "pid" entry, has a list of "comm"s.
65 * this is because we want to track different programs different, while
66 * exec will reuse the original pid (by design).
67 * Each comm has a list of samples that will be used to draw
68 * final graph.
69 */
70
71struct per_pid {
72 struct per_pid *next;
73
74 int pid;
75 int ppid;
76
77 u64 start_time;
78 u64 end_time;
79 u64 total_time;
80 int display;
81
82 struct per_pidcomm *all;
83 struct per_pidcomm *current;
84};
85
86
87struct per_pidcomm {
88 struct per_pidcomm *next;
89
90 u64 start_time;
91 u64 end_time;
92 u64 total_time;
93
94 int Y;
95 int display;
96
97 long state;
98 u64 state_since;
99
100 char *comm;
101
102 struct cpu_sample *samples;
103};
104
105struct sample_wrapper {
106 struct sample_wrapper *next;
107
108 u64 timestamp;
109 unsigned char data[0];
110};
111
112#define TYPE_NONE 0
113#define TYPE_RUNNING 1
114#define TYPE_WAITING 2
115#define TYPE_BLOCKED 3
116
117struct cpu_sample {
118 struct cpu_sample *next;
119
120 u64 start_time;
121 u64 end_time;
122 int type;
123 int cpu;
124};
125
126static struct per_pid *all_data;
127
128#define CSTATE 1
129#define PSTATE 2
130
131struct power_event {
132 struct power_event *next;
133 int type;
134 int state;
135 u64 start_time;
136 u64 end_time;
137 int cpu;
138};
139
140struct wake_event {
141 struct wake_event *next;
142 int waker;
143 int wakee;
144 u64 time;
145};
146
147static struct power_event *power_events;
148static struct wake_event *wake_events;
149
150struct process_filter;
151struct process_filter {
152 char *name;
153 int pid;
154 struct process_filter *next;
155};
156
157static struct process_filter *process_filter;
158
159
160static struct per_pid *find_create_pid(int pid)
161{
162 struct per_pid *cursor = all_data;
163
164 while (cursor) {
165 if (cursor->pid == pid)
166 return cursor;
167 cursor = cursor->next;
168 }
169 cursor = malloc(sizeof(struct per_pid));
170 assert(cursor != NULL);
171 memset(cursor, 0, sizeof(struct per_pid));
172 cursor->pid = pid;
173 cursor->next = all_data;
174 all_data = cursor;
175 return cursor;
176}
177
178static void pid_set_comm(int pid, char *comm)
179{
180 struct per_pid *p;
181 struct per_pidcomm *c;
182 p = find_create_pid(pid);
183 c = p->all;
184 while (c) {
185 if (c->comm && strcmp(c->comm, comm) == 0) {
186 p->current = c;
187 return;
188 }
189 if (!c->comm) {
190 c->comm = strdup(comm);
191 p->current = c;
192 return;
193 }
194 c = c->next;
195 }
196 c = malloc(sizeof(struct per_pidcomm));
197 assert(c != NULL);
198 memset(c, 0, sizeof(struct per_pidcomm));
199 c->comm = strdup(comm);
200 p->current = c;
201 c->next = p->all;
202 p->all = c;
203}
204
205static void pid_fork(int pid, int ppid, u64 timestamp)
206{
207 struct per_pid *p, *pp;
208 p = find_create_pid(pid);
209 pp = find_create_pid(ppid);
210 p->ppid = ppid;
211 if (pp->current && pp->current->comm && !p->current)
212 pid_set_comm(pid, pp->current->comm);
213
214 p->start_time = timestamp;
215 if (p->current) {
216 p->current->start_time = timestamp;
217 p->current->state_since = timestamp;
218 }
219}
220
221static void pid_exit(int pid, u64 timestamp)
222{
223 struct per_pid *p;
224 p = find_create_pid(pid);
225 p->end_time = timestamp;
226 if (p->current)
227 p->current->end_time = timestamp;
228}
229
230static void
231pid_put_sample(int pid, int type, unsigned int cpu, u64 start, u64 end)
232{
233 struct per_pid *p;
234 struct per_pidcomm *c;
235 struct cpu_sample *sample;
236
237 p = find_create_pid(pid);
238 c = p->current;
239 if (!c) {
240 c = malloc(sizeof(struct per_pidcomm));
241 assert(c != NULL);
242 memset(c, 0, sizeof(struct per_pidcomm));
243 p->current = c;
244 c->next = p->all;
245 p->all = c;
246 }
247
248 sample = malloc(sizeof(struct cpu_sample));
249 assert(sample != NULL);
250 memset(sample, 0, sizeof(struct cpu_sample));
251 sample->start_time = start;
252 sample->end_time = end;
253 sample->type = type;
254 sample->next = c->samples;
255 sample->cpu = cpu;
256 c->samples = sample;
257
258 if (sample->type == TYPE_RUNNING && end > start && start > 0) {
259 c->total_time += (end-start);
260 p->total_time += (end-start);
261 }
262
263 if (c->start_time == 0 || c->start_time > start)
264 c->start_time = start;
265 if (p->start_time == 0 || p->start_time > start)
266 p->start_time = start;
267
268 if (cpu > numcpus)
269 numcpus = cpu;
270}
271
272#define MAX_CPUS 4096
273
274static u64 cpus_cstate_start_times[MAX_CPUS];
275static int cpus_cstate_state[MAX_CPUS];
276static u64 cpus_pstate_start_times[MAX_CPUS];
277static u64 cpus_pstate_state[MAX_CPUS];
278
279static int process_comm_event(event_t *event, struct sample_data *sample __used,
280 struct perf_session *session __used)
281{
282 pid_set_comm(event->comm.tid, event->comm.comm);
283 return 0;
284}
285
286static int process_fork_event(event_t *event, struct sample_data *sample __used,
287 struct perf_session *session __used)
288{
289 pid_fork(event->fork.pid, event->fork.ppid, event->fork.time);
290 return 0;
291}
292
293static int process_exit_event(event_t *event, struct sample_data *sample __used,
294 struct perf_session *session __used)
295{
296 pid_exit(event->fork.pid, event->fork.time);
297 return 0;
298}
299
300struct trace_entry {
301 unsigned short type;
302 unsigned char flags;
303 unsigned char preempt_count;
304 int pid;
305 int lock_depth;
306};
307
308#ifdef SUPPORT_OLD_POWER_EVENTS
309static int use_old_power_events;
310struct power_entry_old {
311 struct trace_entry te;
312 u64 type;
313 u64 value;
314 u64 cpu_id;
315};
316#endif
317
318struct power_processor_entry {
319 struct trace_entry te;
320 u32 state;
321 u32 cpu_id;
322};
323
324#define TASK_COMM_LEN 16
325struct wakeup_entry {
326 struct trace_entry te;
327 char comm[TASK_COMM_LEN];
328 int pid;
329 int prio;
330 int success;
331};
332
333/*
334 * trace_flag_type is an enumeration that holds different
335 * states when a trace occurs. These are:
336 * IRQS_OFF - interrupts were disabled
337 * IRQS_NOSUPPORT - arch does not support irqs_disabled_flags
338 * NEED_RESCED - reschedule is requested
339 * HARDIRQ - inside an interrupt handler
340 * SOFTIRQ - inside a softirq handler
341 */
342enum trace_flag_type {
343 TRACE_FLAG_IRQS_OFF = 0x01,
344 TRACE_FLAG_IRQS_NOSUPPORT = 0x02,
345 TRACE_FLAG_NEED_RESCHED = 0x04,
346 TRACE_FLAG_HARDIRQ = 0x08,
347 TRACE_FLAG_SOFTIRQ = 0x10,
348};
349
350
351
352struct sched_switch {
353 struct trace_entry te;
354 char prev_comm[TASK_COMM_LEN];
355 int prev_pid;
356 int prev_prio;
357 long prev_state; /* Arjan weeps. */
358 char next_comm[TASK_COMM_LEN];
359 int next_pid;
360 int next_prio;
361};
362
363static void c_state_start(int cpu, u64 timestamp, int state)
364{
365 cpus_cstate_start_times[cpu] = timestamp;
366 cpus_cstate_state[cpu] = state;
367}
368
369static void c_state_end(int cpu, u64 timestamp)
370{
371 struct power_event *pwr;
372 pwr = malloc(sizeof(struct power_event));
373 if (!pwr)
374 return;
375 memset(pwr, 0, sizeof(struct power_event));
376
377 pwr->state = cpus_cstate_state[cpu];
378 pwr->start_time = cpus_cstate_start_times[cpu];
379 pwr->end_time = timestamp;
380 pwr->cpu = cpu;
381 pwr->type = CSTATE;
382 pwr->next = power_events;
383
384 power_events = pwr;
385}
386
387static void p_state_change(int cpu, u64 timestamp, u64 new_freq)
388{
389 struct power_event *pwr;
390 pwr = malloc(sizeof(struct power_event));
391
392 if (new_freq > 8000000) /* detect invalid data */
393 return;
394
395 if (!pwr)
396 return;
397 memset(pwr, 0, sizeof(struct power_event));
398
399 pwr->state = cpus_pstate_state[cpu];
400 pwr->start_time = cpus_pstate_start_times[cpu];
401 pwr->end_time = timestamp;
402 pwr->cpu = cpu;
403 pwr->type = PSTATE;
404 pwr->next = power_events;
405
406 if (!pwr->start_time)
407 pwr->start_time = first_time;
408
409 power_events = pwr;
410
411 cpus_pstate_state[cpu] = new_freq;
412 cpus_pstate_start_times[cpu] = timestamp;
413
414 if ((u64)new_freq > max_freq)
415 max_freq = new_freq;
416
417 if (new_freq < min_freq || min_freq == 0)
418 min_freq = new_freq;
419
420 if (new_freq == max_freq - 1000)
421 turbo_frequency = max_freq;
422}
423
424static void
425sched_wakeup(int cpu, u64 timestamp, int pid, struct trace_entry *te)
426{
427 struct wake_event *we;
428 struct per_pid *p;
429 struct wakeup_entry *wake = (void *)te;
430
431 we = malloc(sizeof(struct wake_event));
432 if (!we)
433 return;
434
435 memset(we, 0, sizeof(struct wake_event));
436 we->time = timestamp;
437 we->waker = pid;
438
439 if ((te->flags & TRACE_FLAG_HARDIRQ) || (te->flags & TRACE_FLAG_SOFTIRQ))
440 we->waker = -1;
441
442 we->wakee = wake->pid;
443 we->next = wake_events;
444 wake_events = we;
445 p = find_create_pid(we->wakee);
446
447 if (p && p->current && p->current->state == TYPE_NONE) {
448 p->current->state_since = timestamp;
449 p->current->state = TYPE_WAITING;
450 }
451 if (p && p->current && p->current->state == TYPE_BLOCKED) {
452 pid_put_sample(p->pid, p->current->state, cpu, p->current->state_since, timestamp);
453 p->current->state_since = timestamp;
454 p->current->state = TYPE_WAITING;
455 }
456}
457
458static void sched_switch(int cpu, u64 timestamp, struct trace_entry *te)
459{
460 struct per_pid *p = NULL, *prev_p;
461 struct sched_switch *sw = (void *)te;
462
463
464 prev_p = find_create_pid(sw->prev_pid);
465
466 p = find_create_pid(sw->next_pid);
467
468 if (prev_p->current && prev_p->current->state != TYPE_NONE)
469 pid_put_sample(sw->prev_pid, TYPE_RUNNING, cpu, prev_p->current->state_since, timestamp);
470 if (p && p->current) {
471 if (p->current->state != TYPE_NONE)
472 pid_put_sample(sw->next_pid, p->current->state, cpu, p->current->state_since, timestamp);
473
474 p->current->state_since = timestamp;
475 p->current->state = TYPE_RUNNING;
476 }
477
478 if (prev_p->current) {
479 prev_p->current->state = TYPE_NONE;
480 prev_p->current->state_since = timestamp;
481 if (sw->prev_state & 2)
482 prev_p->current->state = TYPE_BLOCKED;
483 if (sw->prev_state == 0)
484 prev_p->current->state = TYPE_WAITING;
485 }
486}
487
488
489static int process_sample_event(event_t *event __used,
490 struct sample_data *sample,
491 struct perf_session *session)
492{
493 struct trace_entry *te;
494
495 if (session->sample_type & PERF_SAMPLE_TIME) {
496 if (!first_time || first_time > sample->time)
497 first_time = sample->time;
498 if (last_time < sample->time)
499 last_time = sample->time;
500 }
501
502 te = (void *)sample->raw_data;
503 if (session->sample_type & PERF_SAMPLE_RAW && sample->raw_size > 0) {
504 char *event_str;
505#ifdef SUPPORT_OLD_POWER_EVENTS
506 struct power_entry_old *peo;
507 peo = (void *)te;
508#endif
509 event_str = perf_header__find_event(te->type);
510
511 if (!event_str)
512 return 0;
513
514 if (strcmp(event_str, "power:cpu_idle") == 0) {
515 struct power_processor_entry *ppe = (void *)te;
516 if (ppe->state == (u32)PWR_EVENT_EXIT)
517 c_state_end(ppe->cpu_id, sample->time);
518 else
519 c_state_start(ppe->cpu_id, sample->time,
520 ppe->state);
521 }
522 else if (strcmp(event_str, "power:cpu_frequency") == 0) {
523 struct power_processor_entry *ppe = (void *)te;
524 p_state_change(ppe->cpu_id, sample->time, ppe->state);
525 }
526
527 else if (strcmp(event_str, "sched:sched_wakeup") == 0)
528 sched_wakeup(sample->cpu, sample->time, sample->pid, te);
529
530 else if (strcmp(event_str, "sched:sched_switch") == 0)
531 sched_switch(sample->cpu, sample->time, te);
532
533#ifdef SUPPORT_OLD_POWER_EVENTS
534 if (use_old_power_events) {
535 if (strcmp(event_str, "power:power_start") == 0)
536 c_state_start(peo->cpu_id, sample->time,
537 peo->value);
538
539 else if (strcmp(event_str, "power:power_end") == 0)
540 c_state_end(sample->cpu, sample->time);
541
542 else if (strcmp(event_str,
543 "power:power_frequency") == 0)
544 p_state_change(peo->cpu_id, sample->time,
545 peo->value);
546 }
547#endif
548 }
549 return 0;
550}
551
552/*
553 * After the last sample we need to wrap up the current C/P state
554 * and close out each CPU for these.
555 */
556static void end_sample_processing(void)
557{
558 u64 cpu;
559 struct power_event *pwr;
560
561 for (cpu = 0; cpu <= numcpus; cpu++) {
562 pwr = malloc(sizeof(struct power_event));
563 if (!pwr)
564 return;
565 memset(pwr, 0, sizeof(struct power_event));
566
567 /* C state */
568#if 0
569 pwr->state = cpus_cstate_state[cpu];
570 pwr->start_time = cpus_cstate_start_times[cpu];
571 pwr->end_time = last_time;
572 pwr->cpu = cpu;
573 pwr->type = CSTATE;
574 pwr->next = power_events;
575
576 power_events = pwr;
577#endif
578 /* P state */
579
580 pwr = malloc(sizeof(struct power_event));
581 if (!pwr)
582 return;
583 memset(pwr, 0, sizeof(struct power_event));
584
585 pwr->state = cpus_pstate_state[cpu];
586 pwr->start_time = cpus_pstate_start_times[cpu];
587 pwr->end_time = last_time;
588 pwr->cpu = cpu;
589 pwr->type = PSTATE;
590 pwr->next = power_events;
591
592 if (!pwr->start_time)
593 pwr->start_time = first_time;
594 if (!pwr->state)
595 pwr->state = min_freq;
596 power_events = pwr;
597 }
598}
599
600/*
601 * Sort the pid datastructure
602 */
603static void sort_pids(void)
604{
605 struct per_pid *new_list, *p, *cursor, *prev;
606 /* sort by ppid first, then by pid, lowest to highest */
607
608 new_list = NULL;
609
610 while (all_data) {
611 p = all_data;
612 all_data = p->next;
613 p->next = NULL;
614
615 if (new_list == NULL) {
616 new_list = p;
617 p->next = NULL;
618 continue;
619 }
620 prev = NULL;
621 cursor = new_list;
622 while (cursor) {
623 if (cursor->ppid > p->ppid ||
624 (cursor->ppid == p->ppid && cursor->pid > p->pid)) {
625 /* must insert before */
626 if (prev) {
627 p->next = prev->next;
628 prev->next = p;
629 cursor = NULL;
630 continue;
631 } else {
632 p->next = new_list;
633 new_list = p;
634 cursor = NULL;
635 continue;
636 }
637 }
638
639 prev = cursor;
640 cursor = cursor->next;
641 if (!cursor)
642 prev->next = p;
643 }
644 }
645 all_data = new_list;
646}
647
648
649static void draw_c_p_states(void)
650{
651 struct power_event *pwr;
652 pwr = power_events;
653
654 /*
655 * two pass drawing so that the P state bars are on top of the C state blocks
656 */
657 while (pwr) {
658 if (pwr->type == CSTATE)
659 svg_cstate(pwr->cpu, pwr->start_time, pwr->end_time, pwr->state);
660 pwr = pwr->next;
661 }
662
663 pwr = power_events;
664 while (pwr) {
665 if (pwr->type == PSTATE) {
666 if (!pwr->state)
667 pwr->state = min_freq;
668 svg_pstate(pwr->cpu, pwr->start_time, pwr->end_time, pwr->state);
669 }
670 pwr = pwr->next;
671 }
672}
673
674static void draw_wakeups(void)
675{
676 struct wake_event *we;
677 struct per_pid *p;
678 struct per_pidcomm *c;
679
680 we = wake_events;
681 while (we) {
682 int from = 0, to = 0;
683 char *task_from = NULL, *task_to = NULL;
684
685 /* locate the column of the waker and wakee */
686 p = all_data;
687 while (p) {
688 if (p->pid == we->waker || p->pid == we->wakee) {
689 c = p->all;
690 while (c) {
691 if (c->Y && c->start_time <= we->time && c->end_time >= we->time) {
692 if (p->pid == we->waker && !from) {
693 from = c->Y;
694 task_from = strdup(c->comm);
695 }
696 if (p->pid == we->wakee && !to) {
697 to = c->Y;
698 task_to = strdup(c->comm);
699 }
700 }
701 c = c->next;
702 }
703 c = p->all;
704 while (c) {
705 if (p->pid == we->waker && !from) {
706 from = c->Y;
707 task_from = strdup(c->comm);
708 }
709 if (p->pid == we->wakee && !to) {
710 to = c->Y;
711 task_to = strdup(c->comm);
712 }
713 c = c->next;
714 }
715 }
716 p = p->next;
717 }
718
719 if (!task_from) {
720 task_from = malloc(40);
721 sprintf(task_from, "[%i]", we->waker);
722 }
723 if (!task_to) {
724 task_to = malloc(40);
725 sprintf(task_to, "[%i]", we->wakee);
726 }
727
728 if (we->waker == -1)
729 svg_interrupt(we->time, to);
730 else if (from && to && abs(from - to) == 1)
731 svg_wakeline(we->time, from, to);
732 else
733 svg_partial_wakeline(we->time, from, task_from, to, task_to);
734 we = we->next;
735
736 free(task_from);
737 free(task_to);
738 }
739}
740
741static void draw_cpu_usage(void)
742{
743 struct per_pid *p;
744 struct per_pidcomm *c;
745 struct cpu_sample *sample;
746 p = all_data;
747 while (p) {
748 c = p->all;
749 while (c) {
750 sample = c->samples;
751 while (sample) {
752 if (sample->type == TYPE_RUNNING)
753 svg_process(sample->cpu, sample->start_time, sample->end_time, "sample", c->comm);
754
755 sample = sample->next;
756 }
757 c = c->next;
758 }
759 p = p->next;
760 }
761}
762
763static void draw_process_bars(void)
764{
765 struct per_pid *p;
766 struct per_pidcomm *c;
767 struct cpu_sample *sample;
768 int Y = 0;
769
770 Y = 2 * numcpus + 2;
771
772 p = all_data;
773 while (p) {
774 c = p->all;
775 while (c) {
776 if (!c->display) {
777 c->Y = 0;
778 c = c->next;
779 continue;
780 }
781
782 svg_box(Y, c->start_time, c->end_time, "process");
783 sample = c->samples;
784 while (sample) {
785 if (sample->type == TYPE_RUNNING)
786 svg_sample(Y, sample->cpu, sample->start_time, sample->end_time);
787 if (sample->type == TYPE_BLOCKED)
788 svg_box(Y, sample->start_time, sample->end_time, "blocked");
789 if (sample->type == TYPE_WAITING)
790 svg_waiting(Y, sample->start_time, sample->end_time);
791 sample = sample->next;
792 }
793
794 if (c->comm) {
795 char comm[256];
796 if (c->total_time > 5000000000) /* 5 seconds */
797 sprintf(comm, "%s:%i (%2.2fs)", c->comm, p->pid, c->total_time / 1000000000.0);
798 else
799 sprintf(comm, "%s:%i (%3.1fms)", c->comm, p->pid, c->total_time / 1000000.0);
800
801 svg_text(Y, c->start_time, comm);
802 }
803 c->Y = Y;
804 Y++;
805 c = c->next;
806 }
807 p = p->next;
808 }
809}
810
811static void add_process_filter(const char *string)
812{
813 struct process_filter *filt;
814 int pid;
815
816 pid = strtoull(string, NULL, 10);
817 filt = malloc(sizeof(struct process_filter));
818 if (!filt)
819 return;
820
821 filt->name = strdup(string);
822 filt->pid = pid;
823 filt->next = process_filter;
824
825 process_filter = filt;
826}
827
828static int passes_filter(struct per_pid *p, struct per_pidcomm *c)
829{
830 struct process_filter *filt;
831 if (!process_filter)
832 return 1;
833
834 filt = process_filter;
835 while (filt) {
836 if (filt->pid && p->pid == filt->pid)
837 return 1;
838 if (strcmp(filt->name, c->comm) == 0)
839 return 1;
840 filt = filt->next;
841 }
842 return 0;
843}
844
845static int determine_display_tasks_filtered(void)
846{
847 struct per_pid *p;
848 struct per_pidcomm *c;
849 int count = 0;
850
851 p = all_data;
852 while (p) {
853 p->display = 0;
854 if (p->start_time == 1)
855 p->start_time = first_time;
856
857 /* no exit marker, task kept running to the end */
858 if (p->end_time == 0)
859 p->end_time = last_time;
860
861 c = p->all;
862
863 while (c) {
864 c->display = 0;
865
866 if (c->start_time == 1)
867 c->start_time = first_time;
868
869 if (passes_filter(p, c)) {
870 c->display = 1;
871 p->display = 1;
872 count++;
873 }
874
875 if (c->end_time == 0)
876 c->end_time = last_time;
877
878 c = c->next;
879 }
880 p = p->next;
881 }
882 return count;
883}
884
885static int determine_display_tasks(u64 threshold)
886{
887 struct per_pid *p;
888 struct per_pidcomm *c;
889 int count = 0;
890
891 if (process_filter)
892 return determine_display_tasks_filtered();
893
894 p = all_data;
895 while (p) {
896 p->display = 0;
897 if (p->start_time == 1)
898 p->start_time = first_time;
899
900 /* no exit marker, task kept running to the end */
901 if (p->end_time == 0)
902 p->end_time = last_time;
903 if (p->total_time >= threshold && !power_only)
904 p->display = 1;
905
906 c = p->all;
907
908 while (c) {
909 c->display = 0;
910
911 if (c->start_time == 1)
912 c->start_time = first_time;
913
914 if (c->total_time >= threshold && !power_only) {
915 c->display = 1;
916 count++;
917 }
918
919 if (c->end_time == 0)
920 c->end_time = last_time;
921
922 c = c->next;
923 }
924 p = p->next;
925 }
926 return count;
927}
928
929
930
931#define TIME_THRESH 10000000
932
933static void write_svg_file(const char *filename)
934{
935 u64 i;
936 int count;
937
938 numcpus++;
939
940
941 count = determine_display_tasks(TIME_THRESH);
942
943 /* We'd like to show at least 15 tasks; be less picky if we have fewer */
944 if (count < 15)
945 count = determine_display_tasks(TIME_THRESH / 10);
946
947 open_svg(filename, numcpus, count, first_time, last_time);
948
949 svg_time_grid();
950 svg_legenda();
951
952 for (i = 0; i < numcpus; i++)
953 svg_cpu_box(i, max_freq, turbo_frequency);
954
955 draw_cpu_usage();
956 draw_process_bars();
957 draw_c_p_states();
958 draw_wakeups();
959
960 svg_close();
961}
962
963static struct perf_event_ops event_ops = {
964 .comm = process_comm_event,
965 .fork = process_fork_event,
966 .exit = process_exit_event,
967 .sample = process_sample_event,
968 .ordered_samples = true,
969};
970
971static int __cmd_timechart(void)
972{
973 struct perf_session *session = perf_session__new(input_name, O_RDONLY,
974 0, false, &event_ops);
975 int ret = -EINVAL;
976
977 if (session == NULL)
978 return -ENOMEM;
979
980 if (!perf_session__has_traces(session, "timechart record"))
981 goto out_delete;
982
983 ret = perf_session__process_events(session, &event_ops);
984 if (ret)
985 goto out_delete;
986
987 end_sample_processing();
988
989 sort_pids();
990
991 write_svg_file(output_name);
992
993 pr_info("Written %2.1f seconds of trace to %s.\n",
994 (last_time - first_time) / 1000000000.0, output_name);
995out_delete:
996 perf_session__delete(session);
997 return ret;
998}
999
1000static const char * const timechart_usage[] = {
1001 "perf timechart [<options>] {record}",
1002 NULL
1003};
1004
1005#ifdef SUPPORT_OLD_POWER_EVENTS
1006static const char * const record_old_args[] = {
1007 "record",
1008 "-a",
1009 "-R",
1010 "-f",
1011 "-c", "1",
1012 "-e", "power:power_start",
1013 "-e", "power:power_end",
1014 "-e", "power:power_frequency",
1015 "-e", "sched:sched_wakeup",
1016 "-e", "sched:sched_switch",
1017};
1018#endif
1019
1020static const char * const record_new_args[] = {
1021 "record",
1022 "-a",
1023 "-R",
1024 "-f",
1025 "-c", "1",
1026 "-e", "power:cpu_frequency",
1027 "-e", "power:cpu_idle",
1028 "-e", "sched:sched_wakeup",
1029 "-e", "sched:sched_switch",
1030};
1031
1032static int __cmd_record(int argc, const char **argv)
1033{
1034 unsigned int rec_argc, i, j;
1035 const char **rec_argv;
1036 const char * const *record_args = record_new_args;
1037 unsigned int record_elems = ARRAY_SIZE(record_new_args);
1038
1039#ifdef SUPPORT_OLD_POWER_EVENTS
1040 if (!is_valid_tracepoint("power:cpu_idle") &&
1041 is_valid_tracepoint("power:power_start")) {
1042 use_old_power_events = 1;
1043 record_args = record_old_args;
1044 record_elems = ARRAY_SIZE(record_old_args);
1045 }
1046#endif
1047
1048 rec_argc = record_elems + argc - 1;
1049 rec_argv = calloc(rec_argc + 1, sizeof(char *));
1050
1051 if (rec_argv == NULL)
1052 return -ENOMEM;
1053
1054 for (i = 0; i < record_elems; i++)
1055 rec_argv[i] = strdup(record_args[i]);
1056
1057 for (j = 1; j < (unsigned int)argc; j++, i++)
1058 rec_argv[i] = argv[j];
1059
1060 return cmd_record(i, rec_argv, NULL);
1061}
1062
1063static int
1064parse_process(const struct option *opt __used, const char *arg, int __used unset)
1065{
1066 if (arg)
1067 add_process_filter(arg);
1068 return 0;
1069}
1070
1071static const struct option options[] = {
1072 OPT_STRING('i', "input", &input_name, "file",
1073 "input file name"),
1074 OPT_STRING('o', "output", &output_name, "file",
1075 "output file name"),
1076 OPT_INTEGER('w', "width", &svg_page_width,
1077 "page width"),
1078 OPT_BOOLEAN('P', "power-only", &power_only,
1079 "output power data only"),
1080 OPT_CALLBACK('p', "process", NULL, "process",
1081 "process selector. Pass a pid or process name.",
1082 parse_process),
1083 OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
1084 "Look for files with symbols relative to this directory"),
1085 OPT_END()
1086};
1087
1088
1089int cmd_timechart(int argc, const char **argv, const char *prefix __used)
1090{
1091 argc = parse_options(argc, argv, options, timechart_usage,
1092 PARSE_OPT_STOP_AT_NON_OPTION);
1093
1094 symbol__init();
1095
1096 if (argc && !strncmp(argv[0], "rec", 3))
1097 return __cmd_record(argc, argv);
1098 else if (argc)
1099 usage_with_options(timechart_usage, options);
1100
1101 setup_pager();
1102
1103 return __cmd_timechart();
1104}
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index 5352b5e352e..6ce4042421b 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -20,17 +20,26 @@
20 20
21#include "perf.h" 21#include "perf.h"
22 22
23#include "util/symbol.h"
24#include "util/color.h" 23#include "util/color.h"
24#include "util/evsel.h"
25#include "util/session.h"
26#include "util/symbol.h"
27#include "util/thread.h"
25#include "util/util.h" 28#include "util/util.h"
26#include "util/rbtree.h" 29#include <linux/rbtree.h>
27#include "util/parse-options.h" 30#include "util/parse-options.h"
28#include "util/parse-events.h" 31#include "util/parse-events.h"
32#include "util/cpumap.h"
33#include "util/xyarray.h"
34
35#include "util/debug.h"
29 36
30#include <assert.h> 37#include <assert.h>
31#include <fcntl.h> 38#include <fcntl.h>
32 39
33#include <stdio.h> 40#include <stdio.h>
41#include <termios.h>
42#include <unistd.h>
34 43
35#include <errno.h> 44#include <errno.h>
36#include <time.h> 45#include <time.h>
@@ -48,55 +57,346 @@
48#include <linux/unistd.h> 57#include <linux/unistd.h>
49#include <linux/types.h> 58#include <linux/types.h>
50 59
51static int fd[MAX_NR_CPUS][MAX_COUNTERS]; 60#define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y))
52 61
53static int system_wide = 0; 62static bool system_wide = false;
54 63
55static int default_interval = 100000; 64static int default_interval = 0;
56 65
57static u64 count_filter = 5; 66static int count_filter = 5;
58static int print_entries = 15; 67static int print_entries;
59 68
60static int target_pid = -1; 69static int target_pid = -1;
61static int profile_cpu = -1; 70static int target_tid = -1;
62static int nr_cpus = 0; 71static struct thread_map *threads;
63static unsigned int realtime_prio = 0; 72static bool inherit = false;
64static int group = 0; 73static struct cpu_map *cpus;
74static int realtime_prio = 0;
75static bool group = false;
65static unsigned int page_size; 76static unsigned int page_size;
66static unsigned int mmap_pages = 16; 77static unsigned int mmap_pages = 16;
67static int freq = 0; 78static int freq = 1000; /* 1 KHz */
68static int verbose = 0; 79
80static int delay_secs = 2;
81static bool zero = false;
82static bool dump_symtab = false;
83
84static bool hide_kernel_symbols = false;
85static bool hide_user_symbols = false;
86static struct winsize winsize;
69 87
70static char *sym_filter; 88/*
71static unsigned long filter_start; 89 * Source
72static unsigned long filter_end; 90 */
91
92struct source_line {
93 u64 eip;
94 unsigned long count[MAX_COUNTERS];
95 char *line;
96 struct source_line *next;
97};
73 98
74static int delay_secs = 2; 99static const char *sym_filter = NULL;
75static int zero; 100struct sym_entry *sym_filter_entry = NULL;
76static int dump_symtab; 101struct sym_entry *sym_filter_entry_sched = NULL;
102static int sym_pcnt_filter = 5;
103static int sym_counter = 0;
104static struct perf_evsel *sym_evsel = NULL;
105static int display_weighted = -1;
106static const char *cpu_list;
77 107
78/* 108/*
79 * Symbols 109 * Symbols
80 */ 110 */
81 111
82static u64 min_ip; 112struct sym_entry_source {
83static u64 max_ip = -1ll; 113 struct source_line *source;
114 struct source_line *lines;
115 struct source_line **lines_tail;
116 pthread_mutex_t lock;
117};
84 118
85struct sym_entry { 119struct sym_entry {
86 struct rb_node rb_node; 120 struct rb_node rb_node;
87 struct list_head node; 121 struct list_head node;
88 unsigned long count[MAX_COUNTERS];
89 unsigned long snap_count; 122 unsigned long snap_count;
90 double weight; 123 double weight;
91 int skip; 124 int skip;
125 u16 name_len;
126 u8 origin;
127 struct map *map;
128 struct sym_entry_source *src;
129 unsigned long count[0];
92}; 130};
93 131
94struct sym_entry *sym_filter_entry; 132/*
133 * Source functions
134 */
95 135
96struct dso *kernel_dso; 136static inline struct symbol *sym_entry__symbol(struct sym_entry *self)
137{
138 return ((void *)self) + symbol_conf.priv_size;
139}
140
141void get_term_dimensions(struct winsize *ws)
142{
143 char *s = getenv("LINES");
144
145 if (s != NULL) {
146 ws->ws_row = atoi(s);
147 s = getenv("COLUMNS");
148 if (s != NULL) {
149 ws->ws_col = atoi(s);
150 if (ws->ws_row && ws->ws_col)
151 return;
152 }
153 }
154#ifdef TIOCGWINSZ
155 if (ioctl(1, TIOCGWINSZ, ws) == 0 &&
156 ws->ws_row && ws->ws_col)
157 return;
158#endif
159 ws->ws_row = 25;
160 ws->ws_col = 80;
161}
162
163static void update_print_entries(struct winsize *ws)
164{
165 print_entries = ws->ws_row;
166
167 if (print_entries > 9)
168 print_entries -= 9;
169}
170
171static void sig_winch_handler(int sig __used)
172{
173 get_term_dimensions(&winsize);
174 update_print_entries(&winsize);
175}
176
177static int parse_source(struct sym_entry *syme)
178{
179 struct symbol *sym;
180 struct sym_entry_source *source;
181 struct map *map;
182 FILE *file;
183 char command[PATH_MAX*2];
184 const char *path;
185 u64 len;
186
187 if (!syme)
188 return -1;
189
190 sym = sym_entry__symbol(syme);
191 map = syme->map;
192
193 /*
194 * We can't annotate with just /proc/kallsyms
195 */
196 if (map->dso->origin == DSO__ORIG_KERNEL)
197 return -1;
198
199 if (syme->src == NULL) {
200 syme->src = zalloc(sizeof(*source));
201 if (syme->src == NULL)
202 return -1;
203 pthread_mutex_init(&syme->src->lock, NULL);
204 }
205
206 source = syme->src;
207
208 if (source->lines) {
209 pthread_mutex_lock(&source->lock);
210 goto out_assign;
211 }
212 path = map->dso->long_name;
213
214 len = sym->end - sym->start;
215
216 sprintf(command,
217 "objdump --start-address=%#0*Lx --stop-address=%#0*Lx -dS %s",
218 BITS_PER_LONG / 4, map__rip_2objdump(map, sym->start),
219 BITS_PER_LONG / 4, map__rip_2objdump(map, sym->end), path);
220
221 file = popen(command, "r");
222 if (!file)
223 return -1;
224
225 pthread_mutex_lock(&source->lock);
226 source->lines_tail = &source->lines;
227 while (!feof(file)) {
228 struct source_line *src;
229 size_t dummy = 0;
230 char *c, *sep;
231
232 src = malloc(sizeof(struct source_line));
233 assert(src != NULL);
234 memset(src, 0, sizeof(struct source_line));
235
236 if (getline(&src->line, &dummy, file) < 0)
237 break;
238 if (!src->line)
239 break;
240
241 c = strchr(src->line, '\n');
242 if (c)
243 *c = 0;
244
245 src->next = NULL;
246 *source->lines_tail = src;
247 source->lines_tail = &src->next;
248
249 src->eip = strtoull(src->line, &sep, 16);
250 if (*sep == ':')
251 src->eip = map__objdump_2ip(map, src->eip);
252 else /* this line has no ip info (e.g. source line) */
253 src->eip = 0;
254 }
255 pclose(file);
256out_assign:
257 sym_filter_entry = syme;
258 pthread_mutex_unlock(&source->lock);
259 return 0;
260}
261
262static void __zero_source_counters(struct sym_entry *syme)
263{
264 int i;
265 struct source_line *line;
266
267 line = syme->src->lines;
268 while (line) {
269 for (i = 0; i < nr_counters; i++)
270 line->count[i] = 0;
271 line = line->next;
272 }
273}
274
275static void record_precise_ip(struct sym_entry *syme, int counter, u64 ip)
276{
277 struct source_line *line;
278
279 if (syme != sym_filter_entry)
280 return;
281
282 if (pthread_mutex_trylock(&syme->src->lock))
283 return;
284
285 if (syme->src == NULL || syme->src->source == NULL)
286 goto out_unlock;
287
288 for (line = syme->src->lines; line; line = line->next) {
289 /* skip lines without IP info */
290 if (line->eip == 0)
291 continue;
292 if (line->eip == ip) {
293 line->count[counter]++;
294 break;
295 }
296 if (line->eip > ip)
297 break;
298 }
299out_unlock:
300 pthread_mutex_unlock(&syme->src->lock);
301}
302
303#define PATTERN_LEN (BITS_PER_LONG / 4 + 2)
304
305static void lookup_sym_source(struct sym_entry *syme)
306{
307 struct symbol *symbol = sym_entry__symbol(syme);
308 struct source_line *line;
309 char pattern[PATTERN_LEN + 1];
310
311 sprintf(pattern, "%0*Lx <", BITS_PER_LONG / 4,
312 map__rip_2objdump(syme->map, symbol->start));
313
314 pthread_mutex_lock(&syme->src->lock);
315 for (line = syme->src->lines; line; line = line->next) {
316 if (memcmp(line->line, pattern, PATTERN_LEN) == 0) {
317 syme->src->source = line;
318 break;
319 }
320 }
321 pthread_mutex_unlock(&syme->src->lock);
322}
323
324static void show_lines(struct source_line *queue, int count, int total)
325{
326 int i;
327 struct source_line *line;
328
329 line = queue;
330 for (i = 0; i < count; i++) {
331 float pcnt = 100.0*(float)line->count[sym_counter]/(float)total;
332
333 printf("%8li %4.1f%%\t%s\n", line->count[sym_counter], pcnt, line->line);
334 line = line->next;
335 }
336}
337
338#define TRACE_COUNT 3
339
340static void show_details(struct sym_entry *syme)
341{
342 struct symbol *symbol;
343 struct source_line *line;
344 struct source_line *line_queue = NULL;
345 int displayed = 0;
346 int line_queue_count = 0, total = 0, more = 0;
347
348 if (!syme)
349 return;
350
351 if (!syme->src->source)
352 lookup_sym_source(syme);
353
354 if (!syme->src->source)
355 return;
356
357 symbol = sym_entry__symbol(syme);
358 printf("Showing %s for %s\n", event_name(sym_evsel), symbol->name);
359 printf(" Events Pcnt (>=%d%%)\n", sym_pcnt_filter);
360
361 pthread_mutex_lock(&syme->src->lock);
362 line = syme->src->source;
363 while (line) {
364 total += line->count[sym_counter];
365 line = line->next;
366 }
367
368 line = syme->src->source;
369 while (line) {
370 float pcnt = 0.0;
371
372 if (!line_queue_count)
373 line_queue = line;
374 line_queue_count++;
375
376 if (line->count[sym_counter])
377 pcnt = 100.0 * line->count[sym_counter] / (float)total;
378 if (pcnt >= (float)sym_pcnt_filter) {
379 if (displayed <= print_entries)
380 show_lines(line_queue, line_queue_count, total);
381 else more++;
382 displayed += line_queue_count;
383 line_queue_count = 0;
384 line_queue = NULL;
385 } else if (line_queue_count > TRACE_COUNT) {
386 line_queue = line_queue->next;
387 line_queue_count--;
388 }
389
390 line->count[sym_counter] = zero ? 0 : line->count[sym_counter] * 7 / 8;
391 line = line->next;
392 }
393 pthread_mutex_unlock(&syme->src->lock);
394 if (more)
395 printf("%d lines not displayed, maybe increase display entries [e]\n", more);
396}
97 397
98/* 398/*
99 * Symbols will be added here in record_ip and will get out 399 * Symbols will be added here in event__process_sample and will get out
100 * after decayed. 400 * after decayed.
101 */ 401 */
102static LIST_HEAD(active_symbols); 402static LIST_HEAD(active_symbols);
@@ -110,6 +410,9 @@ static double sym_weight(const struct sym_entry *sym)
110 double weight = sym->snap_count; 410 double weight = sym->snap_count;
111 int counter; 411 int counter;
112 412
413 if (!display_weighted)
414 return weight;
415
113 for (counter = 1; counter < nr_counters-1; counter++) 416 for (counter = 1; counter < nr_counters-1; counter++)
114 weight *= sym->count[counter]; 417 weight *= sym->count[counter];
115 418
@@ -119,7 +422,9 @@ static double sym_weight(const struct sym_entry *sym)
119} 422}
120 423
121static long samples; 424static long samples;
122static long userspace_samples; 425static long kernel_samples, us_samples;
426static long exact_samples;
427static long guest_us_samples, guest_kernel_samples;
123static const char CONSOLE_CLEAR[] = ""; 428static const char CONSOLE_CLEAR[] = "";
124 429
125static void __list_insert_active_sym(struct sym_entry *syme) 430static void __list_insert_active_sym(struct sym_entry *syme)
@@ -157,15 +462,23 @@ static void rb_insert_active_sym(struct rb_root *tree, struct sym_entry *se)
157static void print_sym_table(void) 462static void print_sym_table(void)
158{ 463{
159 int printed = 0, j; 464 int printed = 0, j;
160 int counter; 465 struct perf_evsel *counter;
466 int snap = !display_weighted ? sym_counter : 0;
161 float samples_per_sec = samples/delay_secs; 467 float samples_per_sec = samples/delay_secs;
162 float ksamples_per_sec = (samples-userspace_samples)/delay_secs; 468 float ksamples_per_sec = kernel_samples/delay_secs;
469 float us_samples_per_sec = (us_samples)/delay_secs;
470 float guest_kernel_samples_per_sec = (guest_kernel_samples)/delay_secs;
471 float guest_us_samples_per_sec = (guest_us_samples)/delay_secs;
472 float esamples_percent = (100.0*exact_samples)/samples;
163 float sum_ksamples = 0.0; 473 float sum_ksamples = 0.0;
164 struct sym_entry *syme, *n; 474 struct sym_entry *syme, *n;
165 struct rb_root tmp = RB_ROOT; 475 struct rb_root tmp = RB_ROOT;
166 struct rb_node *nd; 476 struct rb_node *nd;
477 int sym_width = 0, dso_width = 0, dso_short_width = 0;
478 const int win_width = winsize.ws_col - 1;
167 479
168 samples = userspace_samples = 0; 480 samples = us_samples = kernel_samples = exact_samples = 0;
481 guest_kernel_samples = guest_us_samples = 0;
169 482
170 /* Sort the active symbols */ 483 /* Sort the active symbols */
171 pthread_mutex_lock(&active_symbols_lock); 484 pthread_mutex_lock(&active_symbols_lock);
@@ -173,8 +486,16 @@ static void print_sym_table(void)
173 pthread_mutex_unlock(&active_symbols_lock); 486 pthread_mutex_unlock(&active_symbols_lock);
174 487
175 list_for_each_entry_safe_from(syme, n, &active_symbols, node) { 488 list_for_each_entry_safe_from(syme, n, &active_symbols, node) {
176 syme->snap_count = syme->count[0]; 489 syme->snap_count = syme->count[snap];
177 if (syme->snap_count != 0) { 490 if (syme->snap_count != 0) {
491
492 if ((hide_user_symbols &&
493 syme->origin == PERF_RECORD_MISC_USER) ||
494 (hide_kernel_symbols &&
495 syme->origin == PERF_RECORD_MISC_KERNEL)) {
496 list_remove_active_sym(syme);
497 continue;
498 }
178 syme->weight = sym_weight(syme); 499 syme->weight = sym_weight(syme);
179 rb_insert_active_sym(&tmp, syme); 500 rb_insert_active_sym(&tmp, syme);
180 sum_ksamples += syme->snap_count; 501 sum_ksamples += syme->snap_count;
@@ -187,22 +508,46 @@ static void print_sym_table(void)
187 508
188 puts(CONSOLE_CLEAR); 509 puts(CONSOLE_CLEAR);
189 510
190 printf( 511 printf("%-*.*s\n", win_width, win_width, graph_dotted_line);
191"------------------------------------------------------------------------------\n"); 512 if (!perf_guest) {
192 printf( " PerfTop:%8.0f irqs/sec kernel:%4.1f%% [", 513 printf(" PerfTop:%8.0f irqs/sec kernel:%4.1f%%"
193 samples_per_sec, 514 " exact: %4.1f%% [",
194 100.0 - (100.0*((samples_per_sec-ksamples_per_sec)/samples_per_sec))); 515 samples_per_sec,
516 100.0 - (100.0 * ((samples_per_sec - ksamples_per_sec) /
517 samples_per_sec)),
518 esamples_percent);
519 } else {
520 printf(" PerfTop:%8.0f irqs/sec kernel:%4.1f%% us:%4.1f%%"
521 " guest kernel:%4.1f%% guest us:%4.1f%%"
522 " exact: %4.1f%% [",
523 samples_per_sec,
524 100.0 - (100.0 * ((samples_per_sec-ksamples_per_sec) /
525 samples_per_sec)),
526 100.0 - (100.0 * ((samples_per_sec-us_samples_per_sec) /
527 samples_per_sec)),
528 100.0 - (100.0 * ((samples_per_sec -
529 guest_kernel_samples_per_sec) /
530 samples_per_sec)),
531 100.0 - (100.0 * ((samples_per_sec -
532 guest_us_samples_per_sec) /
533 samples_per_sec)),
534 esamples_percent);
535 }
195 536
196 if (nr_counters == 1) { 537 if (nr_counters == 1 || !display_weighted) {
197 printf("%Ld", (u64)attrs[0].sample_period); 538 struct perf_evsel *first;
539 first = list_entry(evsel_list.next, struct perf_evsel, node);
540 printf("%Ld", first->attr.sample_period);
198 if (freq) 541 if (freq)
199 printf("Hz "); 542 printf("Hz ");
200 else 543 else
201 printf(" "); 544 printf(" ");
202 } 545 }
203 546
204 for (counter = 0; counter < nr_counters; counter++) { 547 if (!display_weighted)
205 if (counter) 548 printf("%s", event_name(sym_evsel));
549 else list_for_each_entry(counter, &evsel_list, node) {
550 if (counter->idx)
206 printf("/"); 551 printf("/");
207 552
208 printf("%s", event_name(counter)); 553 printf("%s", event_name(counter));
@@ -212,85 +557,401 @@ static void print_sym_table(void)
212 557
213 if (target_pid != -1) 558 if (target_pid != -1)
214 printf(" (target_pid: %d", target_pid); 559 printf(" (target_pid: %d", target_pid);
560 else if (target_tid != -1)
561 printf(" (target_tid: %d", target_tid);
215 else 562 else
216 printf(" (all"); 563 printf(" (all");
217 564
218 if (profile_cpu != -1) 565 if (cpu_list)
219 printf(", cpu: %d)\n", profile_cpu); 566 printf(", CPU%s: %s)\n", cpus->nr > 1 ? "s" : "", cpu_list);
220 else { 567 else {
221 if (target_pid != -1) 568 if (target_tid != -1)
222 printf(")\n"); 569 printf(")\n");
223 else 570 else
224 printf(", %d CPUs)\n", nr_cpus); 571 printf(", %d CPU%s)\n", cpus->nr, cpus->nr > 1 ? "s" : "");
572 }
573
574 printf("%-*.*s\n", win_width, win_width, graph_dotted_line);
575
576 if (sym_filter_entry) {
577 show_details(sym_filter_entry);
578 return;
225 } 579 }
226 580
227 printf("------------------------------------------------------------------------------\n\n"); 581 /*
582 * Find the longest symbol name that will be displayed
583 */
584 for (nd = rb_first(&tmp); nd; nd = rb_next(nd)) {
585 syme = rb_entry(nd, struct sym_entry, rb_node);
586 if (++printed > print_entries ||
587 (int)syme->snap_count < count_filter)
588 continue;
589
590 if (syme->map->dso->long_name_len > dso_width)
591 dso_width = syme->map->dso->long_name_len;
592
593 if (syme->map->dso->short_name_len > dso_short_width)
594 dso_short_width = syme->map->dso->short_name_len;
595
596 if (syme->name_len > sym_width)
597 sym_width = syme->name_len;
598 }
599
600 printed = 0;
228 601
602 if (sym_width + dso_width > winsize.ws_col - 29) {
603 dso_width = dso_short_width;
604 if (sym_width + dso_width > winsize.ws_col - 29)
605 sym_width = winsize.ws_col - dso_width - 29;
606 }
607 putchar('\n');
229 if (nr_counters == 1) 608 if (nr_counters == 1)
230 printf(" samples pcnt"); 609 printf(" samples pcnt");
231 else 610 else
232 printf(" weight samples pcnt"); 611 printf(" weight samples pcnt");
233 612
234 printf(" RIP kernel function\n" 613 if (verbose)
235 " ______ _______ _____ ________________ _______________\n\n" 614 printf(" RIP ");
236 ); 615 printf(" %-*.*s DSO\n", sym_width, sym_width, "function");
616 printf(" %s _______ _____",
617 nr_counters == 1 ? " " : "______");
618 if (verbose)
619 printf(" ________________");
620 printf(" %-*.*s", sym_width, sym_width, graph_line);
621 printf(" %-*.*s", dso_width, dso_width, graph_line);
622 puts("\n");
237 623
238 for (nd = rb_first(&tmp); nd; nd = rb_next(nd)) { 624 for (nd = rb_first(&tmp); nd; nd = rb_next(nd)) {
239 struct sym_entry *syme = rb_entry(nd, struct sym_entry, rb_node); 625 struct symbol *sym;
240 struct symbol *sym = (struct symbol *)(syme + 1);
241 char *color = PERF_COLOR_NORMAL;
242 double pcnt; 626 double pcnt;
243 627
244 if (++printed > print_entries || syme->snap_count < count_filter) 628 syme = rb_entry(nd, struct sym_entry, rb_node);
629 sym = sym_entry__symbol(syme);
630 if (++printed > print_entries || (int)syme->snap_count < count_filter)
245 continue; 631 continue;
246 632
247 pcnt = 100.0 - (100.0 * ((sum_ksamples - syme->snap_count) / 633 pcnt = 100.0 - (100.0 * ((sum_ksamples - syme->snap_count) /
248 sum_ksamples)); 634 sum_ksamples));
249 635
250 /* 636 if (nr_counters == 1 || !display_weighted)
251 * We color high-overhead entries in red, mid-overhead 637 printf("%20.2f ", syme->weight);
252 * entries in green - and keep the low overhead places 638 else
253 * normal: 639 printf("%9.1f %10ld ", syme->weight, syme->snap_count);
254 */ 640
255 if (pcnt >= 5.0) { 641 percent_color_fprintf(stdout, "%4.1f%%", pcnt);
256 color = PERF_COLOR_RED; 642 if (verbose)
257 } else { 643 printf(" %016llx", sym->start);
258 if (pcnt >= 0.5) 644 printf(" %-*.*s", sym_width, sym_width, sym->name);
259 color = PERF_COLOR_GREEN; 645 printf(" %-*.*s\n", dso_width, dso_width,
646 dso_width >= syme->map->dso->long_name_len ?
647 syme->map->dso->long_name :
648 syme->map->dso->short_name);
649 }
650}
651
652static void prompt_integer(int *target, const char *msg)
653{
654 char *buf = malloc(0), *p;
655 size_t dummy = 0;
656 int tmp;
657
658 fprintf(stdout, "\n%s: ", msg);
659 if (getline(&buf, &dummy, stdin) < 0)
660 return;
661
662 p = strchr(buf, '\n');
663 if (p)
664 *p = 0;
665
666 p = buf;
667 while(*p) {
668 if (!isdigit(*p))
669 goto out_free;
670 p++;
671 }
672 tmp = strtoul(buf, NULL, 10);
673 *target = tmp;
674out_free:
675 free(buf);
676}
677
678static void prompt_percent(int *target, const char *msg)
679{
680 int tmp = 0;
681
682 prompt_integer(&tmp, msg);
683 if (tmp >= 0 && tmp <= 100)
684 *target = tmp;
685}
686
687static void prompt_symbol(struct sym_entry **target, const char *msg)
688{
689 char *buf = malloc(0), *p;
690 struct sym_entry *syme = *target, *n, *found = NULL;
691 size_t dummy = 0;
692
693 /* zero counters of active symbol */
694 if (syme) {
695 pthread_mutex_lock(&syme->src->lock);
696 __zero_source_counters(syme);
697 *target = NULL;
698 pthread_mutex_unlock(&syme->src->lock);
699 }
700
701 fprintf(stdout, "\n%s: ", msg);
702 if (getline(&buf, &dummy, stdin) < 0)
703 goto out_free;
704
705 p = strchr(buf, '\n');
706 if (p)
707 *p = 0;
708
709 pthread_mutex_lock(&active_symbols_lock);
710 syme = list_entry(active_symbols.next, struct sym_entry, node);
711 pthread_mutex_unlock(&active_symbols_lock);
712
713 list_for_each_entry_safe_from(syme, n, &active_symbols, node) {
714 struct symbol *sym = sym_entry__symbol(syme);
715
716 if (!strcmp(buf, sym->name)) {
717 found = syme;
718 break;
260 } 719 }
720 }
261 721
262 if (nr_counters == 1) 722 if (!found) {
263 printf("%20.2f - ", syme->weight); 723 fprintf(stderr, "Sorry, %s is not active.\n", buf);
264 else 724 sleep(1);
265 printf("%9.1f %10ld - ", syme->weight, syme->snap_count); 725 return;
726 } else
727 parse_source(found);
728
729out_free:
730 free(buf);
731}
732
733static void print_mapped_keys(void)
734{
735 char *name = NULL;
736
737 if (sym_filter_entry) {
738 struct symbol *sym = sym_entry__symbol(sym_filter_entry);
739 name = sym->name;
740 }
741
742 fprintf(stdout, "\nMapped keys:\n");
743 fprintf(stdout, "\t[d] display refresh delay. \t(%d)\n", delay_secs);
744 fprintf(stdout, "\t[e] display entries (lines). \t(%d)\n", print_entries);
266 745
267 color_fprintf(stdout, color, "%4.1f%%", pcnt); 746 if (nr_counters > 1)
268 printf(" - %016llx : %s\n", sym->start, sym->name); 747 fprintf(stdout, "\t[E] active event counter. \t(%s)\n", event_name(sym_evsel));
748
749 fprintf(stdout, "\t[f] profile display filter (count). \t(%d)\n", count_filter);
750
751 fprintf(stdout, "\t[F] annotate display filter (percent). \t(%d%%)\n", sym_pcnt_filter);
752 fprintf(stdout, "\t[s] annotate symbol. \t(%s)\n", name?: "NULL");
753 fprintf(stdout, "\t[S] stop annotation.\n");
754
755 if (nr_counters > 1)
756 fprintf(stdout, "\t[w] toggle display weighted/count[E]r. \t(%d)\n", display_weighted ? 1 : 0);
757
758 fprintf(stdout,
759 "\t[K] hide kernel_symbols symbols. \t(%s)\n",
760 hide_kernel_symbols ? "yes" : "no");
761 fprintf(stdout,
762 "\t[U] hide user symbols. \t(%s)\n",
763 hide_user_symbols ? "yes" : "no");
764 fprintf(stdout, "\t[z] toggle sample zeroing. \t(%d)\n", zero ? 1 : 0);
765 fprintf(stdout, "\t[qQ] quit.\n");
766}
767
768static int key_mapped(int c)
769{
770 switch (c) {
771 case 'd':
772 case 'e':
773 case 'f':
774 case 'z':
775 case 'q':
776 case 'Q':
777 case 'K':
778 case 'U':
779 case 'F':
780 case 's':
781 case 'S':
782 return 1;
783 case 'E':
784 case 'w':
785 return nr_counters > 1 ? 1 : 0;
786 default:
787 break;
269 } 788 }
789
790 return 0;
270} 791}
271 792
272static void *display_thread(void *arg) 793static void handle_keypress(struct perf_session *session, int c)
273{ 794{
274 struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; 795 if (!key_mapped(c)) {
275 int delay_msecs = delay_secs * 1000; 796 struct pollfd stdin_poll = { .fd = 0, .events = POLLIN };
797 struct termios tc, save;
798
799 print_mapped_keys();
800 fprintf(stdout, "\nEnter selection, or unmapped key to continue: ");
801 fflush(stdout);
802
803 tcgetattr(0, &save);
804 tc = save;
805 tc.c_lflag &= ~(ICANON | ECHO);
806 tc.c_cc[VMIN] = 0;
807 tc.c_cc[VTIME] = 0;
808 tcsetattr(0, TCSANOW, &tc);
809
810 poll(&stdin_poll, 1, -1);
811 c = getc(stdin);
812
813 tcsetattr(0, TCSAFLUSH, &save);
814 if (!key_mapped(c))
815 return;
816 }
817
818 switch (c) {
819 case 'd':
820 prompt_integer(&delay_secs, "Enter display delay");
821 if (delay_secs < 1)
822 delay_secs = 1;
823 break;
824 case 'e':
825 prompt_integer(&print_entries, "Enter display entries (lines)");
826 if (print_entries == 0) {
827 sig_winch_handler(SIGWINCH);
828 signal(SIGWINCH, sig_winch_handler);
829 } else
830 signal(SIGWINCH, SIG_DFL);
831 break;
832 case 'E':
833 if (nr_counters > 1) {
834 fprintf(stderr, "\nAvailable events:");
835
836 list_for_each_entry(sym_evsel, &evsel_list, node)
837 fprintf(stderr, "\n\t%d %s", sym_evsel->idx, event_name(sym_evsel));
838
839 prompt_integer(&sym_counter, "Enter details event counter");
840
841 if (sym_counter >= nr_counters) {
842 sym_evsel = list_entry(evsel_list.next, struct perf_evsel, node);
843 sym_counter = 0;
844 fprintf(stderr, "Sorry, no such event, using %s.\n", event_name(sym_evsel));
845 sleep(1);
846 break;
847 }
848 list_for_each_entry(sym_evsel, &evsel_list, node)
849 if (sym_evsel->idx == sym_counter)
850 break;
851 } else sym_counter = 0;
852 break;
853 case 'f':
854 prompt_integer(&count_filter, "Enter display event count filter");
855 break;
856 case 'F':
857 prompt_percent(&sym_pcnt_filter, "Enter details display event filter (percent)");
858 break;
859 case 'K':
860 hide_kernel_symbols = !hide_kernel_symbols;
861 break;
862 case 'q':
863 case 'Q':
864 printf("exiting.\n");
865 if (dump_symtab)
866 perf_session__fprintf_dsos(session, stderr);
867 exit(0);
868 case 's':
869 prompt_symbol(&sym_filter_entry, "Enter details symbol");
870 break;
871 case 'S':
872 if (!sym_filter_entry)
873 break;
874 else {
875 struct sym_entry *syme = sym_filter_entry;
876
877 pthread_mutex_lock(&syme->src->lock);
878 sym_filter_entry = NULL;
879 __zero_source_counters(syme);
880 pthread_mutex_unlock(&syme->src->lock);
881 }
882 break;
883 case 'U':
884 hide_user_symbols = !hide_user_symbols;
885 break;
886 case 'w':
887 display_weighted = ~display_weighted;
888 break;
889 case 'z':
890 zero = !zero;
891 break;
892 default:
893 break;
894 }
895}
276 896
277 printf("PerfTop refresh period: %d seconds\n", delay_secs); 897static void *display_thread(void *arg __used)
898{
899 struct pollfd stdin_poll = { .fd = 0, .events = POLLIN };
900 struct termios tc, save;
901 int delay_msecs, c;
902 struct perf_session *session = (struct perf_session *) arg;
903
904 tcgetattr(0, &save);
905 tc = save;
906 tc.c_lflag &= ~(ICANON | ECHO);
907 tc.c_cc[VMIN] = 0;
908 tc.c_cc[VTIME] = 0;
909
910repeat:
911 delay_msecs = delay_secs * 1000;
912 tcsetattr(0, TCSANOW, &tc);
913 /* trash return*/
914 getc(stdin);
278 915
279 do { 916 do {
280 print_sym_table(); 917 print_sym_table();
281 } while (!poll(&stdin_poll, 1, delay_msecs) == 1); 918 } while (!poll(&stdin_poll, 1, delay_msecs) == 1);
282 919
283 printf("key pressed - exiting.\n"); 920 c = getc(stdin);
284 exit(0); 921 tcsetattr(0, TCSAFLUSH, &save);
922
923 handle_keypress(session, c);
924 goto repeat;
285 925
286 return NULL; 926 return NULL;
287} 927}
288 928
289static int symbol_filter(struct dso *self, struct symbol *sym) 929/* Tag samples to be skipped. */
930static const char *skip_symbols[] = {
931 "default_idle",
932 "cpu_idle",
933 "enter_idle",
934 "exit_idle",
935 "mwait_idle",
936 "mwait_idle_with_hints",
937 "poll_idle",
938 "ppc64_runlatch_off",
939 "pseries_dedicated_idle_sleep",
940 NULL
941};
942
943static int symbol_filter(struct map *map, struct symbol *sym)
290{ 944{
291 static int filter_match;
292 struct sym_entry *syme; 945 struct sym_entry *syme;
293 const char *name = sym->name; 946 const char *name = sym->name;
947 int i;
948
949 /*
950 * ppc64 uses function descriptors and appends a '.' to the
951 * start of every instruction address. Remove it.
952 */
953 if (name[0] == '.')
954 name++;
294 955
295 if (!strcmp(name, "_text") || 956 if (!strcmp(name, "_text") ||
296 !strcmp(name, "_etext") || 957 !strcmp(name, "_etext") ||
@@ -301,119 +962,157 @@ static int symbol_filter(struct dso *self, struct symbol *sym)
301 strstr(name, "_text_end")) 962 strstr(name, "_text_end"))
302 return 1; 963 return 1;
303 964
304 syme = dso__sym_priv(self, sym); 965 syme = symbol__priv(sym);
305 /* Tag samples to be skipped. */ 966 syme->map = map;
306 if (!strcmp("default_idle", name) || 967 syme->src = NULL;
307 !strcmp("cpu_idle", name) || 968
308 !strcmp("enter_idle", name) || 969 if (!sym_filter_entry && sym_filter && !strcmp(name, sym_filter)) {
309 !strcmp("exit_idle", name) || 970 /* schedule initial sym_filter_entry setup */
310 !strcmp("mwait_idle", name)) 971 sym_filter_entry_sched = syme;
311 syme->skip = 1; 972 sym_filter = NULL;
312
313 if (filter_match == 1) {
314 filter_end = sym->start;
315 filter_match = -1;
316 if (filter_end - filter_start > 10000) {
317 fprintf(stderr,
318 "hm, too large filter symbol <%s> - skipping.\n",
319 sym_filter);
320 fprintf(stderr, "symbol filter start: %016lx\n",
321 filter_start);
322 fprintf(stderr, " end: %016lx\n",
323 filter_end);
324 filter_end = filter_start = 0;
325 sym_filter = NULL;
326 sleep(1);
327 }
328 } 973 }
329 974
330 if (filter_match == 0 && sym_filter && !strcmp(name, sym_filter)) { 975 for (i = 0; skip_symbols[i]; i++) {
331 filter_match = 1; 976 if (!strcmp(skip_symbols[i], name)) {
332 filter_start = sym->start; 977 syme->skip = 1;
978 break;
979 }
333 } 980 }
334 981
982 if (!syme->skip)
983 syme->name_len = strlen(sym->name);
335 984
336 return 0; 985 return 0;
337} 986}
338 987
339static int parse_symbols(void) 988static void event__process_sample(const event_t *self,
989 struct sample_data *sample,
990 struct perf_session *session,
991 struct perf_evsel *evsel)
340{ 992{
341 struct rb_node *node; 993 u64 ip = self->ip.ip;
342 struct symbol *sym; 994 struct sym_entry *syme;
343 995 struct addr_location al;
344 kernel_dso = dso__new("[kernel]", sizeof(struct sym_entry)); 996 struct machine *machine;
345 if (kernel_dso == NULL) 997 u8 origin = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
346 return -1;
347
348 if (dso__load_kernel(kernel_dso, NULL, symbol_filter, 1) != 0)
349 goto out_delete_dso;
350
351 node = rb_first(&kernel_dso->syms);
352 sym = rb_entry(node, struct symbol, rb_node);
353 min_ip = sym->start;
354 998
355 node = rb_last(&kernel_dso->syms); 999 ++samples;
356 sym = rb_entry(node, struct symbol, rb_node);
357 max_ip = sym->end;
358 1000
359 if (dump_symtab) 1001 switch (origin) {
360 dso__fprintf(kernel_dso, stderr); 1002 case PERF_RECORD_MISC_USER:
1003 ++us_samples;
1004 if (hide_user_symbols)
1005 return;
1006 machine = perf_session__find_host_machine(session);
1007 break;
1008 case PERF_RECORD_MISC_KERNEL:
1009 ++kernel_samples;
1010 if (hide_kernel_symbols)
1011 return;
1012 machine = perf_session__find_host_machine(session);
1013 break;
1014 case PERF_RECORD_MISC_GUEST_KERNEL:
1015 ++guest_kernel_samples;
1016 machine = perf_session__find_machine(session, self->ip.pid);
1017 break;
1018 case PERF_RECORD_MISC_GUEST_USER:
1019 ++guest_us_samples;
1020 /*
1021 * TODO: we don't process guest user from host side
1022 * except simple counting.
1023 */
1024 return;
1025 default:
1026 return;
1027 }
361 1028
362 return 0; 1029 if (!machine && perf_guest) {
1030 pr_err("Can't find guest [%d]'s kernel information\n",
1031 self->ip.pid);
1032 return;
1033 }
363 1034
364out_delete_dso: 1035 if (self->header.misc & PERF_RECORD_MISC_EXACT_IP)
365 dso__delete(kernel_dso); 1036 exact_samples++;
366 kernel_dso = NULL;
367 return -1;
368}
369 1037
370#define TRACE_COUNT 3 1038 if (event__preprocess_sample(self, session, &al, sample,
1039 symbol_filter) < 0 ||
1040 al.filtered)
1041 return;
371 1042
372/* 1043 if (al.sym == NULL) {
373 * Binary search in the histogram table and record the hit: 1044 /*
374 */ 1045 * As we do lazy loading of symtabs we only will know if the
375static void record_ip(u64 ip, int counter) 1046 * specified vmlinux file is invalid when we actually have a
376{ 1047 * hit in kernel space and then try to load it. So if we get
377 struct symbol *sym = dso__find_symbol(kernel_dso, ip); 1048 * here and there are _no_ symbols in the DSO backing the
1049 * kernel map, bail out.
1050 *
1051 * We may never get here, for instance, if we use -K/
1052 * --hide-kernel-symbols, even if the user specifies an
1053 * invalid --vmlinux ;-)
1054 */
1055 if (al.map == machine->vmlinux_maps[MAP__FUNCTION] &&
1056 RB_EMPTY_ROOT(&al.map->dso->symbols[MAP__FUNCTION])) {
1057 pr_err("The %s file can't be used\n",
1058 symbol_conf.vmlinux_name);
1059 exit(1);
1060 }
378 1061
379 if (sym != NULL) { 1062 return;
380 struct sym_entry *syme = dso__sym_priv(kernel_dso, sym); 1063 }
381 1064
382 if (!syme->skip) { 1065 /* let's see, whether we need to install initial sym_filter_entry */
383 syme->count[counter]++; 1066 if (sym_filter_entry_sched) {
384 pthread_mutex_lock(&active_symbols_lock); 1067 sym_filter_entry = sym_filter_entry_sched;
385 if (list_empty(&syme->node) || !syme->node.next) 1068 sym_filter_entry_sched = NULL;
386 __list_insert_active_sym(syme); 1069 if (parse_source(sym_filter_entry) < 0) {
387 pthread_mutex_unlock(&active_symbols_lock); 1070 struct symbol *sym = sym_entry__symbol(sym_filter_entry);
388 return; 1071
1072 pr_err("Can't annotate %s", sym->name);
1073 if (sym_filter_entry->map->dso->origin == DSO__ORIG_KERNEL) {
1074 pr_err(": No vmlinux file was found in the path:\n");
1075 machine__fprintf_vmlinux_path(machine, stderr);
1076 } else
1077 pr_err(".\n");
1078 exit(1);
389 } 1079 }
390 } 1080 }
391 1081
392 samples--; 1082 syme = symbol__priv(al.sym);
393} 1083 if (!syme->skip) {
394 1084 syme->count[evsel->idx]++;
395static void process_event(u64 ip, int counter) 1085 syme->origin = origin;
396{ 1086 record_precise_ip(syme, evsel->idx, ip);
397 samples++; 1087 pthread_mutex_lock(&active_symbols_lock);
398 1088 if (list_empty(&syme->node) || !syme->node.next)
399 if (ip < min_ip || ip > max_ip) { 1089 __list_insert_active_sym(syme);
400 userspace_samples++; 1090 pthread_mutex_unlock(&active_symbols_lock);
401 return;
402 } 1091 }
403
404 record_ip(ip, counter);
405} 1092}
406 1093
407struct mmap_data { 1094struct mmap_data {
408 int counter;
409 void *base; 1095 void *base;
410 unsigned int mask; 1096 int mask;
411 unsigned int prev; 1097 unsigned int prev;
412}; 1098};
413 1099
1100static int perf_evsel__alloc_mmap_per_thread(struct perf_evsel *evsel,
1101 int ncpus, int nthreads)
1102{
1103 evsel->priv = xyarray__new(ncpus, nthreads, sizeof(struct mmap_data));
1104 return evsel->priv != NULL ? 0 : -ENOMEM;
1105}
1106
1107static void perf_evsel__free_mmap(struct perf_evsel *evsel)
1108{
1109 xyarray__delete(evsel->priv);
1110 evsel->priv = NULL;
1111}
1112
414static unsigned int mmap_read_head(struct mmap_data *md) 1113static unsigned int mmap_read_head(struct mmap_data *md)
415{ 1114{
416 struct perf_counter_mmap_page *pc = md->base; 1115 struct perf_event_mmap_page *pc = md->base;
417 int head; 1116 int head;
418 1117
419 head = pc->data_head; 1118 head = pc->data_head;
@@ -422,17 +1121,18 @@ static unsigned int mmap_read_head(struct mmap_data *md)
422 return head; 1121 return head;
423} 1122}
424 1123
425struct timeval last_read, this_read; 1124static void perf_session__mmap_read_counter(struct perf_session *self,
426 1125 struct perf_evsel *evsel,
427static void mmap_read_counter(struct mmap_data *md) 1126 int cpu, int thread_idx)
428{ 1127{
1128 struct xyarray *mmap_array = evsel->priv;
1129 struct mmap_data *md = xyarray__entry(mmap_array, cpu, thread_idx);
429 unsigned int head = mmap_read_head(md); 1130 unsigned int head = mmap_read_head(md);
430 unsigned int old = md->prev; 1131 unsigned int old = md->prev;
431 unsigned char *data = md->base + page_size; 1132 unsigned char *data = md->base + page_size;
1133 struct sample_data sample;
432 int diff; 1134 int diff;
433 1135
434 gettimeofday(&this_read, NULL);
435
436 /* 1136 /*
437 * If we're further behind than half the buffer, there's a chance 1137 * If we're further behind than half the buffer, there's a chance
438 * the writer will bite our tail and mess up the samples under us. 1138 * the writer will bite our tail and mess up the samples under us.
@@ -443,14 +1143,7 @@ static void mmap_read_counter(struct mmap_data *md)
443 */ 1143 */
444 diff = head - old; 1144 diff = head - old;
445 if (diff > md->mask / 2 || diff < 0) { 1145 if (diff > md->mask / 2 || diff < 0) {
446 struct timeval iv; 1146 fprintf(stderr, "WARNING: failed to keep up with mmap data.\n");
447 unsigned long msecs;
448
449 timersub(&this_read, &last_read, &iv);
450 msecs = iv.tv_sec*1000 + iv.tv_usec/1000;
451
452 fprintf(stderr, "WARNING: failed to keep up with mmap data."
453 " Last read %lu msecs ago.\n", msecs);
454 1147
455 /* 1148 /*
456 * head points to a known good entry, start there. 1149 * head points to a known good entry, start there.
@@ -458,29 +1151,7 @@ static void mmap_read_counter(struct mmap_data *md)
458 old = head; 1151 old = head;
459 } 1152 }
460 1153
461 last_read = this_read;
462
463 for (; old != head;) { 1154 for (; old != head;) {
464 struct ip_event {
465 struct perf_event_header header;
466 u64 ip;
467 u32 pid, target_pid;
468 };
469 struct mmap_event {
470 struct perf_event_header header;
471 u32 pid, target_pid;
472 u64 start;
473 u64 len;
474 u64 pgoff;
475 char filename[PATH_MAX];
476 };
477
478 typedef union event_union {
479 struct perf_event_header header;
480 struct ip_event ip;
481 struct mmap_event mmap;
482 } event_t;
483
484 event_t *event = (event_t *)&data[old & md->mask]; 1155 event_t *event = (event_t *)&data[old & md->mask];
485 1156
486 event_t event_copy; 1157 event_t event_copy;
@@ -507,116 +1178,151 @@ static void mmap_read_counter(struct mmap_data *md)
507 event = &event_copy; 1178 event = &event_copy;
508 } 1179 }
509 1180
1181 event__parse_sample(event, self, &sample);
1182 if (event->header.type == PERF_RECORD_SAMPLE)
1183 event__process_sample(event, &sample, self, evsel);
1184 else
1185 event__process(event, &sample, self);
510 old += size; 1186 old += size;
511
512 if (event->header.misc & PERF_EVENT_MISC_OVERFLOW) {
513 if (event->header.type & PERF_SAMPLE_IP)
514 process_event(event->ip.ip, md->counter);
515 }
516 } 1187 }
517 1188
518 md->prev = old; 1189 md->prev = old;
519} 1190}
520 1191
521static struct pollfd event_array[MAX_NR_CPUS * MAX_COUNTERS]; 1192static struct pollfd *event_array;
522static struct mmap_data mmap_array[MAX_NR_CPUS][MAX_COUNTERS];
523 1193
524static void mmap_read(void) 1194static void perf_session__mmap_read(struct perf_session *self)
525{ 1195{
526 int i, counter; 1196 struct perf_evsel *counter;
527 1197 int i, thread_index;
528 for (i = 0; i < nr_cpus; i++) { 1198
529 for (counter = 0; counter < nr_counters; counter++) 1199 for (i = 0; i < cpus->nr; i++) {
530 mmap_read_counter(&mmap_array[i][counter]); 1200 list_for_each_entry(counter, &evsel_list, node) {
1201 for (thread_index = 0;
1202 thread_index < threads->nr;
1203 thread_index++) {
1204 perf_session__mmap_read_counter(self,
1205 counter, i, thread_index);
1206 }
1207 }
531 } 1208 }
532} 1209}
533 1210
534int nr_poll; 1211int nr_poll;
535int group_fd; 1212int group_fd;
536 1213
537static void start_counter(int i, int counter) 1214static void start_counter(int i, struct perf_evsel *evsel)
538{ 1215{
539 struct perf_counter_attr *attr; 1216 struct xyarray *mmap_array = evsel->priv;
540 unsigned int cpu; 1217 struct mmap_data *mm;
1218 struct perf_event_attr *attr;
1219 int cpu = -1;
1220 int thread_index;
541 1221
542 cpu = profile_cpu; 1222 if (target_tid == -1)
543 if (target_pid == -1 && profile_cpu == -1) 1223 cpu = cpus->map[i];
544 cpu = i;
545 1224
546 attr = attrs + counter; 1225 attr = &evsel->attr;
547 1226
548 attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID; 1227 attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID;
549 attr->freq = freq;
550 1228
551try_again: 1229 if (freq) {
552 fd[i][counter] = sys_perf_counter_open(attr, target_pid, cpu, group_fd, 0); 1230 attr->sample_type |= PERF_SAMPLE_PERIOD;
1231 attr->freq = 1;
1232 attr->sample_freq = freq;
1233 }
1234
1235 attr->inherit = (cpu < 0) && inherit;
1236 attr->mmap = 1;
553 1237
554 if (fd[i][counter] < 0) { 1238 for (thread_index = 0; thread_index < threads->nr; thread_index++) {
555 int err = errno; 1239try_again:
1240 FD(evsel, i, thread_index) = sys_perf_event_open(attr,
1241 threads->map[thread_index], cpu, group_fd, 0);
1242
1243 if (FD(evsel, i, thread_index) < 0) {
1244 int err = errno;
1245
1246 if (err == EPERM || err == EACCES)
1247 die("Permission error - are you root?\n"
1248 "\t Consider tweaking"
1249 " /proc/sys/kernel/perf_event_paranoid.\n");
1250 if (err == ENOENT)
1251 die("%s event is not supported. ", event_name(evsel));
1252 /*
1253 * If it's cycles then fall back to hrtimer
1254 * based cpu-clock-tick sw counter, which
1255 * is always available even if no PMU support:
1256 */
1257 if (attr->type == PERF_TYPE_HARDWARE
1258 && attr->config == PERF_COUNT_HW_CPU_CYCLES) {
1259
1260 if (verbose)
1261 warning(" ... trying to fall back to cpu-clock-ticks\n");
1262
1263 attr->type = PERF_TYPE_SOFTWARE;
1264 attr->config = PERF_COUNT_SW_CPU_CLOCK;
1265 goto try_again;
1266 }
1267 printf("\n");
1268 error("sys_perf_event_open() syscall returned with %d (%s). /bin/dmesg may provide additional information.\n",
1269 FD(evsel, i, thread_index), strerror(err));
1270 die("No CONFIG_PERF_EVENTS=y kernel support configured?\n");
1271 exit(-1);
1272 }
1273 assert(FD(evsel, i, thread_index) >= 0);
1274 fcntl(FD(evsel, i, thread_index), F_SETFL, O_NONBLOCK);
556 1275
557 if (err == EPERM)
558 die("No permission - are you root?\n");
559 /* 1276 /*
560 * If it's cycles then fall back to hrtimer 1277 * First counter acts as the group leader:
561 * based cpu-clock-tick sw counter, which
562 * is always available even if no PMU support:
563 */ 1278 */
564 if (attr->type == PERF_TYPE_HARDWARE 1279 if (group && group_fd == -1)
565 && attr->config == PERF_COUNT_HW_CPU_CYCLES) { 1280 group_fd = FD(evsel, i, thread_index);
566 1281
567 if (verbose) 1282 event_array[nr_poll].fd = FD(evsel, i, thread_index);
568 warning(" ... trying to fall back to cpu-clock-ticks\n"); 1283 event_array[nr_poll].events = POLLIN;
569 1284 nr_poll++;
570 attr->type = PERF_TYPE_SOFTWARE; 1285
571 attr->config = PERF_COUNT_SW_CPU_CLOCK; 1286 mm = xyarray__entry(mmap_array, i, thread_index);
572 goto try_again; 1287 mm->prev = 0;
573 } 1288 mm->mask = mmap_pages*page_size - 1;
574 printf("\n"); 1289 mm->base = mmap(NULL, (mmap_pages+1)*page_size,
575 error("perfcounter syscall returned with %d (%s)\n", 1290 PROT_READ, MAP_SHARED, FD(evsel, i, thread_index), 0);
576 fd[i][counter], strerror(err)); 1291 if (mm->base == MAP_FAILED)
577 die("No CONFIG_PERF_COUNTERS=y kernel support configured?\n"); 1292 die("failed to mmap with %d (%s)\n", errno, strerror(errno));
578 exit(-1);
579 } 1293 }
580 assert(fd[i][counter] >= 0);
581 fcntl(fd[i][counter], F_SETFL, O_NONBLOCK);
582
583 /*
584 * First counter acts as the group leader:
585 */
586 if (group && group_fd == -1)
587 group_fd = fd[i][counter];
588
589 event_array[nr_poll].fd = fd[i][counter];
590 event_array[nr_poll].events = POLLIN;
591 nr_poll++;
592
593 mmap_array[i][counter].counter = counter;
594 mmap_array[i][counter].prev = 0;
595 mmap_array[i][counter].mask = mmap_pages*page_size - 1;
596 mmap_array[i][counter].base = mmap(NULL, (mmap_pages+1)*page_size,
597 PROT_READ, MAP_SHARED, fd[i][counter], 0);
598 if (mmap_array[i][counter].base == MAP_FAILED)
599 die("failed to mmap with %d (%s)\n", errno, strerror(errno));
600} 1294}
601 1295
602static int __cmd_top(void) 1296static int __cmd_top(void)
603{ 1297{
604 pthread_t thread; 1298 pthread_t thread;
605 int i, counter; 1299 struct perf_evsel *counter;
606 int ret; 1300 int i, ret;
1301 /*
1302 * FIXME: perf_session__new should allow passing a O_MMAP, so that all this
1303 * mmap reading, etc is encapsulated in it. Use O_WRONLY for now.
1304 */
1305 struct perf_session *session = perf_session__new(NULL, O_WRONLY, false, false, NULL);
1306 if (session == NULL)
1307 return -ENOMEM;
1308
1309 if (target_tid != -1)
1310 event__synthesize_thread(target_tid, event__process, session);
1311 else
1312 event__synthesize_threads(event__process, session);
607 1313
608 for (i = 0; i < nr_cpus; i++) { 1314 for (i = 0; i < cpus->nr; i++) {
609 group_fd = -1; 1315 group_fd = -1;
610 for (counter = 0; counter < nr_counters; counter++) 1316 list_for_each_entry(counter, &evsel_list, node)
611 start_counter(i, counter); 1317 start_counter(i, counter);
612 } 1318 }
613 1319
614 /* Wait for a minimal set of events before starting the snapshot */ 1320 /* Wait for a minimal set of events before starting the snapshot */
615 poll(event_array, nr_poll, 100); 1321 poll(&event_array[0], nr_poll, 100);
616 1322
617 mmap_read(); 1323 perf_session__mmap_read(session);
618 1324
619 if (pthread_create(&thread, NULL, display_thread, NULL)) { 1325 if (pthread_create(&thread, NULL, display_thread, session)) {
620 printf("Could not create display thread.\n"); 1326 printf("Could not create display thread.\n");
621 exit(-1); 1327 exit(-1);
622 } 1328 }
@@ -634,7 +1340,7 @@ static int __cmd_top(void)
634 while (1) { 1340 while (1) {
635 int hits = samples; 1341 int hits = samples;
636 1342
637 mmap_read(); 1343 perf_session__mmap_read(session);
638 1344
639 if (hits == samples) 1345 if (hits == samples)
640 ret = poll(event_array, nr_poll, 100); 1346 ret = poll(event_array, nr_poll, 100);
@@ -655,13 +1361,18 @@ static const struct option options[] = {
655 OPT_INTEGER('c', "count", &default_interval, 1361 OPT_INTEGER('c', "count", &default_interval,
656 "event period to sample"), 1362 "event period to sample"),
657 OPT_INTEGER('p', "pid", &target_pid, 1363 OPT_INTEGER('p', "pid", &target_pid,
658 "profile events on existing pid"), 1364 "profile events on existing process id"),
1365 OPT_INTEGER('t', "tid", &target_tid,
1366 "profile events on existing thread id"),
659 OPT_BOOLEAN('a', "all-cpus", &system_wide, 1367 OPT_BOOLEAN('a', "all-cpus", &system_wide,
660 "system-wide collection from all CPUs"), 1368 "system-wide collection from all CPUs"),
661 OPT_INTEGER('C', "CPU", &profile_cpu, 1369 OPT_STRING('C', "cpu", &cpu_list, "cpu",
662 "CPU to profile on"), 1370 "list of cpus to monitor"),
663 OPT_INTEGER('m', "mmap-pages", &mmap_pages, 1371 OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
664 "number of mmap data pages"), 1372 "file", "vmlinux pathname"),
1373 OPT_BOOLEAN('K', "hide_kernel_symbols", &hide_kernel_symbols,
1374 "hide kernel symbols"),
1375 OPT_UINTEGER('m', "mmap-pages", &mmap_pages, "number of mmap data pages"),
665 OPT_INTEGER('r', "realtime", &realtime_prio, 1376 OPT_INTEGER('r', "realtime", &realtime_prio,
666 "collect data with this RT SCHED_FIFO priority"), 1377 "collect data with this RT SCHED_FIFO priority"),
667 OPT_INTEGER('d', "delay", &delay_secs, 1378 OPT_INTEGER('d', "delay", &delay_secs,
@@ -672,22 +1383,27 @@ static const struct option options[] = {
672 "only display functions with more events than this"), 1383 "only display functions with more events than this"),
673 OPT_BOOLEAN('g', "group", &group, 1384 OPT_BOOLEAN('g', "group", &group,
674 "put the counters into a counter group"), 1385 "put the counters into a counter group"),
675 OPT_STRING('s', "sym-filter", &sym_filter, "pattern", 1386 OPT_BOOLEAN('i', "inherit", &inherit,
676 "only display symbols matchig this pattern"), 1387 "child tasks inherit counters"),
677 OPT_BOOLEAN('z', "zero", &group, 1388 OPT_STRING('s', "sym-annotate", &sym_filter, "symbol name",
1389 "symbol to annotate"),
1390 OPT_BOOLEAN('z', "zero", &zero,
678 "zero history across updates"), 1391 "zero history across updates"),
679 OPT_INTEGER('F', "freq", &freq, 1392 OPT_INTEGER('F', "freq", &freq,
680 "profile at this frequency"), 1393 "profile at this frequency"),
681 OPT_INTEGER('E', "entries", &print_entries, 1394 OPT_INTEGER('E', "entries", &print_entries,
682 "display this many functions"), 1395 "display this many functions"),
683 OPT_BOOLEAN('v', "verbose", &verbose, 1396 OPT_BOOLEAN('U', "hide_user_symbols", &hide_user_symbols,
1397 "hide user symbols"),
1398 OPT_INCR('v', "verbose", &verbose,
684 "be more verbose (show counter open errors, etc)"), 1399 "be more verbose (show counter open errors, etc)"),
685 OPT_END() 1400 OPT_END()
686}; 1401};
687 1402
688int cmd_top(int argc, const char **argv, const char *prefix) 1403int cmd_top(int argc, const char **argv, const char *prefix __used)
689{ 1404{
690 int counter; 1405 struct perf_evsel *pos;
1406 int status = -ENOMEM;
691 1407
692 page_size = sysconf(_SC_PAGE_SIZE); 1408 page_size = sysconf(_SC_PAGE_SIZE);
693 1409
@@ -695,42 +1411,85 @@ int cmd_top(int argc, const char **argv, const char *prefix)
695 if (argc) 1411 if (argc)
696 usage_with_options(top_usage, options); 1412 usage_with_options(top_usage, options);
697 1413
698 if (freq) { 1414 if (target_pid != -1)
699 default_interval = freq; 1415 target_tid = target_pid;
700 freq = 1; 1416
1417 threads = thread_map__new(target_pid, target_tid);
1418 if (threads == NULL) {
1419 pr_err("Problems finding threads of monitor\n");
1420 usage_with_options(top_usage, options);
701 } 1421 }
702 1422
1423 event_array = malloc((sizeof(struct pollfd) *
1424 MAX_NR_CPUS * MAX_COUNTERS * threads->nr));
1425 if (!event_array)
1426 return -ENOMEM;
1427
703 /* CPU and PID are mutually exclusive */ 1428 /* CPU and PID are mutually exclusive */
704 if (target_pid != -1 && profile_cpu != -1) { 1429 if (target_tid > 0 && cpu_list) {
705 printf("WARNING: PID switch overriding CPU\n"); 1430 printf("WARNING: PID switch overriding CPU\n");
706 sleep(1); 1431 sleep(1);
707 profile_cpu = -1; 1432 cpu_list = NULL;
708 } 1433 }
709 1434
710 if (!nr_counters) 1435 if (!nr_counters && perf_evsel_list__create_default() < 0) {
711 nr_counters = 1; 1436 pr_err("Not enough memory for event selector list\n");
1437 return -ENOMEM;
1438 }
712 1439
713 if (delay_secs < 1) 1440 if (delay_secs < 1)
714 delay_secs = 1; 1441 delay_secs = 1;
715 1442
716 parse_symbols();
717
718 /* 1443 /*
719 * Fill in the ones not specifically initialized via -c: 1444 * User specified count overrides default frequency.
720 */ 1445 */
721 for (counter = 0; counter < nr_counters; counter++) { 1446 if (default_interval)
722 if (attrs[counter].sample_period) 1447 freq = 0;
1448 else if (freq) {
1449 default_interval = freq;
1450 } else {
1451 fprintf(stderr, "frequency and count are zero, aborting\n");
1452 exit(EXIT_FAILURE);
1453 }
1454
1455 if (target_tid != -1)
1456 cpus = cpu_map__dummy_new();
1457 else
1458 cpus = cpu_map__new(cpu_list);
1459
1460 if (cpus == NULL)
1461 usage_with_options(top_usage, options);
1462
1463 list_for_each_entry(pos, &evsel_list, node) {
1464 if (perf_evsel__alloc_mmap_per_thread(pos, cpus->nr, threads->nr) < 0 ||
1465 perf_evsel__alloc_fd(pos, cpus->nr, threads->nr) < 0)
1466 goto out_free_fd;
1467 /*
1468 * Fill in the ones not specifically initialized via -c:
1469 */
1470 if (pos->attr.sample_period)
723 continue; 1471 continue;
724 1472
725 attrs[counter].sample_period = default_interval; 1473 pos->attr.sample_period = default_interval;
726 } 1474 }
727 1475
728 nr_cpus = sysconf(_SC_NPROCESSORS_ONLN); 1476 symbol_conf.priv_size = (sizeof(struct sym_entry) +
729 assert(nr_cpus <= MAX_NR_CPUS); 1477 (nr_counters + 1) * sizeof(unsigned long));
730 assert(nr_cpus >= 0); 1478
1479 symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL);
1480 if (symbol__init() < 0)
1481 return -1;
1482
1483 get_term_dimensions(&winsize);
1484 if (print_entries == 0) {
1485 update_print_entries(&winsize);
1486 signal(SIGWINCH, sig_winch_handler);
1487 }
731 1488
732 if (target_pid != -1 || profile_cpu != -1) 1489 status = __cmd_top();
733 nr_cpus = 1; 1490out_free_fd:
1491 list_for_each_entry(pos, &evsel_list, node)
1492 perf_evsel__free_mmap(pos);
734 1493
735 return __cmd_top(); 1494 return status;
736} 1495}
diff --git a/tools/perf/builtin.h b/tools/perf/builtin.h
index 51d168230ee..c7798c7f24e 100644
--- a/tools/perf/builtin.h
+++ b/tools/perf/builtin.h
@@ -15,12 +15,25 @@ extern int read_line_with_nul(char *buf, int size, FILE *file);
15extern int check_pager_config(const char *cmd); 15extern int check_pager_config(const char *cmd);
16 16
17extern int cmd_annotate(int argc, const char **argv, const char *prefix); 17extern int cmd_annotate(int argc, const char **argv, const char *prefix);
18extern int cmd_bench(int argc, const char **argv, const char *prefix);
19extern int cmd_buildid_cache(int argc, const char **argv, const char *prefix);
20extern int cmd_buildid_list(int argc, const char **argv, const char *prefix);
21extern int cmd_diff(int argc, const char **argv, const char *prefix);
18extern int cmd_help(int argc, const char **argv, const char *prefix); 22extern int cmd_help(int argc, const char **argv, const char *prefix);
23extern int cmd_sched(int argc, const char **argv, const char *prefix);
24extern int cmd_list(int argc, const char **argv, const char *prefix);
19extern int cmd_record(int argc, const char **argv, const char *prefix); 25extern int cmd_record(int argc, const char **argv, const char *prefix);
20extern int cmd_report(int argc, const char **argv, const char *prefix); 26extern int cmd_report(int argc, const char **argv, const char *prefix);
21extern int cmd_stat(int argc, const char **argv, const char *prefix); 27extern int cmd_stat(int argc, const char **argv, const char *prefix);
28extern int cmd_timechart(int argc, const char **argv, const char *prefix);
22extern int cmd_top(int argc, const char **argv, const char *prefix); 29extern int cmd_top(int argc, const char **argv, const char *prefix);
30extern int cmd_script(int argc, const char **argv, const char *prefix);
23extern int cmd_version(int argc, const char **argv, const char *prefix); 31extern int cmd_version(int argc, const char **argv, const char *prefix);
24extern int cmd_list(int argc, const char **argv, const char *prefix); 32extern int cmd_probe(int argc, const char **argv, const char *prefix);
33extern int cmd_kmem(int argc, const char **argv, const char *prefix);
34extern int cmd_lock(int argc, const char **argv, const char *prefix);
35extern int cmd_kvm(int argc, const char **argv, const char *prefix);
36extern int cmd_test(int argc, const char **argv, const char *prefix);
37extern int cmd_inject(int argc, const char **argv, const char *prefix);
25 38
26#endif 39#endif
diff --git a/tools/perf/command-list.txt b/tools/perf/command-list.txt
index eebce30afbc..16b5088cf8f 100644
--- a/tools/perf/command-list.txt
+++ b/tools/perf/command-list.txt
@@ -3,8 +3,22 @@
3# command name category [deprecated] [common] 3# command name category [deprecated] [common]
4# 4#
5perf-annotate mainporcelain common 5perf-annotate mainporcelain common
6perf-archive mainporcelain common
7perf-bench mainporcelain common
8perf-buildid-cache mainporcelain common
9perf-buildid-list mainporcelain common
10perf-diff mainporcelain common
11perf-inject mainporcelain common
6perf-list mainporcelain common 12perf-list mainporcelain common
13perf-sched mainporcelain common
7perf-record mainporcelain common 14perf-record mainporcelain common
8perf-report mainporcelain common 15perf-report mainporcelain common
9perf-stat mainporcelain common 16perf-stat mainporcelain common
17perf-timechart mainporcelain common
10perf-top mainporcelain common 18perf-top mainporcelain common
19perf-script mainporcelain common
20perf-probe mainporcelain common
21perf-kmem mainporcelain common
22perf-lock mainporcelain common
23perf-kvm mainporcelain common
24perf-test mainporcelain common
diff --git a/tools/perf/design.txt b/tools/perf/design.txt
index f71e0d245cb..bd0bb1b1279 100644
--- a/tools/perf/design.txt
+++ b/tools/perf/design.txt
@@ -18,10 +18,10 @@ underlying hardware counters.
18Performance counters are accessed via special file descriptors. 18Performance counters are accessed via special file descriptors.
19There's one file descriptor per virtual counter used. 19There's one file descriptor per virtual counter used.
20 20
21The special file descriptor is opened via the perf_counter_open() 21The special file descriptor is opened via the perf_event_open()
22system call: 22system call:
23 23
24 int sys_perf_counter_open(struct perf_counter_hw_event *hw_event_uptr, 24 int sys_perf_event_open(struct perf_event_attr *hw_event_uptr,
25 pid_t pid, int cpu, int group_fd, 25 pid_t pid, int cpu, int group_fd,
26 unsigned long flags); 26 unsigned long flags);
27 27
@@ -32,9 +32,9 @@ can be used to set the blocking mode, etc.
32Multiple counters can be kept open at a time, and the counters 32Multiple counters can be kept open at a time, and the counters
33can be poll()ed. 33can be poll()ed.
34 34
35When creating a new counter fd, 'perf_counter_hw_event' is: 35When creating a new counter fd, 'perf_event_attr' is:
36 36
37struct perf_counter_hw_event { 37struct perf_event_attr {
38 /* 38 /*
39 * The MSB of the config word signifies if the rest contains cpu 39 * The MSB of the config word signifies if the rest contains cpu
40 * specific (raw) counter configuration data, if unset, the next 40 * specific (raw) counter configuration data, if unset, the next
@@ -93,7 +93,7 @@ specified by 'event_id':
93 93
94/* 94/*
95 * Generalized performance counter event types, used by the hw_event.event_id 95 * Generalized performance counter event types, used by the hw_event.event_id
96 * parameter of the sys_perf_counter_open() syscall: 96 * parameter of the sys_perf_event_open() syscall:
97 */ 97 */
98enum hw_event_ids { 98enum hw_event_ids {
99 /* 99 /*
@@ -101,10 +101,10 @@ enum hw_event_ids {
101 */ 101 */
102 PERF_COUNT_HW_CPU_CYCLES = 0, 102 PERF_COUNT_HW_CPU_CYCLES = 0,
103 PERF_COUNT_HW_INSTRUCTIONS = 1, 103 PERF_COUNT_HW_INSTRUCTIONS = 1,
104 PERF_COUNT_HW_CACHE_REFERENCES = 2, 104 PERF_COUNT_HW_CACHE_REFERENCES = 2,
105 PERF_COUNT_HW_CACHE_MISSES = 3, 105 PERF_COUNT_HW_CACHE_MISSES = 3,
106 PERF_COUNT_HW_BRANCH_INSTRUCTIONS = 4, 106 PERF_COUNT_HW_BRANCH_INSTRUCTIONS = 4,
107 PERF_COUNT_HW_BRANCH_MISSES = 5, 107 PERF_COUNT_HW_BRANCH_MISSES = 5,
108 PERF_COUNT_HW_BUS_CYCLES = 6, 108 PERF_COUNT_HW_BUS_CYCLES = 6,
109}; 109};
110 110
@@ -131,12 +131,14 @@ software events, selected by 'event_id':
131 */ 131 */
132enum sw_event_ids { 132enum sw_event_ids {
133 PERF_COUNT_SW_CPU_CLOCK = 0, 133 PERF_COUNT_SW_CPU_CLOCK = 0,
134 PERF_COUNT_SW_TASK_CLOCK = 1, 134 PERF_COUNT_SW_TASK_CLOCK = 1,
135 PERF_COUNT_SW_PAGE_FAULTS = 2, 135 PERF_COUNT_SW_PAGE_FAULTS = 2,
136 PERF_COUNT_SW_CONTEXT_SWITCHES = 3, 136 PERF_COUNT_SW_CONTEXT_SWITCHES = 3,
137 PERF_COUNT_SW_CPU_MIGRATIONS = 4, 137 PERF_COUNT_SW_CPU_MIGRATIONS = 4,
138 PERF_COUNT_SW_PAGE_FAULTS_MIN = 5, 138 PERF_COUNT_SW_PAGE_FAULTS_MIN = 5,
139 PERF_COUNT_SW_PAGE_FAULTS_MAJ = 6, 139 PERF_COUNT_SW_PAGE_FAULTS_MAJ = 6,
140 PERF_COUNT_SW_ALIGNMENT_FAULTS = 7,
141 PERF_COUNT_SW_EMULATION_FAULTS = 8,
140}; 142};
141 143
142Counters of the type PERF_TYPE_TRACEPOINT are available when the ftrace event 144Counters of the type PERF_TYPE_TRACEPOINT are available when the ftrace event
@@ -159,7 +161,7 @@ in size.
159 * reads on the counter should return the indicated quantities, 161 * reads on the counter should return the indicated quantities,
160 * in increasing order of bit value, after the counter value. 162 * in increasing order of bit value, after the counter value.
161 */ 163 */
162enum perf_counter_read_format { 164enum perf_event_read_format {
163 PERF_FORMAT_TOTAL_TIME_ENABLED = 1, 165 PERF_FORMAT_TOTAL_TIME_ENABLED = 1,
164 PERF_FORMAT_TOTAL_TIME_RUNNING = 2, 166 PERF_FORMAT_TOTAL_TIME_RUNNING = 2,
165}; 167};
@@ -178,7 +180,7 @@ interrupt:
178 * Bits that can be set in hw_event.record_type to request information 180 * Bits that can be set in hw_event.record_type to request information
179 * in the overflow packets. 181 * in the overflow packets.
180 */ 182 */
181enum perf_counter_record_format { 183enum perf_event_record_format {
182 PERF_RECORD_IP = 1U << 0, 184 PERF_RECORD_IP = 1U << 0,
183 PERF_RECORD_TID = 1U << 1, 185 PERF_RECORD_TID = 1U << 1,
184 PERF_RECORD_TIME = 1U << 2, 186 PERF_RECORD_TIME = 1U << 2,
@@ -228,7 +230,7 @@ these events are recorded in the ring-buffer (see below).
228The 'comm' bit allows tracking of process comm data on process creation. 230The 'comm' bit allows tracking of process comm data on process creation.
229This too is recorded in the ring-buffer (see below). 231This too is recorded in the ring-buffer (see below).
230 232
231The 'pid' parameter to the perf_counter_open() system call allows the 233The 'pid' parameter to the perf_event_open() system call allows the
232counter to be specific to a task: 234counter to be specific to a task:
233 235
234 pid == 0: if the pid parameter is zero, the counter is attached to the 236 pid == 0: if the pid parameter is zero, the counter is attached to the
@@ -258,7 +260,7 @@ The 'flags' parameter is currently unused and must be zero.
258 260
259The 'group_fd' parameter allows counter "groups" to be set up. A 261The 'group_fd' parameter allows counter "groups" to be set up. A
260counter group has one counter which is the group "leader". The leader 262counter group has one counter which is the group "leader". The leader
261is created first, with group_fd = -1 in the perf_counter_open call 263is created first, with group_fd = -1 in the perf_event_open call
262that creates it. The rest of the group members are created 264that creates it. The rest of the group members are created
263subsequently, with group_fd giving the fd of the group leader. 265subsequently, with group_fd giving the fd of the group leader.
264(A single counter on its own is created with group_fd = -1 and is 266(A single counter on its own is created with group_fd = -1 and is
@@ -277,13 +279,13 @@ tracking are logged into a ring-buffer. This ring-buffer is created and
277accessed through mmap(). 279accessed through mmap().
278 280
279The mmap size should be 1+2^n pages, where the first page is a meta-data page 281The mmap size should be 1+2^n pages, where the first page is a meta-data page
280(struct perf_counter_mmap_page) that contains various bits of information such 282(struct perf_event_mmap_page) that contains various bits of information such
281as where the ring-buffer head is. 283as where the ring-buffer head is.
282 284
283/* 285/*
284 * Structure of the page that can be mapped via mmap 286 * Structure of the page that can be mapped via mmap
285 */ 287 */
286struct perf_counter_mmap_page { 288struct perf_event_mmap_page {
287 __u32 version; /* version number of this structure */ 289 __u32 version; /* version number of this structure */
288 __u32 compat_version; /* lowest version this is compat with */ 290 __u32 compat_version; /* lowest version this is compat with */
289 291
@@ -317,7 +319,7 @@ struct perf_counter_mmap_page {
317 * Control data for the mmap() data buffer. 319 * Control data for the mmap() data buffer.
318 * 320 *
319 * User-space reading this value should issue an rmb(), on SMP capable 321 * User-space reading this value should issue an rmb(), on SMP capable
320 * platforms, after reading this value -- see perf_counter_wakeup(). 322 * platforms, after reading this value -- see perf_event_wakeup().
321 */ 323 */
322 __u32 data_head; /* head in the data section */ 324 __u32 data_head; /* head in the data section */
323}; 325};
@@ -327,9 +329,9 @@ NOTE: the hw-counter userspace bits are arch specific and are currently only
327 329
328The following 2^n pages are the ring-buffer which contains events of the form: 330The following 2^n pages are the ring-buffer which contains events of the form:
329 331
330#define PERF_EVENT_MISC_KERNEL (1 << 0) 332#define PERF_RECORD_MISC_KERNEL (1 << 0)
331#define PERF_EVENT_MISC_USER (1 << 1) 333#define PERF_RECORD_MISC_USER (1 << 1)
332#define PERF_EVENT_MISC_OVERFLOW (1 << 2) 334#define PERF_RECORD_MISC_OVERFLOW (1 << 2)
333 335
334struct perf_event_header { 336struct perf_event_header {
335 __u32 type; 337 __u32 type;
@@ -353,8 +355,8 @@ enum perf_event_type {
353 * char filename[]; 355 * char filename[];
354 * }; 356 * };
355 */ 357 */
356 PERF_EVENT_MMAP = 1, 358 PERF_RECORD_MMAP = 1,
357 PERF_EVENT_MUNMAP = 2, 359 PERF_RECORD_MUNMAP = 2,
358 360
359 /* 361 /*
360 * struct { 362 * struct {
@@ -364,10 +366,10 @@ enum perf_event_type {
364 * char comm[]; 366 * char comm[];
365 * }; 367 * };
366 */ 368 */
367 PERF_EVENT_COMM = 3, 369 PERF_RECORD_COMM = 3,
368 370
369 /* 371 /*
370 * When header.misc & PERF_EVENT_MISC_OVERFLOW the event_type field 372 * When header.misc & PERF_RECORD_MISC_OVERFLOW the event_type field
371 * will be PERF_RECORD_* 373 * will be PERF_RECORD_*
372 * 374 *
373 * struct { 375 * struct {
@@ -397,7 +399,7 @@ Notification of new events is possible through poll()/select()/epoll() and
397fcntl() managing signals. 399fcntl() managing signals.
398 400
399Normally a notification is generated for every page filled, however one can 401Normally a notification is generated for every page filled, however one can
400additionally set perf_counter_hw_event.wakeup_events to generate one every 402additionally set perf_event_attr.wakeup_events to generate one every
401so many counter overflow events. 403so many counter overflow events.
402 404
403Future work will include a splice() interface to the ring-buffer. 405Future work will include a splice() interface to the ring-buffer.
@@ -409,11 +411,11 @@ events but does continue to exist and maintain its count value.
409 411
410An individual counter or counter group can be enabled with 412An individual counter or counter group can be enabled with
411 413
412 ioctl(fd, PERF_COUNTER_IOC_ENABLE); 414 ioctl(fd, PERF_EVENT_IOC_ENABLE);
413 415
414or disabled with 416or disabled with
415 417
416 ioctl(fd, PERF_COUNTER_IOC_DISABLE); 418 ioctl(fd, PERF_EVENT_IOC_DISABLE);
417 419
418Enabling or disabling the leader of a group enables or disables the 420Enabling or disabling the leader of a group enables or disables the
419whole group; that is, while the group leader is disabled, none of the 421whole group; that is, while the group leader is disabled, none of the
@@ -424,16 +426,16 @@ other counter.
424 426
425Additionally, non-inherited overflow counters can use 427Additionally, non-inherited overflow counters can use
426 428
427 ioctl(fd, PERF_COUNTER_IOC_REFRESH, nr); 429 ioctl(fd, PERF_EVENT_IOC_REFRESH, nr);
428 430
429to enable a counter for 'nr' events, after which it gets disabled again. 431to enable a counter for 'nr' events, after which it gets disabled again.
430 432
431A process can enable or disable all the counter groups that are 433A process can enable or disable all the counter groups that are
432attached to it, using prctl: 434attached to it, using prctl:
433 435
434 prctl(PR_TASK_PERF_COUNTERS_ENABLE); 436 prctl(PR_TASK_PERF_EVENTS_ENABLE);
435 437
436 prctl(PR_TASK_PERF_COUNTERS_DISABLE); 438 prctl(PR_TASK_PERF_EVENTS_DISABLE);
437 439
438This applies to all counters on the current process, whether created 440This applies to all counters on the current process, whether created
439by this process or by another, and doesn't affect any counters that 441by this process or by another, and doesn't affect any counters that
@@ -447,11 +449,14 @@ Arch requirements
447If your architecture does not have hardware performance metrics, you can 449If your architecture does not have hardware performance metrics, you can
448still use the generic software counters based on hrtimers for sampling. 450still use the generic software counters based on hrtimers for sampling.
449 451
450So to start with, in order to add HAVE_PERF_COUNTERS to your Kconfig, you 452So to start with, in order to add HAVE_PERF_EVENTS to your Kconfig, you
451will need at least this: 453will need at least this:
452 - asm/perf_counter.h - a basic stub will suffice at first 454 - asm/perf_event.h - a basic stub will suffice at first
453 - support for atomic64 types (and associated helper functions) 455 - support for atomic64 types (and associated helper functions)
454 - set_perf_counter_pending() implemented 456 - set_perf_event_pending() implemented
455 457
456If your architecture does have hardware capabilities, you can override the 458If your architecture does have hardware capabilities, you can override the
457weak stub hw_perf_counter_init() to register hardware counters. 459weak stub hw_perf_event_init() to register hardware counters.
460
461Architectures that have d-cache aliassing issues, such as Sparc and ARM,
462should select PERF_USE_VMALLOC in order to avoid these for perf mmap().
diff --git a/tools/perf/feature-tests.mak b/tools/perf/feature-tests.mak
new file mode 100644
index 00000000000..b041ca67a2c
--- /dev/null
+++ b/tools/perf/feature-tests.mak
@@ -0,0 +1,130 @@
1define SOURCE_HELLO
2#include <stdio.h>
3int main(void)
4{
5 return puts(\"hi\");
6}
7endef
8
9ifndef NO_DWARF
10define SOURCE_DWARF
11#include <dwarf.h>
12#include <elfutils/libdw.h>
13#include <elfutils/version.h>
14#ifndef _ELFUTILS_PREREQ
15#error
16#endif
17
18int main(void)
19{
20 Dwarf *dbg = dwarf_begin(0, DWARF_C_READ);
21 return (long)dbg;
22}
23endef
24endif
25
26define SOURCE_LIBELF
27#include <libelf.h>
28
29int main(void)
30{
31 Elf *elf = elf_begin(0, ELF_C_READ, 0);
32 return (long)elf;
33}
34endef
35
36define SOURCE_GLIBC
37#include <gnu/libc-version.h>
38
39int main(void)
40{
41 const char *version = gnu_get_libc_version();
42 return (long)version;
43}
44endef
45
46define SOURCE_ELF_MMAP
47#include <libelf.h>
48int main(void)
49{
50 Elf *elf = elf_begin(0, ELF_C_READ_MMAP, 0);
51 return (long)elf;
52}
53endef
54
55ifndef NO_NEWT
56define SOURCE_NEWT
57#include <newt.h>
58
59int main(void)
60{
61 newtInit();
62 newtCls();
63 return newtFinished();
64}
65endef
66endif
67
68ifndef NO_LIBPERL
69define SOURCE_PERL_EMBED
70#include <EXTERN.h>
71#include <perl.h>
72
73int main(void)
74{
75perl_alloc();
76return 0;
77}
78endef
79endif
80
81ifndef NO_LIBPYTHON
82define SOURCE_PYTHON_EMBED
83#include <Python.h>
84
85int main(void)
86{
87 Py_Initialize();
88 return 0;
89}
90endef
91endif
92
93define SOURCE_BFD
94#include <bfd.h>
95
96int main(void)
97{
98 bfd_demangle(0, 0, 0);
99 return 0;
100}
101endef
102
103define SOURCE_CPLUS_DEMANGLE
104extern char *cplus_demangle(const char *, int);
105
106int main(void)
107{
108 cplus_demangle(0, 0);
109 return 0;
110}
111endef
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
124# try-cc
125# Usage: option = $(call try-cc, source-to-build, cc-options)
126try-cc = $(shell sh -c \
127 'TMP="$(OUTPUT)$(TMPOUT).$$$$"; \
128 echo "$(1)" | \
129 $(CC) -x c - $(2) -o "$$TMP" > /dev/null 2>&1 && echo y; \
130 rm -f "$$TMP"')
diff --git a/tools/perf/perf-archive.sh b/tools/perf/perf-archive.sh
new file mode 100644
index 00000000000..677e59d62a8
--- /dev/null
+++ b/tools/perf/perf-archive.sh
@@ -0,0 +1,46 @@
1#!/bin/bash
2# perf archive
3# Arnaldo Carvalho de Melo <acme@redhat.com>
4
5PERF_DATA=perf.data
6if [ $# -ne 0 ] ; then
7 PERF_DATA=$1
8fi
9
10#
11# PERF_BUILDID_DIR environment variable set by perf
12# path to buildid directory, default to $HOME/.debug
13#
14if [ -z $PERF_BUILDID_DIR ]; then
15 PERF_BUILDID_DIR=~/.debug/
16else
17 # append / to make substitutions work
18 PERF_BUILDID_DIR=$PERF_BUILDID_DIR/
19fi
20
21BUILDIDS=$(mktemp /tmp/perf-archive-buildids.XXXXXX)
22NOBUILDID=0000000000000000000000000000000000000000
23
24perf buildid-list -i $PERF_DATA --with-hits | grep -v "^$NOBUILDID " > $BUILDIDS
25if [ ! -s $BUILDIDS ] ; then
26 echo "perf archive: no build-ids found"
27 rm -f $BUILDIDS
28 exit 1
29fi
30
31MANIFEST=$(mktemp /tmp/perf-archive-manifest.XXXXXX)
32
33cut -d ' ' -f 1 $BUILDIDS | \
34while read build_id ; do
35 linkname=$PERF_BUILDID_DIR.build-id/${build_id:0:2}/${build_id:2}
36 filename=$(readlink -f $linkname)
37 echo ${linkname#$PERF_BUILDID_DIR} >> $MANIFEST
38 echo ${filename#$PERF_BUILDID_DIR} >> $MANIFEST
39done
40
41tar cfj $PERF_DATA.tar.bz2 -C $PERF_BUILDID_DIR -T $MANIFEST
42rm -f $MANIFEST $BUILDIDS
43echo -e "Now please run:\n"
44echo -e "$ tar xvf $PERF_DATA.tar.bz2 -C ~/.debug\n"
45echo "wherever you need to run 'perf report' on."
46exit 0
diff --git a/tools/perf/perf.c b/tools/perf/perf.c
index 4eb72593370..5b1ecd66bb3 100644
--- a/tools/perf/perf.c
+++ b/tools/perf/perf.c
@@ -12,6 +12,8 @@
12#include "util/cache.h" 12#include "util/cache.h"
13#include "util/quote.h" 13#include "util/quote.h"
14#include "util/run-command.h" 14#include "util/run-command.h"
15#include "util/parse-events.h"
16#include "util/debugfs.h"
15 17
16const char perf_usage_string[] = 18const char perf_usage_string[] =
17 "perf [--version] [--help] COMMAND [ARGS]"; 19 "perf [--version] [--help] COMMAND [ARGS]";
@@ -19,12 +21,16 @@ const char perf_usage_string[] =
19const char perf_more_info_string[] = 21const char perf_more_info_string[] =
20 "See 'perf help COMMAND' for more information on a specific command."; 22 "See 'perf help COMMAND' for more information on a specific command.";
21 23
24int use_browser = -1;
22static int use_pager = -1; 25static int use_pager = -1;
26
23struct pager_config { 27struct pager_config {
24 const char *cmd; 28 const char *cmd;
25 int val; 29 int val;
26}; 30};
27 31
32static char debugfs_mntpt[MAXPATHLEN];
33
28static int pager_command_config(const char *var, const char *value, void *data) 34static int pager_command_config(const char *var, const char *value, void *data)
29{ 35{
30 struct pager_config *c = data; 36 struct pager_config *c = data;
@@ -43,7 +49,26 @@ int check_pager_config(const char *cmd)
43 return c.val; 49 return c.val;
44} 50}
45 51
46static void commit_pager_choice(void) { 52static int tui_command_config(const char *var, const char *value, void *data)
53{
54 struct pager_config *c = data;
55 if (!prefixcmp(var, "tui.") && !strcmp(var + 4, c->cmd))
56 c->val = perf_config_bool(var, value);
57 return 0;
58}
59
60/* returns 0 for "no tui", 1 for "use tui", and -1 for "not specified" */
61static int check_tui_config(const char *cmd)
62{
63 struct pager_config c;
64 c.cmd = cmd;
65 c.val = -1;
66 perf_config(tui_command_config, &c);
67 return c.val;
68}
69
70static void commit_pager_choice(void)
71{
47 switch (use_pager) { 72 switch (use_pager) {
48 case 0: 73 case 0:
49 setenv("PERF_PAGER", "cat", 1); 74 setenv("PERF_PAGER", "cat", 1);
@@ -56,7 +81,16 @@ static void commit_pager_choice(void) {
56 } 81 }
57} 82}
58 83
59static int handle_options(const char*** argv, int* argc, int* envchanged) 84static void set_debugfs_path(void)
85{
86 char *path;
87
88 path = getenv(PERF_DEBUGFS_ENVIRONMENT);
89 snprintf(debugfs_path, MAXPATHLEN, "%s/%s", path ?: debugfs_mntpt,
90 "tracing/events");
91}
92
93static int handle_options(const char ***argv, int *argc, int *envchanged)
60{ 94{
61 int handled = 0; 95 int handled = 0;
62 96
@@ -76,8 +110,8 @@ static int handle_options(const char*** argv, int* argc, int* envchanged)
76 /* 110 /*
77 * Check remaining flags. 111 * Check remaining flags.
78 */ 112 */
79 if (!prefixcmp(cmd, "--exec-path")) { 113 if (!prefixcmp(cmd, CMD_EXEC_PATH)) {
80 cmd += 11; 114 cmd += strlen(CMD_EXEC_PATH);
81 if (*cmd == '=') 115 if (*cmd == '=')
82 perf_set_argv_exec_path(cmd + 1); 116 perf_set_argv_exec_path(cmd + 1);
83 else { 117 else {
@@ -95,7 +129,7 @@ static int handle_options(const char*** argv, int* argc, int* envchanged)
95 *envchanged = 1; 129 *envchanged = 1;
96 } else if (!strcmp(cmd, "--perf-dir")) { 130 } else if (!strcmp(cmd, "--perf-dir")) {
97 if (*argc < 2) { 131 if (*argc < 2) {
98 fprintf(stderr, "No directory given for --perf-dir.\n" ); 132 fprintf(stderr, "No directory given for --perf-dir.\n");
99 usage(perf_usage_string); 133 usage(perf_usage_string);
100 } 134 }
101 setenv(PERF_DIR_ENVIRONMENT, (*argv)[1], 1); 135 setenv(PERF_DIR_ENVIRONMENT, (*argv)[1], 1);
@@ -104,13 +138,13 @@ static int handle_options(const char*** argv, int* argc, int* envchanged)
104 (*argv)++; 138 (*argv)++;
105 (*argc)--; 139 (*argc)--;
106 handled++; 140 handled++;
107 } else if (!prefixcmp(cmd, "--perf-dir=")) { 141 } else if (!prefixcmp(cmd, CMD_PERF_DIR)) {
108 setenv(PERF_DIR_ENVIRONMENT, cmd + 10, 1); 142 setenv(PERF_DIR_ENVIRONMENT, cmd + strlen(CMD_PERF_DIR), 1);
109 if (envchanged) 143 if (envchanged)
110 *envchanged = 1; 144 *envchanged = 1;
111 } else if (!strcmp(cmd, "--work-tree")) { 145 } else if (!strcmp(cmd, "--work-tree")) {
112 if (*argc < 2) { 146 if (*argc < 2) {
113 fprintf(stderr, "No directory given for --work-tree.\n" ); 147 fprintf(stderr, "No directory given for --work-tree.\n");
114 usage(perf_usage_string); 148 usage(perf_usage_string);
115 } 149 }
116 setenv(PERF_WORK_TREE_ENVIRONMENT, (*argv)[1], 1); 150 setenv(PERF_WORK_TREE_ENVIRONMENT, (*argv)[1], 1);
@@ -118,8 +152,24 @@ static int handle_options(const char*** argv, int* argc, int* envchanged)
118 *envchanged = 1; 152 *envchanged = 1;
119 (*argv)++; 153 (*argv)++;
120 (*argc)--; 154 (*argc)--;
121 } else if (!prefixcmp(cmd, "--work-tree=")) { 155 } else if (!prefixcmp(cmd, CMD_WORK_TREE)) {
122 setenv(PERF_WORK_TREE_ENVIRONMENT, cmd + 12, 1); 156 setenv(PERF_WORK_TREE_ENVIRONMENT, cmd + strlen(CMD_WORK_TREE), 1);
157 if (envchanged)
158 *envchanged = 1;
159 } else if (!strcmp(cmd, "--debugfs-dir")) {
160 if (*argc < 2) {
161 fprintf(stderr, "No directory given for --debugfs-dir.\n");
162 usage(perf_usage_string);
163 }
164 strncpy(debugfs_mntpt, (*argv)[1], MAXPATHLEN);
165 debugfs_mntpt[MAXPATHLEN - 1] = '\0';
166 if (envchanged)
167 *envchanged = 1;
168 (*argv)++;
169 (*argc)--;
170 } else if (!prefixcmp(cmd, CMD_DEBUGFS_DIR)) {
171 strncpy(debugfs_mntpt, cmd + strlen(CMD_DEBUGFS_DIR), MAXPATHLEN);
172 debugfs_mntpt[MAXPATHLEN - 1] = '\0';
123 if (envchanged) 173 if (envchanged)
124 *envchanged = 1; 174 *envchanged = 1;
125 } else { 175 } else {
@@ -138,7 +188,7 @@ static int handle_alias(int *argcp, const char ***argv)
138{ 188{
139 int envchanged = 0, ret = 0, saved_errno = errno; 189 int envchanged = 0, ret = 0, saved_errno = errno;
140 int count, option_count; 190 int count, option_count;
141 const char** new_argv; 191 const char **new_argv;
142 const char *alias_command; 192 const char *alias_command;
143 char *alias_string; 193 char *alias_string;
144 194
@@ -180,11 +230,11 @@ static int handle_alias(int *argcp, const char ***argv)
180 if (!strcmp(alias_command, new_argv[0])) 230 if (!strcmp(alias_command, new_argv[0]))
181 die("recursive alias: %s", alias_command); 231 die("recursive alias: %s", alias_command);
182 232
183 new_argv = realloc(new_argv, sizeof(char*) * 233 new_argv = realloc(new_argv, sizeof(char *) *
184 (count + *argcp + 1)); 234 (count + *argcp + 1));
185 /* insert after command name */ 235 /* insert after command name */
186 memcpy(new_argv + count, *argv + 1, sizeof(char*) * *argcp); 236 memcpy(new_argv + count, *argv + 1, sizeof(char *) * *argcp);
187 new_argv[count+*argcp] = NULL; 237 new_argv[count + *argcp] = NULL;
188 238
189 *argv = new_argv; 239 *argv = new_argv;
190 *argcp += count - 1; 240 *argcp += count - 1;
@@ -223,16 +273,21 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
223 if (p->option & RUN_SETUP) 273 if (p->option & RUN_SETUP)
224 prefix = NULL; /* setup_perf_directory(); */ 274 prefix = NULL; /* setup_perf_directory(); */
225 275
276 if (use_browser == -1)
277 use_browser = check_tui_config(p->cmd);
278
226 if (use_pager == -1 && p->option & RUN_SETUP) 279 if (use_pager == -1 && p->option & RUN_SETUP)
227 use_pager = check_pager_config(p->cmd); 280 use_pager = check_pager_config(p->cmd);
228 if (use_pager == -1 && p->option & USE_PAGER) 281 if (use_pager == -1 && p->option & USE_PAGER)
229 use_pager = 1; 282 use_pager = 1;
230 commit_pager_choice(); 283 commit_pager_choice();
231 284 set_debugfs_path();
232 if (p->option & NEED_WORK_TREE)
233 /* setup_work_tree() */;
234 285
235 status = p->fn(argc, argv, prefix); 286 status = p->fn(argc, argv, prefix);
287 exit_browser(status);
288
289 perf_evsel_list__delete();
290
236 if (status) 291 if (status)
237 return status & 0xff; 292 return status & 0xff;
238 293
@@ -257,16 +312,29 @@ static void handle_internal_command(int argc, const char **argv)
257{ 312{
258 const char *cmd = argv[0]; 313 const char *cmd = argv[0];
259 static struct cmd_struct commands[] = { 314 static struct cmd_struct commands[] = {
260 { "help", cmd_help, 0 }, 315 { "buildid-cache", cmd_buildid_cache, 0 },
261 { "list", cmd_list, 0 }, 316 { "buildid-list", cmd_buildid_list, 0 },
262 { "record", cmd_record, 0 }, 317 { "diff", cmd_diff, 0 },
263 { "report", cmd_report, 0 }, 318 { "help", cmd_help, 0 },
264 { "stat", cmd_stat, 0 }, 319 { "list", cmd_list, 0 },
265 { "top", cmd_top, 0 }, 320 { "record", cmd_record, 0 },
266 { "annotate", cmd_annotate, 0 }, 321 { "report", cmd_report, 0 },
267 { "version", cmd_version, 0 }, 322 { "bench", cmd_bench, 0 },
323 { "stat", cmd_stat, 0 },
324 { "timechart", cmd_timechart, 0 },
325 { "top", cmd_top, 0 },
326 { "annotate", cmd_annotate, 0 },
327 { "version", cmd_version, 0 },
328 { "script", cmd_script, 0 },
329 { "sched", cmd_sched, 0 },
330 { "probe", cmd_probe, 0 },
331 { "kmem", cmd_kmem, 0 },
332 { "lock", cmd_lock, 0 },
333 { "kvm", cmd_kvm, 0 },
334 { "test", cmd_test, 0 },
335 { "inject", cmd_inject, 0 },
268 }; 336 };
269 int i; 337 unsigned int i;
270 static const char ext[] = STRIP_EXTENSION; 338 static const char ext[] = STRIP_EXTENSION;
271 339
272 if (sizeof(ext) > 1) { 340 if (sizeof(ext) > 1) {
@@ -349,6 +417,16 @@ static int run_argv(int *argcp, const char ***argv)
349 return done_alias; 417 return done_alias;
350} 418}
351 419
420/* mini /proc/mounts parser: searching for "^blah /mount/point debugfs" */
421static void get_debugfs_mntpt(void)
422{
423 const char *path = debugfs_mount(NULL);
424
425 if (path)
426 strncpy(debugfs_mntpt, path, sizeof(debugfs_mntpt));
427 else
428 debugfs_mntpt[0] = '\0';
429}
352 430
353int main(int argc, const char **argv) 431int main(int argc, const char **argv)
354{ 432{
@@ -357,7 +435,8 @@ int main(int argc, const char **argv)
357 cmd = perf_extract_argv0_path(argv[0]); 435 cmd = perf_extract_argv0_path(argv[0]);
358 if (!cmd) 436 if (!cmd)
359 cmd = "perf-help"; 437 cmd = "perf-help";
360 438 /* get debugfs mount point from /proc/mounts */
439 get_debugfs_mntpt();
361 /* 440 /*
362 * "perf-xxxx" is the same as "perf xxxx", but we obviously: 441 * "perf-xxxx" is the same as "perf xxxx", but we obviously:
363 * 442 *
@@ -380,6 +459,9 @@ int main(int argc, const char **argv)
380 argc--; 459 argc--;
381 handle_options(&argv, &argc, NULL); 460 handle_options(&argv, &argc, NULL);
382 commit_pager_choice(); 461 commit_pager_choice();
462 set_debugfs_path();
463 set_buildid_dir();
464
383 if (argc > 0) { 465 if (argc > 0) {
384 if (!prefixcmp(argv[0], "--")) 466 if (!prefixcmp(argv[0], "--"))
385 argv[0] += 2; 467 argv[0] += 2;
@@ -394,15 +476,15 @@ int main(int argc, const char **argv)
394 476
395 /* 477 /*
396 * We use PATH to find perf commands, but we prepend some higher 478 * We use PATH to find perf commands, but we prepend some higher
397 * precidence paths: the "--exec-path" option, the PERF_EXEC_PATH 479 * precedence paths: the "--exec-path" option, the PERF_EXEC_PATH
398 * environment, and the $(perfexecdir) from the Makefile at build 480 * environment, and the $(perfexecdir) from the Makefile at build
399 * time. 481 * time.
400 */ 482 */
401 setup_path(); 483 setup_path();
402 484
403 while (1) { 485 while (1) {
404 static int done_help = 0; 486 static int done_help;
405 static int was_alias = 0; 487 static int was_alias;
406 488
407 was_alias = run_argv(&argc, &argv); 489 was_alias = run_argv(&argc, &argv);
408 if (errno != ENOENT) 490 if (errno != ENOENT)
diff --git a/tools/perf/perf.h b/tools/perf/perf.h
index ceb68aa51f7..95aaf565c70 100644
--- a/tools/perf/perf.h
+++ b/tools/perf/perf.h
@@ -1,7 +1,17 @@
1#ifndef _PERF_PERF_H 1#ifndef _PERF_PERF_H
2#define _PERF_PERF_H 2#define _PERF_PERF_H
3 3
4#if defined(__x86_64__) || defined(__i386__) 4struct winsize;
5
6void get_term_dimensions(struct winsize *ws);
7
8#if defined(__i386__)
9#include "../../arch/x86/include/asm/unistd.h"
10#define rmb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory")
11#define cpu_relax() asm volatile("rep; nop" ::: "memory");
12#endif
13
14#if defined(__x86_64__)
5#include "../../arch/x86/include/asm/unistd.h" 15#include "../../arch/x86/include/asm/unistd.h"
6#define rmb() asm volatile("lfence" ::: "memory") 16#define rmb() asm volatile("lfence" ::: "memory")
7#define cpu_relax() asm volatile("rep; nop" ::: "memory"); 17#define cpu_relax() asm volatile("rep; nop" ::: "memory");
@@ -19,20 +29,77 @@
19#define cpu_relax() asm volatile("" ::: "memory"); 29#define cpu_relax() asm volatile("" ::: "memory");
20#endif 30#endif
21 31
32#ifdef __sh__
33#include "../../arch/sh/include/asm/unistd.h"
34#if defined(__SH4A__) || defined(__SH5__)
35# define rmb() asm volatile("synco" ::: "memory")
36#else
37# define rmb() asm volatile("" ::: "memory")
38#endif
39#define cpu_relax() asm volatile("" ::: "memory")
40#endif
41
42#ifdef __hppa__
43#include "../../arch/parisc/include/asm/unistd.h"
44#define rmb() asm volatile("" ::: "memory")
45#define cpu_relax() asm volatile("" ::: "memory");
46#endif
47
48#ifdef __sparc__
49#include "../../arch/sparc/include/asm/unistd.h"
50#define rmb() asm volatile("":::"memory")
51#define cpu_relax() asm volatile("":::"memory")
52#endif
53
54#ifdef __alpha__
55#include "../../arch/alpha/include/asm/unistd.h"
56#define rmb() asm volatile("mb" ::: "memory")
57#define cpu_relax() asm volatile("" ::: "memory")
58#endif
59
60#ifdef __ia64__
61#include "../../arch/ia64/include/asm/unistd.h"
62#define rmb() asm volatile ("mf" ::: "memory")
63#define cpu_relax() asm volatile ("hint @pause" ::: "memory")
64#endif
65
66#ifdef __arm__
67#include "../../arch/arm/include/asm/unistd.h"
68/*
69 * Use the __kuser_memory_barrier helper in the CPU helper page. See
70 * arch/arm/kernel/entry-armv.S in the kernel source for details.
71 */
72#define rmb() ((void(*)(void))0xffff0fa0)()
73#define cpu_relax() asm volatile("":::"memory")
74#endif
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
22#include <time.h> 88#include <time.h>
23#include <unistd.h> 89#include <unistd.h>
24#include <sys/types.h> 90#include <sys/types.h>
25#include <sys/syscall.h> 91#include <sys/syscall.h>
26 92
27#include "../../include/linux/perf_counter.h" 93#include "../../include/linux/perf_event.h"
28#include "types.h" 94#include "util/types.h"
95#include <stdbool.h>
29 96
30/* 97/*
31 * prctl(PR_TASK_PERF_COUNTERS_DISABLE) will (cheaply) disable all 98 * prctl(PR_TASK_PERF_EVENTS_DISABLE) will (cheaply) disable all
32 * counters in the current task. 99 * counters in the current task.
33 */ 100 */
34#define PR_TASK_PERF_COUNTERS_DISABLE 31 101#define PR_TASK_PERF_EVENTS_DISABLE 31
35#define PR_TASK_PERF_COUNTERS_ENABLE 32 102#define PR_TASK_PERF_EVENTS_ENABLE 32
36 103
37#ifndef NSEC_PER_SEC 104#ifndef NSEC_PER_SEC
38# define NSEC_PER_SEC 1000000000ULL 105# define NSEC_PER_SEC 1000000000ULL
@@ -60,22 +127,23 @@ static inline unsigned long long rdclock(void)
60 _min1 < _min2 ? _min1 : _min2; }) 127 _min1 < _min2 ? _min1 : _min2; })
61 128
62static inline int 129static inline int
63sys_perf_counter_open(struct perf_counter_attr *attr, 130sys_perf_event_open(struct perf_event_attr *attr,
64 pid_t pid, int cpu, int group_fd, 131 pid_t pid, int cpu, int group_fd,
65 unsigned long flags) 132 unsigned long flags)
66{ 133{
67 attr->size = sizeof(*attr); 134 attr->size = sizeof(*attr);
68 return syscall(__NR_perf_counter_open, attr, pid, cpu, 135 return syscall(__NR_perf_event_open, attr, pid, cpu,
69 group_fd, flags); 136 group_fd, flags);
70} 137}
71 138
72#define MAX_COUNTERS 256 139#define MAX_COUNTERS 256
73#define MAX_NR_CPUS 256 140#define MAX_NR_CPUS 256
74 141
75struct perf_file_header { 142struct ip_callchain {
76 u64 version; 143 u64 nr;
77 u64 sample_type; 144 u64 ips[0];
78 u64 data_size;
79}; 145};
80 146
147extern bool perf_host, perf_guest;
148
81#endif 149#endif
diff --git a/tools/perf/scripts/perl/Perf-Trace-Util/Context.c b/tools/perf/scripts/perl/Perf-Trace-Util/Context.c
new file mode 100644
index 00000000000..790ceba6ad3
--- /dev/null
+++ b/tools/perf/scripts/perl/Perf-Trace-Util/Context.c
@@ -0,0 +1,135 @@
1/*
2 * This file was generated automatically by ExtUtils::ParseXS version 2.18_02 from the
3 * contents of Context.xs. Do not edit this file, edit Context.xs instead.
4 *
5 * ANY CHANGES MADE HERE WILL BE LOST!
6 *
7 */
8
9#line 1 "Context.xs"
10/*
11 * Context.xs. XS interfaces for perf script.
12 *
13 * Copyright (C) 2009 Tom Zanussi <tzanussi@gmail.com>
14 *
15 * This program is free software; you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation; either version 2 of the License, or
18 * (at your option) any later version.
19 *
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, write to the Free Software
27 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 *
29 */
30
31#include "EXTERN.h"
32#include "perl.h"
33#include "XSUB.h"
34#include "../../../perf.h"
35#include "../../../util/trace-event.h"
36
37#ifndef PERL_UNUSED_VAR
38# define PERL_UNUSED_VAR(var) if (0) var = var
39#endif
40
41#line 42 "Context.c"
42
43XS(XS_Perf__Trace__Context_common_pc); /* prototype to pass -Wmissing-prototypes */
44XS(XS_Perf__Trace__Context_common_pc)
45{
46#ifdef dVAR
47 dVAR; dXSARGS;
48#else
49 dXSARGS;
50#endif
51 if (items != 1)
52 Perl_croak(aTHX_ "Usage: %s(%s)", "Perf::Trace::Context::common_pc", "context");
53 PERL_UNUSED_VAR(cv); /* -W */
54 {
55 struct scripting_context * context = INT2PTR(struct scripting_context *,SvIV(ST(0)));
56 int RETVAL;
57 dXSTARG;
58
59 RETVAL = common_pc(context);
60 XSprePUSH; PUSHi((IV)RETVAL);
61 }
62 XSRETURN(1);
63}
64
65
66XS(XS_Perf__Trace__Context_common_flags); /* prototype to pass -Wmissing-prototypes */
67XS(XS_Perf__Trace__Context_common_flags)
68{
69#ifdef dVAR
70 dVAR; dXSARGS;
71#else
72 dXSARGS;
73#endif
74 if (items != 1)
75 Perl_croak(aTHX_ "Usage: %s(%s)", "Perf::Trace::Context::common_flags", "context");
76 PERL_UNUSED_VAR(cv); /* -W */
77 {
78 struct scripting_context * context = INT2PTR(struct scripting_context *,SvIV(ST(0)));
79 int RETVAL;
80 dXSTARG;
81
82 RETVAL = common_flags(context);
83 XSprePUSH; PUSHi((IV)RETVAL);
84 }
85 XSRETURN(1);
86}
87
88
89XS(XS_Perf__Trace__Context_common_lock_depth); /* prototype to pass -Wmissing-prototypes */
90XS(XS_Perf__Trace__Context_common_lock_depth)
91{
92#ifdef dVAR
93 dVAR; dXSARGS;
94#else
95 dXSARGS;
96#endif
97 if (items != 1)
98 Perl_croak(aTHX_ "Usage: %s(%s)", "Perf::Trace::Context::common_lock_depth", "context");
99 PERL_UNUSED_VAR(cv); /* -W */
100 {
101 struct scripting_context * context = INT2PTR(struct scripting_context *,SvIV(ST(0)));
102 int RETVAL;
103 dXSTARG;
104
105 RETVAL = common_lock_depth(context);
106 XSprePUSH; PUSHi((IV)RETVAL);
107 }
108 XSRETURN(1);
109}
110
111#ifdef __cplusplus
112extern "C"
113#endif
114XS(boot_Perf__Trace__Context); /* prototype to pass -Wmissing-prototypes */
115XS(boot_Perf__Trace__Context)
116{
117#ifdef dVAR
118 dVAR; dXSARGS;
119#else
120 dXSARGS;
121#endif
122 const char* file = __FILE__;
123
124 PERL_UNUSED_VAR(cv); /* -W */
125 PERL_UNUSED_VAR(items); /* -W */
126 XS_VERSION_BOOTCHECK ;
127
128 newXSproto("Perf::Trace::Context::common_pc", XS_Perf__Trace__Context_common_pc, file, "$");
129 newXSproto("Perf::Trace::Context::common_flags", XS_Perf__Trace__Context_common_flags, file, "$");
130 newXSproto("Perf::Trace::Context::common_lock_depth", XS_Perf__Trace__Context_common_lock_depth, file, "$");
131 if (PL_unitcheckav)
132 call_list(PL_scopestack_ix, PL_unitcheckav);
133 XSRETURN_YES;
134}
135
diff --git a/tools/perf/scripts/perl/Perf-Trace-Util/Context.xs b/tools/perf/scripts/perl/Perf-Trace-Util/Context.xs
new file mode 100644
index 00000000000..c1e2ed1ed34
--- /dev/null
+++ b/tools/perf/scripts/perl/Perf-Trace-Util/Context.xs
@@ -0,0 +1,42 @@
1/*
2 * Context.xs. XS interfaces for perf script.
3 *
4 * Copyright (C) 2009 Tom Zanussi <tzanussi@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20 */
21
22#include "EXTERN.h"
23#include "perl.h"
24#include "XSUB.h"
25#include "../../../perf.h"
26#include "../../../util/script-event.h"
27
28MODULE = Perf::Trace::Context PACKAGE = Perf::Trace::Context
29PROTOTYPES: ENABLE
30
31int
32common_pc(context)
33 struct scripting_context * context
34
35int
36common_flags(context)
37 struct scripting_context * context
38
39int
40common_lock_depth(context)
41 struct scripting_context * context
42
diff --git a/tools/perf/scripts/perl/Perf-Trace-Util/Makefile.PL b/tools/perf/scripts/perl/Perf-Trace-Util/Makefile.PL
new file mode 100644
index 00000000000..decdeb0f678
--- /dev/null
+++ b/tools/perf/scripts/perl/Perf-Trace-Util/Makefile.PL
@@ -0,0 +1,17 @@
1use 5.010000;
2use ExtUtils::MakeMaker;
3# See lib/ExtUtils/MakeMaker.pm for details of how to influence
4# the contents of the Makefile that is written.
5WriteMakefile(
6 NAME => 'Perf::Trace::Context',
7 VERSION_FROM => 'lib/Perf/Trace/Context.pm', # finds $VERSION
8 PREREQ_PM => {}, # e.g., Module::Name => 1.1
9 ($] >= 5.005 ? ## Add these new keywords supported since 5.005
10 (ABSTRACT_FROM => 'lib/Perf/Trace/Context.pm', # retrieve abstract from module
11 AUTHOR => 'Tom Zanussi <tzanussi@gmail.com>') : ()),
12 LIBS => [''], # e.g., '-lm'
13 DEFINE => '-I ../..', # e.g., '-DHAVE_SOMETHING'
14 INC => '-I.', # e.g., '-I. -I/usr/include/other'
15 # Un-comment this if you add C files to link with later:
16 OBJECT => 'Context.o', # link all the C files too
17);
diff --git a/tools/perf/scripts/perl/Perf-Trace-Util/README b/tools/perf/scripts/perl/Perf-Trace-Util/README
new file mode 100644
index 00000000000..2f0c7f3043e
--- /dev/null
+++ b/tools/perf/scripts/perl/Perf-Trace-Util/README
@@ -0,0 +1,59 @@
1Perf-Trace-Util version 0.01
2============================
3
4This module contains utility functions for use with perf script.
5
6Core.pm and Util.pm are pure Perl modules; Core.pm contains routines
7that the core perf support for Perl calls on and should always be
8'used', while Util.pm contains useful but optional utility functions
9that scripts may want to use. Context.pm contains the Perl->C
10interface that allows scripts to access data in the embedding perf
11executable; scripts wishing to do that should 'use Context.pm'.
12
13The Perl->C perf interface is completely driven by Context.xs. If you
14want to add new Perl functions that end up accessing C data in the
15perf executable, you add desciptions of the new functions here.
16scripting_context is a pointer to the perf data in the perf executable
17that you want to access - it's passed as the second parameter,
18$context, to all handler functions.
19
20After you do that:
21
22 perl Makefile.PL # to create a Makefile for the next step
23 make # to create Context.c
24
25 edit Context.c to add const to the char* file = __FILE__ line in
26 XS(boot_Perf__Trace__Context) to silence a warning/error.
27
28 You can delete the Makefile, object files and anything else that was
29 generated e.g. blib and shared library, etc, except for of course
30 Context.c
31
32 You should then be able to run the normal perf make as usual.
33
34INSTALLATION
35
36Building perf with perf script Perl scripting should install this
37module in the right place.
38
39You should make sure libperl and ExtUtils/Embed.pm are installed first
40e.g. apt-get install libperl-dev or yum install perl-ExtUtils-Embed.
41
42DEPENDENCIES
43
44This module requires these other modules and libraries:
45
46 None
47
48COPYRIGHT AND LICENCE
49
50Copyright (C) 2009 by Tom Zanussi <tzanussi@gmail.com>
51
52This library is free software; you can redistribute it and/or modify
53it under the same terms as Perl itself, either Perl version 5.10.0 or,
54at your option, any later version of Perl 5 you may have available.
55
56Alternatively, this software may be distributed under the terms of the
57GNU General Public License ("GPL") version 2 as published by the Free
58Software Foundation.
59
diff --git a/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Context.pm b/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Context.pm
new file mode 100644
index 00000000000..4e2f6039ac9
--- /dev/null
+++ b/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Context.pm
@@ -0,0 +1,55 @@
1package Perf::Trace::Context;
2
3use 5.010000;
4use strict;
5use warnings;
6
7require Exporter;
8
9our @ISA = qw(Exporter);
10
11our %EXPORT_TAGS = ( 'all' => [ qw(
12) ] );
13
14our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
15
16our @EXPORT = qw(
17 common_pc common_flags common_lock_depth
18);
19
20our $VERSION = '0.01';
21
22require XSLoader;
23XSLoader::load('Perf::Trace::Context', $VERSION);
24
251;
26__END__
27=head1 NAME
28
29Perf::Trace::Context - Perl extension for accessing functions in perf.
30
31=head1 SYNOPSIS
32
33 use Perf::Trace::Context;
34
35=head1 SEE ALSO
36
37Perf (script) documentation
38
39=head1 AUTHOR
40
41Tom Zanussi, E<lt>tzanussi@gmail.com<gt>
42
43=head1 COPYRIGHT AND LICENSE
44
45Copyright (C) 2009 by Tom Zanussi
46
47This library is free software; you can redistribute it and/or modify
48it under the same terms as Perl itself, either Perl version 5.10.0 or,
49at your option, any later version of Perl 5 you may have available.
50
51Alternatively, this software may be distributed under the terms of the
52GNU General Public License ("GPL") version 2 as published by the Free
53Software Foundation.
54
55=cut
diff --git a/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Core.pm b/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Core.pm
new file mode 100644
index 00000000000..9158458d3ee
--- /dev/null
+++ b/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Core.pm
@@ -0,0 +1,192 @@
1package Perf::Trace::Core;
2
3use 5.010000;
4use strict;
5use warnings;
6
7require Exporter;
8
9our @ISA = qw(Exporter);
10
11our %EXPORT_TAGS = ( 'all' => [ qw(
12) ] );
13
14our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
15
16our @EXPORT = qw(
17define_flag_field define_flag_value flag_str dump_flag_fields
18define_symbolic_field define_symbolic_value symbol_str dump_symbolic_fields
19trace_flag_str
20);
21
22our $VERSION = '0.01';
23
24my %trace_flags = (0x00 => "NONE",
25 0x01 => "IRQS_OFF",
26 0x02 => "IRQS_NOSUPPORT",
27 0x04 => "NEED_RESCHED",
28 0x08 => "HARDIRQ",
29 0x10 => "SOFTIRQ");
30
31sub trace_flag_str
32{
33 my ($value) = @_;
34
35 my $string;
36
37 my $print_delim = 0;
38
39 foreach my $idx (sort {$a <=> $b} keys %trace_flags) {
40 if (!$value && !$idx) {
41 $string .= "NONE";
42 last;
43 }
44
45 if ($idx && ($value & $idx) == $idx) {
46 if ($print_delim) {
47 $string .= " | ";
48 }
49 $string .= "$trace_flags{$idx}";
50 $print_delim = 1;
51 $value &= ~$idx;
52 }
53 }
54
55 return $string;
56}
57
58my %flag_fields;
59my %symbolic_fields;
60
61sub flag_str
62{
63 my ($event_name, $field_name, $value) = @_;
64
65 my $string;
66
67 if ($flag_fields{$event_name}{$field_name}) {
68 my $print_delim = 0;
69 foreach my $idx (sort {$a <=> $b} keys %{$flag_fields{$event_name}{$field_name}{"values"}}) {
70 if (!$value && !$idx) {
71 $string .= "$flag_fields{$event_name}{$field_name}{'values'}{$idx}";
72 last;
73 }
74 if ($idx && ($value & $idx) == $idx) {
75 if ($print_delim && $flag_fields{$event_name}{$field_name}{'delim'}) {
76 $string .= " $flag_fields{$event_name}{$field_name}{'delim'} ";
77 }
78 $string .= "$flag_fields{$event_name}{$field_name}{'values'}{$idx}";
79 $print_delim = 1;
80 $value &= ~$idx;
81 }
82 }
83 }
84
85 return $string;
86}
87
88sub define_flag_field
89{
90 my ($event_name, $field_name, $delim) = @_;
91
92 $flag_fields{$event_name}{$field_name}{"delim"} = $delim;
93}
94
95sub define_flag_value
96{
97 my ($event_name, $field_name, $value, $field_str) = @_;
98
99 $flag_fields{$event_name}{$field_name}{"values"}{$value} = $field_str;
100}
101
102sub dump_flag_fields
103{
104 for my $event (keys %flag_fields) {
105 print "event $event:\n";
106 for my $field (keys %{$flag_fields{$event}}) {
107 print " field: $field:\n";
108 print " delim: $flag_fields{$event}{$field}{'delim'}\n";
109 foreach my $idx (sort {$a <=> $b} keys %{$flag_fields{$event}{$field}{"values"}}) {
110 print " value $idx: $flag_fields{$event}{$field}{'values'}{$idx}\n";
111 }
112 }
113 }
114}
115
116sub symbol_str
117{
118 my ($event_name, $field_name, $value) = @_;
119
120 if ($symbolic_fields{$event_name}{$field_name}) {
121 foreach my $idx (sort {$a <=> $b} keys %{$symbolic_fields{$event_name}{$field_name}{"values"}}) {
122 if (!$value && !$idx) {
123 return "$symbolic_fields{$event_name}{$field_name}{'values'}{$idx}";
124 last;
125 }
126 if ($value == $idx) {
127 return "$symbolic_fields{$event_name}{$field_name}{'values'}{$idx}";
128 }
129 }
130 }
131
132 return undef;
133}
134
135sub define_symbolic_field
136{
137 my ($event_name, $field_name) = @_;
138
139 # nothing to do, really
140}
141
142sub define_symbolic_value
143{
144 my ($event_name, $field_name, $value, $field_str) = @_;
145
146 $symbolic_fields{$event_name}{$field_name}{"values"}{$value} = $field_str;
147}
148
149sub dump_symbolic_fields
150{
151 for my $event (keys %symbolic_fields) {
152 print "event $event:\n";
153 for my $field (keys %{$symbolic_fields{$event}}) {
154 print " field: $field:\n";
155 foreach my $idx (sort {$a <=> $b} keys %{$symbolic_fields{$event}{$field}{"values"}}) {
156 print " value $idx: $symbolic_fields{$event}{$field}{'values'}{$idx}\n";
157 }
158 }
159 }
160}
161
1621;
163__END__
164=head1 NAME
165
166Perf::Trace::Core - Perl extension for perf script
167
168=head1 SYNOPSIS
169
170 use Perf::Trace::Core
171
172=head1 SEE ALSO
173
174Perf (script) documentation
175
176=head1 AUTHOR
177
178Tom Zanussi, E<lt>tzanussi@gmail.com<gt>
179
180=head1 COPYRIGHT AND LICENSE
181
182Copyright (C) 2009 by Tom Zanussi
183
184This library is free software; you can redistribute it and/or modify
185it under the same terms as Perl itself, either Perl version 5.10.0 or,
186at your option, any later version of Perl 5 you may have available.
187
188Alternatively, this software may be distributed under the terms of the
189GNU General Public License ("GPL") version 2 as published by the Free
190Software Foundation.
191
192=cut
diff --git a/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Util.pm b/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Util.pm
new file mode 100644
index 00000000000..05350011462
--- /dev/null
+++ b/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Util.pm
@@ -0,0 +1,94 @@
1package Perf::Trace::Util;
2
3use 5.010000;
4use strict;
5use warnings;
6
7require Exporter;
8
9our @ISA = qw(Exporter);
10
11our %EXPORT_TAGS = ( 'all' => [ qw(
12) ] );
13
14our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
15
16our @EXPORT = qw(
17avg nsecs nsecs_secs nsecs_nsecs nsecs_usecs print_nsecs
18clear_term
19);
20
21our $VERSION = '0.01';
22
23sub avg
24{
25 my ($total, $n) = @_;
26
27 return $total / $n;
28}
29
30my $NSECS_PER_SEC = 1000000000;
31
32sub nsecs
33{
34 my ($secs, $nsecs) = @_;
35
36 return $secs * $NSECS_PER_SEC + $nsecs;
37}
38
39sub nsecs_secs {
40 my ($nsecs) = @_;
41
42 return $nsecs / $NSECS_PER_SEC;
43}
44
45sub nsecs_nsecs {
46 my ($nsecs) = @_;
47
48 return $nsecs % $NSECS_PER_SEC;
49}
50
51sub nsecs_str {
52 my ($nsecs) = @_;
53
54 my $str = sprintf("%5u.%09u", nsecs_secs($nsecs), nsecs_nsecs($nsecs));
55
56 return $str;
57}
58
59sub clear_term
60{
61 print "\x1b[H\x1b[2J";
62}
63
641;
65__END__
66=head1 NAME
67
68Perf::Trace::Util - Perl extension for perf script
69
70=head1 SYNOPSIS
71
72 use Perf::Trace::Util;
73
74=head1 SEE ALSO
75
76Perf (script) documentation
77
78=head1 AUTHOR
79
80Tom Zanussi, E<lt>tzanussi@gmail.com<gt>
81
82=head1 COPYRIGHT AND LICENSE
83
84Copyright (C) 2009 by Tom Zanussi
85
86This library is free software; you can redistribute it and/or modify
87it under the same terms as Perl itself, either Perl version 5.10.0 or,
88at your option, any later version of Perl 5 you may have available.
89
90Alternatively, this software may be distributed under the terms of the
91GNU General Public License ("GPL") version 2 as published by the Free
92Software Foundation.
93
94=cut
diff --git a/tools/perf/scripts/perl/Perf-Trace-Util/typemap b/tools/perf/scripts/perl/Perf-Trace-Util/typemap
new file mode 100644
index 00000000000..840836804aa
--- /dev/null
+++ b/tools/perf/scripts/perl/Perf-Trace-Util/typemap
@@ -0,0 +1 @@
struct scripting_context * T_PTR
diff --git a/tools/perf/scripts/perl/bin/check-perf-trace-record b/tools/perf/scripts/perl/bin/check-perf-trace-record
new file mode 100644
index 00000000000..423ad6aed05
--- /dev/null
+++ b/tools/perf/scripts/perl/bin/check-perf-trace-record
@@ -0,0 +1,2 @@
1#!/bin/bash
2perf record -a -e kmem:kmalloc -e irq:softirq_entry -e kmem:kfree
diff --git a/tools/perf/scripts/perl/bin/failed-syscalls-record b/tools/perf/scripts/perl/bin/failed-syscalls-record
new file mode 100644
index 00000000000..8104895a7b6
--- /dev/null
+++ b/tools/perf/scripts/perl/bin/failed-syscalls-record
@@ -0,0 +1,2 @@
1#!/bin/bash
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
new file mode 100644
index 00000000000..9f83cc1ad8b
--- /dev/null
+++ b/tools/perf/scripts/perl/bin/failed-syscalls-report
@@ -0,0 +1,10 @@
1#!/bin/bash
2# description: system-wide failed syscalls
3# args: [comm]
4if [ $# -gt 0 ] ; then
5 if ! expr match "$1" "-" > /dev/null ; then
6 comm=$1
7 shift
8 fi
9fi
10perf script $@ -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
new file mode 100644
index 00000000000..33efc8673aa
--- /dev/null
+++ b/tools/perf/scripts/perl/bin/rw-by-file-record
@@ -0,0 +1,3 @@
1#!/bin/bash
2perf record -e syscalls:sys_enter_read -e syscalls:sys_enter_write $@
3
diff --git a/tools/perf/scripts/perl/bin/rw-by-file-report b/tools/perf/scripts/perl/bin/rw-by-file-report
new file mode 100644
index 00000000000..77200b3f310
--- /dev/null
+++ b/tools/perf/scripts/perl/bin/rw-by-file-report
@@ -0,0 +1,10 @@
1#!/bin/bash
2# description: r/w activity for a program, by file
3# args: <comm>
4if [ $# -lt 1 ] ; then
5 echo "usage: rw-by-file <comm>"
6 exit
7fi
8comm=$1
9shift
10perf script $@ -s "$PERF_EXEC_PATH"/scripts/perl/rw-by-file.pl $comm
diff --git a/tools/perf/scripts/perl/bin/rw-by-pid-record b/tools/perf/scripts/perl/bin/rw-by-pid-record
new file mode 100644
index 00000000000..7cb9db23044
--- /dev/null
+++ b/tools/perf/scripts/perl/bin/rw-by-pid-record
@@ -0,0 +1,2 @@
1#!/bin/bash
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
new file mode 100644
index 00000000000..a27b9f311f9
--- /dev/null
+++ b/tools/perf/scripts/perl/bin/rw-by-pid-report
@@ -0,0 +1,3 @@
1#!/bin/bash
2# description: system-wide r/w activity
3perf script $@ -s "$PERF_EXEC_PATH"/scripts/perl/rw-by-pid.pl
diff --git a/tools/perf/scripts/perl/bin/rwtop-record b/tools/perf/scripts/perl/bin/rwtop-record
new file mode 100644
index 00000000000..7cb9db23044
--- /dev/null
+++ b/tools/perf/scripts/perl/bin/rwtop-record
@@ -0,0 +1,2 @@
1#!/bin/bash
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
new file mode 100644
index 00000000000..83e11ec2e19
--- /dev/null
+++ b/tools/perf/scripts/perl/bin/rwtop-report
@@ -0,0 +1,20 @@
1#!/bin/bash
2# description: system-wide r/w top
3# args: [interval]
4n_args=0
5for i in "$@"
6do
7 if expr match "$i" "-" > /dev/null ; then
8 break
9 fi
10 n_args=$(( $n_args + 1 ))
11done
12if [ "$n_args" -gt 1 ] ; then
13 echo "usage: rwtop-report [interval]"
14 exit
15fi
16if [ "$n_args" -gt 0 ] ; then
17 interval=$1
18 shift
19fi
20perf script $@ -s "$PERF_EXEC_PATH"/scripts/perl/rwtop.pl $interval
diff --git a/tools/perf/scripts/perl/bin/wakeup-latency-record b/tools/perf/scripts/perl/bin/wakeup-latency-record
new file mode 100644
index 00000000000..464251a1bd7
--- /dev/null
+++ b/tools/perf/scripts/perl/bin/wakeup-latency-record
@@ -0,0 +1,6 @@
1#!/bin/bash
2perf record -e sched:sched_switch -e sched:sched_wakeup $@
3
4
5
6
diff --git a/tools/perf/scripts/perl/bin/wakeup-latency-report b/tools/perf/scripts/perl/bin/wakeup-latency-report
new file mode 100644
index 00000000000..889e8130cca
--- /dev/null
+++ b/tools/perf/scripts/perl/bin/wakeup-latency-report
@@ -0,0 +1,3 @@
1#!/bin/bash
2# description: system-wide min/max/avg wakeup latency
3perf script $@ -s "$PERF_EXEC_PATH"/scripts/perl/wakeup-latency.pl
diff --git a/tools/perf/scripts/perl/bin/workqueue-stats-record b/tools/perf/scripts/perl/bin/workqueue-stats-record
new file mode 100644
index 00000000000..8edda9078d5
--- /dev/null
+++ b/tools/perf/scripts/perl/bin/workqueue-stats-record
@@ -0,0 +1,2 @@
1#!/bin/bash
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
new file mode 100644
index 00000000000..6d91411d248
--- /dev/null
+++ b/tools/perf/scripts/perl/bin/workqueue-stats-report
@@ -0,0 +1,3 @@
1#!/bin/bash
2# description: workqueue stats (ins/exe/create/destroy)
3perf script $@ -s "$PERF_EXEC_PATH"/scripts/perl/workqueue-stats.pl
diff --git a/tools/perf/scripts/perl/check-perf-trace.pl b/tools/perf/scripts/perl/check-perf-trace.pl
new file mode 100644
index 00000000000..4e7076c2061
--- /dev/null
+++ b/tools/perf/scripts/perl/check-perf-trace.pl
@@ -0,0 +1,106 @@
1# perf script event handlers, generated by perf script -g perl
2# (c) 2009, Tom Zanussi <tzanussi@gmail.com>
3# Licensed under the terms of the GNU GPL License version 2
4
5# This script tests basic functionality such as flag and symbol
6# strings, common_xxx() calls back into perf, begin, end, unhandled
7# events, etc. Basically, if this script runs successfully and
8# displays expected results, perl scripting support should be ok.
9
10use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib";
11use lib "./Perf-Trace-Util/lib";
12use Perf::Trace::Core;
13use Perf::Trace::Context;
14use Perf::Trace::Util;
15
16sub trace_begin
17{
18 print "trace_begin\n";
19}
20
21sub trace_end
22{
23 print "trace_end\n";
24
25 print_unhandled();
26}
27
28sub irq::softirq_entry
29{
30 my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
31 $common_pid, $common_comm,
32 $vec) = @_;
33
34 print_header($event_name, $common_cpu, $common_secs, $common_nsecs,
35 $common_pid, $common_comm);
36
37 print_uncommon($context);
38
39 printf("vec=%s\n",
40 symbol_str("irq::softirq_entry", "vec", $vec));
41}
42
43sub kmem::kmalloc
44{
45 my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
46 $common_pid, $common_comm,
47 $call_site, $ptr, $bytes_req, $bytes_alloc,
48 $gfp_flags) = @_;
49
50 print_header($event_name, $common_cpu, $common_secs, $common_nsecs,
51 $common_pid, $common_comm);
52
53 print_uncommon($context);
54
55 printf("call_site=%p, ptr=%p, bytes_req=%u, bytes_alloc=%u, ".
56 "gfp_flags=%s\n",
57 $call_site, $ptr, $bytes_req, $bytes_alloc,
58
59 flag_str("kmem::kmalloc", "gfp_flags", $gfp_flags));
60}
61
62# print trace fields not included in handler args
63sub print_uncommon
64{
65 my ($context) = @_;
66
67 printf("common_preempt_count=%d, common_flags=%s, common_lock_depth=%d, ",
68 common_pc($context), trace_flag_str(common_flags($context)),
69 common_lock_depth($context));
70
71}
72
73my %unhandled;
74
75sub print_unhandled
76{
77 if ((scalar keys %unhandled) == 0) {
78 return;
79 }
80
81 print "\nunhandled events:\n\n";
82
83 printf("%-40s %10s\n", "event", "count");
84 printf("%-40s %10s\n", "----------------------------------------",
85 "-----------");
86
87 foreach my $event_name (keys %unhandled) {
88 printf("%-40s %10d\n", $event_name, $unhandled{$event_name});
89 }
90}
91
92sub trace_unhandled
93{
94 my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
95 $common_pid, $common_comm) = @_;
96
97 $unhandled{$event_name}++;
98}
99
100sub print_header
101{
102 my ($event_name, $cpu, $secs, $nsecs, $pid, $comm) = @_;
103
104 printf("%-20s %5u %05u.%09u %8u %-20s ",
105 $event_name, $cpu, $secs, $nsecs, $pid, $comm);
106}
diff --git a/tools/perf/scripts/perl/failed-syscalls.pl b/tools/perf/scripts/perl/failed-syscalls.pl
new file mode 100644
index 00000000000..94bc25a347e
--- /dev/null
+++ b/tools/perf/scripts/perl/failed-syscalls.pl
@@ -0,0 +1,42 @@
1# failed system call counts
2# (c) 2010, Tom Zanussi <tzanussi@gmail.com>
3# Licensed under the terms of the GNU GPL License version 2
4#
5# Displays system-wide failed system call totals
6# If a [comm] arg is specified, only syscalls called by [comm] are displayed.
7
8use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib";
9use lib "./Perf-Trace-Util/lib";
10use Perf::Trace::Core;
11use Perf::Trace::Context;
12use Perf::Trace::Util;
13
14my $for_comm = shift;
15
16my %failed_syscalls;
17
18sub raw_syscalls::sys_exit
19{
20 my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
21 $common_pid, $common_comm,
22 $id, $ret) = @_;
23
24 if ($ret < 0) {
25 $failed_syscalls{$common_comm}++;
26 }
27}
28
29sub trace_end
30{
31 printf("\nfailed syscalls by comm:\n\n");
32
33 printf("%-20s %10s\n", "comm", "# errors");
34 printf("%-20s %6s %10s\n", "--------------------", "----------");
35
36 foreach my $comm (sort {$failed_syscalls{$b} <=> $failed_syscalls{$a}}
37 keys %failed_syscalls) {
38 next if ($for_comm && $comm ne $for_comm);
39
40 printf("%-20s %10s\n", $comm, $failed_syscalls{$comm});
41 }
42}
diff --git a/tools/perf/scripts/perl/rw-by-file.pl b/tools/perf/scripts/perl/rw-by-file.pl
new file mode 100644
index 00000000000..74844ee2be3
--- /dev/null
+++ b/tools/perf/scripts/perl/rw-by-file.pl
@@ -0,0 +1,106 @@
1#!/usr/bin/perl -w
2# (c) 2009, Tom Zanussi <tzanussi@gmail.com>
3# Licensed under the terms of the GNU GPL License version 2
4
5# Display r/w activity for files read/written to for a given program
6
7# The common_* event handler fields are the most useful fields common to
8# all events. They don't necessarily correspond to the 'common_*' fields
9# in the status files. Those fields not available as handler params can
10# be retrieved via script functions of the form get_common_*().
11
12use 5.010000;
13use strict;
14use warnings;
15
16use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib";
17use lib "./Perf-Trace-Util/lib";
18use Perf::Trace::Core;
19use Perf::Trace::Util;
20
21my $usage = "perf script -s rw-by-file.pl <comm>\n";
22
23my $for_comm = shift or die $usage;
24
25my %reads;
26my %writes;
27
28sub syscalls::sys_enter_read
29{
30 my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
31 $common_pid, $common_comm, $nr, $fd, $buf, $count) = @_;
32
33 if ($common_comm eq $for_comm) {
34 $reads{$fd}{bytes_requested} += $count;
35 $reads{$fd}{total_reads}++;
36 }
37}
38
39sub syscalls::sys_enter_write
40{
41 my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
42 $common_pid, $common_comm, $nr, $fd, $buf, $count) = @_;
43
44 if ($common_comm eq $for_comm) {
45 $writes{$fd}{bytes_written} += $count;
46 $writes{$fd}{total_writes}++;
47 }
48}
49
50sub trace_end
51{
52 printf("file read counts for $for_comm:\n\n");
53
54 printf("%6s %10s %10s\n", "fd", "# reads", "bytes_requested");
55 printf("%6s %10s %10s\n", "------", "----------", "-----------");
56
57 foreach my $fd (sort {$reads{$b}{bytes_requested} <=>
58 $reads{$a}{bytes_requested}} keys %reads) {
59 my $total_reads = $reads{$fd}{total_reads};
60 my $bytes_requested = $reads{$fd}{bytes_requested};
61 printf("%6u %10u %10u\n", $fd, $total_reads, $bytes_requested);
62 }
63
64 printf("\nfile write counts for $for_comm:\n\n");
65
66 printf("%6s %10s %10s\n", "fd", "# writes", "bytes_written");
67 printf("%6s %10s %10s\n", "------", "----------", "-----------");
68
69 foreach my $fd (sort {$writes{$b}{bytes_written} <=>
70 $writes{$a}{bytes_written}} keys %writes) {
71 my $total_writes = $writes{$fd}{total_writes};
72 my $bytes_written = $writes{$fd}{bytes_written};
73 printf("%6u %10u %10u\n", $fd, $total_writes, $bytes_written);
74 }
75
76 print_unhandled();
77}
78
79my %unhandled;
80
81sub print_unhandled
82{
83 if ((scalar keys %unhandled) == 0) {
84 return;
85 }
86
87 print "\nunhandled events:\n\n";
88
89 printf("%-40s %10s\n", "event", "count");
90 printf("%-40s %10s\n", "----------------------------------------",
91 "-----------");
92
93 foreach my $event_name (keys %unhandled) {
94 printf("%-40s %10d\n", $event_name, $unhandled{$event_name});
95 }
96}
97
98sub trace_unhandled
99{
100 my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
101 $common_pid, $common_comm) = @_;
102
103 $unhandled{$event_name}++;
104}
105
106
diff --git a/tools/perf/scripts/perl/rw-by-pid.pl b/tools/perf/scripts/perl/rw-by-pid.pl
new file mode 100644
index 00000000000..9db23c9daf5
--- /dev/null
+++ b/tools/perf/scripts/perl/rw-by-pid.pl
@@ -0,0 +1,184 @@
1#!/usr/bin/perl -w
2# (c) 2009, Tom Zanussi <tzanussi@gmail.com>
3# Licensed under the terms of the GNU GPL License version 2
4
5# Display r/w activity for all processes
6
7# The common_* event handler fields are the most useful fields common to
8# all events. They don't necessarily correspond to the 'common_*' fields
9# in the status files. Those fields not available as handler params can
10# be retrieved via script functions of the form get_common_*().
11
12use 5.010000;
13use strict;
14use warnings;
15
16use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib";
17use lib "./Perf-Trace-Util/lib";
18use Perf::Trace::Core;
19use Perf::Trace::Util;
20
21my %reads;
22my %writes;
23
24sub syscalls::sys_exit_read
25{
26 my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
27 $common_pid, $common_comm,
28 $nr, $ret) = @_;
29
30 if ($ret > 0) {
31 $reads{$common_pid}{bytes_read} += $ret;
32 } else {
33 if (!defined ($reads{$common_pid}{bytes_read})) {
34 $reads{$common_pid}{bytes_read} = 0;
35 }
36 $reads{$common_pid}{errors}{$ret}++;
37 }
38}
39
40sub syscalls::sys_enter_read
41{
42 my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
43 $common_pid, $common_comm,
44 $nr, $fd, $buf, $count) = @_;
45
46 $reads{$common_pid}{bytes_requested} += $count;
47 $reads{$common_pid}{total_reads}++;
48 $reads{$common_pid}{comm} = $common_comm;
49}
50
51sub syscalls::sys_exit_write
52{
53 my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
54 $common_pid, $common_comm,
55 $nr, $ret) = @_;
56
57 if ($ret <= 0) {
58 $writes{$common_pid}{errors}{$ret}++;
59 }
60}
61
62sub syscalls::sys_enter_write
63{
64 my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
65 $common_pid, $common_comm,
66 $nr, $fd, $buf, $count) = @_;
67
68 $writes{$common_pid}{bytes_written} += $count;
69 $writes{$common_pid}{total_writes}++;
70 $writes{$common_pid}{comm} = $common_comm;
71}
72
73sub trace_end
74{
75 printf("read counts by pid:\n\n");
76
77 printf("%6s %20s %10s %10s %10s\n", "pid", "comm",
78 "# reads", "bytes_requested", "bytes_read");
79 printf("%6s %-20s %10s %10s %10s\n", "------", "--------------------",
80 "-----------", "----------", "----------");
81
82 foreach my $pid (sort { ($reads{$b}{bytes_read} || 0) <=>
83 ($reads{$a}{bytes_read} || 0) } keys %reads) {
84 my $comm = $reads{$pid}{comm} || "";
85 my $total_reads = $reads{$pid}{total_reads} || 0;
86 my $bytes_requested = $reads{$pid}{bytes_requested} || 0;
87 my $bytes_read = $reads{$pid}{bytes_read} || 0;
88
89 printf("%6s %-20s %10s %10s %10s\n", $pid, $comm,
90 $total_reads, $bytes_requested, $bytes_read);
91 }
92
93 printf("\nfailed reads by pid:\n\n");
94
95 printf("%6s %20s %6s %10s\n", "pid", "comm", "error #", "# errors");
96 printf("%6s %20s %6s %10s\n", "------", "--------------------",
97 "------", "----------");
98
99 my @errcounts = ();
100
101 foreach my $pid (keys %reads) {
102 foreach my $error (keys %{$reads{$pid}{errors}}) {
103 my $comm = $reads{$pid}{comm} || "";
104 my $errcount = $reads{$pid}{errors}{$error} || 0;
105 push @errcounts, [$pid, $comm, $error, $errcount];
106 }
107 }
108
109 @errcounts = sort { $b->[3] <=> $a->[3] } @errcounts;
110
111 for my $i (0 .. $#errcounts) {
112 printf("%6d %-20s %6d %10s\n", $errcounts[$i][0],
113 $errcounts[$i][1], $errcounts[$i][2], $errcounts[$i][3]);
114 }
115
116 printf("\nwrite counts by pid:\n\n");
117
118 printf("%6s %20s %10s %10s\n", "pid", "comm",
119 "# writes", "bytes_written");
120 printf("%6s %-20s %10s %10s\n", "------", "--------------------",
121 "-----------", "----------");
122
123 foreach my $pid (sort { ($writes{$b}{bytes_written} || 0) <=>
124 ($writes{$a}{bytes_written} || 0)} keys %writes) {
125 my $comm = $writes{$pid}{comm} || "";
126 my $total_writes = $writes{$pid}{total_writes} || 0;
127 my $bytes_written = $writes{$pid}{bytes_written} || 0;
128
129 printf("%6s %-20s %10s %10s\n", $pid, $comm,
130 $total_writes, $bytes_written);
131 }
132
133 printf("\nfailed writes by pid:\n\n");
134
135 printf("%6s %20s %6s %10s\n", "pid", "comm", "error #", "# errors");
136 printf("%6s %20s %6s %10s\n", "------", "--------------------",
137 "------", "----------");
138
139 @errcounts = ();
140
141 foreach my $pid (keys %writes) {
142 foreach my $error (keys %{$writes{$pid}{errors}}) {
143 my $comm = $writes{$pid}{comm} || "";
144 my $errcount = $writes{$pid}{errors}{$error} || 0;
145 push @errcounts, [$pid, $comm, $error, $errcount];
146 }
147 }
148
149 @errcounts = sort { $b->[3] <=> $a->[3] } @errcounts;
150
151 for my $i (0 .. $#errcounts) {
152 printf("%6d %-20s %6d %10s\n", $errcounts[$i][0],
153 $errcounts[$i][1], $errcounts[$i][2], $errcounts[$i][3]);
154 }
155
156 print_unhandled();
157}
158
159my %unhandled;
160
161sub print_unhandled
162{
163 if ((scalar keys %unhandled) == 0) {
164 return;
165 }
166
167 print "\nunhandled events:\n\n";
168
169 printf("%-40s %10s\n", "event", "count");
170 printf("%-40s %10s\n", "----------------------------------------",
171 "-----------");
172
173 foreach my $event_name (keys %unhandled) {
174 printf("%-40s %10d\n", $event_name, $unhandled{$event_name});
175 }
176}
177
178sub trace_unhandled
179{
180 my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
181 $common_pid, $common_comm) = @_;
182
183 $unhandled{$event_name}++;
184}
diff --git a/tools/perf/scripts/perl/rwtop.pl b/tools/perf/scripts/perl/rwtop.pl
new file mode 100644
index 00000000000..4bb3ecd3347
--- /dev/null
+++ b/tools/perf/scripts/perl/rwtop.pl
@@ -0,0 +1,199 @@
1#!/usr/bin/perl -w
2# (c) 2010, Tom Zanussi <tzanussi@gmail.com>
3# Licensed under the terms of the GNU GPL License version 2
4
5# read/write top
6#
7# Periodically displays system-wide r/w call activity, broken down by
8# pid. If an [interval] arg is specified, the display will be
9# refreshed every [interval] seconds. The default interval is 3
10# seconds.
11
12use 5.010000;
13use strict;
14use warnings;
15
16use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib";
17use lib "./Perf-Trace-Util/lib";
18use Perf::Trace::Core;
19use Perf::Trace::Util;
20
21my $default_interval = 3;
22my $nlines = 20;
23my $print_thread;
24my $print_pending = 0;
25
26my %reads;
27my %writes;
28
29my $interval = shift;
30if (!$interval) {
31 $interval = $default_interval;
32}
33
34sub syscalls::sys_exit_read
35{
36 my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
37 $common_pid, $common_comm,
38 $nr, $ret) = @_;
39
40 print_check();
41
42 if ($ret > 0) {
43 $reads{$common_pid}{bytes_read} += $ret;
44 } else {
45 if (!defined ($reads{$common_pid}{bytes_read})) {
46 $reads{$common_pid}{bytes_read} = 0;
47 }
48 $reads{$common_pid}{errors}{$ret}++;
49 }
50}
51
52sub syscalls::sys_enter_read
53{
54 my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
55 $common_pid, $common_comm,
56 $nr, $fd, $buf, $count) = @_;
57
58 print_check();
59
60 $reads{$common_pid}{bytes_requested} += $count;
61 $reads{$common_pid}{total_reads}++;
62 $reads{$common_pid}{comm} = $common_comm;
63}
64
65sub syscalls::sys_exit_write
66{
67 my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
68 $common_pid, $common_comm,
69 $nr, $ret) = @_;
70
71 print_check();
72
73 if ($ret <= 0) {
74 $writes{$common_pid}{errors}{$ret}++;
75 }
76}
77
78sub syscalls::sys_enter_write
79{
80 my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
81 $common_pid, $common_comm,
82 $nr, $fd, $buf, $count) = @_;
83
84 print_check();
85
86 $writes{$common_pid}{bytes_written} += $count;
87 $writes{$common_pid}{total_writes}++;
88 $writes{$common_pid}{comm} = $common_comm;
89}
90
91sub trace_begin
92{
93 $SIG{ALRM} = \&set_print_pending;
94 alarm 1;
95}
96
97sub trace_end
98{
99 print_unhandled();
100 print_totals();
101}
102
103sub print_check()
104{
105 if ($print_pending == 1) {
106 $print_pending = 0;
107 print_totals();
108 }
109}
110
111sub set_print_pending()
112{
113 $print_pending = 1;
114 alarm $interval;
115}
116
117sub print_totals
118{
119 my $count;
120
121 $count = 0;
122
123 clear_term();
124
125 printf("\nread counts by pid:\n\n");
126
127 printf("%6s %20s %10s %10s %10s\n", "pid", "comm",
128 "# reads", "bytes_req", "bytes_read");
129 printf("%6s %-20s %10s %10s %10s\n", "------", "--------------------",
130 "----------", "----------", "----------");
131
132 foreach my $pid (sort { ($reads{$b}{bytes_read} || 0) <=>
133 ($reads{$a}{bytes_read} || 0) } keys %reads) {
134 my $comm = $reads{$pid}{comm} || "";
135 my $total_reads = $reads{$pid}{total_reads} || 0;
136 my $bytes_requested = $reads{$pid}{bytes_requested} || 0;
137 my $bytes_read = $reads{$pid}{bytes_read} || 0;
138
139 printf("%6s %-20s %10s %10s %10s\n", $pid, $comm,
140 $total_reads, $bytes_requested, $bytes_read);
141
142 if (++$count == $nlines) {
143 last;
144 }
145 }
146
147 $count = 0;
148
149 printf("\nwrite counts by pid:\n\n");
150
151 printf("%6s %20s %10s %13s\n", "pid", "comm",
152 "# writes", "bytes_written");
153 printf("%6s %-20s %10s %13s\n", "------", "--------------------",
154 "----------", "-------------");
155
156 foreach my $pid (sort { ($writes{$b}{bytes_written} || 0) <=>
157 ($writes{$a}{bytes_written} || 0)} keys %writes) {
158 my $comm = $writes{$pid}{comm} || "";
159 my $total_writes = $writes{$pid}{total_writes} || 0;
160 my $bytes_written = $writes{$pid}{bytes_written} || 0;
161
162 printf("%6s %-20s %10s %13s\n", $pid, $comm,
163 $total_writes, $bytes_written);
164
165 if (++$count == $nlines) {
166 last;
167 }
168 }
169
170 %reads = ();
171 %writes = ();
172}
173
174my %unhandled;
175
176sub print_unhandled
177{
178 if ((scalar keys %unhandled) == 0) {
179 return;
180 }
181
182 print "\nunhandled events:\n\n";
183
184 printf("%-40s %10s\n", "event", "count");
185 printf("%-40s %10s\n", "----------------------------------------",
186 "-----------");
187
188 foreach my $event_name (keys %unhandled) {
189 printf("%-40s %10d\n", $event_name, $unhandled{$event_name});
190 }
191}
192
193sub trace_unhandled
194{
195 my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
196 $common_pid, $common_comm) = @_;
197
198 $unhandled{$event_name}++;
199}
diff --git a/tools/perf/scripts/perl/wakeup-latency.pl b/tools/perf/scripts/perl/wakeup-latency.pl
new file mode 100644
index 00000000000..d9143dcec6c
--- /dev/null
+++ b/tools/perf/scripts/perl/wakeup-latency.pl
@@ -0,0 +1,107 @@
1#!/usr/bin/perl -w
2# (c) 2009, Tom Zanussi <tzanussi@gmail.com>
3# Licensed under the terms of the GNU GPL License version 2
4
5# Display avg/min/max wakeup latency
6
7# The common_* event handler fields are the most useful fields common to
8# all events. They don't necessarily correspond to the 'common_*' fields
9# in the status files. Those fields not available as handler params can
10# be retrieved via script functions of the form get_common_*().
11
12use 5.010000;
13use strict;
14use warnings;
15
16use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib";
17use lib "./Perf-Trace-Util/lib";
18use Perf::Trace::Core;
19use Perf::Trace::Util;
20
21my %last_wakeup;
22
23my $max_wakeup_latency;
24my $min_wakeup_latency;
25my $total_wakeup_latency = 0;
26my $total_wakeups = 0;
27
28sub sched::sched_switch
29{
30 my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
31 $common_pid, $common_comm,
32 $prev_comm, $prev_pid, $prev_prio, $prev_state, $next_comm, $next_pid,
33 $next_prio) = @_;
34
35 my $wakeup_ts = $last_wakeup{$common_cpu}{ts};
36 if ($wakeup_ts) {
37 my $switch_ts = nsecs($common_secs, $common_nsecs);
38 my $wakeup_latency = $switch_ts - $wakeup_ts;
39 if ($wakeup_latency > $max_wakeup_latency) {
40 $max_wakeup_latency = $wakeup_latency;
41 }
42 if ($wakeup_latency < $min_wakeup_latency) {
43 $min_wakeup_latency = $wakeup_latency;
44 }
45 $total_wakeup_latency += $wakeup_latency;
46 $total_wakeups++;
47 }
48 $last_wakeup{$common_cpu}{ts} = 0;
49}
50
51sub sched::sched_wakeup
52{
53 my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
54 $common_pid, $common_comm,
55 $comm, $pid, $prio, $success, $target_cpu) = @_;
56
57 $last_wakeup{$target_cpu}{ts} = nsecs($common_secs, $common_nsecs);
58}
59
60sub trace_begin
61{
62 $min_wakeup_latency = 1000000000;
63 $max_wakeup_latency = 0;
64}
65
66sub trace_end
67{
68 printf("wakeup_latency stats:\n\n");
69 print "total_wakeups: $total_wakeups\n";
70 if ($total_wakeups) {
71 printf("avg_wakeup_latency (ns): %u\n",
72 avg($total_wakeup_latency, $total_wakeups));
73 } else {
74 printf("avg_wakeup_latency (ns): N/A\n");
75 }
76 printf("min_wakeup_latency (ns): %u\n", $min_wakeup_latency);
77 printf("max_wakeup_latency (ns): %u\n", $max_wakeup_latency);
78
79 print_unhandled();
80}
81
82my %unhandled;
83
84sub print_unhandled
85{
86 if ((scalar keys %unhandled) == 0) {
87 return;
88 }
89
90 print "\nunhandled events:\n\n";
91
92 printf("%-40s %10s\n", "event", "count");
93 printf("%-40s %10s\n", "----------------------------------------",
94 "-----------");
95
96 foreach my $event_name (keys %unhandled) {
97 printf("%-40s %10d\n", $event_name, $unhandled{$event_name});
98 }
99}
100
101sub trace_unhandled
102{
103 my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
104 $common_pid, $common_comm) = @_;
105
106 $unhandled{$event_name}++;
107}
diff --git a/tools/perf/scripts/perl/workqueue-stats.pl b/tools/perf/scripts/perl/workqueue-stats.pl
new file mode 100644
index 00000000000..a8eaff5119e
--- /dev/null
+++ b/tools/perf/scripts/perl/workqueue-stats.pl
@@ -0,0 +1,129 @@
1#!/usr/bin/perl -w
2# (c) 2009, Tom Zanussi <tzanussi@gmail.com>
3# Licensed under the terms of the GNU GPL License version 2
4
5# Displays workqueue stats
6#
7# Usage:
8#
9# perf record -c 1 -f -a -R -e workqueue:workqueue_creation -e
10# workqueue:workqueue_destruction -e workqueue:workqueue_execution
11# -e workqueue:workqueue_insertion
12#
13# perf script -p -s tools/perf/scripts/perl/workqueue-stats.pl
14
15use 5.010000;
16use strict;
17use warnings;
18
19use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib";
20use lib "./Perf-Trace-Util/lib";
21use Perf::Trace::Core;
22use Perf::Trace::Util;
23
24my @cpus;
25
26sub workqueue::workqueue_destruction
27{
28 my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
29 $common_pid, $common_comm,
30 $thread_comm, $thread_pid) = @_;
31
32 $cpus[$common_cpu]{$thread_pid}{destroyed}++;
33 $cpus[$common_cpu]{$thread_pid}{comm} = $thread_comm;
34}
35
36sub workqueue::workqueue_creation
37{
38 my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
39 $common_pid, $common_comm,
40 $thread_comm, $thread_pid, $cpu) = @_;
41
42 $cpus[$common_cpu]{$thread_pid}{created}++;
43 $cpus[$common_cpu]{$thread_pid}{comm} = $thread_comm;
44}
45
46sub workqueue::workqueue_execution
47{
48 my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
49 $common_pid, $common_comm,
50 $thread_comm, $thread_pid, $func) = @_;
51
52 $cpus[$common_cpu]{$thread_pid}{executed}++;
53 $cpus[$common_cpu]{$thread_pid}{comm} = $thread_comm;
54}
55
56sub workqueue::workqueue_insertion
57{
58 my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
59 $common_pid, $common_comm,
60 $thread_comm, $thread_pid, $func) = @_;
61
62 $cpus[$common_cpu]{$thread_pid}{inserted}++;
63 $cpus[$common_cpu]{$thread_pid}{comm} = $thread_comm;
64}
65
66sub trace_end
67{
68 print "workqueue work stats:\n\n";
69 my $cpu = 0;
70 printf("%3s %6s %6s\t%-20s\n", "cpu", "ins", "exec", "name");
71 printf("%3s %6s %6s\t%-20s\n", "---", "---", "----", "----");
72 foreach my $pidhash (@cpus) {
73 while ((my $pid, my $wqhash) = each %$pidhash) {
74 my $ins = $$wqhash{'inserted'} || 0;
75 my $exe = $$wqhash{'executed'} || 0;
76 my $comm = $$wqhash{'comm'} || "";
77 if ($ins || $exe) {
78 printf("%3u %6u %6u\t%-20s\n", $cpu, $ins, $exe, $comm);
79 }
80 }
81 $cpu++;
82 }
83
84 $cpu = 0;
85 print "\nworkqueue lifecycle stats:\n\n";
86 printf("%3s %6s %6s\t%-20s\n", "cpu", "created", "destroyed", "name");
87 printf("%3s %6s %6s\t%-20s\n", "---", "-------", "---------", "----");
88 foreach my $pidhash (@cpus) {
89 while ((my $pid, my $wqhash) = each %$pidhash) {
90 my $created = $$wqhash{'created'} || 0;
91 my $destroyed = $$wqhash{'destroyed'} || 0;
92 my $comm = $$wqhash{'comm'} || "";
93 if ($created || $destroyed) {
94 printf("%3u %6u %6u\t%-20s\n", $cpu, $created, $destroyed,
95 $comm);
96 }
97 }
98 $cpu++;
99 }
100
101 print_unhandled();
102}
103
104my %unhandled;
105
106sub print_unhandled
107{
108 if ((scalar keys %unhandled) == 0) {
109 return;
110 }
111
112 print "\nunhandled events:\n\n";
113
114 printf("%-40s %10s\n", "event", "count");
115 printf("%-40s %10s\n", "----------------------------------------",
116 "-----------");
117
118 foreach my $event_name (keys %unhandled) {
119 printf("%-40s %10d\n", $event_name, $unhandled{$event_name});
120 }
121}
122
123sub trace_unhandled
124{
125 my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
126 $common_pid, $common_comm) = @_;
127
128 $unhandled{$event_name}++;
129}
diff --git a/tools/perf/scripts/python/Perf-Trace-Util/Context.c b/tools/perf/scripts/python/Perf-Trace-Util/Context.c
new file mode 100644
index 00000000000..315067b8f55
--- /dev/null
+++ b/tools/perf/scripts/python/Perf-Trace-Util/Context.c
@@ -0,0 +1,88 @@
1/*
2 * Context.c. Python interfaces for perf script.
3 *
4 * Copyright (C) 2010 Tom Zanussi <tzanussi@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20 */
21
22#include <Python.h>
23#include "../../../perf.h"
24#include "../../../util/trace-event.h"
25
26PyMODINIT_FUNC initperf_trace_context(void);
27
28static PyObject *perf_trace_context_common_pc(PyObject *self, PyObject *args)
29{
30 static struct scripting_context *scripting_context;
31 PyObject *context;
32 int retval;
33
34 if (!PyArg_ParseTuple(args, "O", &context))
35 return NULL;
36
37 scripting_context = PyCObject_AsVoidPtr(context);
38 retval = common_pc(scripting_context);
39
40 return Py_BuildValue("i", retval);
41}
42
43static PyObject *perf_trace_context_common_flags(PyObject *self,
44 PyObject *args)
45{
46 static struct scripting_context *scripting_context;
47 PyObject *context;
48 int retval;
49
50 if (!PyArg_ParseTuple(args, "O", &context))
51 return NULL;
52
53 scripting_context = PyCObject_AsVoidPtr(context);
54 retval = common_flags(scripting_context);
55
56 return Py_BuildValue("i", retval);
57}
58
59static PyObject *perf_trace_context_common_lock_depth(PyObject *self,
60 PyObject *args)
61{
62 static struct scripting_context *scripting_context;
63 PyObject *context;
64 int retval;
65
66 if (!PyArg_ParseTuple(args, "O", &context))
67 return NULL;
68
69 scripting_context = PyCObject_AsVoidPtr(context);
70 retval = common_lock_depth(scripting_context);
71
72 return Py_BuildValue("i", retval);
73}
74
75static PyMethodDef ContextMethods[] = {
76 { "common_pc", perf_trace_context_common_pc, METH_VARARGS,
77 "Get the common preempt count event field value."},
78 { "common_flags", perf_trace_context_common_flags, METH_VARARGS,
79 "Get the common flags event field value."},
80 { "common_lock_depth", perf_trace_context_common_lock_depth,
81 METH_VARARGS, "Get the common lock depth event field value."},
82 { NULL, NULL, 0, NULL}
83};
84
85PyMODINIT_FUNC initperf_trace_context(void)
86{
87 (void) Py_InitModule("perf_trace_context", ContextMethods);
88}
diff --git a/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Core.py b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Core.py
new file mode 100644
index 00000000000..de7211e4fa4
--- /dev/null
+++ b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Core.py
@@ -0,0 +1,121 @@
1# Core.py - Python extension for perf script, core functions
2#
3# Copyright (C) 2010 by Tom Zanussi <tzanussi@gmail.com>
4#
5# This software may be distributed under the terms of the GNU General
6# Public License ("GPL") version 2 as published by the Free Software
7# Foundation.
8
9from collections import defaultdict
10
11def autodict():
12 return defaultdict(autodict)
13
14flag_fields = autodict()
15symbolic_fields = autodict()
16
17def define_flag_field(event_name, field_name, delim):
18 flag_fields[event_name][field_name]['delim'] = delim
19
20def define_flag_value(event_name, field_name, value, field_str):
21 flag_fields[event_name][field_name]['values'][value] = field_str
22
23def define_symbolic_field(event_name, field_name):
24 # nothing to do, really
25 pass
26
27def define_symbolic_value(event_name, field_name, value, field_str):
28 symbolic_fields[event_name][field_name]['values'][value] = field_str
29
30def flag_str(event_name, field_name, value):
31 string = ""
32
33 if flag_fields[event_name][field_name]:
34 print_delim = 0
35 keys = flag_fields[event_name][field_name]['values'].keys()
36 keys.sort()
37 for idx in keys:
38 if not value and not idx:
39 string += flag_fields[event_name][field_name]['values'][idx]
40 break
41 if idx and (value & idx) == idx:
42 if print_delim and flag_fields[event_name][field_name]['delim']:
43 string += " " + flag_fields[event_name][field_name]['delim'] + " "
44 string += flag_fields[event_name][field_name]['values'][idx]
45 print_delim = 1
46 value &= ~idx
47
48 return string
49
50def symbol_str(event_name, field_name, value):
51 string = ""
52
53 if symbolic_fields[event_name][field_name]:
54 keys = symbolic_fields[event_name][field_name]['values'].keys()
55 keys.sort()
56 for idx in keys:
57 if not value and not idx:
58 string = symbolic_fields[event_name][field_name]['values'][idx]
59 break
60 if (value == idx):
61 string = symbolic_fields[event_name][field_name]['values'][idx]
62 break
63
64 return string
65
66trace_flags = { 0x00: "NONE", \
67 0x01: "IRQS_OFF", \
68 0x02: "IRQS_NOSUPPORT", \
69 0x04: "NEED_RESCHED", \
70 0x08: "HARDIRQ", \
71 0x10: "SOFTIRQ" }
72
73def trace_flag_str(value):
74 string = ""
75 print_delim = 0
76
77 keys = trace_flags.keys()
78
79 for idx in keys:
80 if not value and not idx:
81 string += "NONE"
82 break
83
84 if idx and (value & idx) == idx:
85 if print_delim:
86 string += " | ";
87 string += trace_flags[idx]
88 print_delim = 1
89 value &= ~idx
90
91 return string
92
93
94def taskState(state):
95 states = {
96 0 : "R",
97 1 : "S",
98 2 : "D",
99 64: "DEAD"
100 }
101
102 if state not in states:
103 return "Unknown"
104
105 return states[state]
106
107
108class EventHeaders:
109 def __init__(self, common_cpu, common_secs, common_nsecs,
110 common_pid, common_comm):
111 self.cpu = common_cpu
112 self.secs = common_secs
113 self.nsecs = common_nsecs
114 self.pid = common_pid
115 self.comm = common_comm
116
117 def ts(self):
118 return (self.secs * (10 ** 9)) + self.nsecs
119
120 def ts_format(self):
121 return "%d.%d" % (self.secs, int(self.nsecs / 1000))
diff --git a/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/SchedGui.py b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/SchedGui.py
new file mode 100644
index 00000000000..fdd92f69905
--- /dev/null
+++ b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/SchedGui.py
@@ -0,0 +1,184 @@
1# SchedGui.py - Python extension for perf script, basic GUI code for
2# traces drawing and overview.
3#
4# Copyright (C) 2010 by Frederic Weisbecker <fweisbec@gmail.com>
5#
6# This software is distributed under the terms of the GNU General
7# Public License ("GPL") version 2 as published by the Free Software
8# Foundation.
9
10
11try:
12 import wx
13except ImportError:
14 raise ImportError, "You need to install the wxpython lib for this script"
15
16
17class RootFrame(wx.Frame):
18 Y_OFFSET = 100
19 RECT_HEIGHT = 100
20 RECT_SPACE = 50
21 EVENT_MARKING_WIDTH = 5
22
23 def __init__(self, sched_tracer, title, parent = None, id = -1):
24 wx.Frame.__init__(self, parent, id, title)
25
26 (self.screen_width, self.screen_height) = wx.GetDisplaySize()
27 self.screen_width -= 10
28 self.screen_height -= 10
29 self.zoom = 0.5
30 self.scroll_scale = 20
31 self.sched_tracer = sched_tracer
32 self.sched_tracer.set_root_win(self)
33 (self.ts_start, self.ts_end) = sched_tracer.interval()
34 self.update_width_virtual()
35 self.nr_rects = sched_tracer.nr_rectangles() + 1
36 self.height_virtual = RootFrame.Y_OFFSET + (self.nr_rects * (RootFrame.RECT_HEIGHT + RootFrame.RECT_SPACE))
37
38 # whole window panel
39 self.panel = wx.Panel(self, size=(self.screen_width, self.screen_height))
40
41 # scrollable container
42 self.scroll = wx.ScrolledWindow(self.panel)
43 self.scroll.SetScrollbars(self.scroll_scale, self.scroll_scale, self.width_virtual / self.scroll_scale, self.height_virtual / self.scroll_scale)
44 self.scroll.EnableScrolling(True, True)
45 self.scroll.SetFocus()
46
47 # scrollable drawing area
48 self.scroll_panel = wx.Panel(self.scroll, size=(self.screen_width - 15, self.screen_height / 2))
49 self.scroll_panel.Bind(wx.EVT_PAINT, self.on_paint)
50 self.scroll_panel.Bind(wx.EVT_KEY_DOWN, self.on_key_press)
51 self.scroll_panel.Bind(wx.EVT_LEFT_DOWN, self.on_mouse_down)
52 self.scroll.Bind(wx.EVT_PAINT, self.on_paint)
53 self.scroll.Bind(wx.EVT_KEY_DOWN, self.on_key_press)
54 self.scroll.Bind(wx.EVT_LEFT_DOWN, self.on_mouse_down)
55
56 self.scroll.Fit()
57 self.Fit()
58
59 self.scroll_panel.SetDimensions(-1, -1, self.width_virtual, self.height_virtual, wx.SIZE_USE_EXISTING)
60
61 self.txt = None
62
63 self.Show(True)
64
65 def us_to_px(self, val):
66 return val / (10 ** 3) * self.zoom
67
68 def px_to_us(self, val):
69 return (val / self.zoom) * (10 ** 3)
70
71 def scroll_start(self):
72 (x, y) = self.scroll.GetViewStart()
73 return (x * self.scroll_scale, y * self.scroll_scale)
74
75 def scroll_start_us(self):
76 (x, y) = self.scroll_start()
77 return self.px_to_us(x)
78
79 def paint_rectangle_zone(self, nr, color, top_color, start, end):
80 offset_px = self.us_to_px(start - self.ts_start)
81 width_px = self.us_to_px(end - self.ts_start)
82
83 offset_py = RootFrame.Y_OFFSET + (nr * (RootFrame.RECT_HEIGHT + RootFrame.RECT_SPACE))
84 width_py = RootFrame.RECT_HEIGHT
85
86 dc = self.dc
87
88 if top_color is not None:
89 (r, g, b) = top_color
90 top_color = wx.Colour(r, g, b)
91 brush = wx.Brush(top_color, wx.SOLID)
92 dc.SetBrush(brush)
93 dc.DrawRectangle(offset_px, offset_py, width_px, RootFrame.EVENT_MARKING_WIDTH)
94 width_py -= RootFrame.EVENT_MARKING_WIDTH
95 offset_py += RootFrame.EVENT_MARKING_WIDTH
96
97 (r ,g, b) = color
98 color = wx.Colour(r, g, b)
99 brush = wx.Brush(color, wx.SOLID)
100 dc.SetBrush(brush)
101 dc.DrawRectangle(offset_px, offset_py, width_px, width_py)
102
103 def update_rectangles(self, dc, start, end):
104 start += self.ts_start
105 end += self.ts_start
106 self.sched_tracer.fill_zone(start, end)
107
108 def on_paint(self, event):
109 dc = wx.PaintDC(self.scroll_panel)
110 self.dc = dc
111
112 width = min(self.width_virtual, self.screen_width)
113 (x, y) = self.scroll_start()
114 start = self.px_to_us(x)
115 end = self.px_to_us(x + width)
116 self.update_rectangles(dc, start, end)
117
118 def rect_from_ypixel(self, y):
119 y -= RootFrame.Y_OFFSET
120 rect = y / (RootFrame.RECT_HEIGHT + RootFrame.RECT_SPACE)
121 height = y % (RootFrame.RECT_HEIGHT + RootFrame.RECT_SPACE)
122
123 if rect < 0 or rect > self.nr_rects - 1 or height > RootFrame.RECT_HEIGHT:
124 return -1
125
126 return rect
127
128 def update_summary(self, txt):
129 if self.txt:
130 self.txt.Destroy()
131 self.txt = wx.StaticText(self.panel, -1, txt, (0, (self.screen_height / 2) + 50))
132
133
134 def on_mouse_down(self, event):
135 (x, y) = event.GetPositionTuple()
136 rect = self.rect_from_ypixel(y)
137 if rect == -1:
138 return
139
140 t = self.px_to_us(x) + self.ts_start
141
142 self.sched_tracer.mouse_down(rect, t)
143
144
145 def update_width_virtual(self):
146 self.width_virtual = self.us_to_px(self.ts_end - self.ts_start)
147
148 def __zoom(self, x):
149 self.update_width_virtual()
150 (xpos, ypos) = self.scroll.GetViewStart()
151 xpos = self.us_to_px(x) / self.scroll_scale
152 self.scroll.SetScrollbars(self.scroll_scale, self.scroll_scale, self.width_virtual / self.scroll_scale, self.height_virtual / self.scroll_scale, xpos, ypos)
153 self.Refresh()
154
155 def zoom_in(self):
156 x = self.scroll_start_us()
157 self.zoom *= 2
158 self.__zoom(x)
159
160 def zoom_out(self):
161 x = self.scroll_start_us()
162 self.zoom /= 2
163 self.__zoom(x)
164
165
166 def on_key_press(self, event):
167 key = event.GetRawKeyCode()
168 if key == ord("+"):
169 self.zoom_in()
170 return
171 if key == ord("-"):
172 self.zoom_out()
173 return
174
175 key = event.GetKeyCode()
176 (x, y) = self.scroll.GetViewStart()
177 if key == wx.WXK_RIGHT:
178 self.scroll.Scroll(x + 1, y)
179 elif key == wx.WXK_LEFT:
180 self.scroll.Scroll(x - 1, y)
181 elif key == wx.WXK_DOWN:
182 self.scroll.Scroll(x, y + 1)
183 elif key == wx.WXK_UP:
184 self.scroll.Scroll(x, y - 1)
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
new file mode 100644
index 00000000000..15c8400240f
--- /dev/null
+++ b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py
@@ -0,0 +1,86 @@
1# Util.py - Python extension for perf script, miscellaneous utility code
2#
3# Copyright (C) 2010 by Tom Zanussi <tzanussi@gmail.com>
4#
5# This software may be distributed under the terms of the GNU General
6# Public License ("GPL") version 2 as published by the Free Software
7# Foundation.
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
17NSECS_PER_SEC = 1000000000
18
19def avg(total, n):
20 return total / n
21
22def nsecs(secs, nsecs):
23 return secs * NSECS_PER_SEC + nsecs
24
25def nsecs_secs(nsecs):
26 return nsecs / NSECS_PER_SEC
27
28def nsecs_nsecs(nsecs):
29 return nsecs % NSECS_PER_SEC
30
31def nsecs_str(nsecs):
32 str = "%5u.%09u" % (nsecs_secs(nsecs), nsecs_nsecs(nsecs)),
33 return str
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
47def clear_term():
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
new file mode 100644
index 00000000000..8104895a7b6
--- /dev/null
+++ b/tools/perf/scripts/python/bin/failed-syscalls-by-pid-record
@@ -0,0 +1,2 @@
1#!/bin/bash
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
new file mode 100644
index 00000000000..fda5096d0cb
--- /dev/null
+++ b/tools/perf/scripts/python/bin/failed-syscalls-by-pid-report
@@ -0,0 +1,10 @@
1#!/bin/bash
2# description: system-wide failed syscalls, by pid
3# args: [comm]
4if [ $# -gt 0 ] ; then
5 if ! expr match "$1" "-" > /dev/null ; then
6 comm=$1
7 shift
8 fi
9fi
10perf script $@ -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 00000000000..b1495c9a9b2
--- /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 00000000000..6c44271091a
--- /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 script $@ -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 00000000000..558754b840a
--- /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 00000000000..8f759291da8
--- /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 script -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
new file mode 100644
index 00000000000..7493fddbe99
--- /dev/null
+++ b/tools/perf/scripts/python/bin/sched-migration-record
@@ -0,0 +1,2 @@
1#!/bin/bash
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
new file mode 100644
index 00000000000..68b037a1849
--- /dev/null
+++ b/tools/perf/scripts/python/bin/sched-migration-report
@@ -0,0 +1,3 @@
1#!/bin/bash
2# description: sched migration overview
3perf script $@ -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
new file mode 100644
index 00000000000..4efbfaa7f6a
--- /dev/null
+++ b/tools/perf/scripts/python/bin/sctop-record
@@ -0,0 +1,2 @@
1#!/bin/bash
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
new file mode 100644
index 00000000000..c32db294124
--- /dev/null
+++ b/tools/perf/scripts/python/bin/sctop-report
@@ -0,0 +1,24 @@
1#!/bin/bash
2# description: syscall top
3# args: [comm] [interval]
4n_args=0
5for i in "$@"
6do
7 if expr match "$i" "-" > /dev/null ; then
8 break
9 fi
10 n_args=$(( $n_args + 1 ))
11done
12if [ "$n_args" -gt 2 ] ; then
13 echo "usage: sctop-report [comm] [interval]"
14 exit
15fi
16if [ "$n_args" -gt 1 ] ; then
17 comm=$1
18 interval=$2
19 shift 2
20elif [ "$n_args" -gt 0 ] ; then
21 interval=$1
22 shift
23fi
24perf script $@ -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
new file mode 100644
index 00000000000..4efbfaa7f6a
--- /dev/null
+++ b/tools/perf/scripts/python/bin/syscall-counts-by-pid-record
@@ -0,0 +1,2 @@
1#!/bin/bash
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
new file mode 100644
index 00000000000..16eb8d65c54
--- /dev/null
+++ b/tools/perf/scripts/python/bin/syscall-counts-by-pid-report
@@ -0,0 +1,10 @@
1#!/bin/bash
2# description: system-wide syscall counts, by pid
3# args: [comm]
4if [ $# -gt 0 ] ; then
5 if ! expr match "$1" "-" > /dev/null ; then
6 comm=$1
7 shift
8 fi
9fi
10perf script $@ -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
new file mode 100644
index 00000000000..4efbfaa7f6a
--- /dev/null
+++ b/tools/perf/scripts/python/bin/syscall-counts-record
@@ -0,0 +1,2 @@
1#!/bin/bash
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
new file mode 100644
index 00000000000..0f0e9d453bb
--- /dev/null
+++ b/tools/perf/scripts/python/bin/syscall-counts-report
@@ -0,0 +1,10 @@
1#!/bin/bash
2# description: system-wide syscall counts
3# args: [comm]
4if [ $# -gt 0 ] ; then
5 if ! expr match "$1" "-" > /dev/null ; then
6 comm=$1
7 shift
8 fi
9fi
10perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/syscall-counts.py $comm
diff --git a/tools/perf/scripts/python/check-perf-trace.py b/tools/perf/scripts/python/check-perf-trace.py
new file mode 100644
index 00000000000..4647a7694cf
--- /dev/null
+++ b/tools/perf/scripts/python/check-perf-trace.py
@@ -0,0 +1,82 @@
1# perf script event handlers, generated by perf script -g python
2# (c) 2010, Tom Zanussi <tzanussi@gmail.com>
3# Licensed under the terms of the GNU GPL License version 2
4#
5# This script tests basic functionality such as flag and symbol
6# strings, common_xxx() calls back into perf, begin, end, unhandled
7# events, etc. Basically, if this script runs successfully and
8# displays expected results, Python scripting support should be ok.
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 Core import *
17from perf_trace_context import *
18
19unhandled = autodict()
20
21def trace_begin():
22 print "trace_begin"
23 pass
24
25def trace_end():
26 print_unhandled()
27
28def irq__softirq_entry(event_name, context, common_cpu,
29 common_secs, common_nsecs, common_pid, common_comm,
30 vec):
31 print_header(event_name, common_cpu, common_secs, common_nsecs,
32 common_pid, common_comm)
33
34 print_uncommon(context)
35
36 print "vec=%s\n" % \
37 (symbol_str("irq__softirq_entry", "vec", vec)),
38
39def kmem__kmalloc(event_name, context, common_cpu,
40 common_secs, common_nsecs, common_pid, common_comm,
41 call_site, ptr, bytes_req, bytes_alloc,
42 gfp_flags):
43 print_header(event_name, common_cpu, common_secs, common_nsecs,
44 common_pid, common_comm)
45
46 print_uncommon(context)
47
48 print "call_site=%u, ptr=%u, bytes_req=%u, " \
49 "bytes_alloc=%u, gfp_flags=%s\n" % \
50 (call_site, ptr, bytes_req, bytes_alloc,
51
52 flag_str("kmem__kmalloc", "gfp_flags", gfp_flags)),
53
54def trace_unhandled(event_name, context, event_fields_dict):
55 try:
56 unhandled[event_name] += 1
57 except TypeError:
58 unhandled[event_name] = 1
59
60def print_header(event_name, cpu, secs, nsecs, pid, comm):
61 print "%-20s %5u %05u.%09u %8u %-20s " % \
62 (event_name, cpu, secs, nsecs, pid, comm),
63
64# print trace fields not included in handler args
65def print_uncommon(context):
66 print "common_preempt_count=%d, common_flags=%s, common_lock_depth=%d, " \
67 % (common_pc(context), trace_flag_str(common_flags(context)), \
68 common_lock_depth(context))
69
70def print_unhandled():
71 keys = unhandled.keys()
72 if not keys:
73 return
74
75 print "\nunhandled events:\n\n",
76
77 print "%-40s %10s\n" % ("event", "count"),
78 print "%-40s %10s\n" % ("----------------------------------------", \
79 "-----------"),
80
81 for event_name in keys:
82 print "%-40s %10d\n" % (event_name, unhandled[event_name])
diff --git a/tools/perf/scripts/python/failed-syscalls-by-pid.py b/tools/perf/scripts/python/failed-syscalls-by-pid.py
new file mode 100644
index 00000000000..85805fac411
--- /dev/null
+++ b/tools/perf/scripts/python/failed-syscalls-by-pid.py
@@ -0,0 +1,73 @@
1# failed system call counts, by pid
2# (c) 2010, Tom Zanussi <tzanussi@gmail.com>
3# Licensed under the terms of the GNU GPL License version 2
4#
5# Displays system-wide failed system call totals, broken down by pid.
6# If a [comm] arg is specified, only syscalls called by [comm] are displayed.
7
8import os
9import sys
10
11sys.path.append(os.environ['PERF_EXEC_PATH'] + \
12 '/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
13
14from perf_trace_context import *
15from Core import *
16from Util import *
17
18usage = "perf script -s syscall-counts-by-pid.py [comm|pid]\n";
19
20for_comm = None
21for_pid = None
22
23if len(sys.argv) > 2:
24 sys.exit(usage)
25
26if len(sys.argv) > 1:
27 try:
28 for_pid = int(sys.argv[1])
29 except:
30 for_comm = sys.argv[1]
31
32syscalls = autodict()
33
34def trace_begin():
35 print "Press control+C to stop and show the summary"
36
37def trace_end():
38 print_error_totals()
39
40def raw_syscalls__sys_exit(event_name, context, common_cpu,
41 common_secs, common_nsecs, common_pid, common_comm,
42 id, ret):
43 if (for_comm and common_comm != for_comm) or \
44 (for_pid and common_pid != for_pid ):
45 return
46
47 if ret < 0:
48 try:
49 syscalls[common_comm][common_pid][id][ret] += 1
50 except TypeError:
51 syscalls[common_comm][common_pid][id][ret] = 1
52
53def print_error_totals():
54 if for_comm is not None:
55 print "\nsyscall errors for %s:\n\n" % (for_comm),
56 else:
57 print "\nsyscall errors:\n\n",
58
59 print "%-30s %10s\n" % ("comm [pid]", "count"),
60 print "%-30s %10s\n" % ("------------------------------", \
61 "----------"),
62
63 comm_keys = syscalls.keys()
64 for comm in comm_keys:
65 pid_keys = syscalls[comm].keys()
66 for pid in pid_keys:
67 print "\n%s [%d]\n" % (comm, pid),
68 id_keys = syscalls[comm][pid].keys()
69 for id in id_keys:
70 print " syscall: %-16s\n" % syscall_name(id),
71 ret_keys = syscalls[comm][pid][id].keys()
72 for ret, val in sorted(syscalls[comm][pid][id].iteritems(), key = lambda(k, v): (v, k), reverse = True):
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 00000000000..11e70a388d4
--- /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 00000000000..9aa0a32972e
--- /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/sched-migration.py b/tools/perf/scripts/python/sched-migration.py
new file mode 100644
index 00000000000..74d55ec08ae
--- /dev/null
+++ b/tools/perf/scripts/python/sched-migration.py
@@ -0,0 +1,461 @@
1#!/usr/bin/python
2#
3# Cpu task migration overview toy
4#
5# Copyright (C) 2010 Frederic Weisbecker <fweisbec@gmail.com>
6#
7# perf script event handlers have been generated by perf script -g python
8#
9# This software is distributed under the terms of the GNU General
10# Public License ("GPL") version 2 as published by the Free Software
11# Foundation.
12
13
14import os
15import sys
16
17from collections import defaultdict
18from UserList import UserList
19
20sys.path.append(os.environ['PERF_EXEC_PATH'] + \
21 '/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
22sys.path.append('scripts/python/Perf-Trace-Util/lib/Perf/Trace')
23
24from perf_trace_context import *
25from Core import *
26from SchedGui import *
27
28
29threads = { 0 : "idle"}
30
31def thread_name(pid):
32 return "%s:%d" % (threads[pid], pid)
33
34class RunqueueEventUnknown:
35 @staticmethod
36 def color():
37 return None
38
39 def __repr__(self):
40 return "unknown"
41
42class RunqueueEventSleep:
43 @staticmethod
44 def color():
45 return (0, 0, 0xff)
46
47 def __init__(self, sleeper):
48 self.sleeper = sleeper
49
50 def __repr__(self):
51 return "%s gone to sleep" % thread_name(self.sleeper)
52
53class RunqueueEventWakeup:
54 @staticmethod
55 def color():
56 return (0xff, 0xff, 0)
57
58 def __init__(self, wakee):
59 self.wakee = wakee
60
61 def __repr__(self):
62 return "%s woke up" % thread_name(self.wakee)
63
64class RunqueueEventFork:
65 @staticmethod
66 def color():
67 return (0, 0xff, 0)
68
69 def __init__(self, child):
70 self.child = child
71
72 def __repr__(self):
73 return "new forked task %s" % thread_name(self.child)
74
75class RunqueueMigrateIn:
76 @staticmethod
77 def color():
78 return (0, 0xf0, 0xff)
79
80 def __init__(self, new):
81 self.new = new
82
83 def __repr__(self):
84 return "task migrated in %s" % thread_name(self.new)
85
86class RunqueueMigrateOut:
87 @staticmethod
88 def color():
89 return (0xff, 0, 0xff)
90
91 def __init__(self, old):
92 self.old = old
93
94 def __repr__(self):
95 return "task migrated out %s" % thread_name(self.old)
96
97class RunqueueSnapshot:
98 def __init__(self, tasks = [0], event = RunqueueEventUnknown()):
99 self.tasks = tuple(tasks)
100 self.event = event
101
102 def sched_switch(self, prev, prev_state, next):
103 event = RunqueueEventUnknown()
104
105 if taskState(prev_state) == "R" and next in self.tasks \
106 and prev in self.tasks:
107 return self
108
109 if taskState(prev_state) != "R":
110 event = RunqueueEventSleep(prev)
111
112 next_tasks = list(self.tasks[:])
113 if prev in self.tasks:
114 if taskState(prev_state) != "R":
115 next_tasks.remove(prev)
116 elif taskState(prev_state) == "R":
117 next_tasks.append(prev)
118
119 if next not in next_tasks:
120 next_tasks.append(next)
121
122 return RunqueueSnapshot(next_tasks, event)
123
124 def migrate_out(self, old):
125 if old not in self.tasks:
126 return self
127 next_tasks = [task for task in self.tasks if task != old]
128
129 return RunqueueSnapshot(next_tasks, RunqueueMigrateOut(old))
130
131 def __migrate_in(self, new, event):
132 if new in self.tasks:
133 self.event = event
134 return self
135 next_tasks = self.tasks[:] + tuple([new])
136
137 return RunqueueSnapshot(next_tasks, event)
138
139 def migrate_in(self, new):
140 return self.__migrate_in(new, RunqueueMigrateIn(new))
141
142 def wake_up(self, new):
143 return self.__migrate_in(new, RunqueueEventWakeup(new))
144
145 def wake_up_new(self, new):
146 return self.__migrate_in(new, RunqueueEventFork(new))
147
148 def load(self):
149 """ Provide the number of tasks on the runqueue.
150 Don't count idle"""
151 return len(self.tasks) - 1
152
153 def __repr__(self):
154 ret = self.tasks.__repr__()
155 ret += self.origin_tostring()
156
157 return ret
158
159class TimeSlice:
160 def __init__(self, start, prev):
161 self.start = start
162 self.prev = prev
163 self.end = start
164 # cpus that triggered the event
165 self.event_cpus = []
166 if prev is not None:
167 self.total_load = prev.total_load
168 self.rqs = prev.rqs.copy()
169 else:
170 self.rqs = defaultdict(RunqueueSnapshot)
171 self.total_load = 0
172
173 def __update_total_load(self, old_rq, new_rq):
174 diff = new_rq.load() - old_rq.load()
175 self.total_load += diff
176
177 def sched_switch(self, ts_list, prev, prev_state, next, cpu):
178 old_rq = self.prev.rqs[cpu]
179 new_rq = old_rq.sched_switch(prev, prev_state, next)
180
181 if old_rq is new_rq:
182 return
183
184 self.rqs[cpu] = new_rq
185 self.__update_total_load(old_rq, new_rq)
186 ts_list.append(self)
187 self.event_cpus = [cpu]
188
189 def migrate(self, ts_list, new, old_cpu, new_cpu):
190 if old_cpu == new_cpu:
191 return
192 old_rq = self.prev.rqs[old_cpu]
193 out_rq = old_rq.migrate_out(new)
194 self.rqs[old_cpu] = out_rq
195 self.__update_total_load(old_rq, out_rq)
196
197 new_rq = self.prev.rqs[new_cpu]
198 in_rq = new_rq.migrate_in(new)
199 self.rqs[new_cpu] = in_rq
200 self.__update_total_load(new_rq, in_rq)
201
202 ts_list.append(self)
203
204 if old_rq is not out_rq:
205 self.event_cpus.append(old_cpu)
206 self.event_cpus.append(new_cpu)
207
208 def wake_up(self, ts_list, pid, cpu, fork):
209 old_rq = self.prev.rqs[cpu]
210 if fork:
211 new_rq = old_rq.wake_up_new(pid)
212 else:
213 new_rq = old_rq.wake_up(pid)
214
215 if new_rq is old_rq:
216 return
217 self.rqs[cpu] = new_rq
218 self.__update_total_load(old_rq, new_rq)
219 ts_list.append(self)
220 self.event_cpus = [cpu]
221
222 def next(self, t):
223 self.end = t
224 return TimeSlice(t, self)
225
226class TimeSliceList(UserList):
227 def __init__(self, arg = []):
228 self.data = arg
229
230 def get_time_slice(self, ts):
231 if len(self.data) == 0:
232 slice = TimeSlice(ts, TimeSlice(-1, None))
233 else:
234 slice = self.data[-1].next(ts)
235 return slice
236
237 def find_time_slice(self, ts):
238 start = 0
239 end = len(self.data)
240 found = -1
241 searching = True
242 while searching:
243 if start == end or start == end - 1:
244 searching = False
245
246 i = (end + start) / 2
247 if self.data[i].start <= ts and self.data[i].end >= ts:
248 found = i
249 end = i
250 continue
251
252 if self.data[i].end < ts:
253 start = i
254
255 elif self.data[i].start > ts:
256 end = i
257
258 return found
259
260 def set_root_win(self, win):
261 self.root_win = win
262
263 def mouse_down(self, cpu, t):
264 idx = self.find_time_slice(t)
265 if idx == -1:
266 return
267
268 ts = self[idx]
269 rq = ts.rqs[cpu]
270 raw = "CPU: %d\n" % cpu
271 raw += "Last event : %s\n" % rq.event.__repr__()
272 raw += "Timestamp : %d.%06d\n" % (ts.start / (10 ** 9), (ts.start % (10 ** 9)) / 1000)
273 raw += "Duration : %6d us\n" % ((ts.end - ts.start) / (10 ** 6))
274 raw += "Load = %d\n" % rq.load()
275 for t in rq.tasks:
276 raw += "%s \n" % thread_name(t)
277
278 self.root_win.update_summary(raw)
279
280 def update_rectangle_cpu(self, slice, cpu):
281 rq = slice.rqs[cpu]
282
283 if slice.total_load != 0:
284 load_rate = rq.load() / float(slice.total_load)
285 else:
286 load_rate = 0
287
288 red_power = int(0xff - (0xff * load_rate))
289 color = (0xff, red_power, red_power)
290
291 top_color = None
292
293 if cpu in slice.event_cpus:
294 top_color = rq.event.color()
295
296 self.root_win.paint_rectangle_zone(cpu, color, top_color, slice.start, slice.end)
297
298 def fill_zone(self, start, end):
299 i = self.find_time_slice(start)
300 if i == -1:
301 return
302
303 for i in xrange(i, len(self.data)):
304 timeslice = self.data[i]
305 if timeslice.start > end:
306 return
307
308 for cpu in timeslice.rqs:
309 self.update_rectangle_cpu(timeslice, cpu)
310
311 def interval(self):
312 if len(self.data) == 0:
313 return (0, 0)
314
315 return (self.data[0].start, self.data[-1].end)
316
317 def nr_rectangles(self):
318 last_ts = self.data[-1]
319 max_cpu = 0
320 for cpu in last_ts.rqs:
321 if cpu > max_cpu:
322 max_cpu = cpu
323 return max_cpu
324
325
326class SchedEventProxy:
327 def __init__(self):
328 self.current_tsk = defaultdict(lambda : -1)
329 self.timeslices = TimeSliceList()
330
331 def sched_switch(self, headers, prev_comm, prev_pid, prev_prio, prev_state,
332 next_comm, next_pid, next_prio):
333 """ Ensure the task we sched out this cpu is really the one
334 we logged. Otherwise we may have missed traces """
335
336 on_cpu_task = self.current_tsk[headers.cpu]
337
338 if on_cpu_task != -1 and on_cpu_task != prev_pid:
339 print "Sched switch event rejected ts: %s cpu: %d prev: %s(%d) next: %s(%d)" % \
340 (headers.ts_format(), headers.cpu, prev_comm, prev_pid, next_comm, next_pid)
341
342 threads[prev_pid] = prev_comm
343 threads[next_pid] = next_comm
344 self.current_tsk[headers.cpu] = next_pid
345
346 ts = self.timeslices.get_time_slice(headers.ts())
347 ts.sched_switch(self.timeslices, prev_pid, prev_state, next_pid, headers.cpu)
348
349 def migrate(self, headers, pid, prio, orig_cpu, dest_cpu):
350 ts = self.timeslices.get_time_slice(headers.ts())
351 ts.migrate(self.timeslices, pid, orig_cpu, dest_cpu)
352
353 def wake_up(self, headers, comm, pid, success, target_cpu, fork):
354 if success == 0:
355 return
356 ts = self.timeslices.get_time_slice(headers.ts())
357 ts.wake_up(self.timeslices, pid, target_cpu, fork)
358
359
360def trace_begin():
361 global parser
362 parser = SchedEventProxy()
363
364def trace_end():
365 app = wx.App(False)
366 timeslices = parser.timeslices
367 frame = RootFrame(timeslices, "Migration")
368 app.MainLoop()
369
370def sched__sched_stat_runtime(event_name, context, common_cpu,
371 common_secs, common_nsecs, common_pid, common_comm,
372 comm, pid, runtime, vruntime):
373 pass
374
375def sched__sched_stat_iowait(event_name, context, common_cpu,
376 common_secs, common_nsecs, common_pid, common_comm,
377 comm, pid, delay):
378 pass
379
380def sched__sched_stat_sleep(event_name, context, common_cpu,
381 common_secs, common_nsecs, common_pid, common_comm,
382 comm, pid, delay):
383 pass
384
385def sched__sched_stat_wait(event_name, context, common_cpu,
386 common_secs, common_nsecs, common_pid, common_comm,
387 comm, pid, delay):
388 pass
389
390def sched__sched_process_fork(event_name, context, common_cpu,
391 common_secs, common_nsecs, common_pid, common_comm,
392 parent_comm, parent_pid, child_comm, child_pid):
393 pass
394
395def sched__sched_process_wait(event_name, context, common_cpu,
396 common_secs, common_nsecs, common_pid, common_comm,
397 comm, pid, prio):
398 pass
399
400def sched__sched_process_exit(event_name, context, common_cpu,
401 common_secs, common_nsecs, common_pid, common_comm,
402 comm, pid, prio):
403 pass
404
405def sched__sched_process_free(event_name, context, common_cpu,
406 common_secs, common_nsecs, common_pid, common_comm,
407 comm, pid, prio):
408 pass
409
410def sched__sched_migrate_task(event_name, context, common_cpu,
411 common_secs, common_nsecs, common_pid, common_comm,
412 comm, pid, prio, orig_cpu,
413 dest_cpu):
414 headers = EventHeaders(common_cpu, common_secs, common_nsecs,
415 common_pid, common_comm)
416 parser.migrate(headers, pid, prio, orig_cpu, dest_cpu)
417
418def sched__sched_switch(event_name, context, common_cpu,
419 common_secs, common_nsecs, common_pid, common_comm,
420 prev_comm, prev_pid, prev_prio, prev_state,
421 next_comm, next_pid, next_prio):
422
423 headers = EventHeaders(common_cpu, common_secs, common_nsecs,
424 common_pid, common_comm)
425 parser.sched_switch(headers, prev_comm, prev_pid, prev_prio, prev_state,
426 next_comm, next_pid, next_prio)
427
428def sched__sched_wakeup_new(event_name, context, common_cpu,
429 common_secs, common_nsecs, common_pid, common_comm,
430 comm, pid, prio, success,
431 target_cpu):
432 headers = EventHeaders(common_cpu, common_secs, common_nsecs,
433 common_pid, common_comm)
434 parser.wake_up(headers, comm, pid, success, target_cpu, 1)
435
436def sched__sched_wakeup(event_name, context, common_cpu,
437 common_secs, common_nsecs, common_pid, common_comm,
438 comm, pid, prio, success,
439 target_cpu):
440 headers = EventHeaders(common_cpu, common_secs, common_nsecs,
441 common_pid, common_comm)
442 parser.wake_up(headers, comm, pid, success, target_cpu, 0)
443
444def sched__sched_wait_task(event_name, context, common_cpu,
445 common_secs, common_nsecs, common_pid, common_comm,
446 comm, pid, prio):
447 pass
448
449def sched__sched_kthread_stop_ret(event_name, context, common_cpu,
450 common_secs, common_nsecs, common_pid, common_comm,
451 ret):
452 pass
453
454def sched__sched_kthread_stop(event_name, context, common_cpu,
455 common_secs, common_nsecs, common_pid, common_comm,
456 comm, pid):
457 pass
458
459def trace_unhandled(event_name, context, common_cpu, common_secs, common_nsecs,
460 common_pid, common_comm):
461 pass
diff --git a/tools/perf/scripts/python/sctop.py b/tools/perf/scripts/python/sctop.py
new file mode 100644
index 00000000000..42c267e292f
--- /dev/null
+++ b/tools/perf/scripts/python/sctop.py
@@ -0,0 +1,75 @@
1# system call top
2# (c) 2010, Tom Zanussi <tzanussi@gmail.com>
3# Licensed under the terms of the GNU GPL License version 2
4#
5# Periodically displays system-wide system call totals, broken down by
6# syscall. If a [comm] arg is specified, only syscalls called by
7# [comm] are displayed. If an [interval] arg is specified, the display
8# will be refreshed every [interval] seconds. The default interval is
9# 3 seconds.
10
11import os, sys, thread, time
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
20usage = "perf script -s sctop.py [comm] [interval]\n";
21
22for_comm = None
23default_interval = 3
24interval = default_interval
25
26if len(sys.argv) > 3:
27 sys.exit(usage)
28
29if len(sys.argv) > 2:
30 for_comm = sys.argv[1]
31 interval = int(sys.argv[2])
32elif len(sys.argv) > 1:
33 try:
34 interval = int(sys.argv[1])
35 except ValueError:
36 for_comm = sys.argv[1]
37 interval = default_interval
38
39syscalls = autodict()
40
41def trace_begin():
42 thread.start_new_thread(print_syscall_totals, (interval,))
43 pass
44
45def raw_syscalls__sys_enter(event_name, context, common_cpu,
46 common_secs, common_nsecs, common_pid, common_comm,
47 id, args):
48 if for_comm is not None:
49 if common_comm != for_comm:
50 return
51 try:
52 syscalls[id] += 1
53 except TypeError:
54 syscalls[id] = 1
55
56def print_syscall_totals(interval):
57 while 1:
58 clear_term()
59 if for_comm is not None:
60 print "\nsyscall events for %s:\n\n" % (for_comm),
61 else:
62 print "\nsyscall events:\n\n",
63
64 print "%-40s %10s\n" % ("event", "count"),
65 print "%-40s %10s\n" % ("----------------------------------------", \
66 "----------"),
67
68 for id, val in sorted(syscalls.iteritems(), key = lambda(k, v): (v, k), \
69 reverse = True):
70 try:
71 print "%-40s %10d\n" % (syscall_name(id), val),
72 except TypeError:
73 pass
74 syscalls.clear()
75 time.sleep(interval)
diff --git a/tools/perf/scripts/python/syscall-counts-by-pid.py b/tools/perf/scripts/python/syscall-counts-by-pid.py
new file mode 100644
index 00000000000..c64d1c55d74
--- /dev/null
+++ b/tools/perf/scripts/python/syscall-counts-by-pid.py
@@ -0,0 +1,69 @@
1# system call counts, by pid
2# (c) 2010, Tom Zanussi <tzanussi@gmail.com>
3# Licensed under the terms of the GNU GPL License version 2
4#
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.
7
8import os, sys
9
10sys.path.append(os.environ['PERF_EXEC_PATH'] + \
11 '/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
12
13from perf_trace_context import *
14from Core import *
15from Util import syscall_name
16
17usage = "perf script -s syscall-counts-by-pid.py [comm]\n";
18
19for_comm = None
20for_pid = None
21
22if len(sys.argv) > 2:
23 sys.exit(usage)
24
25if len(sys.argv) > 1:
26 try:
27 for_pid = int(sys.argv[1])
28 except:
29 for_comm = sys.argv[1]
30
31syscalls = autodict()
32
33def trace_begin():
34 print "Press control+C to stop and show the summary"
35
36def trace_end():
37 print_syscall_totals()
38
39def raw_syscalls__sys_enter(event_name, context, common_cpu,
40 common_secs, common_nsecs, common_pid, common_comm,
41 id, args):
42
43 if (for_comm and common_comm != for_comm) or \
44 (for_pid and common_pid != for_pid ):
45 return
46 try:
47 syscalls[common_comm][common_pid][id] += 1
48 except TypeError:
49 syscalls[common_comm][common_pid][id] = 1
50
51def print_syscall_totals():
52 if for_comm is not None:
53 print "\nsyscall events for %s:\n\n" % (for_comm),
54 else:
55 print "\nsyscall events by comm/pid:\n\n",
56
57 print "%-40s %10s\n" % ("comm [pid]/syscalls", "count"),
58 print "%-40s %10s\n" % ("----------------------------------------", \
59 "----------"),
60
61 comm_keys = syscalls.keys()
62 for comm in comm_keys:
63 pid_keys = syscalls[comm].keys()
64 for pid in pid_keys:
65 print "\n%s [%d]\n" % (comm, pid),
66 id_keys = syscalls[comm][pid].keys()
67 for id, val in sorted(syscalls[comm][pid].iteritems(), \
68 key = lambda(k, v): (v, k), reverse = True):
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
new file mode 100644
index 00000000000..b435d3f188e
--- /dev/null
+++ b/tools/perf/scripts/python/syscall-counts.py
@@ -0,0 +1,59 @@
1# system call counts
2# (c) 2010, Tom Zanussi <tzanussi@gmail.com>
3# Licensed under the terms of the GNU GPL License version 2
4#
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.
7
8import os
9import sys
10
11sys.path.append(os.environ['PERF_EXEC_PATH'] + \
12 '/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
13
14from perf_trace_context import *
15from Core import *
16from Util import syscall_name
17
18usage = "perf script -s syscall-counts.py [comm]\n";
19
20for_comm = None
21
22if len(sys.argv) > 2:
23 sys.exit(usage)
24
25if len(sys.argv) > 1:
26 for_comm = sys.argv[1]
27
28syscalls = autodict()
29
30def trace_begin():
31 print "Press control+C to stop and show the summary"
32
33def trace_end():
34 print_syscall_totals()
35
36def raw_syscalls__sys_enter(event_name, context, common_cpu,
37 common_secs, common_nsecs, common_pid, common_comm,
38 id, args):
39 if for_comm is not None:
40 if common_comm != for_comm:
41 return
42 try:
43 syscalls[id] += 1
44 except TypeError:
45 syscalls[id] = 1
46
47def print_syscall_totals():
48 if for_comm is not None:
49 print "\nsyscall events for %s:\n\n" % (for_comm),
50 else:
51 print "\nsyscall events:\n\n",
52
53 print "%-40s %10s\n" % ("event", "count"),
54 print "%-40s %10s\n" % ("----------------------------------------", \
55 "-----------"),
56
57 for id, val in sorted(syscalls.iteritems(), key = lambda(k, v): (v, k), \
58 reverse = True):
59 print "%-40s %10d\n" % (syscall_name(id), val),
diff --git a/tools/perf/util/PERF-VERSION-GEN b/tools/perf/util/PERF-VERSION-GEN
index c561d1538c0..97d76562a1a 100755
--- a/tools/perf/util/PERF-VERSION-GEN
+++ b/tools/perf/util/PERF-VERSION-GEN
@@ -1,17 +1,17 @@
1#!/bin/sh 1#!/bin/sh
2 2
3GVF=PERF-VERSION-FILE 3if [ $# -eq 1 ] ; then
4DEF_VER=v0.0.1.PERF 4 OUTPUT=$1
5fi
6
7GVF=${OUTPUT}PERF-VERSION-FILE
5 8
6LF=' 9LF='
7' 10'
8 11
9# First see if there is a version file (included in release tarballs), 12# First check if there is a .git to get the version from git describe
10# then try git-describe, then default. 13# otherwise try to get the version from the kernel makefile
11if test -f version 14if test -d ../../.git -o -f ../../.git &&
12then
13 VN=$(cat version) || VN="$DEF_VER"
14elif test -d .git -o -f .git &&
15 VN=$(git describe --abbrev=4 HEAD 2>/dev/null) && 15 VN=$(git describe --abbrev=4 HEAD 2>/dev/null) &&
16 case "$VN" in 16 case "$VN" in
17 *$LF*) (exit 1) ;; 17 *$LF*) (exit 1) ;;
@@ -23,7 +23,12 @@ elif test -d .git -o -f .git &&
23then 23then
24 VN=$(echo "$VN" | sed -e 's/-/./g'); 24 VN=$(echo "$VN" | sed -e 's/-/./g');
25else 25else
26 VN="$DEF_VER" 26 eval `grep '^VERSION\s*=' ../../Makefile|tr -d ' '`
27 eval `grep '^PATCHLEVEL\s*=' ../../Makefile|tr -d ' '`
28 eval `grep '^SUBLEVEL\s*=' ../../Makefile|tr -d ' '`
29 eval `grep '^EXTRAVERSION\s*=' ../../Makefile|tr -d ' '`
30
31 VN="${VERSION}.${PATCHLEVEL}.${SUBLEVEL}${EXTRAVERSION}"
27fi 32fi
28 33
29VN=$(expr "$VN" : v*'\(.*\)') 34VN=$(expr "$VN" : v*'\(.*\)')
diff --git a/tools/perf/util/abspath.c b/tools/perf/util/abspath.c
index 61d33b81fc9..0e76affe9c3 100644
--- a/tools/perf/util/abspath.c
+++ b/tools/perf/util/abspath.c
@@ -1,85 +1,5 @@
1#include "cache.h" 1#include "cache.h"
2 2
3/*
4 * Do not use this for inspecting *tracked* content. When path is a
5 * symlink to a directory, we do not want to say it is a directory when
6 * dealing with tracked content in the working tree.
7 */
8static int is_directory(const char *path)
9{
10 struct stat st;
11 return (!stat(path, &st) && S_ISDIR(st.st_mode));
12}
13
14/* We allow "recursive" symbolic links. Only within reason, though. */
15#define MAXDEPTH 5
16
17const char *make_absolute_path(const char *path)
18{
19 static char bufs[2][PATH_MAX + 1], *buf = bufs[0], *next_buf = bufs[1];
20 char cwd[1024] = "";
21 int buf_index = 1, len;
22
23 int depth = MAXDEPTH;
24 char *last_elem = NULL;
25 struct stat st;
26
27 if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX)
28 die ("Too long path: %.*s", 60, path);
29
30 while (depth--) {
31 if (!is_directory(buf)) {
32 char *last_slash = strrchr(buf, '/');
33 if (last_slash) {
34 *last_slash = '\0';
35 last_elem = xstrdup(last_slash + 1);
36 } else {
37 last_elem = xstrdup(buf);
38 *buf = '\0';
39 }
40 }
41
42 if (*buf) {
43 if (!*cwd && !getcwd(cwd, sizeof(cwd)))
44 die ("Could not get current working directory");
45
46 if (chdir(buf))
47 die ("Could not switch to '%s'", buf);
48 }
49 if (!getcwd(buf, PATH_MAX))
50 die ("Could not get current working directory");
51
52 if (last_elem) {
53 int len = strlen(buf);
54 if (len + strlen(last_elem) + 2 > PATH_MAX)
55 die ("Too long path name: '%s/%s'",
56 buf, last_elem);
57 buf[len] = '/';
58 strcpy(buf + len + 1, last_elem);
59 free(last_elem);
60 last_elem = NULL;
61 }
62
63 if (!lstat(buf, &st) && S_ISLNK(st.st_mode)) {
64 len = readlink(buf, next_buf, PATH_MAX);
65 if (len < 0)
66 die ("Invalid symlink: %s", buf);
67 if (PATH_MAX <= len)
68 die("symbolic link too long: %s", buf);
69 next_buf[len] = '\0';
70 buf = next_buf;
71 buf_index = 1 - buf_index;
72 next_buf = bufs[buf_index];
73 } else
74 break;
75 }
76
77 if (*cwd && chdir(cwd))
78 die ("Could not change back to '%s'", cwd);
79
80 return buf;
81}
82
83static const char *get_pwd_cwd(void) 3static const char *get_pwd_cwd(void)
84{ 4{
85 static char cwd[PATH_MAX + 1]; 5 static char cwd[PATH_MAX + 1];
diff --git a/tools/perf/util/alias.c b/tools/perf/util/alias.c
index 9b3dd2b428d..b8144e80bb1 100644
--- a/tools/perf/util/alias.c
+++ b/tools/perf/util/alias.c
@@ -3,7 +3,7 @@
3static const char *alias_key; 3static const char *alias_key;
4static char *alias_val; 4static char *alias_val;
5 5
6static int alias_lookup_cb(const char *k, const char *v, void *cb) 6static int alias_lookup_cb(const char *k, const char *v, void *cb __used)
7{ 7{
8 if (!prefixcmp(k, "alias.") && !strcmp(k+6, alias_key)) { 8 if (!prefixcmp(k, "alias.") && !strcmp(k+6, alias_key)) {
9 if (!v) 9 if (!v)
diff --git a/tools/perf/util/bitmap.c b/tools/perf/util/bitmap.c
new file mode 100644
index 00000000000..5e230acae1e
--- /dev/null
+++ b/tools/perf/util/bitmap.c
@@ -0,0 +1,21 @@
1/*
2 * From lib/bitmap.c
3 * Helper functions for bitmap.h.
4 *
5 * This source code is licensed under the GNU General Public License,
6 * Version 2. See the file COPYING for more details.
7 */
8#include <linux/bitmap.h>
9
10int __bitmap_weight(const unsigned long *bitmap, int bits)
11{
12 int k, w = 0, lim = bits/BITS_PER_LONG;
13
14 for (k = 0; k < lim; k++)
15 w += hweight_long(bitmap[k]);
16
17 if (bits % BITS_PER_LONG)
18 w += hweight_long(bitmap[k] & BITMAP_LAST_WORD_MASK(bits));
19
20 return w;
21}
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c
new file mode 100644
index 00000000000..deffb8c9607
--- /dev/null
+++ b/tools/perf/util/build-id.c
@@ -0,0 +1,80 @@
1/*
2 * build-id.c
3 *
4 * build-id support
5 *
6 * Copyright (C) 2009, 2010 Red Hat Inc.
7 * Copyright (C) 2009, 2010 Arnaldo Carvalho de Melo <acme@redhat.com>
8 */
9#include "util.h"
10#include <stdio.h>
11#include "build-id.h"
12#include "event.h"
13#include "symbol.h"
14#include <linux/kernel.h>
15#include "debug.h"
16
17static int build_id__mark_dso_hit(event_t *event,
18 struct sample_data *sample __used,
19 struct perf_session *session)
20{
21 struct addr_location al;
22 u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
23 struct thread *thread = perf_session__findnew(session, event->ip.pid);
24
25 if (thread == NULL) {
26 pr_err("problem processing %d event, skipping it.\n",
27 event->header.type);
28 return -1;
29 }
30
31 thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION,
32 event->ip.pid, event->ip.ip, &al);
33
34 if (al.map != NULL)
35 al.map->dso->hit = 1;
36
37 return 0;
38}
39
40static int event__exit_del_thread(event_t *self, struct sample_data *sample __used,
41 struct perf_session *session)
42{
43 struct thread *thread = perf_session__findnew(session, self->fork.tid);
44
45 dump_printf("(%d:%d):(%d:%d)\n", self->fork.pid, self->fork.tid,
46 self->fork.ppid, self->fork.ptid);
47
48 if (thread) {
49 rb_erase(&thread->rb_node, &session->threads);
50 session->last_match = NULL;
51 thread__delete(thread);
52 }
53
54 return 0;
55}
56
57struct perf_event_ops build_id__mark_dso_hit_ops = {
58 .sample = build_id__mark_dso_hit,
59 .mmap = event__process_mmap,
60 .fork = event__process_task,
61 .exit = event__exit_del_thread,
62};
63
64char *dso__build_id_filename(struct dso *self, char *bf, size_t size)
65{
66 char build_id_hex[BUILD_ID_SIZE * 2 + 1];
67
68 if (!self->has_build_id)
69 return NULL;
70
71 build_id__sprintf(self->build_id, sizeof(self->build_id), build_id_hex);
72 if (bf == NULL) {
73 if (asprintf(&bf, "%s/.build-id/%.2s/%s", buildid_dir,
74 build_id_hex, build_id_hex + 2) < 0)
75 return NULL;
76 } else
77 snprintf(bf, size, "%s/.build-id/%.2s/%s", buildid_dir,
78 build_id_hex, build_id_hex + 2);
79 return bf;
80}
diff --git a/tools/perf/util/build-id.h b/tools/perf/util/build-id.h
new file mode 100644
index 00000000000..5dafb00eaa0
--- /dev/null
+++ b/tools/perf/util/build-id.h
@@ -0,0 +1,10 @@
1#ifndef PERF_BUILD_ID_H_
2#define PERF_BUILD_ID_H_ 1
3
4#include "session.h"
5
6extern struct perf_event_ops build_id__mark_dso_hit_ops;
7
8char *dso__build_id_filename(struct dso *self, char *bf, size_t size);
9
10#endif
diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h
index 393d6146d13..a7729797fd9 100644
--- a/tools/perf/util/cache.h
+++ b/tools/perf/util/cache.h
@@ -1,61 +1,29 @@
1#ifndef CACHE_H 1#ifndef __PERF_CACHE_H
2#define CACHE_H 2#define __PERF_CACHE_H
3 3
4#include <stdbool.h>
4#include "util.h" 5#include "util.h"
5#include "strbuf.h" 6#include "strbuf.h"
7#include "../perf.h"
8
9#define CMD_EXEC_PATH "--exec-path"
10#define CMD_PERF_DIR "--perf-dir="
11#define CMD_WORK_TREE "--work-tree="
12#define CMD_DEBUGFS_DIR "--debugfs-dir="
6 13
7#define PERF_DIR_ENVIRONMENT "PERF_DIR" 14#define PERF_DIR_ENVIRONMENT "PERF_DIR"
8#define PERF_WORK_TREE_ENVIRONMENT "PERF_WORK_TREE" 15#define PERF_WORK_TREE_ENVIRONMENT "PERF_WORK_TREE"
9#define DEFAULT_PERF_DIR_ENVIRONMENT ".perf"
10#define DB_ENVIRONMENT "PERF_OBJECT_DIRECTORY"
11#define INDEX_ENVIRONMENT "PERF_INDEX_FILE"
12#define GRAFT_ENVIRONMENT "PERF_GRAFT_FILE"
13#define TEMPLATE_DIR_ENVIRONMENT "PERF_TEMPLATE_DIR"
14#define CONFIG_ENVIRONMENT "PERF_CONFIG"
15#define EXEC_PATH_ENVIRONMENT "PERF_EXEC_PATH" 16#define EXEC_PATH_ENVIRONMENT "PERF_EXEC_PATH"
16#define CEILING_DIRECTORIES_ENVIRONMENT "PERF_CEILING_DIRECTORIES" 17#define DEFAULT_PERF_DIR_ENVIRONMENT ".perf"
17#define PERFATTRIBUTES_FILE ".perfattributes" 18#define PERF_DEBUGFS_ENVIRONMENT "PERF_DEBUGFS_DIR"
18#define INFOATTRIBUTES_FILE "info/attributes"
19#define ATTRIBUTE_MACRO_PREFIX "[attr]"
20 19
21typedef int (*config_fn_t)(const char *, const char *, void *); 20typedef int (*config_fn_t)(const char *, const char *, void *);
22extern int perf_default_config(const char *, const char *, void *); 21extern int perf_default_config(const char *, const char *, void *);
23extern int perf_config_from_file(config_fn_t fn, const char *, void *);
24extern int perf_config(config_fn_t fn, void *); 22extern int perf_config(config_fn_t fn, void *);
25extern int perf_parse_ulong(const char *, unsigned long *);
26extern int perf_config_int(const char *, const char *); 23extern int perf_config_int(const char *, const char *);
27extern unsigned long perf_config_ulong(const char *, const char *);
28extern int perf_config_bool_or_int(const char *, const char *, int *);
29extern int perf_config_bool(const char *, const char *); 24extern int perf_config_bool(const char *, const char *);
30extern int perf_config_string(const char **, const char *, const char *);
31extern int perf_config_set(const char *, const char *);
32extern int perf_config_set_multivar(const char *, const char *, const char *, int);
33extern int perf_config_rename_section(const char *, const char *);
34extern const char *perf_etc_perfconfig(void);
35extern int check_repository_format_version(const char *var, const char *value, void *cb);
36extern int perf_config_system(void);
37extern int perf_config_global(void);
38extern int config_error_nonbool(const char *); 25extern int config_error_nonbool(const char *);
39extern const char *config_exclusive_filename; 26extern const char *perf_config_dirname(const char *, const char *);
40
41#define MAX_PERFNAME (1000)
42extern char perf_default_email[MAX_PERFNAME];
43extern char perf_default_name[MAX_PERFNAME];
44extern int user_ident_explicitly_given;
45
46extern const char *perf_log_output_encoding;
47extern const char *perf_mailmap_file;
48
49/* IO helper functions */
50extern void maybe_flush_or_die(FILE *, const char *);
51extern int copy_fd(int ifd, int ofd);
52extern int copy_file(const char *dst, const char *src, int mode);
53extern ssize_t read_in_full(int fd, void *buf, size_t count);
54extern ssize_t write_in_full(int fd, const void *buf, size_t count);
55extern void write_or_die(int fd, const void *buf, size_t count);
56extern int write_or_whine(int fd, const void *buf, size_t count, const char *msg);
57extern int write_or_whine_pipe(int fd, const void *buf, size_t count, const char *msg);
58extern void fsync_or_die(int fd, const char *);
59 27
60/* pager.c */ 28/* pager.c */
61extern void setup_pager(void); 29extern void setup_pager(void);
@@ -63,8 +31,18 @@ extern const char *pager_program;
63extern int pager_in_use(void); 31extern int pager_in_use(void);
64extern int pager_use_color; 32extern int pager_use_color;
65 33
66extern const char *editor_program; 34extern int use_browser;
67extern const char *excludes_file; 35
36#ifdef NO_NEWT_SUPPORT
37static inline void setup_browser(void)
38{
39 setup_pager();
40}
41static inline void exit_browser(bool wait_for_ok __used) {}
42#else
43void setup_browser(void);
44void exit_browser(bool wait_for_ok);
45#endif
68 46
69char *alias_lookup(const char *alias); 47char *alias_lookup(const char *alias);
70int split_cmdline(char *cmdline, const char ***argv); 48int split_cmdline(char *cmdline, const char ***argv);
@@ -95,25 +73,17 @@ static inline int is_absolute_path(const char *path)
95 return path[0] == '/'; 73 return path[0] == '/';
96} 74}
97 75
98const char *make_absolute_path(const char *path);
99const char *make_nonrelative_path(const char *path); 76const char *make_nonrelative_path(const char *path);
100const char *make_relative_path(const char *abs, const char *base);
101int normalize_path_copy(char *dst, const char *src);
102int longest_ancestor_length(const char *path, const char *prefix_list);
103char *strip_path_suffix(const char *path, const char *suffix); 77char *strip_path_suffix(const char *path, const char *suffix);
104 78
105extern char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2))); 79extern char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
106extern char *perf_path(const char *fmt, ...) __attribute__((format (printf, 1, 2))); 80extern char *perf_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
107/* perf_mkstemp() - create tmp file honoring TMPDIR variable */
108extern int perf_mkstemp(char *path, size_t len, const char *template);
109 81
110extern char *mksnpath(char *buf, size_t n, const char *fmt, ...)
111 __attribute__((format (printf, 3, 4)));
112extern char *perf_snpath(char *buf, size_t n, const char *fmt, ...)
113 __attribute__((format (printf, 3, 4)));
114extern char *perf_pathdup(const char *fmt, ...) 82extern char *perf_pathdup(const char *fmt, ...)
115 __attribute__((format (printf, 1, 2))); 83 __attribute__((format (printf, 1, 2)));
116 84
85#ifdef NO_STRLCPY
117extern 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
118 88
119#endif /* CACHE_H */ 89#endif /* __PERF_CACHE_H */
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c
new file mode 100644
index 00000000000..e12d539417b
--- /dev/null
+++ b/tools/perf/util/callchain.c
@@ -0,0 +1,464 @@
1/*
2 * Copyright (C) 2009-2010, Frederic Weisbecker <fweisbec@gmail.com>
3 *
4 * Handle the callchains from the stream in an ad-hoc radix tree and then
5 * sort them in an rbtree.
6 *
7 * Using a radix for code path provides a fast retrieval and factorizes
8 * memory use. Also that lets us use the paths in a hierarchical graph view.
9 *
10 */
11
12#include <stdlib.h>
13#include <stdio.h>
14#include <stdbool.h>
15#include <errno.h>
16#include <math.h>
17
18#include "util.h"
19#include "callchain.h"
20
21bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event)
22{
23 unsigned int chain_size = event->header.size;
24 chain_size -= (unsigned long)&event->ip.__more_data - (unsigned long)event;
25 return chain->nr * sizeof(u64) <= chain_size;
26}
27
28#define chain_for_each_child(child, parent) \
29 list_for_each_entry(child, &parent->children, brothers)
30
31#define chain_for_each_child_safe(child, next, parent) \
32 list_for_each_entry_safe(child, next, &parent->children, brothers)
33
34static void
35rb_insert_callchain(struct rb_root *root, struct callchain_node *chain,
36 enum chain_mode mode)
37{
38 struct rb_node **p = &root->rb_node;
39 struct rb_node *parent = NULL;
40 struct callchain_node *rnode;
41 u64 chain_cumul = cumul_hits(chain);
42
43 while (*p) {
44 u64 rnode_cumul;
45
46 parent = *p;
47 rnode = rb_entry(parent, struct callchain_node, rb_node);
48 rnode_cumul = cumul_hits(rnode);
49
50 switch (mode) {
51 case CHAIN_FLAT:
52 if (rnode->hit < chain->hit)
53 p = &(*p)->rb_left;
54 else
55 p = &(*p)->rb_right;
56 break;
57 case CHAIN_GRAPH_ABS: /* Falldown */
58 case CHAIN_GRAPH_REL:
59 if (rnode_cumul < chain_cumul)
60 p = &(*p)->rb_left;
61 else
62 p = &(*p)->rb_right;
63 break;
64 case CHAIN_NONE:
65 default:
66 break;
67 }
68 }
69
70 rb_link_node(&chain->rb_node, parent, p);
71 rb_insert_color(&chain->rb_node, root);
72}
73
74static void
75__sort_chain_flat(struct rb_root *rb_root, struct callchain_node *node,
76 u64 min_hit)
77{
78 struct callchain_node *child;
79
80 chain_for_each_child(child, node)
81 __sort_chain_flat(rb_root, child, min_hit);
82
83 if (node->hit && node->hit >= min_hit)
84 rb_insert_callchain(rb_root, node, CHAIN_FLAT);
85}
86
87/*
88 * Once we get every callchains from the stream, we can now
89 * sort them by hit
90 */
91static void
92sort_chain_flat(struct rb_root *rb_root, struct callchain_root *root,
93 u64 min_hit, struct callchain_param *param __used)
94{
95 __sort_chain_flat(rb_root, &root->node, min_hit);
96}
97
98static void __sort_chain_graph_abs(struct callchain_node *node,
99 u64 min_hit)
100{
101 struct callchain_node *child;
102
103 node->rb_root = RB_ROOT;
104
105 chain_for_each_child(child, node) {
106 __sort_chain_graph_abs(child, min_hit);
107 if (cumul_hits(child) >= min_hit)
108 rb_insert_callchain(&node->rb_root, child,
109 CHAIN_GRAPH_ABS);
110 }
111}
112
113static void
114sort_chain_graph_abs(struct rb_root *rb_root, struct callchain_root *chain_root,
115 u64 min_hit, struct callchain_param *param __used)
116{
117 __sort_chain_graph_abs(&chain_root->node, min_hit);
118 rb_root->rb_node = chain_root->node.rb_root.rb_node;
119}
120
121static void __sort_chain_graph_rel(struct callchain_node *node,
122 double min_percent)
123{
124 struct callchain_node *child;
125 u64 min_hit;
126
127 node->rb_root = RB_ROOT;
128 min_hit = ceil(node->children_hit * min_percent);
129
130 chain_for_each_child(child, node) {
131 __sort_chain_graph_rel(child, min_percent);
132 if (cumul_hits(child) >= min_hit)
133 rb_insert_callchain(&node->rb_root, child,
134 CHAIN_GRAPH_REL);
135 }
136}
137
138static void
139sort_chain_graph_rel(struct rb_root *rb_root, struct callchain_root *chain_root,
140 u64 min_hit __used, struct callchain_param *param)
141{
142 __sort_chain_graph_rel(&chain_root->node, param->min_percent / 100.0);
143 rb_root->rb_node = chain_root->node.rb_root.rb_node;
144}
145
146int register_callchain_param(struct callchain_param *param)
147{
148 switch (param->mode) {
149 case CHAIN_GRAPH_ABS:
150 param->sort = sort_chain_graph_abs;
151 break;
152 case CHAIN_GRAPH_REL:
153 param->sort = sort_chain_graph_rel;
154 break;
155 case CHAIN_FLAT:
156 param->sort = sort_chain_flat;
157 break;
158 case CHAIN_NONE:
159 default:
160 return -1;
161 }
162 return 0;
163}
164
165/*
166 * Create a child for a parent. If inherit_children, then the new child
167 * will become the new parent of it's parent children
168 */
169static struct callchain_node *
170create_child(struct callchain_node *parent, bool inherit_children)
171{
172 struct callchain_node *new;
173
174 new = zalloc(sizeof(*new));
175 if (!new) {
176 perror("not enough memory to create child for code path tree");
177 return NULL;
178 }
179 new->parent = parent;
180 INIT_LIST_HEAD(&new->children);
181 INIT_LIST_HEAD(&new->val);
182
183 if (inherit_children) {
184 struct callchain_node *next;
185
186 list_splice(&parent->children, &new->children);
187 INIT_LIST_HEAD(&parent->children);
188
189 chain_for_each_child(next, new)
190 next->parent = new;
191 }
192 list_add_tail(&new->brothers, &parent->children);
193
194 return new;
195}
196
197
198struct resolved_ip {
199 u64 ip;
200 struct map_symbol ms;
201};
202
203struct resolved_chain {
204 u64 nr;
205 struct resolved_ip ips[0];
206};
207
208
209/*
210 * Fill the node with callchain values
211 */
212static void
213fill_node(struct callchain_node *node, struct resolved_chain *chain, int start)
214{
215 unsigned int i;
216
217 for (i = start; i < chain->nr; i++) {
218 struct callchain_list *call;
219
220 call = zalloc(sizeof(*call));
221 if (!call) {
222 perror("not enough memory for the code path tree");
223 return;
224 }
225 call->ip = chain->ips[i].ip;
226 call->ms = chain->ips[i].ms;
227 list_add_tail(&call->list, &node->val);
228 }
229 node->val_nr = chain->nr - start;
230 if (!node->val_nr)
231 pr_warning("Warning: empty node in callchain tree\n");
232}
233
234static void
235add_child(struct callchain_node *parent, struct resolved_chain *chain,
236 int start, u64 period)
237{
238 struct callchain_node *new;
239
240 new = create_child(parent, false);
241 fill_node(new, chain, start);
242
243 new->children_hit = 0;
244 new->hit = period;
245}
246
247/*
248 * Split the parent in two parts (a new child is created) and
249 * give a part of its callchain to the created child.
250 * Then create another child to host the given callchain of new branch
251 */
252static void
253split_add_child(struct callchain_node *parent, struct resolved_chain *chain,
254 struct callchain_list *to_split, int idx_parents, int idx_local,
255 u64 period)
256{
257 struct callchain_node *new;
258 struct list_head *old_tail;
259 unsigned int idx_total = idx_parents + idx_local;
260
261 /* split */
262 new = create_child(parent, true);
263
264 /* split the callchain and move a part to the new child */
265 old_tail = parent->val.prev;
266 list_del_range(&to_split->list, old_tail);
267 new->val.next = &to_split->list;
268 new->val.prev = old_tail;
269 to_split->list.prev = &new->val;
270 old_tail->next = &new->val;
271
272 /* split the hits */
273 new->hit = parent->hit;
274 new->children_hit = parent->children_hit;
275 parent->children_hit = cumul_hits(new);
276 new->val_nr = parent->val_nr - idx_local;
277 parent->val_nr = idx_local;
278
279 /* create a new child for the new branch if any */
280 if (idx_total < chain->nr) {
281 parent->hit = 0;
282 add_child(parent, chain, idx_total, period);
283 parent->children_hit += period;
284 } else {
285 parent->hit = period;
286 }
287}
288
289static int
290append_chain(struct callchain_node *root, struct resolved_chain *chain,
291 unsigned int start, u64 period);
292
293static void
294append_chain_children(struct callchain_node *root, struct resolved_chain *chain,
295 unsigned int start, u64 period)
296{
297 struct callchain_node *rnode;
298
299 /* lookup in childrens */
300 chain_for_each_child(rnode, root) {
301 unsigned int ret = append_chain(rnode, chain, start, period);
302
303 if (!ret)
304 goto inc_children_hit;
305 }
306 /* nothing in children, add to the current node */
307 add_child(root, chain, start, period);
308
309inc_children_hit:
310 root->children_hit += period;
311}
312
313static int
314append_chain(struct callchain_node *root, struct resolved_chain *chain,
315 unsigned int start, u64 period)
316{
317 struct callchain_list *cnode;
318 unsigned int i = start;
319 bool found = false;
320
321 /*
322 * Lookup in the current node
323 * If we have a symbol, then compare the start to match
324 * anywhere inside a function.
325 */
326 list_for_each_entry(cnode, &root->val, list) {
327 struct symbol *sym;
328
329 if (i == chain->nr)
330 break;
331
332 sym = chain->ips[i].ms.sym;
333
334 if (cnode->ms.sym && sym) {
335 if (cnode->ms.sym->start != sym->start)
336 break;
337 } else if (cnode->ip != chain->ips[i].ip)
338 break;
339
340 if (!found)
341 found = true;
342 i++;
343 }
344
345 /* matches not, relay on the parent */
346 if (!found)
347 return -1;
348
349 /* we match only a part of the node. Split it and add the new chain */
350 if (i - start < root->val_nr) {
351 split_add_child(root, chain, cnode, start, i - start, period);
352 return 0;
353 }
354
355 /* we match 100% of the path, increment the hit */
356 if (i - start == root->val_nr && i == chain->nr) {
357 root->hit += period;
358 return 0;
359 }
360
361 /* We match the node and still have a part remaining */
362 append_chain_children(root, chain, i, period);
363
364 return 0;
365}
366
367static void filter_context(struct ip_callchain *old, struct resolved_chain *new,
368 struct map_symbol *syms)
369{
370 int i, j = 0;
371
372 for (i = 0; i < (int)old->nr; i++) {
373 if (old->ips[i] >= PERF_CONTEXT_MAX)
374 continue;
375
376 new->ips[j].ip = old->ips[i];
377 new->ips[j].ms = syms[i];
378 j++;
379 }
380
381 new->nr = j;
382}
383
384
385int callchain_append(struct callchain_root *root, struct ip_callchain *chain,
386 struct map_symbol *syms, u64 period)
387{
388 struct resolved_chain *filtered;
389
390 if (!chain->nr)
391 return 0;
392
393 filtered = zalloc(sizeof(*filtered) +
394 chain->nr * sizeof(struct resolved_ip));
395 if (!filtered)
396 return -ENOMEM;
397
398 filter_context(chain, filtered, syms);
399
400 if (!filtered->nr)
401 goto end;
402
403 append_chain_children(&root->node, filtered, 0, period);
404
405 if (filtered->nr > root->max_depth)
406 root->max_depth = filtered->nr;
407end:
408 free(filtered);
409
410 return 0;
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
new file mode 100644
index 00000000000..c15fb8c24ad
--- /dev/null
+++ b/tools/perf/util/callchain.h
@@ -0,0 +1,75 @@
1#ifndef __PERF_CALLCHAIN_H
2#define __PERF_CALLCHAIN_H
3
4#include "../perf.h"
5#include <linux/list.h>
6#include <linux/rbtree.h>
7#include "event.h"
8#include "symbol.h"
9
10enum chain_mode {
11 CHAIN_NONE,
12 CHAIN_FLAT,
13 CHAIN_GRAPH_ABS,
14 CHAIN_GRAPH_REL
15};
16
17struct callchain_node {
18 struct callchain_node *parent;
19 struct list_head brothers;
20 struct list_head children;
21 struct list_head val;
22 struct rb_node rb_node; /* to sort nodes in an rbtree */
23 struct rb_root rb_root; /* sorted tree of children */
24 unsigned int val_nr;
25 u64 hit;
26 u64 children_hit;
27};
28
29struct callchain_root {
30 u64 max_depth;
31 struct callchain_node node;
32};
33
34struct callchain_param;
35
36typedef void (*sort_chain_func_t)(struct rb_root *, struct callchain_root *,
37 u64, struct callchain_param *);
38
39struct callchain_param {
40 enum chain_mode mode;
41 u32 print_limit;
42 double min_percent;
43 sort_chain_func_t sort;
44};
45
46struct callchain_list {
47 u64 ip;
48 struct map_symbol ms;
49 struct list_head list;
50};
51
52static inline void callchain_init(struct callchain_root *root)
53{
54 INIT_LIST_HEAD(&root->node.brothers);
55 INIT_LIST_HEAD(&root->node.children);
56 INIT_LIST_HEAD(&root->node.val);
57
58 root->node.parent = NULL;
59 root->node.hit = 0;
60 root->node.children_hit = 0;
61 root->max_depth = 0;
62}
63
64static inline u64 cumul_hits(struct callchain_node *node)
65{
66 return node->hit + node->children_hit;
67}
68
69int register_callchain_param(struct callchain_param *param);
70int callchain_append(struct callchain_root *root, struct ip_callchain *chain,
71 struct map_symbol *syms, u64 period);
72int callchain_merge(struct callchain_root *dst, struct callchain_root *src);
73
74bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event);
75#endif /* __PERF_CALLCHAIN_H */
diff --git a/tools/perf/util/color.c b/tools/perf/util/color.c
index 9a8c20ccc53..e191eb9a667 100644
--- a/tools/perf/util/color.c
+++ b/tools/perf/util/color.c
@@ -11,7 +11,8 @@ static int parse_color(const char *name, int len)
11 }; 11 };
12 char *end; 12 char *end;
13 int i; 13 int i;
14 for (i = 0; i < ARRAY_SIZE(color_names); i++) { 14
15 for (i = 0; i < (int)ARRAY_SIZE(color_names); i++) {
15 const char *str = color_names[i]; 16 const char *str = color_names[i];
16 if (!strncasecmp(name, str, len) && !str[len]) 17 if (!strncasecmp(name, str, len) && !str[len])
17 return i - 1; 18 return i - 1;
@@ -28,7 +29,8 @@ static int parse_attr(const char *name, int len)
28 static const char * const attr_names[] = { 29 static const char * const attr_names[] = {
29 "bold", "dim", "ul", "blink", "reverse" 30 "bold", "dim", "ul", "blink", "reverse"
30 }; 31 };
31 int i; 32 unsigned int i;
33
32 for (i = 0; i < ARRAY_SIZE(attr_names); i++) { 34 for (i = 0; i < ARRAY_SIZE(attr_names); i++) {
33 const char *str = attr_names[i]; 35 const char *str = attr_names[i];
34 if (!strncasecmp(name, str, len) && !str[len]) 36 if (!strncasecmp(name, str, len) && !str[len])
@@ -164,7 +166,32 @@ int perf_color_default_config(const char *var, const char *value, void *cb)
164 return perf_default_config(var, value, cb); 166 return perf_default_config(var, value, cb);
165} 167}
166 168
167static int color_vfprintf(FILE *fp, const char *color, const char *fmt, 169static int __color_vsnprintf(char *bf, size_t size, const char *color,
170 const char *fmt, va_list args, const char *trail)
171{
172 int r = 0;
173
174 /*
175 * Auto-detect:
176 */
177 if (perf_use_color_default < 0) {
178 if (isatty(1) || pager_in_use())
179 perf_use_color_default = 1;
180 else
181 perf_use_color_default = 0;
182 }
183
184 if (perf_use_color_default && *color)
185 r += snprintf(bf, size, "%s", color);
186 r += vsnprintf(bf + r, size - r, fmt, args);
187 if (perf_use_color_default && *color)
188 r += snprintf(bf + r, size - r, "%s", PERF_COLOR_RESET);
189 if (trail)
190 r += snprintf(bf + r, size - r, "%s", trail);
191 return r;
192}
193
194static int __color_vfprintf(FILE *fp, const char *color, const char *fmt,
168 va_list args, const char *trail) 195 va_list args, const char *trail)
169{ 196{
170 int r = 0; 197 int r = 0;
@@ -189,7 +216,28 @@ static int color_vfprintf(FILE *fp, const char *color, const char *fmt,
189 return r; 216 return r;
190} 217}
191 218
219int color_vsnprintf(char *bf, size_t size, const char *color,
220 const char *fmt, va_list args)
221{
222 return __color_vsnprintf(bf, size, color, fmt, args, NULL);
223}
224
225int color_vfprintf(FILE *fp, const char *color, const char *fmt, va_list args)
226{
227 return __color_vfprintf(fp, color, fmt, args, NULL);
228}
229
230int color_snprintf(char *bf, size_t size, const char *color,
231 const char *fmt, ...)
232{
233 va_list args;
234 int r;
192 235
236 va_start(args, fmt);
237 r = color_vsnprintf(bf, size, color, fmt, args);
238 va_end(args);
239 return r;
240}
193 241
194int color_fprintf(FILE *fp, const char *color, const char *fmt, ...) 242int color_fprintf(FILE *fp, const char *color, const char *fmt, ...)
195{ 243{
@@ -197,7 +245,7 @@ int color_fprintf(FILE *fp, const char *color, const char *fmt, ...)
197 int r; 245 int r;
198 246
199 va_start(args, fmt); 247 va_start(args, fmt);
200 r = color_vfprintf(fp, color, fmt, args, NULL); 248 r = color_vfprintf(fp, color, fmt, args);
201 va_end(args); 249 va_end(args);
202 return r; 250 return r;
203} 251}
@@ -207,7 +255,7 @@ int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...)
207 va_list args; 255 va_list args;
208 int r; 256 int r;
209 va_start(args, fmt); 257 va_start(args, fmt);
210 r = color_vfprintf(fp, color, fmt, args, "\n"); 258 r = __color_vfprintf(fp, color, fmt, args, "\n");
211 va_end(args); 259 va_end(args);
212 return r; 260 return r;
213} 261}
@@ -222,10 +270,12 @@ int color_fwrite_lines(FILE *fp, const char *color,
222{ 270{
223 if (!*color) 271 if (!*color)
224 return fwrite(buf, count, 1, fp) != 1; 272 return fwrite(buf, count, 1, fp) != 1;
273
225 while (count) { 274 while (count) {
226 char *p = memchr(buf, '\n', count); 275 char *p = memchr(buf, '\n', count);
276
227 if (p != buf && (fputs(color, fp) < 0 || 277 if (p != buf && (fputs(color, fp) < 0 ||
228 fwrite(buf, p ? p - buf : count, 1, fp) != 1 || 278 fwrite(buf, p ? (size_t)(p - buf) : count, 1, fp) != 1 ||
229 fputs(PERF_COLOR_RESET, fp) < 0)) 279 fputs(PERF_COLOR_RESET, fp) < 0))
230 return -1; 280 return -1;
231 if (!p) 281 if (!p)
@@ -238,4 +288,37 @@ int color_fwrite_lines(FILE *fp, const char *color,
238 return 0; 288 return 0;
239} 289}
240 290
291const char *get_percent_color(double percent)
292{
293 const char *color = PERF_COLOR_NORMAL;
294
295 /*
296 * We color high-overhead entries in red, mid-overhead
297 * entries in green - and keep the low overhead places
298 * normal:
299 */
300 if (percent >= MIN_RED)
301 color = PERF_COLOR_RED;
302 else {
303 if (percent > MIN_GREEN)
304 color = PERF_COLOR_GREEN;
305 }
306 return color;
307}
308
309int percent_color_fprintf(FILE *fp, const char *fmt, double percent)
310{
311 int r;
312 const char *color;
241 313
314 color = get_percent_color(percent);
315 r = color_fprintf(fp, color, fmt, percent);
316
317 return r;
318}
319
320int percent_color_snprintf(char *bf, size_t size, const char *fmt, double percent)
321{
322 const char *color = get_percent_color(percent);
323 return color_snprintf(bf, size, color, fmt, percent);
324}
diff --git a/tools/perf/util/color.h b/tools/perf/util/color.h
index 5abfd379582..dea082b7960 100644
--- a/tools/perf/util/color.h
+++ b/tools/perf/util/color.h
@@ -1,5 +1,5 @@
1#ifndef COLOR_H 1#ifndef __PERF_COLOR_H
2#define COLOR_H 2#define __PERF_COLOR_H
3 3
4/* "\033[1;38;5;2xx;48;5;2xxm\0" is 23 bytes */ 4/* "\033[1;38;5;2xx;48;5;2xxm\0" is 23 bytes */
5#define COLOR_MAXLEN 24 5#define COLOR_MAXLEN 24
@@ -15,6 +15,9 @@
15#define PERF_COLOR_CYAN "\033[36m" 15#define PERF_COLOR_CYAN "\033[36m"
16#define PERF_COLOR_BG_RED "\033[41m" 16#define PERF_COLOR_BG_RED "\033[41m"
17 17
18#define MIN_GREEN 0.5
19#define MIN_RED 5.0
20
18/* 21/*
19 * This variable stores the value of color.ui 22 * This variable stores the value of color.ui
20 */ 23 */
@@ -29,8 +32,15 @@ int perf_color_default_config(const char *var, const char *value, void *cb);
29int perf_config_colorbool(const char *var, const char *value, int stdout_is_tty); 32int perf_config_colorbool(const char *var, const char *value, int stdout_is_tty);
30void color_parse(const char *value, const char *var, char *dst); 33void color_parse(const char *value, const char *var, char *dst);
31void color_parse_mem(const char *value, int len, const char *var, char *dst); 34void color_parse_mem(const char *value, int len, const char *var, char *dst);
35int color_vsnprintf(char *bf, size_t size, const char *color,
36 const char *fmt, va_list args);
37int color_vfprintf(FILE *fp, const char *color, const char *fmt, va_list args);
32int color_fprintf(FILE *fp, const char *color, const char *fmt, ...); 38int color_fprintf(FILE *fp, const char *color, const char *fmt, ...);
39int color_snprintf(char *bf, size_t size, const char *color, const char *fmt, ...);
33int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...); 40int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...);
34int color_fwrite_lines(FILE *fp, const char *color, size_t count, const char *buf); 41int color_fwrite_lines(FILE *fp, const char *color, size_t count, const char *buf);
42int percent_color_snprintf(char *bf, size_t size, const char *fmt, double percent);
43int percent_color_fprintf(FILE *fp, const char *fmt, double percent);
44const char *get_percent_color(double percent);
35 45
36#endif /* COLOR_H */ 46#endif /* __PERF_COLOR_H */
diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c
index 3dd13faa6a2..e02d78cae70 100644
--- a/tools/perf/util/config.c
+++ b/tools/perf/util/config.c
@@ -11,12 +11,17 @@
11 11
12#define MAXNAME (256) 12#define MAXNAME (256)
13 13
14#define DEBUG_CACHE_DIR ".debug"
15
16
17char buildid_dir[MAXPATHLEN]; /* root dir for buildid, binary cache */
18
14static FILE *config_file; 19static FILE *config_file;
15static const char *config_file_name; 20static const char *config_file_name;
16static int config_linenr; 21static int config_linenr;
17static int config_file_eof; 22static int config_file_eof;
18 23
19const char *config_exclusive_filename = NULL; 24static const char *config_exclusive_filename;
20 25
21static int get_next_char(void) 26static int get_next_char(void)
22{ 27{
@@ -47,10 +52,12 @@ static int get_next_char(void)
47static char *parse_value(void) 52static char *parse_value(void)
48{ 53{
49 static char value[1024]; 54 static char value[1024];
50 int quote = 0, comment = 0, len = 0, space = 0; 55 int quote = 0, comment = 0, space = 0;
56 size_t len = 0;
51 57
52 for (;;) { 58 for (;;) {
53 int c = get_next_char(); 59 int c = get_next_char();
60
54 if (len >= sizeof(value) - 1) 61 if (len >= sizeof(value) - 1)
55 return NULL; 62 return NULL;
56 if (c == '\n') { 63 if (c == '\n') {
@@ -125,7 +132,7 @@ static int get_value(config_fn_t fn, void *data, char *name, unsigned int len)
125 break; 132 break;
126 if (!iskeychar(c)) 133 if (!iskeychar(c))
127 break; 134 break;
128 name[len++] = tolower(c); 135 name[len++] = c;
129 if (len >= MAXNAME) 136 if (len >= MAXNAME)
130 return -1; 137 return -1;
131 } 138 }
@@ -158,17 +165,18 @@ static int get_extended_base_var(char *name, int baselen, int c)
158 name[baselen++] = '.'; 165 name[baselen++] = '.';
159 166
160 for (;;) { 167 for (;;) {
161 int c = get_next_char(); 168 int ch = get_next_char();
162 if (c == '\n') 169
170 if (ch == '\n')
163 return -1; 171 return -1;
164 if (c == '"') 172 if (ch == '"')
165 break; 173 break;
166 if (c == '\\') { 174 if (ch == '\\') {
167 c = get_next_char(); 175 ch = get_next_char();
168 if (c == '\n') 176 if (ch == '\n')
169 return -1; 177 return -1;
170 } 178 }
171 name[baselen++] = c; 179 name[baselen++] = ch;
172 if (baselen > MAXNAME / 2) 180 if (baselen > MAXNAME / 2)
173 return -1; 181 return -1;
174 } 182 }
@@ -288,19 +296,6 @@ static int perf_parse_long(const char *value, long *ret)
288 return 0; 296 return 0;
289} 297}
290 298
291int perf_parse_ulong(const char *value, unsigned long *ret)
292{
293 if (value && *value) {
294 char *end;
295 unsigned long val = strtoul(value, &end, 0);
296 if (!parse_unit_factor(end, &val))
297 return 0;
298 *ret = val;
299 return 1;
300 }
301 return 0;
302}
303
304static void die_bad_config(const char *name) 299static void die_bad_config(const char *name)
305{ 300{
306 if (config_file_name) 301 if (config_file_name)
@@ -316,15 +311,7 @@ int perf_config_int(const char *name, const char *value)
316 return ret; 311 return ret;
317} 312}
318 313
319unsigned long perf_config_ulong(const char *name, const char *value) 314static int perf_config_bool_or_int(const char *name, const char *value, int *is_bool)
320{
321 unsigned long ret;
322 if (!perf_parse_ulong(value, &ret))
323 die_bad_config(name);
324 return ret;
325}
326
327int perf_config_bool_or_int(const char *name, const char *value, int *is_bool)
328{ 315{
329 *is_bool = 1; 316 *is_bool = 1;
330 if (!value) 317 if (!value)
@@ -345,21 +332,20 @@ int perf_config_bool(const char *name, const char *value)
345 return !!perf_config_bool_or_int(name, value, &discard); 332 return !!perf_config_bool_or_int(name, value, &discard);
346} 333}
347 334
348int perf_config_string(const char **dest, const char *var, const char *value) 335const char *perf_config_dirname(const char *name, const char *value)
349{ 336{
350 if (!value) 337 if (!name)
351 return config_error_nonbool(var); 338 return NULL;
352 *dest = strdup(value); 339 return value;
353 return 0;
354} 340}
355 341
356static int perf_default_core_config(const char *var, const char *value) 342static int perf_default_core_config(const char *var __used, const char *value __used)
357{ 343{
358 /* Add other config variables here and to Documentation/config.txt. */ 344 /* Add other config variables here and to Documentation/config.txt. */
359 return 0; 345 return 0;
360} 346}
361 347
362int perf_default_config(const char *var, const char *value, void *dummy) 348int perf_default_config(const char *var, const char *value, void *dummy __used)
363{ 349{
364 if (!prefixcmp(var, "core.")) 350 if (!prefixcmp(var, "core."))
365 return perf_default_core_config(var, value); 351 return perf_default_core_config(var, value);
@@ -368,7 +354,7 @@ int perf_default_config(const char *var, const char *value, void *dummy)
368 return 0; 354 return 0;
369} 355}
370 356
371int perf_config_from_file(config_fn_t fn, const char *filename, void *data) 357static int perf_config_from_file(config_fn_t fn, const char *filename, void *data)
372{ 358{
373 int ret; 359 int ret;
374 FILE *f = fopen(filename, "r"); 360 FILE *f = fopen(filename, "r");
@@ -386,7 +372,7 @@ int perf_config_from_file(config_fn_t fn, const char *filename, void *data)
386 return ret; 372 return ret;
387} 373}
388 374
389const char *perf_etc_perfconfig(void) 375static const char *perf_etc_perfconfig(void)
390{ 376{
391 static const char *system_wide; 377 static const char *system_wide;
392 if (!system_wide) 378 if (!system_wide)
@@ -400,12 +386,12 @@ static int perf_env_bool(const char *k, int def)
400 return v ? perf_config_bool(k, v) : def; 386 return v ? perf_config_bool(k, v) : def;
401} 387}
402 388
403int perf_config_system(void) 389static int perf_config_system(void)
404{ 390{
405 return !perf_env_bool("PERF_CONFIG_NOSYSTEM", 0); 391 return !perf_env_bool("PERF_CONFIG_NOSYSTEM", 0);
406} 392}
407 393
408int perf_config_global(void) 394static int perf_config_global(void)
409{ 395{
410 return !perf_env_bool("PERF_CONFIG_NOGLOBAL", 0); 396 return !perf_env_bool("PERF_CONFIG_NOGLOBAL", 0);
411} 397}
@@ -447,427 +433,60 @@ int perf_config(config_fn_t fn, void *data)
447} 433}
448 434
449/* 435/*
450 * Find all the stuff for perf_config_set() below. 436 * Call this to report error for your variable that should not
437 * get a boolean value (i.e. "[my] var" means "true").
451 */ 438 */
452 439int config_error_nonbool(const char *var)
453#define MAX_MATCHES 512
454
455static struct {
456 int baselen;
457 char* key;
458 int do_not_match;
459 regex_t* value_regex;
460 int multi_replace;
461 size_t offset[MAX_MATCHES];
462 enum { START, SECTION_SEEN, SECTION_END_SEEN, KEY_SEEN } state;
463 int seen;
464} store;
465
466static int matches(const char* key, const char* value)
467{
468 return !strcmp(key, store.key) &&
469 (store.value_regex == NULL ||
470 (store.do_not_match ^
471 !regexec(store.value_regex, value, 0, NULL, 0)));
472}
473
474static int store_aux(const char* key, const char* value, void *cb)
475{ 440{
476 const char *ep; 441 return error("Missing value for '%s'", var);
477 size_t section_len;
478
479 switch (store.state) {
480 case KEY_SEEN:
481 if (matches(key, value)) {
482 if (store.seen == 1 && store.multi_replace == 0) {
483 warning("%s has multiple values", key);
484 } else if (store.seen >= MAX_MATCHES) {
485 error("too many matches for %s", key);
486 return 1;
487 }
488
489 store.offset[store.seen] = ftell(config_file);
490 store.seen++;
491 }
492 break;
493 case SECTION_SEEN:
494 /*
495 * What we are looking for is in store.key (both
496 * section and var), and its section part is baselen
497 * long. We found key (again, both section and var).
498 * We would want to know if this key is in the same
499 * section as what we are looking for. We already
500 * know we are in the same section as what should
501 * hold store.key.
502 */
503 ep = strrchr(key, '.');
504 section_len = ep - key;
505
506 if ((section_len != store.baselen) ||
507 memcmp(key, store.key, section_len+1)) {
508 store.state = SECTION_END_SEEN;
509 break;
510 }
511
512 /*
513 * Do not increment matches: this is no match, but we
514 * just made sure we are in the desired section.
515 */
516 store.offset[store.seen] = ftell(config_file);
517 /* fallthru */
518 case SECTION_END_SEEN:
519 case START:
520 if (matches(key, value)) {
521 store.offset[store.seen] = ftell(config_file);
522 store.state = KEY_SEEN;
523 store.seen++;
524 } else {
525 if (strrchr(key, '.') - key == store.baselen &&
526 !strncmp(key, store.key, store.baselen)) {
527 store.state = SECTION_SEEN;
528 store.offset[store.seen] = ftell(config_file);
529 }
530 }
531 }
532 return 0;
533} 442}
534 443
535static int store_write_section(int fd, const char* key) 444struct buildid_dir_config {
536{ 445 char *dir;
537 const char *dot; 446};
538 int i, success;
539 struct strbuf sb = STRBUF_INIT;
540
541 dot = memchr(key, '.', store.baselen);
542 if (dot) {
543 strbuf_addf(&sb, "[%.*s \"", (int)(dot - key), key);
544 for (i = dot - key + 1; i < store.baselen; i++) {
545 if (key[i] == '"' || key[i] == '\\')
546 strbuf_addch(&sb, '\\');
547 strbuf_addch(&sb, key[i]);
548 }
549 strbuf_addstr(&sb, "\"]\n");
550 } else {
551 strbuf_addf(&sb, "[%.*s]\n", store.baselen, key);
552 }
553
554 success = write_in_full(fd, sb.buf, sb.len) == sb.len;
555 strbuf_release(&sb);
556
557 return success;
558}
559 447
560static int store_write_pair(int fd, const char* key, const char* value) 448static int buildid_dir_command_config(const char *var, const char *value,
449 void *data)
561{ 450{
562 int i, success; 451 struct buildid_dir_config *c = data;
563 int length = strlen(key + store.baselen + 1); 452 const char *v;
564 const char *quote = "";
565 struct strbuf sb = STRBUF_INIT;
566
567 /*
568 * Check to see if the value needs to be surrounded with a dq pair.
569 * Note that problematic characters are always backslash-quoted; this
570 * check is about not losing leading or trailing SP and strings that
571 * follow beginning-of-comment characters (i.e. ';' and '#') by the
572 * configuration parser.
573 */
574 if (value[0] == ' ')
575 quote = "\"";
576 for (i = 0; value[i]; i++)
577 if (value[i] == ';' || value[i] == '#')
578 quote = "\"";
579 if (i && value[i - 1] == ' ')
580 quote = "\"";
581
582 strbuf_addf(&sb, "\t%.*s = %s",
583 length, key + store.baselen + 1, quote);
584
585 for (i = 0; value[i]; i++)
586 switch (value[i]) {
587 case '\n':
588 strbuf_addstr(&sb, "\\n");
589 break;
590 case '\t':
591 strbuf_addstr(&sb, "\\t");
592 break;
593 case '"':
594 case '\\':
595 strbuf_addch(&sb, '\\');
596 default:
597 strbuf_addch(&sb, value[i]);
598 break;
599 }
600 strbuf_addf(&sb, "%s\n", quote);
601 453
602 success = write_in_full(fd, sb.buf, sb.len) == sb.len; 454 /* same dir for all commands */
603 strbuf_release(&sb); 455 if (!prefixcmp(var, "buildid.") && !strcmp(var + 8, "dir")) {
604 456 v = perf_config_dirname(var, value);
605 return success; 457 if (!v)
606} 458 return -1;
607 459 strncpy(c->dir, v, MAXPATHLEN-1);
608static ssize_t find_beginning_of_line(const char* contents, size_t size, 460 c->dir[MAXPATHLEN-1] = '\0';
609 size_t offset_, int* found_bracket)
610{
611 size_t equal_offset = size, bracket_offset = size;
612 ssize_t offset;
613
614contline:
615 for (offset = offset_-2; offset > 0
616 && contents[offset] != '\n'; offset--)
617 switch (contents[offset]) {
618 case '=': equal_offset = offset; break;
619 case ']': bracket_offset = offset; break;
620 }
621 if (offset > 0 && contents[offset-1] == '\\') {
622 offset_ = offset;
623 goto contline;
624 } 461 }
625 if (bracket_offset < equal_offset) { 462 return 0;
626 *found_bracket = 1;
627 offset = bracket_offset+1;
628 } else
629 offset++;
630
631 return offset;
632} 463}
633 464
634int perf_config_set(const char* key, const char* value) 465static void check_buildid_dir_config(void)
635{ 466{
636 return perf_config_set_multivar(key, value, NULL, 0); 467 struct buildid_dir_config c;
468 c.dir = buildid_dir;
469 perf_config(buildid_dir_command_config, &c);
637} 470}
638 471
639/* 472void set_buildid_dir(void)
640 * If value==NULL, unset in (remove from) config,
641 * if value_regex!=NULL, disregard key/value pairs where value does not match.
642 * if multi_replace==0, nothing, or only one matching key/value is replaced,
643 * else all matching key/values (regardless how many) are removed,
644 * before the new pair is written.
645 *
646 * Returns 0 on success.
647 *
648 * This function does this:
649 *
650 * - it locks the config file by creating ".perf/config.lock"
651 *
652 * - it then parses the config using store_aux() as validator to find
653 * the position on the key/value pair to replace. If it is to be unset,
654 * it must be found exactly once.
655 *
656 * - the config file is mmap()ed and the part before the match (if any) is
657 * written to the lock file, then the changed part and the rest.
658 *
659 * - the config file is removed and the lock file rename()d to it.
660 *
661 */
662int perf_config_set_multivar(const char* key, const char* value,
663 const char* value_regex, int multi_replace)
664{ 473{
665 int i, dot; 474 buildid_dir[0] = '\0';
666 int fd = -1, in_fd;
667 int ret = 0;
668 char* config_filename;
669 const char* last_dot = strrchr(key, '.');
670
671 if (config_exclusive_filename)
672 config_filename = strdup(config_exclusive_filename);
673 else
674 config_filename = perf_pathdup("config");
675
676 /*
677 * Since "key" actually contains the section name and the real
678 * key name separated by a dot, we have to know where the dot is.
679 */
680
681 if (last_dot == NULL) {
682 error("key does not contain a section: %s", key);
683 ret = 2;
684 goto out_free;
685 }
686 store.baselen = last_dot - key;
687
688 store.multi_replace = multi_replace;
689
690 /*
691 * Validate the key and while at it, lower case it for matching.
692 */
693 store.key = malloc(strlen(key) + 1);
694 dot = 0;
695 for (i = 0; key[i]; i++) {
696 unsigned char c = key[i];
697 if (c == '.')
698 dot = 1;
699 /* Leave the extended basename untouched.. */
700 if (!dot || i > store.baselen) {
701 if (!iskeychar(c) || (i == store.baselen+1 && !isalpha(c))) {
702 error("invalid key: %s", key);
703 free(store.key);
704 ret = 1;
705 goto out_free;
706 }
707 c = tolower(c);
708 } else if (c == '\n') {
709 error("invalid key (newline): %s", key);
710 free(store.key);
711 ret = 1;
712 goto out_free;
713 }
714 store.key[i] = c;
715 }
716 store.key[i] = 0;
717
718 /*
719 * If .perf/config does not exist yet, write a minimal version.
720 */
721 in_fd = open(config_filename, O_RDONLY);
722 if ( in_fd < 0 ) {
723 free(store.key);
724
725 if ( ENOENT != errno ) {
726 error("opening %s: %s", config_filename,
727 strerror(errno));
728 ret = 3; /* same as "invalid config file" */
729 goto out_free;
730 }
731 /* if nothing to unset, error out */
732 if (value == NULL) {
733 ret = 5;
734 goto out_free;
735 }
736 475
737 store.key = (char*)key; 476 /* try config file */
738 if (!store_write_section(fd, key) || 477 check_buildid_dir_config();
739 !store_write_pair(fd, key, value))
740 goto write_err_out;
741 } else {
742 struct stat st;
743 char* contents;
744 size_t contents_sz, copy_begin, copy_end;
745 int i, new_line = 0;
746
747 if (value_regex == NULL)
748 store.value_regex = NULL;
749 else {
750 if (value_regex[0] == '!') {
751 store.do_not_match = 1;
752 value_regex++;
753 } else
754 store.do_not_match = 0;
755
756 store.value_regex = (regex_t*)malloc(sizeof(regex_t));
757 if (regcomp(store.value_regex, value_regex,
758 REG_EXTENDED)) {
759 error("invalid pattern: %s", value_regex);
760 free(store.value_regex);
761 ret = 6;
762 goto out_free;
763 }
764 }
765 478
766 store.offset[0] = 0; 479 /* default to $HOME/.debug */
767 store.state = START; 480 if (buildid_dir[0] == '\0') {
768 store.seen = 0; 481 char *v = getenv("HOME");
769 482 if (v) {
770 /* 483 snprintf(buildid_dir, MAXPATHLEN-1, "%s/%s",
771 * After this, store.offset will contain the *end* offset 484 v, DEBUG_CACHE_DIR);
772 * of the last match, or remain at 0 if no match was found. 485 } else {
773 * As a side effect, we make sure to transform only a valid 486 strncpy(buildid_dir, DEBUG_CACHE_DIR, MAXPATHLEN-1);
774 * existing config file.
775 */
776 if (perf_config_from_file(store_aux, config_filename, NULL)) {
777 error("invalid config file %s", config_filename);
778 free(store.key);
779 if (store.value_regex != NULL) {
780 regfree(store.value_regex);
781 free(store.value_regex);
782 }
783 ret = 3;
784 goto out_free;
785 }
786
787 free(store.key);
788 if (store.value_regex != NULL) {
789 regfree(store.value_regex);
790 free(store.value_regex);
791 }
792
793 /* if nothing to unset, or too many matches, error out */
794 if ((store.seen == 0 && value == NULL) ||
795 (store.seen > 1 && multi_replace == 0)) {
796 ret = 5;
797 goto out_free;
798 }
799
800 fstat(in_fd, &st);
801 contents_sz = xsize_t(st.st_size);
802 contents = mmap(NULL, contents_sz, PROT_READ,
803 MAP_PRIVATE, in_fd, 0);
804 close(in_fd);
805
806 if (store.seen == 0)
807 store.seen = 1;
808
809 for (i = 0, copy_begin = 0; i < store.seen; i++) {
810 if (store.offset[i] == 0) {
811 store.offset[i] = copy_end = contents_sz;
812 } else if (store.state != KEY_SEEN) {
813 copy_end = store.offset[i];
814 } else
815 copy_end = find_beginning_of_line(
816 contents, contents_sz,
817 store.offset[i]-2, &new_line);
818
819 if (copy_end > 0 && contents[copy_end-1] != '\n')
820 new_line = 1;
821
822 /* write the first part of the config */
823 if (copy_end > copy_begin) {
824 if (write_in_full(fd, contents + copy_begin,
825 copy_end - copy_begin) <
826 copy_end - copy_begin)
827 goto write_err_out;
828 if (new_line &&
829 write_in_full(fd, "\n", 1) != 1)
830 goto write_err_out;
831 }
832 copy_begin = store.offset[i];
833 }
834
835 /* write the pair (value == NULL means unset) */
836 if (value != NULL) {
837 if (store.state == START) {
838 if (!store_write_section(fd, key))
839 goto write_err_out;
840 }
841 if (!store_write_pair(fd, key, value))
842 goto write_err_out;
843 } 487 }
844 488 buildid_dir[MAXPATHLEN-1] = '\0';
845 /* write the rest of the config */
846 if (copy_begin < contents_sz)
847 if (write_in_full(fd, contents + copy_begin,
848 contents_sz - copy_begin) <
849 contents_sz - copy_begin)
850 goto write_err_out;
851
852 munmap(contents, contents_sz);
853 } 489 }
854 490 /* for communicating with external commands */
855 ret = 0; 491 setenv("PERF_BUILDID_DIR", buildid_dir, 1);
856
857out_free:
858 free(config_filename);
859 return ret;
860
861write_err_out:
862 goto out_free;
863
864}
865
866/*
867 * Call this to report error for your variable that should not
868 * get a boolean value (i.e. "[my] var" means "true").
869 */
870int config_error_nonbool(const char *var)
871{
872 return error("Missing value for '%s'", var);
873} 492}
diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c
new file mode 100644
index 00000000000..3ccaa104338
--- /dev/null
+++ b/tools/perf/util/cpumap.c
@@ -0,0 +1,179 @@
1#include "util.h"
2#include "../perf.h"
3#include "cpumap.h"
4#include <assert.h>
5#include <stdio.h>
6
7static struct cpu_map *cpu_map__default_new(void)
8{
9 struct cpu_map *cpus;
10 int nr_cpus;
11
12 nr_cpus = sysconf(_SC_NPROCESSORS_ONLN);
13 if (nr_cpus < 0)
14 return NULL;
15
16 cpus = malloc(sizeof(*cpus) + nr_cpus * sizeof(int));
17 if (cpus != NULL) {
18 int i;
19 for (i = 0; i < nr_cpus; ++i)
20 cpus->map[i] = i;
21
22 cpus->nr = nr_cpus;
23 }
24
25 return cpus;
26}
27
28static struct cpu_map *cpu_map__trim_new(int nr_cpus, int *tmp_cpus)
29{
30 size_t payload_size = nr_cpus * sizeof(int);
31 struct cpu_map *cpus = malloc(sizeof(*cpus) + payload_size);
32
33 if (cpus != NULL) {
34 cpus->nr = nr_cpus;
35 memcpy(cpus->map, tmp_cpus, payload_size);
36 }
37
38 return cpus;
39}
40
41static struct cpu_map *cpu_map__read_all_cpu_map(void)
42{
43 struct cpu_map *cpus = NULL;
44 FILE *onlnf;
45 int nr_cpus = 0;
46 int *tmp_cpus = NULL, *tmp;
47 int max_entries = 0;
48 int n, cpu, prev;
49 char sep;
50
51 onlnf = fopen("/sys/devices/system/cpu/online", "r");
52 if (!onlnf)
53 return cpu_map__default_new();
54
55 sep = 0;
56 prev = -1;
57 for (;;) {
58 n = fscanf(onlnf, "%u%c", &cpu, &sep);
59 if (n <= 0)
60 break;
61 if (prev >= 0) {
62 int new_max = nr_cpus + cpu - prev - 1;
63
64 if (new_max >= max_entries) {
65 max_entries = new_max + MAX_NR_CPUS / 2;
66 tmp = realloc(tmp_cpus, max_entries * sizeof(int));
67 if (tmp == NULL)
68 goto out_free_tmp;
69 tmp_cpus = tmp;
70 }
71
72 while (++prev < cpu)
73 tmp_cpus[nr_cpus++] = prev;
74 }
75 if (nr_cpus == max_entries) {
76 max_entries += MAX_NR_CPUS;
77 tmp = realloc(tmp_cpus, max_entries * sizeof(int));
78 if (tmp == NULL)
79 goto out_free_tmp;
80 tmp_cpus = tmp;
81 }
82
83 tmp_cpus[nr_cpus++] = cpu;
84 if (n == 2 && sep == '-')
85 prev = cpu;
86 else
87 prev = -1;
88 if (n == 1 || sep == '\n')
89 break;
90 }
91
92 if (nr_cpus > 0)
93 cpus = cpu_map__trim_new(nr_cpus, tmp_cpus);
94 else
95 cpus = cpu_map__default_new();
96out_free_tmp:
97 free(tmp_cpus);
98 fclose(onlnf);
99 return cpus;
100}
101
102struct cpu_map *cpu_map__new(const char *cpu_list)
103{
104 struct cpu_map *cpus = NULL;
105 unsigned long start_cpu, end_cpu = 0;
106 char *p = NULL;
107 int i, nr_cpus = 0;
108 int *tmp_cpus = NULL, *tmp;
109 int max_entries = 0;
110
111 if (!cpu_list)
112 return cpu_map__read_all_cpu_map();
113
114 if (!isdigit(*cpu_list))
115 goto out;
116
117 while (isdigit(*cpu_list)) {
118 p = NULL;
119 start_cpu = strtoul(cpu_list, &p, 0);
120 if (start_cpu >= INT_MAX
121 || (*p != '\0' && *p != ',' && *p != '-'))
122 goto invalid;
123
124 if (*p == '-') {
125 cpu_list = ++p;
126 p = NULL;
127 end_cpu = strtoul(cpu_list, &p, 0);
128
129 if (end_cpu >= INT_MAX || (*p != '\0' && *p != ','))
130 goto invalid;
131
132 if (end_cpu < start_cpu)
133 goto invalid;
134 } else {
135 end_cpu = start_cpu;
136 }
137
138 for (; start_cpu <= end_cpu; start_cpu++) {
139 /* check for duplicates */
140 for (i = 0; i < nr_cpus; i++)
141 if (tmp_cpus[i] == (int)start_cpu)
142 goto invalid;
143
144 if (nr_cpus == max_entries) {
145 max_entries += MAX_NR_CPUS;
146 tmp = realloc(tmp_cpus, max_entries * sizeof(int));
147 if (tmp == NULL)
148 goto invalid;
149 tmp_cpus = tmp;
150 }
151 tmp_cpus[nr_cpus++] = (int)start_cpu;
152 }
153 if (*p)
154 ++p;
155
156 cpu_list = p;
157 }
158
159 if (nr_cpus > 0)
160 cpus = cpu_map__trim_new(nr_cpus, tmp_cpus);
161 else
162 cpus = cpu_map__default_new();
163invalid:
164 free(tmp_cpus);
165out:
166 return cpus;
167}
168
169struct cpu_map *cpu_map__dummy_new(void)
170{
171 struct cpu_map *cpus = malloc(sizeof(*cpus) + sizeof(int));
172
173 if (cpus != NULL) {
174 cpus->nr = 1;
175 cpus->map[0] = -1;
176 }
177
178 return cpus;
179}
diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h
new file mode 100644
index 00000000000..f7a4f42f630
--- /dev/null
+++ b/tools/perf/util/cpumap.h
@@ -0,0 +1,13 @@
1#ifndef __PERF_CPUMAP_H
2#define __PERF_CPUMAP_H
3
4struct cpu_map {
5 int nr;
6 int map[];
7};
8
9struct cpu_map *cpu_map__new(const char *cpu_list);
10struct cpu_map *cpu_map__dummy_new(void);
11void *cpu_map__delete(struct cpu_map *map);
12
13#endif /* __PERF_CPUMAP_H */
diff --git a/tools/perf/util/ctype.c b/tools/perf/util/ctype.c
index 0b791bd346b..35073621e5d 100644
--- a/tools/perf/util/ctype.c
+++ b/tools/perf/util/ctype.c
@@ -29,3 +29,11 @@ unsigned char sane_ctype[256] = {
29 A, A, A, A, A, A, A, A, A, A, A, R, R, P, P, 0, /* 112..127 */ 29 A, A, A, A, A, A, A, A, A, A, A, R, R, P, P, 0, /* 112..127 */
30 /* Nothing in the 128.. range */ 30 /* Nothing in the 128.. range */
31}; 31};
32
33const char *graph_line =
34 "_____________________________________________________________________"
35 "_____________________________________________________________________";
36const char *graph_dotted_line =
37 "---------------------------------------------------------------------"
38 "---------------------------------------------------------------------"
39 "---------------------------------------------------------------------";
diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c
new file mode 100644
index 00000000000..01bbe8ecec3
--- /dev/null
+++ b/tools/perf/util/debug.c
@@ -0,0 +1,94 @@
1/* For general debugging purposes */
2
3#include "../perf.h"
4
5#include <string.h>
6#include <stdarg.h>
7#include <stdio.h>
8
9#include "cache.h"
10#include "color.h"
11#include "event.h"
12#include "debug.h"
13#include "util.h"
14
15int verbose;
16bool dump_trace = false, quiet = false;
17
18int eprintf(int level, const char *fmt, ...)
19{
20 va_list args;
21 int ret = 0;
22
23 if (verbose >= level) {
24 va_start(args, fmt);
25 if (use_browser > 0)
26 ret = ui_helpline__show_help(fmt, args);
27 else
28 ret = vfprintf(stderr, fmt, args);
29 va_end(args);
30 }
31
32 return ret;
33}
34
35int dump_printf(const char *fmt, ...)
36{
37 va_list args;
38 int ret = 0;
39
40 if (dump_trace) {
41 va_start(args, fmt);
42 ret = vprintf(fmt, args);
43 va_end(args);
44 }
45
46 return ret;
47}
48
49#ifdef NO_NEWT_SUPPORT
50void ui__warning(const char *format, ...)
51{
52 va_list args;
53
54 va_start(args, format);
55 vfprintf(stderr, format, args);
56 va_end(args);
57}
58#endif
59
60void trace_event(event_t *event)
61{
62 unsigned char *raw_event = (void *)event;
63 const char *color = PERF_COLOR_BLUE;
64 int i, j;
65
66 if (!dump_trace)
67 return;
68
69 printf(".");
70 color_fprintf(stdout, color, "\n. ... raw event: size %d bytes\n",
71 event->header.size);
72
73 for (i = 0; i < event->header.size; i++) {
74 if ((i & 15) == 0) {
75 printf(".");
76 color_fprintf(stdout, color, " %04x: ", i);
77 }
78
79 color_fprintf(stdout, color, " %02x", raw_event[i]);
80
81 if (((i & 15) == 15) || i == event->header.size-1) {
82 color_fprintf(stdout, color, " ");
83 for (j = 0; j < 15-(i & 15); j++)
84 color_fprintf(stdout, color, " ");
85 for (j = i & ~15; j <= i; j++) {
86 color_fprintf(stdout, color, "%c",
87 isprint(raw_event[j]) ?
88 raw_event[j] : '.');
89 }
90 color_fprintf(stdout, color, "\n");
91 }
92 }
93 printf(".\n");
94}
diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h
new file mode 100644
index 00000000000..ca35fd66b5d
--- /dev/null
+++ b/tools/perf/util/debug.h
@@ -0,0 +1,40 @@
1/* For debugging general purposes */
2#ifndef __PERF_DEBUG_H
3#define __PERF_DEBUG_H
4
5#include <stdbool.h>
6#include "event.h"
7
8extern int verbose;
9extern bool quiet, dump_trace;
10
11int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
12void trace_event(event_t *event);
13
14struct ui_progress;
15
16#ifdef NO_NEWT_SUPPORT
17static inline int ui_helpline__show_help(const char *format __used, va_list ap __used)
18{
19 return 0;
20}
21
22static inline struct ui_progress *ui_progress__new(const char *title __used,
23 u64 total __used)
24{
25 return (struct ui_progress *)1;
26}
27
28static inline void ui_progress__update(struct ui_progress *self __used,
29 u64 curr __used) {}
30
31static inline void ui_progress__delete(struct ui_progress *self __used) {}
32#else
33extern char ui_helpline__last_msg[];
34int ui_helpline__show_help(const char *format, va_list ap);
35#include "ui/progress.h"
36#endif
37
38void ui__warning(const char *format, ...) __attribute__((format(printf, 1, 2)));
39
40#endif /* __PERF_DEBUG_H */
diff --git a/tools/perf/util/debugfs.c b/tools/perf/util/debugfs.c
new file mode 100644
index 00000000000..a88fefc0cc0
--- /dev/null
+++ b/tools/perf/util/debugfs.c
@@ -0,0 +1,240 @@
1#include "util.h"
2#include "debugfs.h"
3#include "cache.h"
4
5static int debugfs_premounted;
6static char debugfs_mountpoint[MAX_PATH+1];
7
8static const char *debugfs_known_mountpoints[] = {
9 "/sys/kernel/debug/",
10 "/debug/",
11 0,
12};
13
14/* use this to force a umount */
15void debugfs_force_cleanup(void)
16{
17 debugfs_find_mountpoint();
18 debugfs_premounted = 0;
19 debugfs_umount();
20}
21
22/* construct a full path to a debugfs element */
23int debugfs_make_path(const char *element, char *buffer, int size)
24{
25 int len;
26
27 if (strlen(debugfs_mountpoint) == 0) {
28 buffer[0] = '\0';
29 return -1;
30 }
31
32 len = strlen(debugfs_mountpoint) + strlen(element) + 1;
33 if (len >= size)
34 return len+1;
35
36 snprintf(buffer, size-1, "%s/%s", debugfs_mountpoint, element);
37 return 0;
38}
39
40static int debugfs_found;
41
42/* find the path to the mounted debugfs */
43const char *debugfs_find_mountpoint(void)
44{
45 const char **ptr;
46 char type[100];
47 FILE *fp;
48
49 if (debugfs_found)
50 return (const char *) debugfs_mountpoint;
51
52 ptr = debugfs_known_mountpoints;
53 while (*ptr) {
54 if (debugfs_valid_mountpoint(*ptr) == 0) {
55 debugfs_found = 1;
56 strcpy(debugfs_mountpoint, *ptr);
57 return debugfs_mountpoint;
58 }
59 ptr++;
60 }
61
62 /* give up and parse /proc/mounts */
63 fp = fopen("/proc/mounts", "r");
64 if (fp == NULL)
65 die("Can't open /proc/mounts for read");
66
67 while (fscanf(fp, "%*s %"
68 STR(MAX_PATH)
69 "s %99s %*s %*d %*d\n",
70 debugfs_mountpoint, type) == 2) {
71 if (strcmp(type, "debugfs") == 0)
72 break;
73 }
74 fclose(fp);
75
76 if (strcmp(type, "debugfs") != 0)
77 return NULL;
78
79 debugfs_found = 1;
80
81 return debugfs_mountpoint;
82}
83
84/* verify that a mountpoint is actually a debugfs instance */
85
86int debugfs_valid_mountpoint(const char *debugfs)
87{
88 struct statfs st_fs;
89
90 if (statfs(debugfs, &st_fs) < 0)
91 return -ENOENT;
92 else if (st_fs.f_type != (long) DEBUGFS_MAGIC)
93 return -ENOENT;
94
95 return 0;
96}
97
98
99int debugfs_valid_entry(const char *path)
100{
101 struct stat st;
102
103 if (stat(path, &st))
104 return -errno;
105
106 return 0;
107}
108
109/* mount the debugfs somewhere if it's not mounted */
110
111char *debugfs_mount(const char *mountpoint)
112{
113 /* see if it's already mounted */
114 if (debugfs_find_mountpoint()) {
115 debugfs_premounted = 1;
116 return debugfs_mountpoint;
117 }
118
119 /* if not mounted and no argument */
120 if (mountpoint == NULL) {
121 /* see if environment variable set */
122 mountpoint = getenv(PERF_DEBUGFS_ENVIRONMENT);
123 /* if no environment variable, use default */
124 if (mountpoint == NULL)
125 mountpoint = "/sys/kernel/debug";
126 }
127
128 if (mount(NULL, mountpoint, "debugfs", 0, NULL) < 0)
129 return NULL;
130
131 /* save the mountpoint */
132 strncpy(debugfs_mountpoint, mountpoint, sizeof(debugfs_mountpoint));
133 debugfs_found = 1;
134
135 return debugfs_mountpoint;
136}
137
138/* umount the debugfs */
139
140int debugfs_umount(void)
141{
142 char umountcmd[128];
143 int ret;
144
145 /* if it was already mounted, leave it */
146 if (debugfs_premounted)
147 return 0;
148
149 /* make sure it's a valid mount point */
150 ret = debugfs_valid_mountpoint(debugfs_mountpoint);
151 if (ret)
152 return ret;
153
154 snprintf(umountcmd, sizeof(umountcmd),
155 "/bin/umount %s", debugfs_mountpoint);
156 return system(umountcmd);
157}
158
159int debugfs_write(const char *entry, const char *value)
160{
161 char path[MAX_PATH+1];
162 int ret, count;
163 int fd;
164
165 /* construct the path */
166 snprintf(path, sizeof(path), "%s/%s", debugfs_mountpoint, entry);
167
168 /* verify that it exists */
169 ret = debugfs_valid_entry(path);
170 if (ret)
171 return ret;
172
173 /* get how many chars we're going to write */
174 count = strlen(value);
175
176 /* open the debugfs entry */
177 fd = open(path, O_RDWR);
178 if (fd < 0)
179 return -errno;
180
181 while (count > 0) {
182 /* write it */
183 ret = write(fd, value, count);
184 if (ret <= 0) {
185 if (ret == EAGAIN)
186 continue;
187 close(fd);
188 return -errno;
189 }
190 count -= ret;
191 }
192
193 /* close it */
194 close(fd);
195
196 /* return success */
197 return 0;
198}
199
200/*
201 * read a debugfs entry
202 * returns the number of chars read or a negative errno
203 */
204int debugfs_read(const char *entry, char *buffer, size_t size)
205{
206 char path[MAX_PATH+1];
207 int ret;
208 int fd;
209
210 /* construct the path */
211 snprintf(path, sizeof(path), "%s/%s", debugfs_mountpoint, entry);
212
213 /* verify that it exists */
214 ret = debugfs_valid_entry(path);
215 if (ret)
216 return ret;
217
218 /* open the debugfs entry */
219 fd = open(path, O_RDONLY);
220 if (fd < 0)
221 return -errno;
222
223 do {
224 /* read it */
225 ret = read(fd, buffer, size);
226 if (ret == 0) {
227 close(fd);
228 return EOF;
229 }
230 } while (ret < 0 && errno == EAGAIN);
231
232 /* close it */
233 close(fd);
234
235 /* make *sure* there's a null character at the end */
236 buffer[ret] = '\0';
237
238 /* return the number of chars read */
239 return ret;
240}
diff --git a/tools/perf/util/debugfs.h b/tools/perf/util/debugfs.h
new file mode 100644
index 00000000000..83a02879745
--- /dev/null
+++ b/tools/perf/util/debugfs.h
@@ -0,0 +1,25 @@
1#ifndef __DEBUGFS_H__
2#define __DEBUGFS_H__
3
4#include <sys/mount.h>
5
6#ifndef MAX_PATH
7# define MAX_PATH 256
8#endif
9
10#ifndef STR
11# define _STR(x) #x
12# define STR(x) _STR(x)
13#endif
14
15extern const char *debugfs_find_mountpoint(void);
16extern int debugfs_valid_mountpoint(const char *debugfs);
17extern int debugfs_valid_entry(const char *path);
18extern char *debugfs_mount(const char *mountpoint);
19extern int debugfs_umount(void);
20extern int debugfs_write(const char *entry, const char *value);
21extern int debugfs_read(const char *entry, char *buffer, size_t size);
22extern void debugfs_force_cleanup(void);
23extern int debugfs_make_path(const char *element, char *buffer, int size);
24
25#endif /* __DEBUGFS_H__ */
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
new file mode 100644
index 00000000000..2302ec051bb
--- /dev/null
+++ b/tools/perf/util/event.c
@@ -0,0 +1,952 @@
1#include <linux/types.h>
2#include "event.h"
3#include "debug.h"
4#include "session.h"
5#include "sort.h"
6#include "string.h"
7#include "strlist.h"
8#include "thread.h"
9
10static const char *event__name[] = {
11 [0] = "TOTAL",
12 [PERF_RECORD_MMAP] = "MMAP",
13 [PERF_RECORD_LOST] = "LOST",
14 [PERF_RECORD_COMM] = "COMM",
15 [PERF_RECORD_EXIT] = "EXIT",
16 [PERF_RECORD_THROTTLE] = "THROTTLE",
17 [PERF_RECORD_UNTHROTTLE] = "UNTHROTTLE",
18 [PERF_RECORD_FORK] = "FORK",
19 [PERF_RECORD_READ] = "READ",
20 [PERF_RECORD_SAMPLE] = "SAMPLE",
21 [PERF_RECORD_HEADER_ATTR] = "ATTR",
22 [PERF_RECORD_HEADER_EVENT_TYPE] = "EVENT_TYPE",
23 [PERF_RECORD_HEADER_TRACING_DATA] = "TRACING_DATA",
24 [PERF_RECORD_HEADER_BUILD_ID] = "BUILD_ID",
25 [PERF_RECORD_FINISHED_ROUND] = "FINISHED_ROUND",
26};
27
28const char *event__get_event_name(unsigned int id)
29{
30 if (id >= ARRAY_SIZE(event__name))
31 return "INVALID";
32 if (!event__name[id])
33 return "UNKNOWN";
34 return event__name[id];
35}
36
37static struct sample_data synth_sample = {
38 .pid = -1,
39 .tid = -1,
40 .time = -1,
41 .stream_id = -1,
42 .cpu = -1,
43 .period = 1,
44};
45
46static pid_t event__synthesize_comm(event_t *event, pid_t pid, int full,
47 event__handler_t process,
48 struct perf_session *session)
49{
50 char filename[PATH_MAX];
51 char bf[BUFSIZ];
52 FILE *fp;
53 size_t size = 0;
54 DIR *tasks;
55 struct dirent dirent, *next;
56 pid_t tgid = 0;
57
58 snprintf(filename, sizeof(filename), "/proc/%d/status", pid);
59
60 fp = fopen(filename, "r");
61 if (fp == NULL) {
62out_race:
63 /*
64 * We raced with a task exiting - just return:
65 */
66 pr_debug("couldn't open %s\n", filename);
67 return 0;
68 }
69
70 memset(&event->comm, 0, sizeof(event->comm));
71
72 while (!event->comm.comm[0] || !event->comm.pid) {
73 if (fgets(bf, sizeof(bf), fp) == NULL) {
74 pr_warning("couldn't get COMM and pgid, malformed %s\n", filename);
75 goto out;
76 }
77
78 if (memcmp(bf, "Name:", 5) == 0) {
79 char *name = bf + 5;
80 while (*name && isspace(*name))
81 ++name;
82 size = strlen(name) - 1;
83 memcpy(event->comm.comm, name, size++);
84 } else if (memcmp(bf, "Tgid:", 5) == 0) {
85 char *tgids = bf + 5;
86 while (*tgids && isspace(*tgids))
87 ++tgids;
88 tgid = event->comm.pid = atoi(tgids);
89 }
90 }
91
92 event->comm.header.type = PERF_RECORD_COMM;
93 size = ALIGN(size, sizeof(u64));
94 memset(event->comm.comm + size, 0, session->id_hdr_size);
95 event->comm.header.size = (sizeof(event->comm) -
96 (sizeof(event->comm.comm) - size) +
97 session->id_hdr_size);
98 if (!full) {
99 event->comm.tid = pid;
100
101 process(event, &synth_sample, session);
102 goto out;
103 }
104
105 snprintf(filename, sizeof(filename), "/proc/%d/task", pid);
106
107 tasks = opendir(filename);
108 if (tasks == NULL)
109 goto out_race;
110
111 while (!readdir_r(tasks, &dirent, &next) && next) {
112 char *end;
113 pid = strtol(dirent.d_name, &end, 10);
114 if (*end)
115 continue;
116
117 event->comm.tid = pid;
118
119 process(event, &synth_sample, session);
120 }
121
122 closedir(tasks);
123out:
124 fclose(fp);
125
126 return tgid;
127}
128
129static int event__synthesize_mmap_events(event_t *event, pid_t pid, pid_t tgid,
130 event__handler_t process,
131 struct perf_session *session)
132{
133 char filename[PATH_MAX];
134 FILE *fp;
135
136 snprintf(filename, sizeof(filename), "/proc/%d/maps", pid);
137
138 fp = fopen(filename, "r");
139 if (fp == NULL) {
140 /*
141 * We raced with a task exiting - just return:
142 */
143 pr_debug("couldn't open %s\n", filename);
144 return -1;
145 }
146
147 event->header.type = PERF_RECORD_MMAP;
148 /*
149 * Just like the kernel, see __perf_event_mmap in kernel/perf_event.c
150 */
151 event->header.misc = PERF_RECORD_MISC_USER;
152
153 while (1) {
154 char bf[BUFSIZ], *pbf = bf;
155 int n;
156 size_t size;
157 if (fgets(bf, sizeof(bf), fp) == NULL)
158 break;
159
160 /* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */
161 n = hex2u64(pbf, &event->mmap.start);
162 if (n < 0)
163 continue;
164 pbf += n + 1;
165 n = hex2u64(pbf, &event->mmap.len);
166 if (n < 0)
167 continue;
168 pbf += n + 3;
169 if (*pbf == 'x') { /* vm_exec */
170 char *execname = strchr(bf, '/');
171
172 /* Catch VDSO */
173 if (execname == NULL)
174 execname = strstr(bf, "[vdso]");
175
176 if (execname == NULL)
177 continue;
178
179 pbf += 3;
180 n = hex2u64(pbf, &event->mmap.pgoff);
181
182 size = strlen(execname);
183 execname[size - 1] = '\0'; /* Remove \n */
184 memcpy(event->mmap.filename, execname, size);
185 size = ALIGN(size, sizeof(u64));
186 event->mmap.len -= event->mmap.start;
187 event->mmap.header.size = (sizeof(event->mmap) -
188 (sizeof(event->mmap.filename) - size));
189 memset(event->mmap.filename + size, 0, session->id_hdr_size);
190 event->mmap.header.size += session->id_hdr_size;
191 event->mmap.pid = tgid;
192 event->mmap.tid = pid;
193
194 process(event, &synth_sample, session);
195 }
196 }
197
198 fclose(fp);
199 return 0;
200}
201
202int event__synthesize_modules(event__handler_t process,
203 struct perf_session *session,
204 struct machine *machine)
205{
206 struct rb_node *nd;
207 struct map_groups *kmaps = &machine->kmaps;
208 event_t *event = zalloc(sizeof(event->mmap) + session->id_hdr_size);
209
210 if (event == NULL) {
211 pr_debug("Not enough memory synthesizing mmap event "
212 "for kernel modules\n");
213 return -1;
214 }
215
216 event->header.type = PERF_RECORD_MMAP;
217
218 /*
219 * kernel uses 0 for user space maps, see kernel/perf_event.c
220 * __perf_event_mmap
221 */
222 if (machine__is_host(machine))
223 event->header.misc = PERF_RECORD_MISC_KERNEL;
224 else
225 event->header.misc = PERF_RECORD_MISC_GUEST_KERNEL;
226
227 for (nd = rb_first(&kmaps->maps[MAP__FUNCTION]);
228 nd; nd = rb_next(nd)) {
229 size_t size;
230 struct map *pos = rb_entry(nd, struct map, rb_node);
231
232 if (pos->dso->kernel)
233 continue;
234
235 size = ALIGN(pos->dso->long_name_len + 1, sizeof(u64));
236 event->mmap.header.type = PERF_RECORD_MMAP;
237 event->mmap.header.size = (sizeof(event->mmap) -
238 (sizeof(event->mmap.filename) - size));
239 memset(event->mmap.filename + size, 0, session->id_hdr_size);
240 event->mmap.header.size += session->id_hdr_size;
241 event->mmap.start = pos->start;
242 event->mmap.len = pos->end - pos->start;
243 event->mmap.pid = machine->pid;
244
245 memcpy(event->mmap.filename, pos->dso->long_name,
246 pos->dso->long_name_len + 1);
247 process(event, &synth_sample, session);
248 }
249
250 free(event);
251 return 0;
252}
253
254static int __event__synthesize_thread(event_t *comm_event, event_t *mmap_event,
255 pid_t pid, event__handler_t process,
256 struct perf_session *session)
257{
258 pid_t tgid = event__synthesize_comm(comm_event, pid, 1, process,
259 session);
260 if (tgid == -1)
261 return -1;
262 return event__synthesize_mmap_events(mmap_event, pid, tgid,
263 process, session);
264}
265
266int event__synthesize_thread(pid_t pid, event__handler_t process,
267 struct perf_session *session)
268{
269 event_t *comm_event, *mmap_event;
270 int err = -1;
271
272 comm_event = malloc(sizeof(comm_event->comm) + session->id_hdr_size);
273 if (comm_event == NULL)
274 goto out;
275
276 mmap_event = malloc(sizeof(mmap_event->mmap) + session->id_hdr_size);
277 if (mmap_event == NULL)
278 goto out_free_comm;
279
280 err = __event__synthesize_thread(comm_event, mmap_event, pid,
281 process, session);
282 free(mmap_event);
283out_free_comm:
284 free(comm_event);
285out:
286 return err;
287}
288
289int event__synthesize_threads(event__handler_t process,
290 struct perf_session *session)
291{
292 DIR *proc;
293 struct dirent dirent, *next;
294 event_t *comm_event, *mmap_event;
295 int err = -1;
296
297 comm_event = malloc(sizeof(comm_event->comm) + session->id_hdr_size);
298 if (comm_event == NULL)
299 goto out;
300
301 mmap_event = malloc(sizeof(mmap_event->mmap) + session->id_hdr_size);
302 if (mmap_event == NULL)
303 goto out_free_comm;
304
305 proc = opendir("/proc");
306 if (proc == NULL)
307 goto out_free_mmap;
308
309 while (!readdir_r(proc, &dirent, &next) && next) {
310 char *end;
311 pid_t pid = strtol(dirent.d_name, &end, 10);
312
313 if (*end) /* only interested in proper numerical dirents */
314 continue;
315
316 __event__synthesize_thread(comm_event, mmap_event, pid,
317 process, session);
318 }
319
320 closedir(proc);
321 err = 0;
322out_free_mmap:
323 free(mmap_event);
324out_free_comm:
325 free(comm_event);
326out:
327 return err;
328}
329
330struct process_symbol_args {
331 const char *name;
332 u64 start;
333};
334
335static int find_symbol_cb(void *arg, const char *name, char type,
336 u64 start, u64 end __used)
337{
338 struct process_symbol_args *args = arg;
339
340 /*
341 * Must be a function or at least an alias, as in PARISC64, where "_text" is
342 * an 'A' to the same address as "_stext".
343 */
344 if (!(symbol_type__is_a(type, MAP__FUNCTION) ||
345 type == 'A') || strcmp(name, args->name))
346 return 0;
347
348 args->start = start;
349 return 1;
350}
351
352int event__synthesize_kernel_mmap(event__handler_t process,
353 struct perf_session *session,
354 struct machine *machine,
355 const char *symbol_name)
356{
357 size_t size;
358 const char *filename, *mmap_name;
359 char path[PATH_MAX];
360 char name_buff[PATH_MAX];
361 struct map *map;
362 int err;
363 /*
364 * We should get this from /sys/kernel/sections/.text, but till that is
365 * available use this, and after it is use this as a fallback for older
366 * kernels.
367 */
368 struct process_symbol_args args = { .name = symbol_name, };
369 event_t *event = zalloc(sizeof(event->mmap) + session->id_hdr_size);
370
371 if (event == NULL) {
372 pr_debug("Not enough memory synthesizing mmap event "
373 "for kernel modules\n");
374 return -1;
375 }
376
377 mmap_name = machine__mmap_name(machine, name_buff, sizeof(name_buff));
378 if (machine__is_host(machine)) {
379 /*
380 * kernel uses PERF_RECORD_MISC_USER for user space maps,
381 * see kernel/perf_event.c __perf_event_mmap
382 */
383 event->header.misc = PERF_RECORD_MISC_KERNEL;
384 filename = "/proc/kallsyms";
385 } else {
386 event->header.misc = PERF_RECORD_MISC_GUEST_KERNEL;
387 if (machine__is_default_guest(machine))
388 filename = (char *) symbol_conf.default_guest_kallsyms;
389 else {
390 sprintf(path, "%s/proc/kallsyms", machine->root_dir);
391 filename = path;
392 }
393 }
394
395 if (kallsyms__parse(filename, &args, find_symbol_cb) <= 0)
396 return -ENOENT;
397
398 map = machine->vmlinux_maps[MAP__FUNCTION];
399 size = snprintf(event->mmap.filename, sizeof(event->mmap.filename),
400 "%s%s", mmap_name, symbol_name) + 1;
401 size = ALIGN(size, sizeof(u64));
402 event->mmap.header.type = PERF_RECORD_MMAP;
403 event->mmap.header.size = (sizeof(event->mmap) -
404 (sizeof(event->mmap.filename) - size) + session->id_hdr_size);
405 event->mmap.pgoff = args.start;
406 event->mmap.start = map->start;
407 event->mmap.len = map->end - event->mmap.start;
408 event->mmap.pid = machine->pid;
409
410 err = process(event, &synth_sample, session);
411 free(event);
412
413 return err;
414}
415
416static void thread__comm_adjust(struct thread *self, struct hists *hists)
417{
418 char *comm = self->comm;
419
420 if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep &&
421 (!symbol_conf.comm_list ||
422 strlist__has_entry(symbol_conf.comm_list, comm))) {
423 u16 slen = strlen(comm);
424
425 if (hists__new_col_len(hists, HISTC_COMM, slen))
426 hists__set_col_len(hists, HISTC_THREAD, slen + 6);
427 }
428}
429
430static int thread__set_comm_adjust(struct thread *self, const char *comm,
431 struct hists *hists)
432{
433 int ret = thread__set_comm(self, comm);
434
435 if (ret)
436 return ret;
437
438 thread__comm_adjust(self, hists);
439
440 return 0;
441}
442
443int event__process_comm(event_t *self, struct sample_data *sample __used,
444 struct perf_session *session)
445{
446 struct thread *thread = perf_session__findnew(session, self->comm.tid);
447
448 dump_printf(": %s:%d\n", self->comm.comm, self->comm.tid);
449
450 if (thread == NULL || thread__set_comm_adjust(thread, self->comm.comm,
451 &session->hists)) {
452 dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n");
453 return -1;
454 }
455
456 return 0;
457}
458
459int event__process_lost(event_t *self, struct sample_data *sample __used,
460 struct perf_session *session)
461{
462 dump_printf(": id:%Ld: lost:%Ld\n", self->lost.id, self->lost.lost);
463 session->hists.stats.total_lost += self->lost.lost;
464 return 0;
465}
466
467static void event_set_kernel_mmap_len(struct map **maps, event_t *self)
468{
469 maps[MAP__FUNCTION]->start = self->mmap.start;
470 maps[MAP__FUNCTION]->end = self->mmap.start + self->mmap.len;
471 /*
472 * Be a bit paranoid here, some perf.data file came with
473 * a zero sized synthesized MMAP event for the kernel.
474 */
475 if (maps[MAP__FUNCTION]->end == 0)
476 maps[MAP__FUNCTION]->end = ~0ULL;
477}
478
479static int event__process_kernel_mmap(event_t *self,
480 struct perf_session *session)
481{
482 struct map *map;
483 char kmmap_prefix[PATH_MAX];
484 struct machine *machine;
485 enum dso_kernel_type kernel_type;
486 bool is_kernel_mmap;
487
488 machine = perf_session__findnew_machine(session, self->mmap.pid);
489 if (!machine) {
490 pr_err("Can't find id %d's machine\n", self->mmap.pid);
491 goto out_problem;
492 }
493
494 machine__mmap_name(machine, kmmap_prefix, sizeof(kmmap_prefix));
495 if (machine__is_host(machine))
496 kernel_type = DSO_TYPE_KERNEL;
497 else
498 kernel_type = DSO_TYPE_GUEST_KERNEL;
499
500 is_kernel_mmap = memcmp(self->mmap.filename,
501 kmmap_prefix,
502 strlen(kmmap_prefix)) == 0;
503 if (self->mmap.filename[0] == '/' ||
504 (!is_kernel_mmap && self->mmap.filename[0] == '[')) {
505
506 char short_module_name[1024];
507 char *name, *dot;
508
509 if (self->mmap.filename[0] == '/') {
510 name = strrchr(self->mmap.filename, '/');
511 if (name == NULL)
512 goto out_problem;
513
514 ++name; /* skip / */
515 dot = strrchr(name, '.');
516 if (dot == NULL)
517 goto out_problem;
518 snprintf(short_module_name, sizeof(short_module_name),
519 "[%.*s]", (int)(dot - name), name);
520 strxfrchar(short_module_name, '-', '_');
521 } else
522 strcpy(short_module_name, self->mmap.filename);
523
524 map = machine__new_module(machine, self->mmap.start,
525 self->mmap.filename);
526 if (map == NULL)
527 goto out_problem;
528
529 name = strdup(short_module_name);
530 if (name == NULL)
531 goto out_problem;
532
533 map->dso->short_name = name;
534 map->dso->sname_alloc = 1;
535 map->end = map->start + self->mmap.len;
536 } else if (is_kernel_mmap) {
537 const char *symbol_name = (self->mmap.filename +
538 strlen(kmmap_prefix));
539 /*
540 * Should be there already, from the build-id table in
541 * the header.
542 */
543 struct dso *kernel = __dsos__findnew(&machine->kernel_dsos,
544 kmmap_prefix);
545 if (kernel == NULL)
546 goto out_problem;
547
548 kernel->kernel = kernel_type;
549 if (__machine__create_kernel_maps(machine, kernel) < 0)
550 goto out_problem;
551
552 event_set_kernel_mmap_len(machine->vmlinux_maps, self);
553 perf_session__set_kallsyms_ref_reloc_sym(machine->vmlinux_maps,
554 symbol_name,
555 self->mmap.pgoff);
556 if (machine__is_default_guest(machine)) {
557 /*
558 * preload dso of guest kernel and modules
559 */
560 dso__load(kernel, machine->vmlinux_maps[MAP__FUNCTION],
561 NULL);
562 }
563 }
564 return 0;
565out_problem:
566 return -1;
567}
568
569int event__process_mmap(event_t *self, struct sample_data *sample __used,
570 struct perf_session *session)
571{
572 struct machine *machine;
573 struct thread *thread;
574 struct map *map;
575 u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
576 int ret = 0;
577
578 dump_printf(" %d/%d: [%#Lx(%#Lx) @ %#Lx]: %s\n",
579 self->mmap.pid, self->mmap.tid, self->mmap.start,
580 self->mmap.len, self->mmap.pgoff, self->mmap.filename);
581
582 if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL ||
583 cpumode == PERF_RECORD_MISC_KERNEL) {
584 ret = event__process_kernel_mmap(self, session);
585 if (ret < 0)
586 goto out_problem;
587 return 0;
588 }
589
590 machine = perf_session__find_host_machine(session);
591 if (machine == NULL)
592 goto out_problem;
593 thread = perf_session__findnew(session, self->mmap.pid);
594 if (thread == NULL)
595 goto out_problem;
596 map = map__new(&machine->user_dsos, self->mmap.start,
597 self->mmap.len, self->mmap.pgoff,
598 self->mmap.pid, self->mmap.filename,
599 MAP__FUNCTION);
600 if (map == NULL)
601 goto out_problem;
602
603 thread__insert_map(thread, map);
604 return 0;
605
606out_problem:
607 dump_printf("problem processing PERF_RECORD_MMAP, skipping event.\n");
608 return 0;
609}
610
611int event__process_task(event_t *self, struct sample_data *sample __used,
612 struct perf_session *session)
613{
614 struct thread *thread = perf_session__findnew(session, self->fork.tid);
615 struct thread *parent = perf_session__findnew(session, self->fork.ptid);
616
617 dump_printf("(%d:%d):(%d:%d)\n", self->fork.pid, self->fork.tid,
618 self->fork.ppid, self->fork.ptid);
619
620 if (self->header.type == PERF_RECORD_EXIT) {
621 perf_session__remove_thread(session, thread);
622 return 0;
623 }
624
625 if (thread == NULL || parent == NULL ||
626 thread__fork(thread, parent) < 0) {
627 dump_printf("problem processing PERF_RECORD_FORK, skipping event.\n");
628 return -1;
629 }
630
631 return 0;
632}
633
634int event__process(event_t *event, struct sample_data *sample,
635 struct perf_session *session)
636{
637 switch (event->header.type) {
638 case PERF_RECORD_COMM:
639 event__process_comm(event, sample, session);
640 break;
641 case PERF_RECORD_MMAP:
642 event__process_mmap(event, sample, session);
643 break;
644 case PERF_RECORD_FORK:
645 case PERF_RECORD_EXIT:
646 event__process_task(event, sample, session);
647 break;
648 default:
649 break;
650 }
651
652 return 0;
653}
654
655void thread__find_addr_map(struct thread *self,
656 struct perf_session *session, u8 cpumode,
657 enum map_type type, pid_t pid, u64 addr,
658 struct addr_location *al)
659{
660 struct map_groups *mg = &self->mg;
661 struct machine *machine = NULL;
662
663 al->thread = self;
664 al->addr = addr;
665 al->cpumode = cpumode;
666 al->filtered = false;
667
668 if (cpumode == PERF_RECORD_MISC_KERNEL && perf_host) {
669 al->level = 'k';
670 machine = perf_session__find_host_machine(session);
671 if (machine == NULL) {
672 al->map = NULL;
673 return;
674 }
675 mg = &machine->kmaps;
676 } else if (cpumode == PERF_RECORD_MISC_USER && perf_host) {
677 al->level = '.';
678 machine = perf_session__find_host_machine(session);
679 } else if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL && perf_guest) {
680 al->level = 'g';
681 machine = perf_session__find_machine(session, pid);
682 if (machine == NULL) {
683 al->map = NULL;
684 return;
685 }
686 mg = &machine->kmaps;
687 } else {
688 /*
689 * 'u' means guest os user space.
690 * TODO: We don't support guest user space. Might support late.
691 */
692 if (cpumode == PERF_RECORD_MISC_GUEST_USER && perf_guest)
693 al->level = 'u';
694 else
695 al->level = 'H';
696 al->map = NULL;
697
698 if ((cpumode == PERF_RECORD_MISC_GUEST_USER ||
699 cpumode == PERF_RECORD_MISC_GUEST_KERNEL) &&
700 !perf_guest)
701 al->filtered = true;
702 if ((cpumode == PERF_RECORD_MISC_USER ||
703 cpumode == PERF_RECORD_MISC_KERNEL) &&
704 !perf_host)
705 al->filtered = true;
706
707 return;
708 }
709try_again:
710 al->map = map_groups__find(mg, type, al->addr);
711 if (al->map == NULL) {
712 /*
713 * If this is outside of all known maps, and is a negative
714 * address, try to look it up in the kernel dso, as it might be
715 * a vsyscall or vdso (which executes in user-mode).
716 *
717 * XXX This is nasty, we should have a symbol list in the
718 * "[vdso]" dso, but for now lets use the old trick of looking
719 * in the whole kernel symbol list.
720 */
721 if ((long long)al->addr < 0 &&
722 cpumode == PERF_RECORD_MISC_KERNEL &&
723 machine && mg != &machine->kmaps) {
724 mg = &machine->kmaps;
725 goto try_again;
726 }
727 } else
728 al->addr = al->map->map_ip(al->map, al->addr);
729}
730
731void thread__find_addr_location(struct thread *self,
732 struct perf_session *session, u8 cpumode,
733 enum map_type type, pid_t pid, u64 addr,
734 struct addr_location *al,
735 symbol_filter_t filter)
736{
737 thread__find_addr_map(self, session, cpumode, type, pid, addr, al);
738 if (al->map != NULL)
739 al->sym = map__find_symbol(al->map, al->addr, filter);
740 else
741 al->sym = NULL;
742}
743
744static void dso__calc_col_width(struct dso *self, struct hists *hists)
745{
746 if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep &&
747 (!symbol_conf.dso_list ||
748 strlist__has_entry(symbol_conf.dso_list, self->name))) {
749 u16 slen = dso__name_len(self);
750 hists__new_col_len(hists, HISTC_DSO, slen);
751 }
752
753 self->slen_calculated = 1;
754}
755
756int event__preprocess_sample(const event_t *self, struct perf_session *session,
757 struct addr_location *al, struct sample_data *data,
758 symbol_filter_t filter)
759{
760 u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
761 struct thread *thread = perf_session__findnew(session, self->ip.pid);
762
763 if (thread == NULL)
764 return -1;
765
766 if (symbol_conf.comm_list &&
767 !strlist__has_entry(symbol_conf.comm_list, thread->comm))
768 goto out_filtered;
769
770 dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid);
771 /*
772 * Have we already created the kernel maps for the host machine?
773 *
774 * This should have happened earlier, when we processed the kernel MMAP
775 * events, but for older perf.data files there was no such thing, so do
776 * it now.
777 */
778 if (cpumode == PERF_RECORD_MISC_KERNEL &&
779 session->host_machine.vmlinux_maps[MAP__FUNCTION] == NULL)
780 machine__create_kernel_maps(&session->host_machine);
781
782 thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION,
783 self->ip.pid, self->ip.ip, al);
784 dump_printf(" ...... dso: %s\n",
785 al->map ? al->map->dso->long_name :
786 al->level == 'H' ? "[hypervisor]" : "<not found>");
787 al->sym = NULL;
788 al->cpu = data->cpu;
789
790 if (al->map) {
791 if (symbol_conf.dso_list &&
792 (!al->map || !al->map->dso ||
793 !(strlist__has_entry(symbol_conf.dso_list,
794 al->map->dso->short_name) ||
795 (al->map->dso->short_name != al->map->dso->long_name &&
796 strlist__has_entry(symbol_conf.dso_list,
797 al->map->dso->long_name)))))
798 goto out_filtered;
799 /*
800 * We have to do this here as we may have a dso with no symbol
801 * hit that has a name longer than the ones with symbols
802 * sampled.
803 */
804 if (!sort_dso.elide && !al->map->dso->slen_calculated)
805 dso__calc_col_width(al->map->dso, &session->hists);
806
807 al->sym = map__find_symbol(al->map, al->addr, filter);
808 } else {
809 const unsigned int unresolved_col_width = BITS_PER_LONG / 4;
810
811 if (hists__col_len(&session->hists, HISTC_DSO) < unresolved_col_width &&
812 !symbol_conf.col_width_list_str && !symbol_conf.field_sep &&
813 !symbol_conf.dso_list)
814 hists__set_col_len(&session->hists, HISTC_DSO,
815 unresolved_col_width);
816 }
817
818 if (symbol_conf.sym_list && al->sym &&
819 !strlist__has_entry(symbol_conf.sym_list, al->sym->name))
820 goto out_filtered;
821
822 return 0;
823
824out_filtered:
825 al->filtered = true;
826 return 0;
827}
828
829static int event__parse_id_sample(const event_t *event,
830 struct perf_session *session,
831 struct sample_data *sample)
832{
833 const u64 *array;
834 u64 type;
835
836 sample->cpu = sample->pid = sample->tid = -1;
837 sample->stream_id = sample->id = sample->time = -1ULL;
838
839 if (!session->sample_id_all)
840 return 0;
841
842 array = event->sample.array;
843 array += ((event->header.size -
844 sizeof(event->header)) / sizeof(u64)) - 1;
845 type = session->sample_type;
846
847 if (type & PERF_SAMPLE_CPU) {
848 u32 *p = (u32 *)array;
849 sample->cpu = *p;
850 array--;
851 }
852
853 if (type & PERF_SAMPLE_STREAM_ID) {
854 sample->stream_id = *array;
855 array--;
856 }
857
858 if (type & PERF_SAMPLE_ID) {
859 sample->id = *array;
860 array--;
861 }
862
863 if (type & PERF_SAMPLE_TIME) {
864 sample->time = *array;
865 array--;
866 }
867
868 if (type & PERF_SAMPLE_TID) {
869 u32 *p = (u32 *)array;
870 sample->pid = p[0];
871 sample->tid = p[1];
872 }
873
874 return 0;
875}
876
877int event__parse_sample(const event_t *event, struct perf_session *session,
878 struct sample_data *data)
879{
880 const u64 *array;
881 u64 type;
882
883 if (event->header.type != PERF_RECORD_SAMPLE)
884 return event__parse_id_sample(event, session, data);
885
886 array = event->sample.array;
887 type = session->sample_type;
888
889 if (type & PERF_SAMPLE_IP) {
890 data->ip = event->ip.ip;
891 array++;
892 }
893
894 if (type & PERF_SAMPLE_TID) {
895 u32 *p = (u32 *)array;
896 data->pid = p[0];
897 data->tid = p[1];
898 array++;
899 }
900
901 if (type & PERF_SAMPLE_TIME) {
902 data->time = *array;
903 array++;
904 }
905
906 if (type & PERF_SAMPLE_ADDR) {
907 data->addr = *array;
908 array++;
909 }
910
911 data->id = -1ULL;
912 if (type & PERF_SAMPLE_ID) {
913 data->id = *array;
914 array++;
915 }
916
917 if (type & PERF_SAMPLE_STREAM_ID) {
918 data->stream_id = *array;
919 array++;
920 }
921
922 if (type & PERF_SAMPLE_CPU) {
923 u32 *p = (u32 *)array;
924 data->cpu = *p;
925 array++;
926 } else
927 data->cpu = -1;
928
929 if (type & PERF_SAMPLE_PERIOD) {
930 data->period = *array;
931 array++;
932 }
933
934 if (type & PERF_SAMPLE_READ) {
935 pr_debug("PERF_SAMPLE_READ is unsuported for now\n");
936 return -1;
937 }
938
939 if (type & PERF_SAMPLE_CALLCHAIN) {
940 data->callchain = (struct ip_callchain *)array;
941 array += 1 + data->callchain->nr;
942 }
943
944 if (type & PERF_SAMPLE_RAW) {
945 u32 *p = (u32 *)array;
946 data->raw_size = *p;
947 p++;
948 data->raw_data = p;
949 }
950
951 return 0;
952}
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
new file mode 100644
index 00000000000..2b7e91902f1
--- /dev/null
+++ b/tools/perf/util/event.h
@@ -0,0 +1,177 @@
1#ifndef __PERF_RECORD_H
2#define __PERF_RECORD_H
3
4#include <limits.h>
5
6#include "../perf.h"
7#include "map.h"
8
9/*
10 * PERF_SAMPLE_IP | PERF_SAMPLE_TID | *
11 */
12struct ip_event {
13 struct perf_event_header header;
14 u64 ip;
15 u32 pid, tid;
16 unsigned char __more_data[];
17};
18
19struct mmap_event {
20 struct perf_event_header header;
21 u32 pid, tid;
22 u64 start;
23 u64 len;
24 u64 pgoff;
25 char filename[PATH_MAX];
26};
27
28struct comm_event {
29 struct perf_event_header header;
30 u32 pid, tid;
31 char comm[16];
32};
33
34struct fork_event {
35 struct perf_event_header header;
36 u32 pid, ppid;
37 u32 tid, ptid;
38 u64 time;
39};
40
41struct lost_event {
42 struct perf_event_header header;
43 u64 id;
44 u64 lost;
45};
46
47/*
48 * PERF_FORMAT_ENABLED | PERF_FORMAT_RUNNING | PERF_FORMAT_ID
49 */
50struct read_event {
51 struct perf_event_header header;
52 u32 pid, tid;
53 u64 value;
54 u64 time_enabled;
55 u64 time_running;
56 u64 id;
57};
58
59struct sample_event {
60 struct perf_event_header header;
61 u64 array[];
62};
63
64struct sample_data {
65 u64 ip;
66 u32 pid, tid;
67 u64 time;
68 u64 addr;
69 u64 id;
70 u64 stream_id;
71 u64 period;
72 u32 cpu;
73 u32 raw_size;
74 void *raw_data;
75 struct ip_callchain *callchain;
76};
77
78#define BUILD_ID_SIZE 20
79
80struct build_id_event {
81 struct perf_event_header header;
82 pid_t pid;
83 u8 build_id[ALIGN(BUILD_ID_SIZE, sizeof(u64))];
84 char filename[];
85};
86
87enum perf_user_event_type { /* above any possible kernel type */
88 PERF_RECORD_USER_TYPE_START = 64,
89 PERF_RECORD_HEADER_ATTR = 64,
90 PERF_RECORD_HEADER_EVENT_TYPE = 65,
91 PERF_RECORD_HEADER_TRACING_DATA = 66,
92 PERF_RECORD_HEADER_BUILD_ID = 67,
93 PERF_RECORD_FINISHED_ROUND = 68,
94 PERF_RECORD_HEADER_MAX
95};
96
97struct attr_event {
98 struct perf_event_header header;
99 struct perf_event_attr attr;
100 u64 id[];
101};
102
103#define MAX_EVENT_NAME 64
104
105struct perf_trace_event_type {
106 u64 event_id;
107 char name[MAX_EVENT_NAME];
108};
109
110struct event_type_event {
111 struct perf_event_header header;
112 struct perf_trace_event_type event_type;
113};
114
115struct tracing_data_event {
116 struct perf_event_header header;
117 u32 size;
118};
119
120typedef union event_union {
121 struct perf_event_header header;
122 struct ip_event ip;
123 struct mmap_event mmap;
124 struct comm_event comm;
125 struct fork_event fork;
126 struct lost_event lost;
127 struct read_event read;
128 struct sample_event sample;
129 struct attr_event attr;
130 struct event_type_event event_type;
131 struct tracing_data_event tracing_data;
132 struct build_id_event build_id;
133} event_t;
134
135void event__print_totals(void);
136
137struct perf_session;
138
139typedef int (*event__handler_synth_t)(event_t *event,
140 struct perf_session *session);
141typedef int (*event__handler_t)(event_t *event, struct sample_data *sample,
142 struct perf_session *session);
143
144int event__synthesize_thread(pid_t pid, event__handler_t process,
145 struct perf_session *session);
146int event__synthesize_threads(event__handler_t process,
147 struct perf_session *session);
148int event__synthesize_kernel_mmap(event__handler_t process,
149 struct perf_session *session,
150 struct machine *machine,
151 const char *symbol_name);
152
153int event__synthesize_modules(event__handler_t process,
154 struct perf_session *session,
155 struct machine *machine);
156
157int event__process_comm(event_t *self, struct sample_data *sample,
158 struct perf_session *session);
159int event__process_lost(event_t *self, struct sample_data *sample,
160 struct perf_session *session);
161int event__process_mmap(event_t *self, struct sample_data *sample,
162 struct perf_session *session);
163int event__process_task(event_t *self, struct sample_data *sample,
164 struct perf_session *session);
165int event__process(event_t *event, struct sample_data *sample,
166 struct perf_session *session);
167
168struct addr_location;
169int event__preprocess_sample(const event_t *self, struct perf_session *session,
170 struct addr_location *al, struct sample_data *data,
171 symbol_filter_t filter);
172int event__parse_sample(const event_t *event, struct perf_session *session,
173 struct sample_data *sample);
174
175const char *event__get_event_name(unsigned int id);
176
177#endif /* __PERF_RECORD_H */
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
new file mode 100644
index 00000000000..f5cfed60af9
--- /dev/null
+++ b/tools/perf/util/evsel.c
@@ -0,0 +1,201 @@
1#include "evsel.h"
2#include "../perf.h"
3#include "util.h"
4#include "cpumap.h"
5#include "thread.h"
6
7#define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y))
8
9struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx)
10{
11 struct perf_evsel *evsel = zalloc(sizeof(*evsel));
12
13 if (evsel != NULL) {
14 evsel->idx = idx;
15 evsel->attr = *attr;
16 INIT_LIST_HEAD(&evsel->node);
17 }
18
19 return evsel;
20}
21
22int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads)
23{
24 evsel->fd = xyarray__new(ncpus, nthreads, sizeof(int));
25 return evsel->fd != NULL ? 0 : -ENOMEM;
26}
27
28int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus)
29{
30 evsel->counts = zalloc((sizeof(*evsel->counts) +
31 (ncpus * sizeof(struct perf_counts_values))));
32 return evsel->counts != NULL ? 0 : -ENOMEM;
33}
34
35void perf_evsel__free_fd(struct perf_evsel *evsel)
36{
37 xyarray__delete(evsel->fd);
38 evsel->fd = NULL;
39}
40
41void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads)
42{
43 int cpu, thread;
44
45 for (cpu = 0; cpu < ncpus; cpu++)
46 for (thread = 0; thread < nthreads; ++thread) {
47 close(FD(evsel, cpu, thread));
48 FD(evsel, cpu, thread) = -1;
49 }
50}
51
52void perf_evsel__delete(struct perf_evsel *evsel)
53{
54 assert(list_empty(&evsel->node));
55 xyarray__delete(evsel->fd);
56 free(evsel);
57}
58
59int __perf_evsel__read_on_cpu(struct perf_evsel *evsel,
60 int cpu, int thread, bool scale)
61{
62 struct perf_counts_values count;
63 size_t nv = scale ? 3 : 1;
64
65 if (FD(evsel, cpu, thread) < 0)
66 return -EINVAL;
67
68 if (evsel->counts == NULL && perf_evsel__alloc_counts(evsel, cpu + 1) < 0)
69 return -ENOMEM;
70
71 if (readn(FD(evsel, cpu, thread), &count, nv * sizeof(u64)) < 0)
72 return -errno;
73
74 if (scale) {
75 if (count.run == 0)
76 count.val = 0;
77 else if (count.run < count.ena)
78 count.val = (u64)((double)count.val * count.ena / count.run + 0.5);
79 } else
80 count.ena = count.run = 0;
81
82 evsel->counts->cpu[cpu] = count;
83 return 0;
84}
85
86int __perf_evsel__read(struct perf_evsel *evsel,
87 int ncpus, int nthreads, bool scale)
88{
89 size_t nv = scale ? 3 : 1;
90 int cpu, thread;
91 struct perf_counts_values *aggr = &evsel->counts->aggr, count;
92
93 aggr->val = 0;
94
95 for (cpu = 0; cpu < ncpus; cpu++) {
96 for (thread = 0; thread < nthreads; thread++) {
97 if (FD(evsel, cpu, thread) < 0)
98 continue;
99
100 if (readn(FD(evsel, cpu, thread),
101 &count, nv * sizeof(u64)) < 0)
102 return -errno;
103
104 aggr->val += count.val;
105 if (scale) {
106 aggr->ena += count.ena;
107 aggr->run += count.run;
108 }
109 }
110 }
111
112 evsel->counts->scaled = 0;
113 if (scale) {
114 if (aggr->run == 0) {
115 evsel->counts->scaled = -1;
116 aggr->val = 0;
117 return 0;
118 }
119
120 if (aggr->run < aggr->ena) {
121 evsel->counts->scaled = 1;
122 aggr->val = (u64)((double)aggr->val * aggr->ena / aggr->run + 0.5);
123 }
124 } else
125 aggr->ena = aggr->run = 0;
126
127 return 0;
128}
129
130static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus,
131 struct thread_map *threads)
132{
133 int cpu, thread;
134
135 if (evsel->fd == NULL &&
136 perf_evsel__alloc_fd(evsel, cpus->nr, threads->nr) < 0)
137 return -1;
138
139 for (cpu = 0; cpu < cpus->nr; cpu++) {
140 for (thread = 0; thread < threads->nr; thread++) {
141 FD(evsel, cpu, thread) = sys_perf_event_open(&evsel->attr,
142 threads->map[thread],
143 cpus->map[cpu], -1, 0);
144 if (FD(evsel, cpu, thread) < 0)
145 goto out_close;
146 }
147 }
148
149 return 0;
150
151out_close:
152 do {
153 while (--thread >= 0) {
154 close(FD(evsel, cpu, thread));
155 FD(evsel, cpu, thread) = -1;
156 }
157 thread = threads->nr;
158 } while (--cpu >= 0);
159 return -1;
160}
161
162static struct {
163 struct cpu_map map;
164 int cpus[1];
165} empty_cpu_map = {
166 .map.nr = 1,
167 .cpus = { -1, },
168};
169
170static struct {
171 struct thread_map map;
172 int threads[1];
173} empty_thread_map = {
174 .map.nr = 1,
175 .threads = { -1, },
176};
177
178int perf_evsel__open(struct perf_evsel *evsel,
179 struct cpu_map *cpus, struct thread_map *threads)
180{
181
182 if (cpus == NULL) {
183 /* Work around old compiler warnings about strict aliasing */
184 cpus = &empty_cpu_map.map;
185 }
186
187 if (threads == NULL)
188 threads = &empty_thread_map.map;
189
190 return __perf_evsel__open(evsel, cpus, threads);
191}
192
193int perf_evsel__open_per_cpu(struct perf_evsel *evsel, struct cpu_map *cpus)
194{
195 return __perf_evsel__open(evsel, cpus, &empty_thread_map.map);
196}
197
198int perf_evsel__open_per_thread(struct perf_evsel *evsel, struct thread_map *threads)
199{
200 return __perf_evsel__open(evsel, &empty_cpu_map.map, threads);
201}
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h
new file mode 100644
index 00000000000..b2d755fe88a
--- /dev/null
+++ b/tools/perf/util/evsel.h
@@ -0,0 +1,115 @@
1#ifndef __PERF_EVSEL_H
2#define __PERF_EVSEL_H 1
3
4#include <linux/list.h>
5#include <stdbool.h>
6#include "../../../include/linux/perf_event.h"
7#include "types.h"
8#include "xyarray.h"
9
10struct perf_counts_values {
11 union {
12 struct {
13 u64 val;
14 u64 ena;
15 u64 run;
16 };
17 u64 values[3];
18 };
19};
20
21struct perf_counts {
22 s8 scaled;
23 struct perf_counts_values aggr;
24 struct perf_counts_values cpu[];
25};
26
27struct perf_evsel {
28 struct list_head node;
29 struct perf_event_attr attr;
30 char *filter;
31 struct xyarray *fd;
32 struct perf_counts *counts;
33 int idx;
34 void *priv;
35};
36
37struct cpu_map;
38struct thread_map;
39
40struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx);
41void perf_evsel__delete(struct perf_evsel *evsel);
42
43int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads);
44int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus);
45void perf_evsel__free_fd(struct perf_evsel *evsel);
46void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads);
47
48int perf_evsel__open_per_cpu(struct perf_evsel *evsel, struct cpu_map *cpus);
49int perf_evsel__open_per_thread(struct perf_evsel *evsel, struct thread_map *threads);
50int perf_evsel__open(struct perf_evsel *evsel,
51 struct cpu_map *cpus, struct thread_map *threads);
52
53#define perf_evsel__match(evsel, t, c) \
54 (evsel->attr.type == PERF_TYPE_##t && \
55 evsel->attr.config == PERF_COUNT_##c)
56
57int __perf_evsel__read_on_cpu(struct perf_evsel *evsel,
58 int cpu, int thread, bool scale);
59
60/**
61 * perf_evsel__read_on_cpu - Read out the results on a CPU and thread
62 *
63 * @evsel - event selector to read value
64 * @cpu - CPU of interest
65 * @thread - thread of interest
66 */
67static inline int perf_evsel__read_on_cpu(struct perf_evsel *evsel,
68 int cpu, int thread)
69{
70 return __perf_evsel__read_on_cpu(evsel, cpu, thread, false);
71}
72
73/**
74 * perf_evsel__read_on_cpu_scaled - Read out the results on a CPU and thread, scaled
75 *
76 * @evsel - event selector to read value
77 * @cpu - CPU of interest
78 * @thread - thread of interest
79 */
80static inline int perf_evsel__read_on_cpu_scaled(struct perf_evsel *evsel,
81 int cpu, int thread)
82{
83 return __perf_evsel__read_on_cpu(evsel, cpu, thread, true);
84}
85
86int __perf_evsel__read(struct perf_evsel *evsel, int ncpus, int nthreads,
87 bool scale);
88
89/**
90 * perf_evsel__read - Read the aggregate results on all CPUs
91 *
92 * @evsel - event selector to read value
93 * @ncpus - Number of cpus affected, from zero
94 * @nthreads - Number of threads affected, from zero
95 */
96static inline int perf_evsel__read(struct perf_evsel *evsel,
97 int ncpus, int nthreads)
98{
99 return __perf_evsel__read(evsel, ncpus, nthreads, false);
100}
101
102/**
103 * perf_evsel__read_scaled - Read the aggregate results on all CPUs, scaled
104 *
105 * @evsel - event selector to read value
106 * @ncpus - Number of cpus affected, from zero
107 * @nthreads - Number of threads affected, from zero
108 */
109static inline int perf_evsel__read_scaled(struct perf_evsel *evsel,
110 int ncpus, int nthreads)
111{
112 return __perf_evsel__read(evsel, ncpus, nthreads, true);
113}
114
115#endif /* __PERF_EVSEL_H */
diff --git a/tools/perf/util/exec_cmd.c b/tools/perf/util/exec_cmd.c
index d3929226315..67eeff57156 100644
--- a/tools/perf/util/exec_cmd.c
+++ b/tools/perf/util/exec_cmd.c
@@ -1,9 +1,11 @@
1#include "cache.h" 1#include "cache.h"
2#include "exec_cmd.h" 2#include "exec_cmd.h"
3#include "quote.h" 3#include "quote.h"
4
5#include <string.h>
6
4#define MAX_ARGS 32 7#define MAX_ARGS 32
5 8
6extern char **environ;
7static const char *argv_exec_path; 9static const char *argv_exec_path;
8static const char *argv0_path; 10static const char *argv0_path;
9 11
@@ -52,7 +54,7 @@ const char *perf_extract_argv0_path(const char *argv0)
52 54
53 if (slash >= argv0) { 55 if (slash >= argv0) {
54 argv0_path = strndup(argv0, slash - argv0); 56 argv0_path = strndup(argv0, slash - argv0);
55 return slash + 1; 57 return argv0_path ? slash + 1 : NULL;
56 } 58 }
57 59
58 return argv0; 60 return argv0;
@@ -114,7 +116,7 @@ void setup_path(void)
114 strbuf_release(&new_path); 116 strbuf_release(&new_path);
115} 117}
116 118
117const char **prepare_perf_cmd(const char **argv) 119static const char **prepare_perf_cmd(const char **argv)
118{ 120{
119 int argc; 121 int argc;
120 const char **nargv; 122 const char **nargv;
diff --git a/tools/perf/util/exec_cmd.h b/tools/perf/util/exec_cmd.h
index effe25eb154..bc4b915963f 100644
--- a/tools/perf/util/exec_cmd.h
+++ b/tools/perf/util/exec_cmd.h
@@ -1,13 +1,12 @@
1#ifndef PERF_EXEC_CMD_H 1#ifndef __PERF_EXEC_CMD_H
2#define PERF_EXEC_CMD_H 2#define __PERF_EXEC_CMD_H
3 3
4extern void perf_set_argv_exec_path(const char *exec_path); 4extern void perf_set_argv_exec_path(const char *exec_path);
5extern const char *perf_extract_argv0_path(const char *path); 5extern const char *perf_extract_argv0_path(const char *path);
6extern const char *perf_exec_path(void); 6extern const char *perf_exec_path(void);
7extern void setup_path(void); 7extern void setup_path(void);
8extern const char **prepare_perf_cmd(const char **argv);
9extern int execv_perf_cmd(const char **argv); /* NULL terminated */ 8extern int execv_perf_cmd(const char **argv); /* NULL terminated */
10extern int execl_perf_cmd(const char *cmd, ...); 9extern int execl_perf_cmd(const char *cmd, ...);
11extern const char *system_path(const char *path); 10extern const char *system_path(const char *path);
12 11
13#endif /* PERF_EXEC_CMD_H */ 12#endif /* __PERF_EXEC_CMD_H */
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
new file mode 100644
index 00000000000..989fa2dee2f
--- /dev/null
+++ b/tools/perf/util/header.c
@@ -0,0 +1,1232 @@
1#define _FILE_OFFSET_BITS 64
2
3#include <sys/types.h>
4#include <byteswap.h>
5#include <unistd.h>
6#include <stdio.h>
7#include <stdlib.h>
8#include <linux/list.h>
9#include <linux/kernel.h>
10
11#include "util.h"
12#include "header.h"
13#include "../perf.h"
14#include "trace-event.h"
15#include "session.h"
16#include "symbol.h"
17#include "debug.h"
18
19static bool no_buildid_cache = false;
20
21/*
22 * Create new perf.data header attribute:
23 */
24struct perf_header_attr *perf_header_attr__new(struct perf_event_attr *attr)
25{
26 struct perf_header_attr *self = malloc(sizeof(*self));
27
28 if (self != NULL) {
29 self->attr = *attr;
30 self->ids = 0;
31 self->size = 1;
32 self->id = malloc(sizeof(u64));
33 if (self->id == NULL) {
34 free(self);
35 self = NULL;
36 }
37 }
38
39 return self;
40}
41
42void perf_header_attr__delete(struct perf_header_attr *self)
43{
44 free(self->id);
45 free(self);
46}
47
48int perf_header_attr__add_id(struct perf_header_attr *self, u64 id)
49{
50 int pos = self->ids;
51
52 self->ids++;
53 if (self->ids > self->size) {
54 int nsize = self->size * 2;
55 u64 *nid = realloc(self->id, nsize * sizeof(u64));
56
57 if (nid == NULL)
58 return -1;
59
60 self->size = nsize;
61 self->id = nid;
62 }
63 self->id[pos] = id;
64 return 0;
65}
66
67int perf_header__init(struct perf_header *self)
68{
69 self->size = 1;
70 self->attr = malloc(sizeof(void *));
71 return self->attr == NULL ? -ENOMEM : 0;
72}
73
74void perf_header__exit(struct perf_header *self)
75{
76 int i;
77 for (i = 0; i < self->attrs; ++i)
78 perf_header_attr__delete(self->attr[i]);
79 free(self->attr);
80}
81
82int perf_header__add_attr(struct perf_header *self,
83 struct perf_header_attr *attr)
84{
85 if (self->frozen)
86 return -1;
87
88 if (self->attrs == self->size) {
89 int nsize = self->size * 2;
90 struct perf_header_attr **nattr;
91
92 nattr = realloc(self->attr, nsize * sizeof(void *));
93 if (nattr == NULL)
94 return -1;
95
96 self->size = nsize;
97 self->attr = nattr;
98 }
99
100 self->attr[self->attrs++] = attr;
101 return 0;
102}
103
104static int event_count;
105static struct perf_trace_event_type *events;
106
107int perf_header__push_event(u64 id, const char *name)
108{
109 if (strlen(name) > MAX_EVENT_NAME)
110 pr_warning("Event %s will be truncated\n", name);
111
112 if (!events) {
113 events = malloc(sizeof(struct perf_trace_event_type));
114 if (events == NULL)
115 return -ENOMEM;
116 } else {
117 struct perf_trace_event_type *nevents;
118
119 nevents = realloc(events, (event_count + 1) * sizeof(*events));
120 if (nevents == NULL)
121 return -ENOMEM;
122 events = nevents;
123 }
124 memset(&events[event_count], 0, sizeof(struct perf_trace_event_type));
125 events[event_count].event_id = id;
126 strncpy(events[event_count].name, name, MAX_EVENT_NAME - 1);
127 event_count++;
128 return 0;
129}
130
131char *perf_header__find_event(u64 id)
132{
133 int i;
134 for (i = 0 ; i < event_count; i++) {
135 if (events[i].event_id == id)
136 return events[i].name;
137 }
138 return NULL;
139}
140
141static const char *__perf_magic = "PERFFILE";
142
143#define PERF_MAGIC (*(u64 *)__perf_magic)
144
145struct perf_file_attr {
146 struct perf_event_attr attr;
147 struct perf_file_section ids;
148};
149
150void perf_header__set_feat(struct perf_header *self, int feat)
151{
152 set_bit(feat, self->adds_features);
153}
154
155void perf_header__clear_feat(struct perf_header *self, int feat)
156{
157 clear_bit(feat, self->adds_features);
158}
159
160bool perf_header__has_feat(const struct perf_header *self, int feat)
161{
162 return test_bit(feat, self->adds_features);
163}
164
165static int do_write(int fd, const void *buf, size_t size)
166{
167 while (size) {
168 int ret = write(fd, buf, size);
169
170 if (ret < 0)
171 return -errno;
172
173 size -= ret;
174 buf += ret;
175 }
176
177 return 0;
178}
179
180#define NAME_ALIGN 64
181
182static int write_padded(int fd, const void *bf, size_t count,
183 size_t count_aligned)
184{
185 static const char zero_buf[NAME_ALIGN];
186 int err = do_write(fd, bf, count);
187
188 if (!err)
189 err = do_write(fd, zero_buf, count_aligned - count);
190
191 return err;
192}
193
194#define dsos__for_each_with_build_id(pos, head) \
195 list_for_each_entry(pos, head, node) \
196 if (!pos->has_build_id) \
197 continue; \
198 else
199
200static int __dsos__write_buildid_table(struct list_head *head, pid_t pid,
201 u16 misc, int fd)
202{
203 struct dso *pos;
204
205 dsos__for_each_with_build_id(pos, head) {
206 int err;
207 struct build_id_event b;
208 size_t len;
209
210 if (!pos->hit)
211 continue;
212 len = pos->long_name_len + 1;
213 len = ALIGN(len, NAME_ALIGN);
214 memset(&b, 0, sizeof(b));
215 memcpy(&b.build_id, pos->build_id, sizeof(pos->build_id));
216 b.pid = pid;
217 b.header.misc = misc;
218 b.header.size = sizeof(b) + len;
219 err = do_write(fd, &b, sizeof(b));
220 if (err < 0)
221 return err;
222 err = write_padded(fd, pos->long_name,
223 pos->long_name_len + 1, len);
224 if (err < 0)
225 return err;
226 }
227
228 return 0;
229}
230
231static int machine__write_buildid_table(struct machine *self, int fd)
232{
233 int err;
234 u16 kmisc = PERF_RECORD_MISC_KERNEL,
235 umisc = PERF_RECORD_MISC_USER;
236
237 if (!machine__is_host(self)) {
238 kmisc = PERF_RECORD_MISC_GUEST_KERNEL;
239 umisc = PERF_RECORD_MISC_GUEST_USER;
240 }
241
242 err = __dsos__write_buildid_table(&self->kernel_dsos, self->pid,
243 kmisc, fd);
244 if (err == 0)
245 err = __dsos__write_buildid_table(&self->user_dsos,
246 self->pid, umisc, fd);
247 return err;
248}
249
250static int dsos__write_buildid_table(struct perf_header *header, int fd)
251{
252 struct perf_session *session = container_of(header,
253 struct perf_session, header);
254 struct rb_node *nd;
255 int err = machine__write_buildid_table(&session->host_machine, fd);
256
257 if (err)
258 return err;
259
260 for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) {
261 struct machine *pos = rb_entry(nd, struct machine, rb_node);
262 err = machine__write_buildid_table(pos, fd);
263 if (err)
264 break;
265 }
266 return err;
267}
268
269int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
270 const char *name, bool is_kallsyms)
271{
272 const size_t size = PATH_MAX;
273 char *realname = realpath(name, NULL),
274 *filename = malloc(size),
275 *linkname = malloc(size), *targetname;
276 int len, err = -1;
277
278 if (realname == NULL || filename == NULL || linkname == NULL)
279 goto out_free;
280
281 len = snprintf(filename, size, "%s%s%s",
282 debugdir, is_kallsyms ? "/" : "", realname);
283 if (mkdir_p(filename, 0755))
284 goto out_free;
285
286 snprintf(filename + len, sizeof(filename) - len, "/%s", sbuild_id);
287
288 if (access(filename, F_OK)) {
289 if (is_kallsyms) {
290 if (copyfile("/proc/kallsyms", filename))
291 goto out_free;
292 } else if (link(realname, filename) && copyfile(name, filename))
293 goto out_free;
294 }
295
296 len = snprintf(linkname, size, "%s/.build-id/%.2s",
297 debugdir, sbuild_id);
298
299 if (access(linkname, X_OK) && mkdir_p(linkname, 0755))
300 goto out_free;
301
302 snprintf(linkname + len, size - len, "/%s", sbuild_id + 2);
303 targetname = filename + strlen(debugdir) - 5;
304 memcpy(targetname, "../..", 5);
305
306 if (symlink(targetname, linkname) == 0)
307 err = 0;
308out_free:
309 free(realname);
310 free(filename);
311 free(linkname);
312 return err;
313}
314
315static int build_id_cache__add_b(const u8 *build_id, size_t build_id_size,
316 const char *name, const char *debugdir,
317 bool is_kallsyms)
318{
319 char sbuild_id[BUILD_ID_SIZE * 2 + 1];
320
321 build_id__sprintf(build_id, build_id_size, sbuild_id);
322
323 return build_id_cache__add_s(sbuild_id, debugdir, name, is_kallsyms);
324}
325
326int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir)
327{
328 const size_t size = PATH_MAX;
329 char *filename = malloc(size),
330 *linkname = malloc(size);
331 int err = -1;
332
333 if (filename == NULL || linkname == NULL)
334 goto out_free;
335
336 snprintf(linkname, size, "%s/.build-id/%.2s/%s",
337 debugdir, sbuild_id, sbuild_id + 2);
338
339 if (access(linkname, F_OK))
340 goto out_free;
341
342 if (readlink(linkname, filename, size) < 0)
343 goto out_free;
344
345 if (unlink(linkname))
346 goto out_free;
347
348 /*
349 * Since the link is relative, we must make it absolute:
350 */
351 snprintf(linkname, size, "%s/.build-id/%.2s/%s",
352 debugdir, sbuild_id, filename);
353
354 if (unlink(linkname))
355 goto out_free;
356
357 err = 0;
358out_free:
359 free(filename);
360 free(linkname);
361 return err;
362}
363
364static int dso__cache_build_id(struct dso *self, const char *debugdir)
365{
366 bool is_kallsyms = self->kernel && self->long_name[0] != '/';
367
368 return build_id_cache__add_b(self->build_id, sizeof(self->build_id),
369 self->long_name, debugdir, is_kallsyms);
370}
371
372static int __dsos__cache_build_ids(struct list_head *head, const char *debugdir)
373{
374 struct dso *pos;
375 int err = 0;
376
377 dsos__for_each_with_build_id(pos, head)
378 if (dso__cache_build_id(pos, debugdir))
379 err = -1;
380
381 return err;
382}
383
384static int machine__cache_build_ids(struct machine *self, const char *debugdir)
385{
386 int ret = __dsos__cache_build_ids(&self->kernel_dsos, debugdir);
387 ret |= __dsos__cache_build_ids(&self->user_dsos, debugdir);
388 return ret;
389}
390
391static int perf_session__cache_build_ids(struct perf_session *self)
392{
393 struct rb_node *nd;
394 int ret;
395 char debugdir[PATH_MAX];
396
397 snprintf(debugdir, sizeof(debugdir), "%s", buildid_dir);
398
399 if (mkdir(debugdir, 0755) != 0 && errno != EEXIST)
400 return -1;
401
402 ret = machine__cache_build_ids(&self->host_machine, debugdir);
403
404 for (nd = rb_first(&self->machines); nd; nd = rb_next(nd)) {
405 struct machine *pos = rb_entry(nd, struct machine, rb_node);
406 ret |= machine__cache_build_ids(pos, debugdir);
407 }
408 return ret ? -1 : 0;
409}
410
411static bool machine__read_build_ids(struct machine *self, bool with_hits)
412{
413 bool ret = __dsos__read_build_ids(&self->kernel_dsos, with_hits);
414 ret |= __dsos__read_build_ids(&self->user_dsos, with_hits);
415 return ret;
416}
417
418static bool perf_session__read_build_ids(struct perf_session *self, bool with_hits)
419{
420 struct rb_node *nd;
421 bool ret = machine__read_build_ids(&self->host_machine, with_hits);
422
423 for (nd = rb_first(&self->machines); nd; nd = rb_next(nd)) {
424 struct machine *pos = rb_entry(nd, struct machine, rb_node);
425 ret |= machine__read_build_ids(pos, with_hits);
426 }
427
428 return ret;
429}
430
431static int perf_header__adds_write(struct perf_header *self, int fd)
432{
433 int nr_sections;
434 struct perf_session *session;
435 struct perf_file_section *feat_sec;
436 int sec_size;
437 u64 sec_start;
438 int idx = 0, err;
439
440 session = container_of(self, struct perf_session, header);
441
442 if (perf_header__has_feat(self, HEADER_BUILD_ID &&
443 !perf_session__read_build_ids(session, true)))
444 perf_header__clear_feat(self, HEADER_BUILD_ID);
445
446 nr_sections = bitmap_weight(self->adds_features, HEADER_FEAT_BITS);
447 if (!nr_sections)
448 return 0;
449
450 feat_sec = calloc(sizeof(*feat_sec), nr_sections);
451 if (feat_sec == NULL)
452 return -ENOMEM;
453
454 sec_size = sizeof(*feat_sec) * nr_sections;
455
456 sec_start = self->data_offset + self->data_size;
457 lseek(fd, sec_start + sec_size, SEEK_SET);
458
459 if (perf_header__has_feat(self, HEADER_TRACE_INFO)) {
460 struct perf_file_section *trace_sec;
461
462 trace_sec = &feat_sec[idx++];
463
464 /* Write trace info */
465 trace_sec->offset = lseek(fd, 0, SEEK_CUR);
466 read_tracing_data(fd, &evsel_list);
467 trace_sec->size = lseek(fd, 0, SEEK_CUR) - trace_sec->offset;
468 }
469
470 if (perf_header__has_feat(self, HEADER_BUILD_ID)) {
471 struct perf_file_section *buildid_sec;
472
473 buildid_sec = &feat_sec[idx++];
474
475 /* Write build-ids */
476 buildid_sec->offset = lseek(fd, 0, SEEK_CUR);
477 err = dsos__write_buildid_table(self, fd);
478 if (err < 0) {
479 pr_debug("failed to write buildid table\n");
480 goto out_free;
481 }
482 buildid_sec->size = lseek(fd, 0, SEEK_CUR) -
483 buildid_sec->offset;
484 if (!no_buildid_cache)
485 perf_session__cache_build_ids(session);
486 }
487
488 lseek(fd, sec_start, SEEK_SET);
489 err = do_write(fd, feat_sec, sec_size);
490 if (err < 0)
491 pr_debug("failed to write feature section\n");
492out_free:
493 free(feat_sec);
494 return err;
495}
496
497int perf_header__write_pipe(int fd)
498{
499 struct perf_pipe_file_header f_header;
500 int err;
501
502 f_header = (struct perf_pipe_file_header){
503 .magic = PERF_MAGIC,
504 .size = sizeof(f_header),
505 };
506
507 err = do_write(fd, &f_header, sizeof(f_header));
508 if (err < 0) {
509 pr_debug("failed to write perf pipe header\n");
510 return err;
511 }
512
513 return 0;
514}
515
516int perf_header__write(struct perf_header *self, int fd, bool at_exit)
517{
518 struct perf_file_header f_header;
519 struct perf_file_attr f_attr;
520 struct perf_header_attr *attr;
521 int i, err;
522
523 lseek(fd, sizeof(f_header), SEEK_SET);
524
525 for (i = 0; i < self->attrs; i++) {
526 attr = self->attr[i];
527
528 attr->id_offset = lseek(fd, 0, SEEK_CUR);
529 err = do_write(fd, attr->id, attr->ids * sizeof(u64));
530 if (err < 0) {
531 pr_debug("failed to write perf header\n");
532 return err;
533 }
534 }
535
536
537 self->attr_offset = lseek(fd, 0, SEEK_CUR);
538
539 for (i = 0; i < self->attrs; i++) {
540 attr = self->attr[i];
541
542 f_attr = (struct perf_file_attr){
543 .attr = attr->attr,
544 .ids = {
545 .offset = attr->id_offset,
546 .size = attr->ids * sizeof(u64),
547 }
548 };
549 err = do_write(fd, &f_attr, sizeof(f_attr));
550 if (err < 0) {
551 pr_debug("failed to write perf header attribute\n");
552 return err;
553 }
554 }
555
556 self->event_offset = lseek(fd, 0, SEEK_CUR);
557 self->event_size = event_count * sizeof(struct perf_trace_event_type);
558 if (events) {
559 err = do_write(fd, events, self->event_size);
560 if (err < 0) {
561 pr_debug("failed to write perf header events\n");
562 return err;
563 }
564 }
565
566 self->data_offset = lseek(fd, 0, SEEK_CUR);
567
568 if (at_exit) {
569 err = perf_header__adds_write(self, fd);
570 if (err < 0)
571 return err;
572 }
573
574 f_header = (struct perf_file_header){
575 .magic = PERF_MAGIC,
576 .size = sizeof(f_header),
577 .attr_size = sizeof(f_attr),
578 .attrs = {
579 .offset = self->attr_offset,
580 .size = self->attrs * sizeof(f_attr),
581 },
582 .data = {
583 .offset = self->data_offset,
584 .size = self->data_size,
585 },
586 .event_types = {
587 .offset = self->event_offset,
588 .size = self->event_size,
589 },
590 };
591
592 memcpy(&f_header.adds_features, &self->adds_features, sizeof(self->adds_features));
593
594 lseek(fd, 0, SEEK_SET);
595 err = do_write(fd, &f_header, sizeof(f_header));
596 if (err < 0) {
597 pr_debug("failed to write perf header\n");
598 return err;
599 }
600 lseek(fd, self->data_offset + self->data_size, SEEK_SET);
601
602 self->frozen = 1;
603 return 0;
604}
605
606static int perf_header__getbuffer64(struct perf_header *self,
607 int fd, void *buf, size_t size)
608{
609 if (readn(fd, buf, size) <= 0)
610 return -1;
611
612 if (self->needs_swap)
613 mem_bswap_64(buf, size);
614
615 return 0;
616}
617
618int perf_header__process_sections(struct perf_header *self, int fd,
619 int (*process)(struct perf_file_section *self,
620 struct perf_header *ph,
621 int feat, int fd))
622{
623 struct perf_file_section *feat_sec;
624 int nr_sections;
625 int sec_size;
626 int idx = 0;
627 int err = -1, feat = 1;
628
629 nr_sections = bitmap_weight(self->adds_features, HEADER_FEAT_BITS);
630 if (!nr_sections)
631 return 0;
632
633 feat_sec = calloc(sizeof(*feat_sec), nr_sections);
634 if (!feat_sec)
635 return -1;
636
637 sec_size = sizeof(*feat_sec) * nr_sections;
638
639 lseek(fd, self->data_offset + self->data_size, SEEK_SET);
640
641 if (perf_header__getbuffer64(self, fd, feat_sec, sec_size))
642 goto out_free;
643
644 err = 0;
645 while (idx < nr_sections && feat < HEADER_LAST_FEATURE) {
646 if (perf_header__has_feat(self, feat)) {
647 struct perf_file_section *sec = &feat_sec[idx++];
648
649 err = process(sec, self, feat, fd);
650 if (err < 0)
651 break;
652 }
653 ++feat;
654 }
655out_free:
656 free(feat_sec);
657 return err;
658}
659
660int perf_file_header__read(struct perf_file_header *self,
661 struct perf_header *ph, int fd)
662{
663 lseek(fd, 0, SEEK_SET);
664
665 if (readn(fd, self, sizeof(*self)) <= 0 ||
666 memcmp(&self->magic, __perf_magic, sizeof(self->magic)))
667 return -1;
668
669 if (self->attr_size != sizeof(struct perf_file_attr)) {
670 u64 attr_size = bswap_64(self->attr_size);
671
672 if (attr_size != sizeof(struct perf_file_attr))
673 return -1;
674
675 mem_bswap_64(self, offsetof(struct perf_file_header,
676 adds_features));
677 ph->needs_swap = true;
678 }
679
680 if (self->size != sizeof(*self)) {
681 /* Support the previous format */
682 if (self->size == offsetof(typeof(*self), adds_features))
683 bitmap_zero(self->adds_features, HEADER_FEAT_BITS);
684 else
685 return -1;
686 }
687
688 memcpy(&ph->adds_features, &self->adds_features,
689 sizeof(ph->adds_features));
690 /*
691 * FIXME: hack that assumes that if we need swap the perf.data file
692 * may be coming from an arch with a different word-size, ergo different
693 * DEFINE_BITMAP format, investigate more later, but for now its mostly
694 * safe to assume that we have a build-id section. Trace files probably
695 * have several other issues in this realm anyway...
696 */
697 if (ph->needs_swap) {
698 memset(&ph->adds_features, 0, sizeof(ph->adds_features));
699 perf_header__set_feat(ph, HEADER_BUILD_ID);
700 }
701
702 ph->event_offset = self->event_types.offset;
703 ph->event_size = self->event_types.size;
704 ph->data_offset = self->data.offset;
705 ph->data_size = self->data.size;
706 return 0;
707}
708
709static int __event_process_build_id(struct build_id_event *bev,
710 char *filename,
711 struct perf_session *session)
712{
713 int err = -1;
714 struct list_head *head;
715 struct machine *machine;
716 u16 misc;
717 struct dso *dso;
718 enum dso_kernel_type dso_type;
719
720 machine = perf_session__findnew_machine(session, bev->pid);
721 if (!machine)
722 goto out;
723
724 misc = bev->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
725
726 switch (misc) {
727 case PERF_RECORD_MISC_KERNEL:
728 dso_type = DSO_TYPE_KERNEL;
729 head = &machine->kernel_dsos;
730 break;
731 case PERF_RECORD_MISC_GUEST_KERNEL:
732 dso_type = DSO_TYPE_GUEST_KERNEL;
733 head = &machine->kernel_dsos;
734 break;
735 case PERF_RECORD_MISC_USER:
736 case PERF_RECORD_MISC_GUEST_USER:
737 dso_type = DSO_TYPE_USER;
738 head = &machine->user_dsos;
739 break;
740 default:
741 goto out;
742 }
743
744 dso = __dsos__findnew(head, filename);
745 if (dso != NULL) {
746 char sbuild_id[BUILD_ID_SIZE * 2 + 1];
747
748 dso__set_build_id(dso, &bev->build_id);
749
750 if (filename[0] == '[')
751 dso->kernel = dso_type;
752
753 build_id__sprintf(dso->build_id, sizeof(dso->build_id),
754 sbuild_id);
755 pr_debug("build id event received for %s: %s\n",
756 dso->long_name, sbuild_id);
757 }
758
759 err = 0;
760out:
761 return err;
762}
763
764static int perf_header__read_build_ids(struct perf_header *self,
765 int input, u64 offset, u64 size)
766{
767 struct perf_session *session = container_of(self,
768 struct perf_session, header);
769 struct build_id_event bev;
770 char filename[PATH_MAX];
771 u64 limit = offset + size;
772 int err = -1;
773
774 while (offset < limit) {
775 ssize_t len;
776
777 if (read(input, &bev, sizeof(bev)) != sizeof(bev))
778 goto out;
779
780 if (self->needs_swap)
781 perf_event_header__bswap(&bev.header);
782
783 len = bev.header.size - sizeof(bev);
784 if (read(input, filename, len) != len)
785 goto out;
786
787 __event_process_build_id(&bev, filename, session);
788
789 offset += bev.header.size;
790 }
791 err = 0;
792out:
793 return err;
794}
795
796static int perf_file_section__process(struct perf_file_section *self,
797 struct perf_header *ph,
798 int feat, int fd)
799{
800 if (lseek(fd, self->offset, SEEK_SET) == (off_t)-1) {
801 pr_debug("Failed to lseek to %Ld offset for feature %d, "
802 "continuing...\n", self->offset, feat);
803 return 0;
804 }
805
806 switch (feat) {
807 case HEADER_TRACE_INFO:
808 trace_report(fd, false);
809 break;
810
811 case HEADER_BUILD_ID:
812 if (perf_header__read_build_ids(ph, fd, self->offset, self->size))
813 pr_debug("Failed to read buildids, continuing...\n");
814 break;
815 default:
816 pr_debug("unknown feature %d, continuing...\n", feat);
817 }
818
819 return 0;
820}
821
822static int perf_file_header__read_pipe(struct perf_pipe_file_header *self,
823 struct perf_header *ph, int fd,
824 bool repipe)
825{
826 if (readn(fd, self, sizeof(*self)) <= 0 ||
827 memcmp(&self->magic, __perf_magic, sizeof(self->magic)))
828 return -1;
829
830 if (repipe && do_write(STDOUT_FILENO, self, sizeof(*self)) < 0)
831 return -1;
832
833 if (self->size != sizeof(*self)) {
834 u64 size = bswap_64(self->size);
835
836 if (size != sizeof(*self))
837 return -1;
838
839 ph->needs_swap = true;
840 }
841
842 return 0;
843}
844
845static int perf_header__read_pipe(struct perf_session *session, int fd)
846{
847 struct perf_header *self = &session->header;
848 struct perf_pipe_file_header f_header;
849
850 if (perf_file_header__read_pipe(&f_header, self, fd,
851 session->repipe) < 0) {
852 pr_debug("incompatible file format\n");
853 return -EINVAL;
854 }
855
856 session->fd = fd;
857
858 return 0;
859}
860
861int perf_header__read(struct perf_session *session, int fd)
862{
863 struct perf_header *self = &session->header;
864 struct perf_file_header f_header;
865 struct perf_file_attr f_attr;
866 u64 f_id;
867 int nr_attrs, nr_ids, i, j;
868
869 if (session->fd_pipe)
870 return perf_header__read_pipe(session, fd);
871
872 if (perf_file_header__read(&f_header, self, fd) < 0) {
873 pr_debug("incompatible file format\n");
874 return -EINVAL;
875 }
876
877 nr_attrs = f_header.attrs.size / sizeof(f_attr);
878 lseek(fd, f_header.attrs.offset, SEEK_SET);
879
880 for (i = 0; i < nr_attrs; i++) {
881 struct perf_header_attr *attr;
882 off_t tmp;
883
884 if (perf_header__getbuffer64(self, fd, &f_attr, sizeof(f_attr)))
885 goto out_errno;
886
887 tmp = lseek(fd, 0, SEEK_CUR);
888
889 attr = perf_header_attr__new(&f_attr.attr);
890 if (attr == NULL)
891 return -ENOMEM;
892
893 nr_ids = f_attr.ids.size / sizeof(u64);
894 lseek(fd, f_attr.ids.offset, SEEK_SET);
895
896 for (j = 0; j < nr_ids; j++) {
897 if (perf_header__getbuffer64(self, fd, &f_id, sizeof(f_id)))
898 goto out_errno;
899
900 if (perf_header_attr__add_id(attr, f_id) < 0) {
901 perf_header_attr__delete(attr);
902 return -ENOMEM;
903 }
904 }
905 if (perf_header__add_attr(self, attr) < 0) {
906 perf_header_attr__delete(attr);
907 return -ENOMEM;
908 }
909
910 lseek(fd, tmp, SEEK_SET);
911 }
912
913 if (f_header.event_types.size) {
914 lseek(fd, f_header.event_types.offset, SEEK_SET);
915 events = malloc(f_header.event_types.size);
916 if (events == NULL)
917 return -ENOMEM;
918 if (perf_header__getbuffer64(self, fd, events,
919 f_header.event_types.size))
920 goto out_errno;
921 event_count = f_header.event_types.size / sizeof(struct perf_trace_event_type);
922 }
923
924 perf_header__process_sections(self, fd, perf_file_section__process);
925
926 lseek(fd, self->data_offset, SEEK_SET);
927
928 self->frozen = 1;
929 return 0;
930out_errno:
931 return -errno;
932}
933
934u64 perf_header__sample_type(struct perf_header *header)
935{
936 u64 type = 0;
937 int i;
938
939 for (i = 0; i < header->attrs; i++) {
940 struct perf_header_attr *attr = header->attr[i];
941
942 if (!type)
943 type = attr->attr.sample_type;
944 else if (type != attr->attr.sample_type)
945 die("non matching sample_type");
946 }
947
948 return type;
949}
950
951bool perf_header__sample_id_all(const struct perf_header *header)
952{
953 bool value = false, first = true;
954 int i;
955
956 for (i = 0; i < header->attrs; i++) {
957 struct perf_header_attr *attr = header->attr[i];
958
959 if (first) {
960 value = attr->attr.sample_id_all;
961 first = false;
962 } else if (value != attr->attr.sample_id_all)
963 die("non matching sample_id_all");
964 }
965
966 return value;
967}
968
969struct perf_event_attr *
970perf_header__find_attr(u64 id, struct perf_header *header)
971{
972 int i;
973
974 /*
975 * We set id to -1 if the data file doesn't contain sample
976 * ids. This can happen when the data file contains one type
977 * of event and in that case, the header can still store the
978 * event attribute information. Check for this and avoid
979 * walking through the entire list of ids which may be large.
980 */
981 if (id == -1ULL) {
982 if (header->attrs > 0)
983 return &header->attr[0]->attr;
984 return NULL;
985 }
986
987 for (i = 0; i < header->attrs; i++) {
988 struct perf_header_attr *attr = header->attr[i];
989 int j;
990
991 for (j = 0; j < attr->ids; j++) {
992 if (attr->id[j] == id)
993 return &attr->attr;
994 }
995 }
996
997 return NULL;
998}
999
1000int event__synthesize_attr(struct perf_event_attr *attr, u16 ids, u64 *id,
1001 event__handler_t process,
1002 struct perf_session *session)
1003{
1004 event_t *ev;
1005 size_t size;
1006 int err;
1007
1008 size = sizeof(struct perf_event_attr);
1009 size = ALIGN(size, sizeof(u64));
1010 size += sizeof(struct perf_event_header);
1011 size += ids * sizeof(u64);
1012
1013 ev = malloc(size);
1014
1015 if (ev == NULL)
1016 return -ENOMEM;
1017
1018 ev->attr.attr = *attr;
1019 memcpy(ev->attr.id, id, ids * sizeof(u64));
1020
1021 ev->attr.header.type = PERF_RECORD_HEADER_ATTR;
1022 ev->attr.header.size = size;
1023
1024 err = process(ev, NULL, session);
1025
1026 free(ev);
1027
1028 return err;
1029}
1030
1031int event__synthesize_attrs(struct perf_header *self, event__handler_t process,
1032 struct perf_session *session)
1033{
1034 struct perf_header_attr *attr;
1035 int i, err = 0;
1036
1037 for (i = 0; i < self->attrs; i++) {
1038 attr = self->attr[i];
1039
1040 err = event__synthesize_attr(&attr->attr, attr->ids, attr->id,
1041 process, session);
1042 if (err) {
1043 pr_debug("failed to create perf header attribute\n");
1044 return err;
1045 }
1046 }
1047
1048 return err;
1049}
1050
1051int event__process_attr(event_t *self, struct perf_session *session)
1052{
1053 struct perf_header_attr *attr;
1054 unsigned int i, ids, n_ids;
1055
1056 attr = perf_header_attr__new(&self->attr.attr);
1057 if (attr == NULL)
1058 return -ENOMEM;
1059
1060 ids = self->header.size;
1061 ids -= (void *)&self->attr.id - (void *)self;
1062 n_ids = ids / sizeof(u64);
1063
1064 for (i = 0; i < n_ids; i++) {
1065 if (perf_header_attr__add_id(attr, self->attr.id[i]) < 0) {
1066 perf_header_attr__delete(attr);
1067 return -ENOMEM;
1068 }
1069 }
1070
1071 if (perf_header__add_attr(&session->header, attr) < 0) {
1072 perf_header_attr__delete(attr);
1073 return -ENOMEM;
1074 }
1075
1076 perf_session__update_sample_type(session);
1077
1078 return 0;
1079}
1080
1081int event__synthesize_event_type(u64 event_id, char *name,
1082 event__handler_t process,
1083 struct perf_session *session)
1084{
1085 event_t ev;
1086 size_t size = 0;
1087 int err = 0;
1088
1089 memset(&ev, 0, sizeof(ev));
1090
1091 ev.event_type.event_type.event_id = event_id;
1092 memset(ev.event_type.event_type.name, 0, MAX_EVENT_NAME);
1093 strncpy(ev.event_type.event_type.name, name, MAX_EVENT_NAME - 1);
1094
1095 ev.event_type.header.type = PERF_RECORD_HEADER_EVENT_TYPE;
1096 size = strlen(name);
1097 size = ALIGN(size, sizeof(u64));
1098 ev.event_type.header.size = sizeof(ev.event_type) -
1099 (sizeof(ev.event_type.event_type.name) - size);
1100
1101 err = process(&ev, NULL, session);
1102
1103 return err;
1104}
1105
1106int event__synthesize_event_types(event__handler_t process,
1107 struct perf_session *session)
1108{
1109 struct perf_trace_event_type *type;
1110 int i, err = 0;
1111
1112 for (i = 0; i < event_count; i++) {
1113 type = &events[i];
1114
1115 err = event__synthesize_event_type(type->event_id, type->name,
1116 process, session);
1117 if (err) {
1118 pr_debug("failed to create perf header event type\n");
1119 return err;
1120 }
1121 }
1122
1123 return err;
1124}
1125
1126int event__process_event_type(event_t *self,
1127 struct perf_session *session __unused)
1128{
1129 if (perf_header__push_event(self->event_type.event_type.event_id,
1130 self->event_type.event_type.name) < 0)
1131 return -ENOMEM;
1132
1133 return 0;
1134}
1135
1136int event__synthesize_tracing_data(int fd, struct list_head *pattrs,
1137 event__handler_t process,
1138 struct perf_session *session __unused)
1139{
1140 event_t ev;
1141 ssize_t size = 0, aligned_size = 0, padding;
1142 int err = 0;
1143
1144 memset(&ev, 0, sizeof(ev));
1145
1146 ev.tracing_data.header.type = PERF_RECORD_HEADER_TRACING_DATA;
1147 size = read_tracing_data_size(fd, pattrs);
1148 if (size <= 0)
1149 return size;
1150 aligned_size = ALIGN(size, sizeof(u64));
1151 padding = aligned_size - size;
1152 ev.tracing_data.header.size = sizeof(ev.tracing_data);
1153 ev.tracing_data.size = aligned_size;
1154
1155 process(&ev, NULL, session);
1156
1157 err = read_tracing_data(fd, pattrs);
1158 write_padded(fd, NULL, 0, padding);
1159
1160 return aligned_size;
1161}
1162
1163int event__process_tracing_data(event_t *self,
1164 struct perf_session *session)
1165{
1166 ssize_t size_read, padding, size = self->tracing_data.size;
1167 off_t offset = lseek(session->fd, 0, SEEK_CUR);
1168 char buf[BUFSIZ];
1169
1170 /* setup for reading amidst mmap */
1171 lseek(session->fd, offset + sizeof(struct tracing_data_event),
1172 SEEK_SET);
1173
1174 size_read = trace_report(session->fd, session->repipe);
1175
1176 padding = ALIGN(size_read, sizeof(u64)) - size_read;
1177
1178 if (read(session->fd, buf, padding) < 0)
1179 die("reading input file");
1180 if (session->repipe) {
1181 int retw = write(STDOUT_FILENO, buf, padding);
1182 if (retw <= 0 || retw != padding)
1183 die("repiping tracing data padding");
1184 }
1185
1186 if (size_read + padding != size)
1187 die("tracing data size mismatch");
1188
1189 return size_read + padding;
1190}
1191
1192int event__synthesize_build_id(struct dso *pos, u16 misc,
1193 event__handler_t process,
1194 struct machine *machine,
1195 struct perf_session *session)
1196{
1197 event_t ev;
1198 size_t len;
1199 int err = 0;
1200
1201 if (!pos->hit)
1202 return err;
1203
1204 memset(&ev, 0, sizeof(ev));
1205
1206 len = pos->long_name_len + 1;
1207 len = ALIGN(len, NAME_ALIGN);
1208 memcpy(&ev.build_id.build_id, pos->build_id, sizeof(pos->build_id));
1209 ev.build_id.header.type = PERF_RECORD_HEADER_BUILD_ID;
1210 ev.build_id.header.misc = misc;
1211 ev.build_id.pid = machine->pid;
1212 ev.build_id.header.size = sizeof(ev.build_id) + len;
1213 memcpy(&ev.build_id.filename, pos->long_name, pos->long_name_len);
1214
1215 err = process(&ev, NULL, session);
1216
1217 return err;
1218}
1219
1220int event__process_build_id(event_t *self,
1221 struct perf_session *session)
1222{
1223 __event_process_build_id(&self->build_id,
1224 self->build_id.filename,
1225 session);
1226 return 0;
1227}
1228
1229void disable_buildid_cache(void)
1230{
1231 no_buildid_cache = true;
1232}
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h
new file mode 100644
index 00000000000..33f16be7b72
--- /dev/null
+++ b/tools/perf/util/header.h
@@ -0,0 +1,128 @@
1#ifndef __PERF_HEADER_H
2#define __PERF_HEADER_H
3
4#include "../../../include/linux/perf_event.h"
5#include <sys/types.h>
6#include <stdbool.h>
7#include "types.h"
8#include "event.h"
9
10#include <linux/bitmap.h>
11
12struct perf_header_attr {
13 struct perf_event_attr attr;
14 int ids, size;
15 u64 *id;
16 off_t id_offset;
17};
18
19enum {
20 HEADER_TRACE_INFO = 1,
21 HEADER_BUILD_ID,
22 HEADER_LAST_FEATURE,
23};
24
25#define HEADER_FEAT_BITS 256
26
27struct perf_file_section {
28 u64 offset;
29 u64 size;
30};
31
32struct perf_file_header {
33 u64 magic;
34 u64 size;
35 u64 attr_size;
36 struct perf_file_section attrs;
37 struct perf_file_section data;
38 struct perf_file_section event_types;
39 DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS);
40};
41
42struct perf_pipe_file_header {
43 u64 magic;
44 u64 size;
45};
46
47struct perf_header;
48
49int perf_file_header__read(struct perf_file_header *self,
50 struct perf_header *ph, int fd);
51
52struct perf_header {
53 int frozen;
54 int attrs, size;
55 bool needs_swap;
56 struct perf_header_attr **attr;
57 s64 attr_offset;
58 u64 data_offset;
59 u64 data_size;
60 u64 event_offset;
61 u64 event_size;
62 DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS);
63};
64
65int perf_header__init(struct perf_header *self);
66void perf_header__exit(struct perf_header *self);
67
68int perf_header__read(struct perf_session *session, int fd);
69int perf_header__write(struct perf_header *self, int fd, bool at_exit);
70int perf_header__write_pipe(int fd);
71
72int perf_header__add_attr(struct perf_header *self,
73 struct perf_header_attr *attr);
74
75int perf_header__push_event(u64 id, const char *name);
76char *perf_header__find_event(u64 id);
77
78struct perf_header_attr *perf_header_attr__new(struct perf_event_attr *attr);
79void perf_header_attr__delete(struct perf_header_attr *self);
80
81int perf_header_attr__add_id(struct perf_header_attr *self, u64 id);
82
83u64 perf_header__sample_type(struct perf_header *header);
84bool perf_header__sample_id_all(const struct perf_header *header);
85struct perf_event_attr *
86perf_header__find_attr(u64 id, struct perf_header *header);
87void perf_header__set_feat(struct perf_header *self, int feat);
88void perf_header__clear_feat(struct perf_header *self, int feat);
89bool perf_header__has_feat(const struct perf_header *self, int feat);
90
91int perf_header__process_sections(struct perf_header *self, int fd,
92 int (*process)(struct perf_file_section *self,
93 struct perf_header *ph,
94 int feat, int fd));
95
96int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
97 const char *name, bool is_kallsyms);
98int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir);
99
100int event__synthesize_attr(struct perf_event_attr *attr, u16 ids, u64 *id,
101 event__handler_t process,
102 struct perf_session *session);
103int event__synthesize_attrs(struct perf_header *self,
104 event__handler_t process,
105 struct perf_session *session);
106int event__process_attr(event_t *self, struct perf_session *session);
107
108int event__synthesize_event_type(u64 event_id, char *name,
109 event__handler_t process,
110 struct perf_session *session);
111int event__synthesize_event_types(event__handler_t process,
112 struct perf_session *session);
113int event__process_event_type(event_t *self,
114 struct perf_session *session);
115
116int event__synthesize_tracing_data(int fd, struct list_head *pattrs,
117 event__handler_t process,
118 struct perf_session *session);
119int event__process_tracing_data(event_t *self,
120 struct perf_session *session);
121
122int event__synthesize_build_id(struct dso *pos, u16 misc,
123 event__handler_t process,
124 struct machine *machine,
125 struct perf_session *session);
126int event__process_build_id(event_t *self, struct perf_session *session);
127
128#endif /* __PERF_HEADER_H */
diff --git a/tools/perf/util/help.c b/tools/perf/util/help.c
index 6653f7dd1d7..6f2975a0035 100644
--- a/tools/perf/util/help.c
+++ b/tools/perf/util/help.c
@@ -4,29 +4,7 @@
4#include "levenshtein.h" 4#include "levenshtein.h"
5#include "help.h" 5#include "help.h"
6 6
7/* most GUI terminals set COLUMNS (although some don't export it) */ 7void add_cmdname(struct cmdnames *cmds, const char *name, size_t len)
8static int term_columns(void)
9{
10 char *col_string = getenv("COLUMNS");
11 int n_cols;
12
13 if (col_string && (n_cols = atoi(col_string)) > 0)
14 return n_cols;
15
16#ifdef TIOCGWINSZ
17 {
18 struct winsize ws;
19 if (!ioctl(1, TIOCGWINSZ, &ws)) {
20 if (ws.ws_col)
21 return ws.ws_col;
22 }
23 }
24#endif
25
26 return 80;
27}
28
29void add_cmdname(struct cmdnames *cmds, const char *name, int len)
30{ 8{
31 struct cmdname *ent = malloc(sizeof(*ent) + len + 1); 9 struct cmdname *ent = malloc(sizeof(*ent) + len + 1);
32 10
@@ -40,7 +18,8 @@ void add_cmdname(struct cmdnames *cmds, const char *name, int len)
40 18
41static void clean_cmdnames(struct cmdnames *cmds) 19static void clean_cmdnames(struct cmdnames *cmds)
42{ 20{
43 int i; 21 unsigned int i;
22
44 for (i = 0; i < cmds->cnt; ++i) 23 for (i = 0; i < cmds->cnt; ++i)
45 free(cmds->names[i]); 24 free(cmds->names[i]);
46 free(cmds->names); 25 free(cmds->names);
@@ -57,7 +36,7 @@ static int cmdname_compare(const void *a_, const void *b_)
57 36
58static void uniq(struct cmdnames *cmds) 37static void uniq(struct cmdnames *cmds)
59{ 38{
60 int i, j; 39 unsigned int i, j;
61 40
62 if (!cmds->cnt) 41 if (!cmds->cnt)
63 return; 42 return;
@@ -71,7 +50,7 @@ static void uniq(struct cmdnames *cmds)
71 50
72void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes) 51void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes)
73{ 52{
74 int ci, cj, ei; 53 size_t ci, cj, ei;
75 int cmp; 54 int cmp;
76 55
77 ci = cj = ei = 0; 56 ci = cj = ei = 0;
@@ -95,9 +74,13 @@ static void pretty_print_string_list(struct cmdnames *cmds, int longest)
95{ 74{
96 int cols = 1, rows; 75 int cols = 1, rows;
97 int space = longest + 1; /* min 1 SP between words */ 76 int space = longest + 1; /* min 1 SP between words */
98 int max_cols = term_columns() - 1; /* don't print *on* the edge */ 77 struct winsize win;
78 int max_cols;
99 int i, j; 79 int i, j;
100 80
81 get_term_dimensions(&win);
82 max_cols = win.ws_col - 1; /* don't print *on* the edge */
83
101 if (space < max_cols) 84 if (space < max_cols)
102 cols = max_cols / space; 85 cols = max_cols / space;
103 rows = (cmds->cnt + cols - 1) / cols; 86 rows = (cmds->cnt + cols - 1) / cols;
@@ -106,8 +89,9 @@ static void pretty_print_string_list(struct cmdnames *cmds, int longest)
106 printf(" "); 89 printf(" ");
107 90
108 for (j = 0; j < cols; j++) { 91 for (j = 0; j < cols; j++) {
109 int n = j * rows + i; 92 unsigned int n = j * rows + i;
110 int size = space; 93 unsigned int size = space;
94
111 if (n >= cmds->cnt) 95 if (n >= cmds->cnt)
112 break; 96 break;
113 if (j == cols-1 || n + rows >= cmds->cnt) 97 if (j == cols-1 || n + rows >= cmds->cnt)
@@ -126,21 +110,6 @@ static int is_executable(const char *name)
126 !S_ISREG(st.st_mode)) 110 !S_ISREG(st.st_mode))
127 return 0; 111 return 0;
128 112
129#ifdef __MINGW32__
130 /* cannot trust the executable bit, peek into the file instead */
131 char buf[3] = { 0 };
132 int n;
133 int fd = open(name, O_RDONLY);
134 st.st_mode &= ~S_IXUSR;
135 if (fd >= 0) {
136 n = read(fd, buf, 2);
137 if (n == 2)
138 /* DOS executables start with "MZ" */
139 if (!strcmp(buf, "#!") || !strcmp(buf, "MZ"))
140 st.st_mode |= S_IXUSR;
141 close(fd);
142 }
143#endif
144 return st.st_mode & S_IXUSR; 113 return st.st_mode & S_IXUSR;
145} 114}
146 115
@@ -223,7 +192,7 @@ void load_command_list(const char *prefix,
223void list_commands(const char *title, struct cmdnames *main_cmds, 192void list_commands(const char *title, struct cmdnames *main_cmds,
224 struct cmdnames *other_cmds) 193 struct cmdnames *other_cmds)
225{ 194{
226 int i, longest = 0; 195 unsigned int i, longest = 0;
227 196
228 for (i = 0; i < main_cmds->cnt; i++) 197 for (i = 0; i < main_cmds->cnt; i++)
229 if (longest < main_cmds->names[i]->len) 198 if (longest < main_cmds->names[i]->len)
@@ -254,7 +223,8 @@ void list_commands(const char *title, struct cmdnames *main_cmds,
254 223
255int is_in_cmdlist(struct cmdnames *c, const char *s) 224int is_in_cmdlist(struct cmdnames *c, const char *s)
256{ 225{
257 int i; 226 unsigned int i;
227
258 for (i = 0; i < c->cnt; i++) 228 for (i = 0; i < c->cnt; i++)
259 if (!strcmp(s, c->names[i]->name)) 229 if (!strcmp(s, c->names[i]->name))
260 return 1; 230 return 1;
@@ -286,7 +256,8 @@ static int levenshtein_compare(const void *p1, const void *p2)
286 256
287static void add_cmd_list(struct cmdnames *cmds, struct cmdnames *old) 257static void add_cmd_list(struct cmdnames *cmds, struct cmdnames *old)
288{ 258{
289 int i; 259 unsigned int i;
260
290 ALLOC_GROW(cmds->names, cmds->cnt + old->cnt, cmds->alloc); 261 ALLOC_GROW(cmds->names, cmds->cnt + old->cnt, cmds->alloc);
291 262
292 for (i = 0; i < old->cnt; i++) 263 for (i = 0; i < old->cnt; i++)
@@ -298,7 +269,7 @@ static void add_cmd_list(struct cmdnames *cmds, struct cmdnames *old)
298 269
299const char *help_unknown_cmd(const char *cmd) 270const char *help_unknown_cmd(const char *cmd)
300{ 271{
301 int i, n = 0, best_similarity = 0; 272 unsigned int i, n = 0, best_similarity = 0;
302 struct cmdnames main_cmds, other_cmds; 273 struct cmdnames main_cmds, other_cmds;
303 274
304 memset(&main_cmds, 0, sizeof(main_cmds)); 275 memset(&main_cmds, 0, sizeof(main_cmds));
@@ -335,7 +306,7 @@ const char *help_unknown_cmd(const char *cmd)
335 306
336 main_cmds.names[0] = NULL; 307 main_cmds.names[0] = NULL;
337 clean_cmdnames(&main_cmds); 308 clean_cmdnames(&main_cmds);
338 fprintf(stderr, "WARNING: You called a Git program named '%s', " 309 fprintf(stderr, "WARNING: You called a perf program named '%s', "
339 "which does not exist.\n" 310 "which does not exist.\n"
340 "Continuing under the assumption that you meant '%s'\n", 311 "Continuing under the assumption that you meant '%s'\n",
341 cmd, assumed); 312 cmd, assumed);
@@ -360,7 +331,7 @@ const char *help_unknown_cmd(const char *cmd)
360 exit(1); 331 exit(1);
361} 332}
362 333
363int cmd_version(int argc, const char **argv, const char *prefix) 334int cmd_version(int argc __used, const char **argv __used, const char *prefix __used)
364{ 335{
365 printf("perf version %s\n", perf_version_string); 336 printf("perf version %s\n", perf_version_string);
366 return 0; 337 return 0;
diff --git a/tools/perf/util/help.h b/tools/perf/util/help.h
index 56bc15406ff..7f5c6dedd71 100644
--- a/tools/perf/util/help.h
+++ b/tools/perf/util/help.h
@@ -1,9 +1,9 @@
1#ifndef HELP_H 1#ifndef __PERF_HELP_H
2#define HELP_H 2#define __PERF_HELP_H
3 3
4struct cmdnames { 4struct cmdnames {
5 int alloc; 5 size_t alloc;
6 int cnt; 6 size_t cnt;
7 struct cmdname { 7 struct cmdname {
8 size_t len; /* also used for similarity index in help.c */ 8 size_t len; /* also used for similarity index in help.c */
9 char name[FLEX_ARRAY]; 9 char name[FLEX_ARRAY];
@@ -19,11 +19,11 @@ static inline void mput_char(char c, unsigned int num)
19void load_command_list(const char *prefix, 19void load_command_list(const char *prefix,
20 struct cmdnames *main_cmds, 20 struct cmdnames *main_cmds,
21 struct cmdnames *other_cmds); 21 struct cmdnames *other_cmds);
22void add_cmdname(struct cmdnames *cmds, const char *name, int len); 22void add_cmdname(struct cmdnames *cmds, const char *name, size_t len);
23/* Here we require that excludes is a sorted list. */ 23/* Here we require that excludes is a sorted list. */
24void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes); 24void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes);
25int is_in_cmdlist(struct cmdnames *c, const char *s); 25int is_in_cmdlist(struct cmdnames *c, const char *s);
26void list_commands(const char *title, struct cmdnames *main_cmds, 26void list_commands(const char *title, struct cmdnames *main_cmds,
27 struct cmdnames *other_cmds); 27 struct cmdnames *other_cmds);
28 28
29#endif /* HELP_H */ 29#endif /* __PERF_HELP_H */
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
new file mode 100644
index 00000000000..c749ba6136a
--- /dev/null
+++ b/tools/perf/util/hist.c
@@ -0,0 +1,1189 @@
1#include "util.h"
2#include "build-id.h"
3#include "hist.h"
4#include "session.h"
5#include "sort.h"
6#include <math.h>
7
8enum hist_filter {
9 HIST_FILTER__DSO,
10 HIST_FILTER__THREAD,
11 HIST_FILTER__PARENT,
12};
13
14struct callchain_param callchain_param = {
15 .mode = CHAIN_GRAPH_REL,
16 .min_percent = 0.5
17};
18
19u16 hists__col_len(struct hists *self, enum hist_column col)
20{
21 return self->col_len[col];
22}
23
24void hists__set_col_len(struct hists *self, enum hist_column col, u16 len)
25{
26 self->col_len[col] = len;
27}
28
29bool hists__new_col_len(struct hists *self, enum hist_column col, u16 len)
30{
31 if (len > hists__col_len(self, col)) {
32 hists__set_col_len(self, col, len);
33 return true;
34 }
35 return false;
36}
37
38static void hists__reset_col_len(struct hists *self)
39{
40 enum hist_column col;
41
42 for (col = 0; col < HISTC_NR_COLS; ++col)
43 hists__set_col_len(self, col, 0);
44}
45
46static void hists__calc_col_len(struct hists *self, struct hist_entry *h)
47{
48 u16 len;
49
50 if (h->ms.sym)
51 hists__new_col_len(self, HISTC_SYMBOL, h->ms.sym->namelen);
52
53 len = thread__comm_len(h->thread);
54 if (hists__new_col_len(self, HISTC_COMM, len))
55 hists__set_col_len(self, HISTC_THREAD, len + 6);
56
57 if (h->ms.map) {
58 len = dso__name_len(h->ms.map->dso);
59 hists__new_col_len(self, HISTC_DSO, len);
60 }
61}
62
63static void hist_entry__add_cpumode_period(struct hist_entry *self,
64 unsigned int cpumode, u64 period)
65{
66 switch (cpumode) {
67 case PERF_RECORD_MISC_KERNEL:
68 self->period_sys += period;
69 break;
70 case PERF_RECORD_MISC_USER:
71 self->period_us += period;
72 break;
73 case PERF_RECORD_MISC_GUEST_KERNEL:
74 self->period_guest_sys += period;
75 break;
76 case PERF_RECORD_MISC_GUEST_USER:
77 self->period_guest_us += period;
78 break;
79 default:
80 break;
81 }
82}
83
84/*
85 * histogram, sorted on item, collects periods
86 */
87
88static struct hist_entry *hist_entry__new(struct hist_entry *template)
89{
90 size_t callchain_size = symbol_conf.use_callchain ? sizeof(struct callchain_root) : 0;
91 struct hist_entry *self = malloc(sizeof(*self) + callchain_size);
92
93 if (self != NULL) {
94 *self = *template;
95 self->nr_events = 1;
96 if (self->ms.map)
97 self->ms.map->referenced = true;
98 if (symbol_conf.use_callchain)
99 callchain_init(self->callchain);
100 }
101
102 return self;
103}
104
105static void hists__inc_nr_entries(struct hists *self, struct hist_entry *h)
106{
107 if (!h->filtered) {
108 hists__calc_col_len(self, h);
109 ++self->nr_entries;
110 }
111}
112
113static u8 symbol__parent_filter(const struct symbol *parent)
114{
115 if (symbol_conf.exclude_other && parent == NULL)
116 return 1 << HIST_FILTER__PARENT;
117 return 0;
118}
119
120struct hist_entry *__hists__add_entry(struct hists *self,
121 struct addr_location *al,
122 struct symbol *sym_parent, u64 period)
123{
124 struct rb_node **p = &self->entries.rb_node;
125 struct rb_node *parent = NULL;
126 struct hist_entry *he;
127 struct hist_entry entry = {
128 .thread = al->thread,
129 .ms = {
130 .map = al->map,
131 .sym = al->sym,
132 },
133 .cpu = al->cpu,
134 .ip = al->addr,
135 .level = al->level,
136 .period = period,
137 .parent = sym_parent,
138 .filtered = symbol__parent_filter(sym_parent),
139 };
140 int cmp;
141
142 while (*p != NULL) {
143 parent = *p;
144 he = rb_entry(parent, struct hist_entry, rb_node);
145
146 cmp = hist_entry__cmp(&entry, he);
147
148 if (!cmp) {
149 he->period += period;
150 ++he->nr_events;
151 goto out;
152 }
153
154 if (cmp < 0)
155 p = &(*p)->rb_left;
156 else
157 p = &(*p)->rb_right;
158 }
159
160 he = hist_entry__new(&entry);
161 if (!he)
162 return NULL;
163 rb_link_node(&he->rb_node, parent, p);
164 rb_insert_color(&he->rb_node, &self->entries);
165 hists__inc_nr_entries(self, he);
166out:
167 hist_entry__add_cpumode_period(he, al->cpumode, period);
168 return he;
169}
170
171int64_t
172hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)
173{
174 struct sort_entry *se;
175 int64_t cmp = 0;
176
177 list_for_each_entry(se, &hist_entry__sort_list, list) {
178 cmp = se->se_cmp(left, right);
179 if (cmp)
180 break;
181 }
182
183 return cmp;
184}
185
186int64_t
187hist_entry__collapse(struct hist_entry *left, struct hist_entry *right)
188{
189 struct sort_entry *se;
190 int64_t cmp = 0;
191
192 list_for_each_entry(se, &hist_entry__sort_list, list) {
193 int64_t (*f)(struct hist_entry *, struct hist_entry *);
194
195 f = se->se_collapse ?: se->se_cmp;
196
197 cmp = f(left, right);
198 if (cmp)
199 break;
200 }
201
202 return cmp;
203}
204
205void hist_entry__free(struct hist_entry *he)
206{
207 free(he);
208}
209
210/*
211 * collapse the histogram
212 */
213
214static bool collapse__insert_entry(struct rb_root *root, struct hist_entry *he)
215{
216 struct rb_node **p = &root->rb_node;
217 struct rb_node *parent = NULL;
218 struct hist_entry *iter;
219 int64_t cmp;
220
221 while (*p != NULL) {
222 parent = *p;
223 iter = rb_entry(parent, struct hist_entry, rb_node);
224
225 cmp = hist_entry__collapse(iter, he);
226
227 if (!cmp) {
228 iter->period += he->period;
229 if (symbol_conf.use_callchain)
230 callchain_merge(iter->callchain, he->callchain);
231 hist_entry__free(he);
232 return false;
233 }
234
235 if (cmp < 0)
236 p = &(*p)->rb_left;
237 else
238 p = &(*p)->rb_right;
239 }
240
241 rb_link_node(&he->rb_node, parent, p);
242 rb_insert_color(&he->rb_node, root);
243 return true;
244}
245
246void hists__collapse_resort(struct hists *self)
247{
248 struct rb_root tmp;
249 struct rb_node *next;
250 struct hist_entry *n;
251
252 if (!sort__need_collapse)
253 return;
254
255 tmp = RB_ROOT;
256 next = rb_first(&self->entries);
257 self->nr_entries = 0;
258 hists__reset_col_len(self);
259
260 while (next) {
261 n = rb_entry(next, struct hist_entry, rb_node);
262 next = rb_next(&n->rb_node);
263
264 rb_erase(&n->rb_node, &self->entries);
265 if (collapse__insert_entry(&tmp, n))
266 hists__inc_nr_entries(self, n);
267 }
268
269 self->entries = tmp;
270}
271
272/*
273 * reverse the map, sort on period.
274 */
275
276static void __hists__insert_output_entry(struct rb_root *entries,
277 struct hist_entry *he,
278 u64 min_callchain_hits)
279{
280 struct rb_node **p = &entries->rb_node;
281 struct rb_node *parent = NULL;
282 struct hist_entry *iter;
283
284 if (symbol_conf.use_callchain)
285 callchain_param.sort(&he->sorted_chain, he->callchain,
286 min_callchain_hits, &callchain_param);
287
288 while (*p != NULL) {
289 parent = *p;
290 iter = rb_entry(parent, struct hist_entry, rb_node);
291
292 if (he->period > iter->period)
293 p = &(*p)->rb_left;
294 else
295 p = &(*p)->rb_right;
296 }
297
298 rb_link_node(&he->rb_node, parent, p);
299 rb_insert_color(&he->rb_node, entries);
300}
301
302void hists__output_resort(struct hists *self)
303{
304 struct rb_root tmp;
305 struct rb_node *next;
306 struct hist_entry *n;
307 u64 min_callchain_hits;
308
309 min_callchain_hits = self->stats.total_period * (callchain_param.min_percent / 100);
310
311 tmp = RB_ROOT;
312 next = rb_first(&self->entries);
313
314 self->nr_entries = 0;
315 hists__reset_col_len(self);
316
317 while (next) {
318 n = rb_entry(next, struct hist_entry, rb_node);
319 next = rb_next(&n->rb_node);
320
321 rb_erase(&n->rb_node, &self->entries);
322 __hists__insert_output_entry(&tmp, n, min_callchain_hits);
323 hists__inc_nr_entries(self, n);
324 }
325
326 self->entries = tmp;
327}
328
329static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin)
330{
331 int i;
332 int ret = fprintf(fp, " ");
333
334 for (i = 0; i < left_margin; i++)
335 ret += fprintf(fp, " ");
336
337 return ret;
338}
339
340static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask,
341 int left_margin)
342{
343 int i;
344 size_t ret = callchain__fprintf_left_margin(fp, left_margin);
345
346 for (i = 0; i < depth; i++)
347 if (depth_mask & (1 << i))
348 ret += fprintf(fp, "| ");
349 else
350 ret += fprintf(fp, " ");
351
352 ret += fprintf(fp, "\n");
353
354 return ret;
355}
356
357static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain,
358 int depth, int depth_mask, int period,
359 u64 total_samples, u64 hits,
360 int left_margin)
361{
362 int i;
363 size_t ret = 0;
364
365 ret += callchain__fprintf_left_margin(fp, left_margin);
366 for (i = 0; i < depth; i++) {
367 if (depth_mask & (1 << i))
368 ret += fprintf(fp, "|");
369 else
370 ret += fprintf(fp, " ");
371 if (!period && i == depth - 1) {
372 double percent;
373
374 percent = hits * 100.0 / total_samples;
375 ret += percent_color_fprintf(fp, "--%2.2f%%-- ", percent);
376 } else
377 ret += fprintf(fp, "%s", " ");
378 }
379 if (chain->ms.sym)
380 ret += fprintf(fp, "%s\n", chain->ms.sym->name);
381 else
382 ret += fprintf(fp, "%p\n", (void *)(long)chain->ip);
383
384 return ret;
385}
386
387static struct symbol *rem_sq_bracket;
388static struct callchain_list rem_hits;
389
390static void init_rem_hits(void)
391{
392 rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6);
393 if (!rem_sq_bracket) {
394 fprintf(stderr, "Not enough memory to display remaining hits\n");
395 return;
396 }
397
398 strcpy(rem_sq_bracket->name, "[...]");
399 rem_hits.ms.sym = rem_sq_bracket;
400}
401
402static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
403 u64 total_samples, int depth,
404 int depth_mask, int left_margin)
405{
406 struct rb_node *node, *next;
407 struct callchain_node *child;
408 struct callchain_list *chain;
409 int new_depth_mask = depth_mask;
410 u64 new_total;
411 u64 remaining;
412 size_t ret = 0;
413 int i;
414 uint entries_printed = 0;
415
416 if (callchain_param.mode == CHAIN_GRAPH_REL)
417 new_total = self->children_hit;
418 else
419 new_total = total_samples;
420
421 remaining = new_total;
422
423 node = rb_first(&self->rb_root);
424 while (node) {
425 u64 cumul;
426
427 child = rb_entry(node, struct callchain_node, rb_node);
428 cumul = cumul_hits(child);
429 remaining -= cumul;
430
431 /*
432 * The depth mask manages the output of pipes that show
433 * the depth. We don't want to keep the pipes of the current
434 * level for the last child of this depth.
435 * Except if we have remaining filtered hits. They will
436 * supersede the last child
437 */
438 next = rb_next(node);
439 if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining))
440 new_depth_mask &= ~(1 << (depth - 1));
441
442 /*
443 * But we keep the older depth mask for the line separator
444 * to keep the level link until we reach the last child
445 */
446 ret += ipchain__fprintf_graph_line(fp, depth, depth_mask,
447 left_margin);
448 i = 0;
449 list_for_each_entry(chain, &child->val, list) {
450 ret += ipchain__fprintf_graph(fp, chain, depth,
451 new_depth_mask, i++,
452 new_total,
453 cumul,
454 left_margin);
455 }
456 ret += __callchain__fprintf_graph(fp, child, new_total,
457 depth + 1,
458 new_depth_mask | (1 << depth),
459 left_margin);
460 node = next;
461 if (++entries_printed == callchain_param.print_limit)
462 break;
463 }
464
465 if (callchain_param.mode == CHAIN_GRAPH_REL &&
466 remaining && remaining != new_total) {
467
468 if (!rem_sq_bracket)
469 return ret;
470
471 new_depth_mask &= ~(1 << (depth - 1));
472
473 ret += ipchain__fprintf_graph(fp, &rem_hits, depth,
474 new_depth_mask, 0, new_total,
475 remaining, left_margin);
476 }
477
478 return ret;
479}
480
481static size_t callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
482 u64 total_samples, int left_margin)
483{
484 struct callchain_list *chain;
485 bool printed = false;
486 int i = 0;
487 int ret = 0;
488 u32 entries_printed = 0;
489
490 list_for_each_entry(chain, &self->val, list) {
491 if (!i++ && sort__first_dimension == SORT_SYM)
492 continue;
493
494 if (!printed) {
495 ret += callchain__fprintf_left_margin(fp, left_margin);
496 ret += fprintf(fp, "|\n");
497 ret += callchain__fprintf_left_margin(fp, left_margin);
498 ret += fprintf(fp, "---");
499
500 left_margin += 3;
501 printed = true;
502 } else
503 ret += callchain__fprintf_left_margin(fp, left_margin);
504
505 if (chain->ms.sym)
506 ret += fprintf(fp, " %s\n", chain->ms.sym->name);
507 else
508 ret += fprintf(fp, " %p\n", (void *)(long)chain->ip);
509
510 if (++entries_printed == callchain_param.print_limit)
511 break;
512 }
513
514 ret += __callchain__fprintf_graph(fp, self, total_samples, 1, 1, left_margin);
515
516 return ret;
517}
518
519static size_t callchain__fprintf_flat(FILE *fp, struct callchain_node *self,
520 u64 total_samples)
521{
522 struct callchain_list *chain;
523 size_t ret = 0;
524
525 if (!self)
526 return 0;
527
528 ret += callchain__fprintf_flat(fp, self->parent, total_samples);
529
530
531 list_for_each_entry(chain, &self->val, list) {
532 if (chain->ip >= PERF_CONTEXT_MAX)
533 continue;
534 if (chain->ms.sym)
535 ret += fprintf(fp, " %s\n", chain->ms.sym->name);
536 else
537 ret += fprintf(fp, " %p\n",
538 (void *)(long)chain->ip);
539 }
540
541 return ret;
542}
543
544static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self,
545 u64 total_samples, int left_margin)
546{
547 struct rb_node *rb_node;
548 struct callchain_node *chain;
549 size_t ret = 0;
550 u32 entries_printed = 0;
551
552 rb_node = rb_first(&self->sorted_chain);
553 while (rb_node) {
554 double percent;
555
556 chain = rb_entry(rb_node, struct callchain_node, rb_node);
557 percent = chain->hit * 100.0 / total_samples;
558 switch (callchain_param.mode) {
559 case CHAIN_FLAT:
560 ret += percent_color_fprintf(fp, " %6.2f%%\n",
561 percent);
562 ret += callchain__fprintf_flat(fp, chain, total_samples);
563 break;
564 case CHAIN_GRAPH_ABS: /* Falldown */
565 case CHAIN_GRAPH_REL:
566 ret += callchain__fprintf_graph(fp, chain, total_samples,
567 left_margin);
568 case CHAIN_NONE:
569 default:
570 break;
571 }
572 ret += fprintf(fp, "\n");
573 if (++entries_printed == callchain_param.print_limit)
574 break;
575 rb_node = rb_next(rb_node);
576 }
577
578 return ret;
579}
580
581int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size,
582 struct hists *hists, struct hists *pair_hists,
583 bool show_displacement, long displacement,
584 bool color, u64 session_total)
585{
586 struct sort_entry *se;
587 u64 period, total, period_sys, period_us, period_guest_sys, period_guest_us;
588 const char *sep = symbol_conf.field_sep;
589 int ret;
590
591 if (symbol_conf.exclude_other && !self->parent)
592 return 0;
593
594 if (pair_hists) {
595 period = self->pair ? self->pair->period : 0;
596 total = pair_hists->stats.total_period;
597 period_sys = self->pair ? self->pair->period_sys : 0;
598 period_us = self->pair ? self->pair->period_us : 0;
599 period_guest_sys = self->pair ? self->pair->period_guest_sys : 0;
600 period_guest_us = self->pair ? self->pair->period_guest_us : 0;
601 } else {
602 period = self->period;
603 total = session_total;
604 period_sys = self->period_sys;
605 period_us = self->period_us;
606 period_guest_sys = self->period_guest_sys;
607 period_guest_us = self->period_guest_us;
608 }
609
610 if (total) {
611 if (color)
612 ret = percent_color_snprintf(s, size,
613 sep ? "%.2f" : " %6.2f%%",
614 (period * 100.0) / total);
615 else
616 ret = snprintf(s, size, sep ? "%.2f" : " %6.2f%%",
617 (period * 100.0) / total);
618 if (symbol_conf.show_cpu_utilization) {
619 ret += percent_color_snprintf(s + ret, size - ret,
620 sep ? "%.2f" : " %6.2f%%",
621 (period_sys * 100.0) / total);
622 ret += percent_color_snprintf(s + ret, size - ret,
623 sep ? "%.2f" : " %6.2f%%",
624 (period_us * 100.0) / total);
625 if (perf_guest) {
626 ret += percent_color_snprintf(s + ret,
627 size - ret,
628 sep ? "%.2f" : " %6.2f%%",
629 (period_guest_sys * 100.0) /
630 total);
631 ret += percent_color_snprintf(s + ret,
632 size - ret,
633 sep ? "%.2f" : " %6.2f%%",
634 (period_guest_us * 100.0) /
635 total);
636 }
637 }
638 } else
639 ret = snprintf(s, size, sep ? "%lld" : "%12lld ", period);
640
641 if (symbol_conf.show_nr_samples) {
642 if (sep)
643 ret += snprintf(s + ret, size - ret, "%c%lld", *sep, period);
644 else
645 ret += snprintf(s + ret, size - ret, "%11lld", period);
646 }
647
648 if (pair_hists) {
649 char bf[32];
650 double old_percent = 0, new_percent = 0, diff;
651
652 if (total > 0)
653 old_percent = (period * 100.0) / total;
654 if (session_total > 0)
655 new_percent = (self->period * 100.0) / session_total;
656
657 diff = new_percent - old_percent;
658
659 if (fabs(diff) >= 0.01)
660 snprintf(bf, sizeof(bf), "%+4.2F%%", diff);
661 else
662 snprintf(bf, sizeof(bf), " ");
663
664 if (sep)
665 ret += snprintf(s + ret, size - ret, "%c%s", *sep, bf);
666 else
667 ret += snprintf(s + ret, size - ret, "%11.11s", bf);
668
669 if (show_displacement) {
670 if (displacement)
671 snprintf(bf, sizeof(bf), "%+4ld", displacement);
672 else
673 snprintf(bf, sizeof(bf), " ");
674
675 if (sep)
676 ret += snprintf(s + ret, size - ret, "%c%s", *sep, bf);
677 else
678 ret += snprintf(s + ret, size - ret, "%6.6s", bf);
679 }
680 }
681
682 list_for_each_entry(se, &hist_entry__sort_list, list) {
683 if (se->elide)
684 continue;
685
686 ret += snprintf(s + ret, size - ret, "%s", sep ?: " ");
687 ret += se->se_snprintf(self, s + ret, size - ret,
688 hists__col_len(hists, se->se_width_idx));
689 }
690
691 return ret;
692}
693
694int hist_entry__fprintf(struct hist_entry *self, struct hists *hists,
695 struct hists *pair_hists, bool show_displacement,
696 long displacement, FILE *fp, u64 session_total)
697{
698 char bf[512];
699 hist_entry__snprintf(self, bf, sizeof(bf), hists, pair_hists,
700 show_displacement, displacement,
701 true, session_total);
702 return fprintf(fp, "%s\n", bf);
703}
704
705static size_t hist_entry__fprintf_callchain(struct hist_entry *self,
706 struct hists *hists, FILE *fp,
707 u64 session_total)
708{
709 int left_margin = 0;
710
711 if (sort__first_dimension == SORT_COMM) {
712 struct sort_entry *se = list_first_entry(&hist_entry__sort_list,
713 typeof(*se), list);
714 left_margin = hists__col_len(hists, se->se_width_idx);
715 left_margin -= thread__comm_len(self->thread);
716 }
717
718 return hist_entry_callchain__fprintf(fp, self, session_total,
719 left_margin);
720}
721
722size_t hists__fprintf(struct hists *self, struct hists *pair,
723 bool show_displacement, FILE *fp)
724{
725 struct sort_entry *se;
726 struct rb_node *nd;
727 size_t ret = 0;
728 unsigned long position = 1;
729 long displacement = 0;
730 unsigned int width;
731 const char *sep = symbol_conf.field_sep;
732 const char *col_width = symbol_conf.col_width_list_str;
733
734 init_rem_hits();
735
736 fprintf(fp, "# %s", pair ? "Baseline" : "Overhead");
737
738 if (symbol_conf.show_nr_samples) {
739 if (sep)
740 fprintf(fp, "%cSamples", *sep);
741 else
742 fputs(" Samples ", fp);
743 }
744
745 if (symbol_conf.show_cpu_utilization) {
746 if (sep) {
747 ret += fprintf(fp, "%csys", *sep);
748 ret += fprintf(fp, "%cus", *sep);
749 if (perf_guest) {
750 ret += fprintf(fp, "%cguest sys", *sep);
751 ret += fprintf(fp, "%cguest us", *sep);
752 }
753 } else {
754 ret += fprintf(fp, " sys ");
755 ret += fprintf(fp, " us ");
756 if (perf_guest) {
757 ret += fprintf(fp, " guest sys ");
758 ret += fprintf(fp, " guest us ");
759 }
760 }
761 }
762
763 if (pair) {
764 if (sep)
765 ret += fprintf(fp, "%cDelta", *sep);
766 else
767 ret += fprintf(fp, " Delta ");
768
769 if (show_displacement) {
770 if (sep)
771 ret += fprintf(fp, "%cDisplacement", *sep);
772 else
773 ret += fprintf(fp, " Displ");
774 }
775 }
776
777 list_for_each_entry(se, &hist_entry__sort_list, list) {
778 if (se->elide)
779 continue;
780 if (sep) {
781 fprintf(fp, "%c%s", *sep, se->se_header);
782 continue;
783 }
784 width = strlen(se->se_header);
785 if (symbol_conf.col_width_list_str) {
786 if (col_width) {
787 hists__set_col_len(self, se->se_width_idx,
788 atoi(col_width));
789 col_width = strchr(col_width, ',');
790 if (col_width)
791 ++col_width;
792 }
793 }
794 if (!hists__new_col_len(self, se->se_width_idx, width))
795 width = hists__col_len(self, se->se_width_idx);
796 fprintf(fp, " %*s", width, se->se_header);
797 }
798 fprintf(fp, "\n");
799
800 if (sep)
801 goto print_entries;
802
803 fprintf(fp, "# ........");
804 if (symbol_conf.show_nr_samples)
805 fprintf(fp, " ..........");
806 if (pair) {
807 fprintf(fp, " ..........");
808 if (show_displacement)
809 fprintf(fp, " .....");
810 }
811 list_for_each_entry(se, &hist_entry__sort_list, list) {
812 unsigned int i;
813
814 if (se->elide)
815 continue;
816
817 fprintf(fp, " ");
818 width = hists__col_len(self, se->se_width_idx);
819 if (width == 0)
820 width = strlen(se->se_header);
821 for (i = 0; i < width; i++)
822 fprintf(fp, ".");
823 }
824
825 fprintf(fp, "\n#\n");
826
827print_entries:
828 for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) {
829 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
830
831 if (show_displacement) {
832 if (h->pair != NULL)
833 displacement = ((long)h->pair->position -
834 (long)position);
835 else
836 displacement = 0;
837 ++position;
838 }
839 ret += hist_entry__fprintf(h, self, pair, show_displacement,
840 displacement, fp, self->stats.total_period);
841
842 if (symbol_conf.use_callchain)
843 ret += hist_entry__fprintf_callchain(h, self, fp,
844 self->stats.total_period);
845 if (h->ms.map == NULL && verbose > 1) {
846 __map_groups__fprintf_maps(&h->thread->mg,
847 MAP__FUNCTION, verbose, fp);
848 fprintf(fp, "%.10s end\n", graph_dotted_line);
849 }
850 }
851
852 free(rem_sq_bracket);
853
854 return ret;
855}
856
857/*
858 * See hists__fprintf to match the column widths
859 */
860unsigned int hists__sort_list_width(struct hists *self)
861{
862 struct sort_entry *se;
863 int ret = 9; /* total % */
864
865 if (symbol_conf.show_cpu_utilization) {
866 ret += 7; /* count_sys % */
867 ret += 6; /* count_us % */
868 if (perf_guest) {
869 ret += 13; /* count_guest_sys % */
870 ret += 12; /* count_guest_us % */
871 }
872 }
873
874 if (symbol_conf.show_nr_samples)
875 ret += 11;
876
877 list_for_each_entry(se, &hist_entry__sort_list, list)
878 if (!se->elide)
879 ret += 2 + hists__col_len(self, se->se_width_idx);
880
881 if (verbose) /* Addr + origin */
882 ret += 3 + BITS_PER_LONG / 4;
883
884 return ret;
885}
886
887static void hists__remove_entry_filter(struct hists *self, struct hist_entry *h,
888 enum hist_filter filter)
889{
890 h->filtered &= ~(1 << filter);
891 if (h->filtered)
892 return;
893
894 ++self->nr_entries;
895 if (h->ms.unfolded)
896 self->nr_entries += h->nr_rows;
897 h->row_offset = 0;
898 self->stats.total_period += h->period;
899 self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events;
900
901 hists__calc_col_len(self, h);
902}
903
904void hists__filter_by_dso(struct hists *self, const struct dso *dso)
905{
906 struct rb_node *nd;
907
908 self->nr_entries = self->stats.total_period = 0;
909 self->stats.nr_events[PERF_RECORD_SAMPLE] = 0;
910 hists__reset_col_len(self);
911
912 for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) {
913 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
914
915 if (symbol_conf.exclude_other && !h->parent)
916 continue;
917
918 if (dso != NULL && (h->ms.map == NULL || h->ms.map->dso != dso)) {
919 h->filtered |= (1 << HIST_FILTER__DSO);
920 continue;
921 }
922
923 hists__remove_entry_filter(self, h, HIST_FILTER__DSO);
924 }
925}
926
927void hists__filter_by_thread(struct hists *self, const struct thread *thread)
928{
929 struct rb_node *nd;
930
931 self->nr_entries = self->stats.total_period = 0;
932 self->stats.nr_events[PERF_RECORD_SAMPLE] = 0;
933 hists__reset_col_len(self);
934
935 for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) {
936 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
937
938 if (thread != NULL && h->thread != thread) {
939 h->filtered |= (1 << HIST_FILTER__THREAD);
940 continue;
941 }
942
943 hists__remove_entry_filter(self, h, HIST_FILTER__THREAD);
944 }
945}
946
947static int symbol__alloc_hist(struct symbol *self)
948{
949 struct sym_priv *priv = symbol__priv(self);
950 const int size = (sizeof(*priv->hist) +
951 (self->end - self->start) * sizeof(u64));
952
953 priv->hist = zalloc(size);
954 return priv->hist == NULL ? -1 : 0;
955}
956
957int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip)
958{
959 unsigned int sym_size, offset;
960 struct symbol *sym = self->ms.sym;
961 struct sym_priv *priv;
962 struct sym_hist *h;
963
964 if (!sym || !self->ms.map)
965 return 0;
966
967 priv = symbol__priv(sym);
968 if (priv->hist == NULL && symbol__alloc_hist(sym) < 0)
969 return -ENOMEM;
970
971 sym_size = sym->end - sym->start;
972 offset = ip - sym->start;
973
974 pr_debug3("%s: ip=%#Lx\n", __func__, self->ms.map->unmap_ip(self->ms.map, ip));
975
976 if (offset >= sym_size)
977 return 0;
978
979 h = priv->hist;
980 h->sum++;
981 h->ip[offset]++;
982
983 pr_debug3("%#Lx %s: period++ [ip: %#Lx, %#Lx] => %Ld\n", self->ms.sym->start,
984 self->ms.sym->name, ip, ip - self->ms.sym->start, h->ip[offset]);
985 return 0;
986}
987
988static struct objdump_line *objdump_line__new(s64 offset, char *line, size_t privsize)
989{
990 struct objdump_line *self = malloc(sizeof(*self) + privsize);
991
992 if (self != NULL) {
993 self->offset = offset;
994 self->line = line;
995 }
996
997 return self;
998}
999
1000void objdump_line__free(struct objdump_line *self)
1001{
1002 free(self->line);
1003 free(self);
1004}
1005
1006static void objdump__add_line(struct list_head *head, struct objdump_line *line)
1007{
1008 list_add_tail(&line->node, head);
1009}
1010
1011struct objdump_line *objdump__get_next_ip_line(struct list_head *head,
1012 struct objdump_line *pos)
1013{
1014 list_for_each_entry_continue(pos, head, node)
1015 if (pos->offset >= 0)
1016 return pos;
1017
1018 return NULL;
1019}
1020
1021static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file,
1022 struct list_head *head, size_t privsize)
1023{
1024 struct symbol *sym = self->ms.sym;
1025 struct objdump_line *objdump_line;
1026 char *line = NULL, *tmp, *tmp2, *c;
1027 size_t line_len;
1028 s64 line_ip, offset = -1;
1029
1030 if (getline(&line, &line_len, file) < 0)
1031 return -1;
1032
1033 if (!line)
1034 return -1;
1035
1036 while (line_len != 0 && isspace(line[line_len - 1]))
1037 line[--line_len] = '\0';
1038
1039 c = strchr(line, '\n');
1040 if (c)
1041 *c = 0;
1042
1043 line_ip = -1;
1044
1045 /*
1046 * Strip leading spaces:
1047 */
1048 tmp = line;
1049 while (*tmp) {
1050 if (*tmp != ' ')
1051 break;
1052 tmp++;
1053 }
1054
1055 if (*tmp) {
1056 /*
1057 * Parse hexa addresses followed by ':'
1058 */
1059 line_ip = strtoull(tmp, &tmp2, 16);
1060 if (*tmp2 != ':' || tmp == tmp2 || tmp2[1] == '\0')
1061 line_ip = -1;
1062 }
1063
1064 if (line_ip != -1) {
1065 u64 start = map__rip_2objdump(self->ms.map, sym->start),
1066 end = map__rip_2objdump(self->ms.map, sym->end);
1067
1068 offset = line_ip - start;
1069 if (offset < 0 || (u64)line_ip > end)
1070 offset = -1;
1071 }
1072
1073 objdump_line = objdump_line__new(offset, line, privsize);
1074 if (objdump_line == NULL) {
1075 free(line);
1076 return -1;
1077 }
1078 objdump__add_line(head, objdump_line);
1079
1080 return 0;
1081}
1082
1083int hist_entry__annotate(struct hist_entry *self, struct list_head *head,
1084 size_t privsize)
1085{
1086 struct symbol *sym = self->ms.sym;
1087 struct map *map = self->ms.map;
1088 struct dso *dso = map->dso;
1089 char *filename = dso__build_id_filename(dso, NULL, 0);
1090 bool free_filename = true;
1091 char command[PATH_MAX * 2];
1092 FILE *file;
1093 int err = 0;
1094 u64 len;
1095 char symfs_filename[PATH_MAX];
1096
1097 if (filename) {
1098 snprintf(symfs_filename, sizeof(symfs_filename), "%s%s",
1099 symbol_conf.symfs, filename);
1100 }
1101
1102 if (filename == NULL) {
1103 if (dso->has_build_id) {
1104 pr_err("Can't annotate %s: not enough memory\n",
1105 sym->name);
1106 return -ENOMEM;
1107 }
1108 goto fallback;
1109 } else if (readlink(symfs_filename, command, sizeof(command)) < 0 ||
1110 strstr(command, "[kernel.kallsyms]") ||
1111 access(symfs_filename, R_OK)) {
1112 free(filename);
1113fallback:
1114 /*
1115 * If we don't have build-ids or the build-id file isn't in the
1116 * cache, or is just a kallsyms file, well, lets hope that this
1117 * DSO is the same as when 'perf record' ran.
1118 */
1119 filename = dso->long_name;
1120 snprintf(symfs_filename, sizeof(symfs_filename), "%s%s",
1121 symbol_conf.symfs, filename);
1122 free_filename = false;
1123 }
1124
1125 if (dso->origin == DSO__ORIG_KERNEL) {
1126 if (dso->annotate_warned)
1127 goto out_free_filename;
1128 err = -ENOENT;
1129 dso->annotate_warned = 1;
1130 pr_err("Can't annotate %s: No vmlinux file was found in the "
1131 "path\n", sym->name);
1132 goto out_free_filename;
1133 }
1134
1135 pr_debug("%s: filename=%s, sym=%s, start=%#Lx, end=%#Lx\n", __func__,
1136 filename, sym->name, map->unmap_ip(map, sym->start),
1137 map->unmap_ip(map, sym->end));
1138
1139 len = sym->end - sym->start;
1140
1141 pr_debug("annotating [%p] %30s : [%p] %30s\n",
1142 dso, dso->long_name, sym, sym->name);
1143
1144 snprintf(command, sizeof(command),
1145 "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS -C %s|grep -v %s|expand",
1146 map__rip_2objdump(map, sym->start),
1147 map__rip_2objdump(map, sym->end),
1148 symfs_filename, filename);
1149
1150 pr_debug("Executing: %s\n", command);
1151
1152 file = popen(command, "r");
1153 if (!file)
1154 goto out_free_filename;
1155
1156 while (!feof(file))
1157 if (hist_entry__parse_objdump_line(self, file, head, privsize) < 0)
1158 break;
1159
1160 pclose(file);
1161out_free_filename:
1162 if (free_filename)
1163 free(filename);
1164 return err;
1165}
1166
1167void hists__inc_nr_events(struct hists *self, u32 type)
1168{
1169 ++self->stats.nr_events[0];
1170 ++self->stats.nr_events[type];
1171}
1172
1173size_t hists__fprintf_nr_events(struct hists *self, FILE *fp)
1174{
1175 int i;
1176 size_t ret = 0;
1177
1178 for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) {
1179 const char *name = event__get_event_name(i);
1180
1181 if (!strcmp(name, "UNKNOWN"))
1182 continue;
1183
1184 ret += fprintf(fp, "%16s events: %10d\n", name,
1185 self->stats.nr_events[i]);
1186 }
1187
1188 return ret;
1189}
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h
new file mode 100644
index 00000000000..ee789856a8c
--- /dev/null
+++ b/tools/perf/util/hist.h
@@ -0,0 +1,150 @@
1#ifndef __PERF_HIST_H
2#define __PERF_HIST_H
3
4#include <linux/types.h>
5#include "callchain.h"
6
7extern struct callchain_param callchain_param;
8
9struct hist_entry;
10struct addr_location;
11struct symbol;
12struct rb_root;
13
14struct objdump_line {
15 struct list_head node;
16 s64 offset;
17 char *line;
18};
19
20void objdump_line__free(struct objdump_line *self);
21struct objdump_line *objdump__get_next_ip_line(struct list_head *head,
22 struct objdump_line *pos);
23
24struct sym_hist {
25 u64 sum;
26 u64 ip[0];
27};
28
29struct sym_ext {
30 struct rb_node node;
31 double percent;
32 char *path;
33};
34
35struct sym_priv {
36 struct sym_hist *hist;
37 struct sym_ext *ext;
38};
39
40/*
41 * The kernel collects the number of events it couldn't send in a stretch and
42 * when possible sends this number in a PERF_RECORD_LOST event. The number of
43 * such "chunks" of lost events is stored in .nr_events[PERF_EVENT_LOST] while
44 * total_lost tells exactly how many events the kernel in fact lost, i.e. it is
45 * the sum of all struct lost_event.lost fields reported.
46 *
47 * The total_period is needed because by default auto-freq is used, so
48 * multipling nr_events[PERF_EVENT_SAMPLE] by a frequency isn't possible to get
49 * the total number of low level events, it is necessary to to sum all struct
50 * sample_event.period and stash the result in total_period.
51 */
52struct events_stats {
53 u64 total_period;
54 u64 total_lost;
55 u64 total_invalid_chains;
56 u32 nr_events[PERF_RECORD_HEADER_MAX];
57 u32 nr_unknown_events;
58 u32 nr_invalid_chains;
59};
60
61enum hist_column {
62 HISTC_SYMBOL,
63 HISTC_DSO,
64 HISTC_THREAD,
65 HISTC_COMM,
66 HISTC_PARENT,
67 HISTC_CPU,
68 HISTC_NR_COLS, /* Last entry */
69};
70
71struct hists {
72 struct rb_node rb_node;
73 struct rb_root entries;
74 u64 nr_entries;
75 struct events_stats stats;
76 u64 config;
77 u64 event_stream;
78 u32 type;
79 u16 col_len[HISTC_NR_COLS];
80};
81
82struct hist_entry *__hists__add_entry(struct hists *self,
83 struct addr_location *al,
84 struct symbol *parent, u64 period);
85extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *);
86extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *);
87int hist_entry__fprintf(struct hist_entry *self, struct hists *hists,
88 struct hists *pair_hists, bool show_displacement,
89 long displacement, FILE *fp, u64 total);
90int hist_entry__snprintf(struct hist_entry *self, char *bf, size_t size,
91 struct hists *hists, struct hists *pair_hists,
92 bool show_displacement, long displacement,
93 bool color, u64 total);
94void hist_entry__free(struct hist_entry *);
95
96void hists__output_resort(struct hists *self);
97void hists__collapse_resort(struct hists *self);
98
99void hists__inc_nr_events(struct hists *self, u32 type);
100size_t hists__fprintf_nr_events(struct hists *self, FILE *fp);
101
102size_t hists__fprintf(struct hists *self, struct hists *pair,
103 bool show_displacement, FILE *fp);
104
105int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip);
106int hist_entry__annotate(struct hist_entry *self, struct list_head *head,
107 size_t privsize);
108
109void hists__filter_by_dso(struct hists *self, const struct dso *dso);
110void hists__filter_by_thread(struct hists *self, const struct thread *thread);
111
112u16 hists__col_len(struct hists *self, enum hist_column col);
113void hists__set_col_len(struct hists *self, enum hist_column col, u16 len);
114bool hists__new_col_len(struct hists *self, enum hist_column col, u16 len);
115
116#ifdef NO_NEWT_SUPPORT
117static inline int hists__browse(struct hists *self __used,
118 const char *helpline __used,
119 const char *ev_name __used)
120{
121 return 0;
122}
123
124static inline int hists__tui_browse_tree(struct rb_root *self __used,
125 const char *help __used)
126{
127 return 0;
128}
129
130static inline int hist_entry__tui_annotate(struct hist_entry *self __used)
131{
132 return 0;
133}
134#define KEY_LEFT -1
135#define KEY_RIGHT -2
136#else
137#include <newt.h>
138int hists__browse(struct hists *self, const char *helpline,
139 const char *ev_name);
140int hist_entry__tui_annotate(struct hist_entry *self);
141
142#define KEY_LEFT NEWT_KEY_LEFT
143#define KEY_RIGHT NEWT_KEY_RIGHT
144
145int hists__tui_browse_tree(struct rb_root *self, const char *help);
146#endif
147
148unsigned int hists__sort_list_width(struct hists *self);
149
150#endif /* __PERF_HIST_H */
diff --git a/tools/perf/util/hweight.c b/tools/perf/util/hweight.c
new file mode 100644
index 00000000000..5c1d0d099f0
--- /dev/null
+++ b/tools/perf/util/hweight.c
@@ -0,0 +1,31 @@
1#include <linux/bitops.h>
2
3/**
4 * hweightN - returns the hamming weight of a N-bit word
5 * @x: the word to weigh
6 *
7 * The Hamming Weight of a number is the total number of bits set in it.
8 */
9
10unsigned int hweight32(unsigned int w)
11{
12 unsigned int res = w - ((w >> 1) & 0x55555555);
13 res = (res & 0x33333333) + ((res >> 2) & 0x33333333);
14 res = (res + (res >> 4)) & 0x0F0F0F0F;
15 res = res + (res >> 8);
16 return (res + (res >> 16)) & 0x000000FF;
17}
18
19unsigned long hweight64(__u64 w)
20{
21#if BITS_PER_LONG == 32
22 return hweight32((unsigned int)(w >> 32)) + hweight32((unsigned int)w);
23#elif BITS_PER_LONG == 64
24 __u64 res = w - ((w >> 1) & 0x5555555555555555ul);
25 res = (res & 0x3333333333333333ul) + ((res >> 2) & 0x3333333333333333ul);
26 res = (res + (res >> 4)) & 0x0F0F0F0F0F0F0F0Ful;
27 res = res + (res >> 8);
28 res = res + (res >> 16);
29 return (res + (res >> 32)) & 0x00000000000000FFul;
30#endif
31}
diff --git a/tools/perf/util/include/asm/asm-offsets.h b/tools/perf/util/include/asm/asm-offsets.h
new file mode 100644
index 00000000000..ed538942523
--- /dev/null
+++ b/tools/perf/util/include/asm/asm-offsets.h
@@ -0,0 +1 @@
/* stub */
diff --git a/tools/perf/util/include/asm/bug.h b/tools/perf/util/include/asm/bug.h
new file mode 100644
index 00000000000..7fcc6810adc
--- /dev/null
+++ b/tools/perf/util/include/asm/bug.h
@@ -0,0 +1,22 @@
1#ifndef _PERF_ASM_GENERIC_BUG_H
2#define _PERF_ASM_GENERIC_BUG_H
3
4#define __WARN_printf(arg...) do { fprintf(stderr, arg); } while (0)
5
6#define WARN(condition, format...) ({ \
7 int __ret_warn_on = !!(condition); \
8 if (unlikely(__ret_warn_on)) \
9 __WARN_printf(format); \
10 unlikely(__ret_warn_on); \
11})
12
13#define WARN_ONCE(condition, format...) ({ \
14 static int __warned; \
15 int __ret_warn_once = !!(condition); \
16 \
17 if (unlikely(__ret_warn_once)) \
18 if (WARN(!__warned, format)) \
19 __warned = 1; \
20 unlikely(__ret_warn_once); \
21})
22#endif
diff --git a/tools/perf/util/include/asm/byteorder.h b/tools/perf/util/include/asm/byteorder.h
new file mode 100644
index 00000000000..b722abe3a62
--- /dev/null
+++ b/tools/perf/util/include/asm/byteorder.h
@@ -0,0 +1,2 @@
1#include <asm/types.h>
2#include "../../../../include/linux/swab.h"
diff --git a/tools/perf/util/include/asm/cpufeature.h b/tools/perf/util/include/asm/cpufeature.h
new file mode 100644
index 00000000000..acffd5e4d1d
--- /dev/null
+++ b/tools/perf/util/include/asm/cpufeature.h
@@ -0,0 +1,9 @@
1
2#ifndef PERF_CPUFEATURE_H
3#define PERF_CPUFEATURE_H
4
5/* cpufeature.h ... dummy header file for including arch/x86/lib/memcpy_64.S */
6
7#define X86_FEATURE_REP_GOOD 0
8
9#endif /* PERF_CPUFEATURE_H */
diff --git a/tools/perf/util/include/asm/dwarf2.h b/tools/perf/util/include/asm/dwarf2.h
new file mode 100644
index 00000000000..bb4198e7837
--- /dev/null
+++ b/tools/perf/util/include/asm/dwarf2.h
@@ -0,0 +1,11 @@
1
2#ifndef PERF_DWARF2_H
3#define PERF_DWARF2_H
4
5/* dwarf2.h ... dummy header file for including arch/x86/lib/memcpy_64.S */
6
7#define CFI_STARTPROC
8#define CFI_ENDPROC
9
10#endif /* PERF_DWARF2_H */
11
diff --git a/tools/perf/util/include/asm/hweight.h b/tools/perf/util/include/asm/hweight.h
new file mode 100644
index 00000000000..36cf26d434a
--- /dev/null
+++ b/tools/perf/util/include/asm/hweight.h
@@ -0,0 +1,8 @@
1#ifndef PERF_HWEIGHT_H
2#define PERF_HWEIGHT_H
3
4#include <linux/types.h>
5unsigned int hweight32(unsigned int w);
6unsigned long hweight64(__u64 w);
7
8#endif /* PERF_HWEIGHT_H */
diff --git a/tools/perf/util/include/asm/swab.h b/tools/perf/util/include/asm/swab.h
new file mode 100644
index 00000000000..ed538942523
--- /dev/null
+++ b/tools/perf/util/include/asm/swab.h
@@ -0,0 +1 @@
/* stub */
diff --git a/tools/perf/util/include/asm/system.h b/tools/perf/util/include/asm/system.h
new file mode 100644
index 00000000000..710cecca972
--- /dev/null
+++ b/tools/perf/util/include/asm/system.h
@@ -0,0 +1 @@
/* Empty */
diff --git a/tools/perf/util/include/asm/uaccess.h b/tools/perf/util/include/asm/uaccess.h
new file mode 100644
index 00000000000..d0f72b8fcc3
--- /dev/null
+++ b/tools/perf/util/include/asm/uaccess.h
@@ -0,0 +1,14 @@
1#ifndef _PERF_ASM_UACCESS_H_
2#define _PERF_ASM_UACCESS_H_
3
4#define __get_user(src, dest) \
5({ \
6 (src) = *dest; \
7 0; \
8})
9
10#define get_user __get_user
11
12#define access_ok(type, addr, size) 1
13
14#endif
diff --git a/tools/perf/util/include/dwarf-regs.h b/tools/perf/util/include/dwarf-regs.h
new file mode 100644
index 00000000000..cf6727e99c4
--- /dev/null
+++ b/tools/perf/util/include/dwarf-regs.h
@@ -0,0 +1,8 @@
1#ifndef _PERF_DWARF_REGS_H_
2#define _PERF_DWARF_REGS_H_
3
4#ifdef DWARF_SUPPORT
5const char *get_arch_regstr(unsigned int n);
6#endif
7
8#endif
diff --git a/tools/perf/util/include/linux/bitmap.h b/tools/perf/util/include/linux/bitmap.h
new file mode 100644
index 00000000000..eda4416efa0
--- /dev/null
+++ b/tools/perf/util/include/linux/bitmap.h
@@ -0,0 +1,35 @@
1#ifndef _PERF_BITOPS_H
2#define _PERF_BITOPS_H
3
4#include <string.h>
5#include <linux/bitops.h>
6
7int __bitmap_weight(const unsigned long *bitmap, int bits);
8
9#define BITMAP_LAST_WORD_MASK(nbits) \
10( \
11 ((nbits) % BITS_PER_LONG) ? \
12 (1UL<<((nbits) % BITS_PER_LONG))-1 : ~0UL \
13)
14
15#define small_const_nbits(nbits) \
16 (__builtin_constant_p(nbits) && (nbits) <= BITS_PER_LONG)
17
18static inline void bitmap_zero(unsigned long *dst, int nbits)
19{
20 if (small_const_nbits(nbits))
21 *dst = 0UL;
22 else {
23 int len = BITS_TO_LONGS(nbits) * sizeof(unsigned long);
24 memset(dst, 0, len);
25 }
26}
27
28static inline int bitmap_weight(const unsigned long *src, int nbits)
29{
30 if (small_const_nbits(nbits))
31 return hweight_long(*src & BITMAP_LAST_WORD_MASK(nbits));
32 return __bitmap_weight(src, nbits);
33}
34
35#endif /* _PERF_BITOPS_H */
diff --git a/tools/perf/util/include/linux/bitops.h b/tools/perf/util/include/linux/bitops.h
new file mode 100644
index 00000000000..8be0b968ca0
--- /dev/null
+++ b/tools/perf/util/include/linux/bitops.h
@@ -0,0 +1,32 @@
1#ifndef _PERF_LINUX_BITOPS_H_
2#define _PERF_LINUX_BITOPS_H_
3
4#include <linux/kernel.h>
5#include <asm/hweight.h>
6
7#define BITS_PER_LONG __WORDSIZE
8#define BITS_PER_BYTE 8
9#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))
10
11static inline void set_bit(int nr, unsigned long *addr)
12{
13 addr[nr / BITS_PER_LONG] |= 1UL << (nr % BITS_PER_LONG);
14}
15
16static inline void clear_bit(int nr, unsigned long *addr)
17{
18 addr[nr / BITS_PER_LONG] &= ~(1UL << (nr % BITS_PER_LONG));
19}
20
21static __always_inline int test_bit(unsigned int nr, const unsigned long *addr)
22{
23 return ((1UL << (nr % BITS_PER_LONG)) &
24 (((unsigned long *)addr)[nr / BITS_PER_LONG])) != 0;
25}
26
27static inline unsigned long hweight_long(unsigned long w)
28{
29 return sizeof(w) == 4 ? hweight32(w) : hweight64(w);
30}
31
32#endif
diff --git a/tools/perf/util/include/linux/compiler.h b/tools/perf/util/include/linux/compiler.h
new file mode 100644
index 00000000000..791f9dd27eb
--- /dev/null
+++ b/tools/perf/util/include/linux/compiler.h
@@ -0,0 +1,12 @@
1#ifndef _PERF_LINUX_COMPILER_H_
2#define _PERF_LINUX_COMPILER_H_
3
4#ifndef __always_inline
5#define __always_inline inline
6#endif
7#define __user
8#define __attribute_const__
9
10#define __used __attribute__((__unused__))
11
12#endif
diff --git a/tools/perf/util/include/linux/ctype.h b/tools/perf/util/include/linux/ctype.h
new file mode 100644
index 00000000000..a53d4ee1e0b
--- /dev/null
+++ b/tools/perf/util/include/linux/ctype.h
@@ -0,0 +1 @@
#include "../util.h"
diff --git a/tools/perf/util/include/linux/hash.h b/tools/perf/util/include/linux/hash.h
new file mode 100644
index 00000000000..201f5739799
--- /dev/null
+++ b/tools/perf/util/include/linux/hash.h
@@ -0,0 +1,5 @@
1#include "../../../../include/linux/hash.h"
2
3#ifndef PERF_HASH_H
4#define PERF_HASH_H
5#endif
diff --git a/tools/perf/util/include/linux/kernel.h b/tools/perf/util/include/linux/kernel.h
new file mode 100644
index 00000000000..1eb804fd3fb
--- /dev/null
+++ b/tools/perf/util/include/linux/kernel.h
@@ -0,0 +1,111 @@
1#ifndef PERF_LINUX_KERNEL_H_
2#define PERF_LINUX_KERNEL_H_
3
4#include <stdarg.h>
5#include <stdio.h>
6#include <stdlib.h>
7#include <assert.h>
8
9#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
10
11#define ALIGN(x,a) __ALIGN_MASK(x,(typeof(x))(a)-1)
12#define __ALIGN_MASK(x,mask) (((x)+(mask))&~(mask))
13
14#ifndef offsetof
15#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
16#endif
17
18#ifndef container_of
19/**
20 * container_of - cast a member of a structure out to the containing structure
21 * @ptr: the pointer to the member.
22 * @type: the type of the container struct this is embedded in.
23 * @member: the name of the member within the struct.
24 *
25 */
26#define container_of(ptr, type, member) ({ \
27 const typeof(((type *)0)->member) * __mptr = (ptr); \
28 (type *)((char *)__mptr - offsetof(type, member)); })
29#endif
30
31#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))
32
33#ifndef max
34#define max(x, y) ({ \
35 typeof(x) _max1 = (x); \
36 typeof(y) _max2 = (y); \
37 (void) (&_max1 == &_max2); \
38 _max1 > _max2 ? _max1 : _max2; })
39#endif
40
41#ifndef min
42#define min(x, y) ({ \
43 typeof(x) _min1 = (x); \
44 typeof(y) _min2 = (y); \
45 (void) (&_min1 == &_min2); \
46 _min1 < _min2 ? _min1 : _min2; })
47#endif
48
49#ifndef BUG_ON
50#define BUG_ON(cond) assert(!(cond))
51#endif
52
53/*
54 * Both need more care to handle endianness
55 * (Don't use bitmap_copy_le() for now)
56 */
57#define cpu_to_le64(x) (x)
58#define cpu_to_le32(x) (x)
59
60static inline int
61vscnprintf(char *buf, size_t size, const char *fmt, va_list args)
62{
63 int i;
64 ssize_t ssize = size;
65
66 i = vsnprintf(buf, size, fmt, args);
67
68 return (i >= ssize) ? (ssize - 1) : i;
69}
70
71static inline int scnprintf(char * buf, size_t size, const char * fmt, ...)
72{
73 va_list args;
74 ssize_t ssize = size;
75 int i;
76
77 va_start(args, fmt);
78 i = vsnprintf(buf, size, fmt, args);
79 va_end(args);
80
81 return (i >= ssize) ? (ssize - 1) : i;
82}
83
84static inline unsigned long
85simple_strtoul(const char *nptr, char **endptr, int base)
86{
87 return strtoul(nptr, endptr, base);
88}
89
90int eprintf(int level,
91 const char *fmt, ...) __attribute__((format(printf, 2, 3)));
92
93#ifndef pr_fmt
94#define pr_fmt(fmt) fmt
95#endif
96
97#define pr_err(fmt, ...) \
98 eprintf(0, pr_fmt(fmt), ##__VA_ARGS__)
99#define pr_warning(fmt, ...) \
100 eprintf(0, pr_fmt(fmt), ##__VA_ARGS__)
101#define pr_info(fmt, ...) \
102 eprintf(0, pr_fmt(fmt), ##__VA_ARGS__)
103#define pr_debug(fmt, ...) \
104 eprintf(1, pr_fmt(fmt), ##__VA_ARGS__)
105#define pr_debugN(n, fmt, ...) \
106 eprintf(n, pr_fmt(fmt), ##__VA_ARGS__)
107#define pr_debug2(fmt, ...) pr_debugN(2, pr_fmt(fmt), ##__VA_ARGS__)
108#define pr_debug3(fmt, ...) pr_debugN(3, pr_fmt(fmt), ##__VA_ARGS__)
109#define pr_debug4(fmt, ...) pr_debugN(4, pr_fmt(fmt), ##__VA_ARGS__)
110
111#endif
diff --git a/tools/perf/util/include/linux/linkage.h b/tools/perf/util/include/linux/linkage.h
new file mode 100644
index 00000000000..06387cffe12
--- /dev/null
+++ b/tools/perf/util/include/linux/linkage.h
@@ -0,0 +1,13 @@
1
2#ifndef PERF_LINUX_LINKAGE_H_
3#define PERF_LINUX_LINKAGE_H_
4
5/* linkage.h ... for including arch/x86/lib/memcpy_64.S */
6
7#define ENTRY(name) \
8 .globl name; \
9 name:
10
11#define ENDPROC(name)
12
13#endif /* PERF_LINUX_LINKAGE_H_ */
diff --git a/tools/perf/util/include/linux/list.h b/tools/perf/util/include/linux/list.h
new file mode 100644
index 00000000000..f5ca26e53fb
--- /dev/null
+++ b/tools/perf/util/include/linux/list.h
@@ -0,0 +1,26 @@
1#include "../../../../include/linux/list.h"
2
3#ifndef PERF_LIST_H
4#define PERF_LIST_H
5/**
6 * list_del_range - deletes range of entries from list.
7 * @begin: first element in the range to delete from the list.
8 * @end: last element in the range to delete from the list.
9 * Note: list_empty on the range of entries does not return true after this,
10 * the entries is in an undefined state.
11 */
12static inline void list_del_range(struct list_head *begin,
13 struct list_head *end)
14{
15 begin->prev->next = end->next;
16 end->next->prev = begin->prev;
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)
26#endif
diff --git a/tools/perf/util/include/linux/module.h b/tools/perf/util/include/linux/module.h
new file mode 100644
index 00000000000..b43e2dc21e0
--- /dev/null
+++ b/tools/perf/util/include/linux/module.h
@@ -0,0 +1,6 @@
1#ifndef PERF_LINUX_MODULE_H
2#define PERF_LINUX_MODULE_H
3
4#define EXPORT_SYMBOL(name)
5
6#endif
diff --git a/tools/perf/util/include/linux/poison.h b/tools/perf/util/include/linux/poison.h
new file mode 100644
index 00000000000..fef6dbc9ce1
--- /dev/null
+++ b/tools/perf/util/include/linux/poison.h
@@ -0,0 +1 @@
#include "../../../../include/linux/poison.h"
diff --git a/tools/perf/util/include/linux/prefetch.h b/tools/perf/util/include/linux/prefetch.h
new file mode 100644
index 00000000000..7841e485d8c
--- /dev/null
+++ b/tools/perf/util/include/linux/prefetch.h
@@ -0,0 +1,6 @@
1#ifndef PERF_LINUX_PREFETCH_H
2#define PERF_LINUX_PREFETCH_H
3
4static inline void prefetch(void *a __attribute__((unused))) { }
5
6#endif
diff --git a/tools/perf/util/include/linux/rbtree.h b/tools/perf/util/include/linux/rbtree.h
new file mode 100644
index 00000000000..7a243a14303
--- /dev/null
+++ b/tools/perf/util/include/linux/rbtree.h
@@ -0,0 +1 @@
#include "../../../../include/linux/rbtree.h"
diff --git a/tools/perf/util/include/linux/string.h b/tools/perf/util/include/linux/string.h
new file mode 100644
index 00000000000..3b2f5900276
--- /dev/null
+++ b/tools/perf/util/include/linux/string.h
@@ -0,0 +1 @@
#include <string.h>
diff --git a/tools/perf/util/include/linux/types.h b/tools/perf/util/include/linux/types.h
new file mode 100644
index 00000000000..12de3b8112f
--- /dev/null
+++ b/tools/perf/util/include/linux/types.h
@@ -0,0 +1,21 @@
1#ifndef _PERF_LINUX_TYPES_H_
2#define _PERF_LINUX_TYPES_H_
3
4#include <asm/types.h>
5
6#define DECLARE_BITMAP(name,bits) \
7 unsigned long name[BITS_TO_LONGS(bits)]
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
21#endif
diff --git a/tools/perf/util/levenshtein.h b/tools/perf/util/levenshtein.h
index 0173abeef52..b0fcb6d8a88 100644
--- a/tools/perf/util/levenshtein.h
+++ b/tools/perf/util/levenshtein.h
@@ -1,8 +1,8 @@
1#ifndef LEVENSHTEIN_H 1#ifndef __PERF_LEVENSHTEIN_H
2#define LEVENSHTEIN_H 2#define __PERF_LEVENSHTEIN_H
3 3
4int levenshtein(const char *string1, const char *string2, 4int levenshtein(const char *string1, const char *string2,
5 int swap_penalty, int substition_penalty, 5 int swap_penalty, int substition_penalty,
6 int insertion_penalty, int deletion_penalty); 6 int insertion_penalty, int deletion_penalty);
7 7
8#endif 8#endif /* __PERF_LEVENSHTEIN_H */
diff --git a/tools/perf/util/list.h b/tools/perf/util/list.h
deleted file mode 100644
index e2548e8072c..00000000000
--- a/tools/perf/util/list.h
+++ /dev/null
@@ -1,603 +0,0 @@
1#ifndef _LINUX_LIST_H
2#define _LINUX_LIST_H
3/*
4 Copyright (C) Cast of dozens, comes from the Linux kernel
5
6 This program is free software; you can redistribute it and/or modify it
7 under the terms of version 2 of the GNU General Public License as
8 published by the Free Software Foundation.
9*/
10
11#include <stddef.h>
12
13/*
14 * These are non-NULL pointers that will result in page faults
15 * under normal circumstances, used to verify that nobody uses
16 * non-initialized list entries.
17 */
18#define LIST_POISON1 ((void *)0x00100100)
19#define LIST_POISON2 ((void *)0x00200200)
20
21/**
22 * container_of - cast a member of a structure out to the containing structure
23 * @ptr: the pointer to the member.
24 * @type: the type of the container struct this is embedded in.
25 * @member: the name of the member within the struct.
26 *
27 */
28#define container_of(ptr, type, member) ({ \
29 const typeof( ((type *)0)->member ) *__mptr = (ptr); \
30 (type *)( (char *)__mptr - offsetof(type,member) );})
31
32/*
33 * Simple doubly linked list implementation.
34 *
35 * Some of the internal functions ("__xxx") are useful when
36 * manipulating whole lists rather than single entries, as
37 * sometimes we already know the next/prev entries and we can
38 * generate better code by using them directly rather than
39 * using the generic single-entry routines.
40 */
41
42struct list_head {
43 struct list_head *next, *prev;
44};
45
46#define LIST_HEAD_INIT(name) { &(name), &(name) }
47
48#define LIST_HEAD(name) \
49 struct list_head name = LIST_HEAD_INIT(name)
50
51static inline void INIT_LIST_HEAD(struct list_head *list)
52{
53 list->next = list;
54 list->prev = list;
55}
56
57/*
58 * Insert a new entry between two known consecutive entries.
59 *
60 * This is only for internal list manipulation where we know
61 * the prev/next entries already!
62 */
63static inline void __list_add(struct list_head *new,
64 struct list_head *prev,
65 struct list_head *next)
66{
67 next->prev = new;
68 new->next = next;
69 new->prev = prev;
70 prev->next = new;
71}
72
73/**
74 * list_add - add a new entry
75 * @new: new entry to be added
76 * @head: list head to add it after
77 *
78 * Insert a new entry after the specified head.
79 * This is good for implementing stacks.
80 */
81static inline void list_add(struct list_head *new, struct list_head *head)
82{
83 __list_add(new, head, head->next);
84}
85
86/**
87 * list_add_tail - add a new entry
88 * @new: new entry to be added
89 * @head: list head to add it before
90 *
91 * Insert a new entry before the specified head.
92 * This is useful for implementing queues.
93 */
94static inline void list_add_tail(struct list_head *new, struct list_head *head)
95{
96 __list_add(new, head->prev, head);
97}
98
99/*
100 * Delete a list entry by making the prev/next entries
101 * point to each other.
102 *
103 * This is only for internal list manipulation where we know
104 * the prev/next entries already!
105 */
106static inline void __list_del(struct list_head * prev, struct list_head * next)
107{
108 next->prev = prev;
109 prev->next = next;
110}
111
112/**
113 * list_del - deletes entry from list.
114 * @entry: the element to delete from the list.
115 * Note: list_empty on entry does not return true after this, the entry is
116 * in an undefined state.
117 */
118static inline void list_del(struct list_head *entry)
119{
120 __list_del(entry->prev, entry->next);
121 entry->next = LIST_POISON1;
122 entry->prev = LIST_POISON2;
123}
124
125/**
126 * list_del_range - deletes range of entries from list.
127 * @beging: first element in the range to delete from the list.
128 * @beging: first element in the range to delete from the list.
129 * Note: list_empty on the range of entries does not return true after this,
130 * the entries is in an undefined state.
131 */
132static inline void list_del_range(struct list_head *begin,
133 struct list_head *end)
134{
135 begin->prev->next = end->next;
136 end->next->prev = begin->prev;
137}
138
139/**
140 * list_replace - replace old entry by new one
141 * @old : the element to be replaced
142 * @new : the new element to insert
143 * Note: if 'old' was empty, it will be overwritten.
144 */
145static inline void list_replace(struct list_head *old,
146 struct list_head *new)
147{
148 new->next = old->next;
149 new->next->prev = new;
150 new->prev = old->prev;
151 new->prev->next = new;
152}
153
154static inline void list_replace_init(struct list_head *old,
155 struct list_head *new)
156{
157 list_replace(old, new);
158 INIT_LIST_HEAD(old);
159}
160
161/**
162 * list_del_init - deletes entry from list and reinitialize it.
163 * @entry: the element to delete from the list.
164 */
165static inline void list_del_init(struct list_head *entry)
166{
167 __list_del(entry->prev, entry->next);
168 INIT_LIST_HEAD(entry);
169}
170
171/**
172 * list_move - delete from one list and add as another's head
173 * @list: the entry to move
174 * @head: the head that will precede our entry
175 */
176static inline void list_move(struct list_head *list, struct list_head *head)
177{
178 __list_del(list->prev, list->next);
179 list_add(list, head);
180}
181
182/**
183 * list_move_tail - delete from one list and add as another's tail
184 * @list: the entry to move
185 * @head: the head that will follow our entry
186 */
187static inline void list_move_tail(struct list_head *list,
188 struct list_head *head)
189{
190 __list_del(list->prev, list->next);
191 list_add_tail(list, head);
192}
193
194/**
195 * list_is_last - tests whether @list is the last entry in list @head
196 * @list: the entry to test
197 * @head: the head of the list
198 */
199static inline int list_is_last(const struct list_head *list,
200 const struct list_head *head)
201{
202 return list->next == head;
203}
204
205/**
206 * list_empty - tests whether a list is empty
207 * @head: the list to test.
208 */
209static inline int list_empty(const struct list_head *head)
210{
211 return head->next == head;
212}
213
214/**
215 * list_empty_careful - tests whether a list is empty and not being modified
216 * @head: the list to test
217 *
218 * Description:
219 * tests whether a list is empty _and_ checks that no other CPU might be
220 * in the process of modifying either member (next or prev)
221 *
222 * NOTE: using list_empty_careful() without synchronization
223 * can only be safe if the only activity that can happen
224 * to the list entry is list_del_init(). Eg. it cannot be used
225 * if another CPU could re-list_add() it.
226 */
227static inline int list_empty_careful(const struct list_head *head)
228{
229 struct list_head *next = head->next;
230 return (next == head) && (next == head->prev);
231}
232
233static inline void __list_splice(struct list_head *list,
234 struct list_head *head)
235{
236 struct list_head *first = list->next;
237 struct list_head *last = list->prev;
238 struct list_head *at = head->next;
239
240 first->prev = head;
241 head->next = first;
242
243 last->next = at;
244 at->prev = last;
245}
246
247/**
248 * list_splice - join two lists
249 * @list: the new list to add.
250 * @head: the place to add it in the first list.
251 */
252static inline void list_splice(struct list_head *list, struct list_head *head)
253{
254 if (!list_empty(list))
255 __list_splice(list, head);
256}
257
258/**
259 * list_splice_init - join two lists and reinitialise the emptied list.
260 * @list: the new list to add.
261 * @head: the place to add it in the first list.
262 *
263 * The list at @list is reinitialised
264 */
265static inline void list_splice_init(struct list_head *list,
266 struct list_head *head)
267{
268 if (!list_empty(list)) {
269 __list_splice(list, head);
270 INIT_LIST_HEAD(list);
271 }
272}
273
274/**
275 * list_entry - get the struct for this entry
276 * @ptr: the &struct list_head pointer.
277 * @type: the type of the struct this is embedded in.
278 * @member: the name of the list_struct within the struct.
279 */
280#define list_entry(ptr, type, member) \
281 container_of(ptr, type, member)
282
283/**
284 * list_first_entry - get the first element from a list
285 * @ptr: the list head to take the element from.
286 * @type: the type of the struct this is embedded in.
287 * @member: the name of the list_struct within the struct.
288 *
289 * Note, that list is expected to be not empty.
290 */
291#define list_first_entry(ptr, type, member) \
292 list_entry((ptr)->next, type, member)
293
294/**
295 * list_for_each - iterate over a list
296 * @pos: the &struct list_head to use as a loop cursor.
297 * @head: the head for your list.
298 */
299#define list_for_each(pos, head) \
300 for (pos = (head)->next; pos != (head); \
301 pos = pos->next)
302
303/**
304 * __list_for_each - iterate over a list
305 * @pos: the &struct list_head to use as a loop cursor.
306 * @head: the head for your list.
307 *
308 * This variant differs from list_for_each() in that it's the
309 * simplest possible list iteration code, no prefetching is done.
310 * Use this for code that knows the list to be very short (empty
311 * or 1 entry) most of the time.
312 */
313#define __list_for_each(pos, head) \
314 for (pos = (head)->next; pos != (head); pos = pos->next)
315
316/**
317 * list_for_each_prev - iterate over a list backwards
318 * @pos: the &struct list_head to use as a loop cursor.
319 * @head: the head for your list.
320 */
321#define list_for_each_prev(pos, head) \
322 for (pos = (head)->prev; pos != (head); \
323 pos = pos->prev)
324
325/**
326 * list_for_each_safe - iterate over a list safe against removal of list entry
327 * @pos: the &struct list_head to use as a loop cursor.
328 * @n: another &struct list_head to use as temporary storage
329 * @head: the head for your list.
330 */
331#define list_for_each_safe(pos, n, head) \
332 for (pos = (head)->next, n = pos->next; pos != (head); \
333 pos = n, n = pos->next)
334
335/**
336 * list_for_each_entry - iterate over list of given type
337 * @pos: the type * to use as a loop cursor.
338 * @head: the head for your list.
339 * @member: the name of the list_struct within the struct.
340 */
341#define list_for_each_entry(pos, head, member) \
342 for (pos = list_entry((head)->next, typeof(*pos), member); \
343 &pos->member != (head); \
344 pos = list_entry(pos->member.next, typeof(*pos), member))
345
346/**
347 * list_for_each_entry_reverse - iterate backwards over list of given type.
348 * @pos: the type * to use as a loop cursor.
349 * @head: the head for your list.
350 * @member: the name of the list_struct within the struct.
351 */
352#define list_for_each_entry_reverse(pos, head, member) \
353 for (pos = list_entry((head)->prev, typeof(*pos), member); \
354 &pos->member != (head); \
355 pos = list_entry(pos->member.prev, typeof(*pos), member))
356
357/**
358 * list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue
359 * @pos: the type * to use as a start point
360 * @head: the head of the list
361 * @member: the name of the list_struct within the struct.
362 *
363 * Prepares a pos entry for use as a start point in list_for_each_entry_continue.
364 */
365#define list_prepare_entry(pos, head, member) \
366 ((pos) ? : list_entry(head, typeof(*pos), member))
367
368/**
369 * list_for_each_entry_continue - continue iteration over list of given type
370 * @pos: the type * to use as a loop cursor.
371 * @head: the head for your list.
372 * @member: the name of the list_struct within the struct.
373 *
374 * Continue to iterate over list of given type, continuing after
375 * the current position.
376 */
377#define list_for_each_entry_continue(pos, head, member) \
378 for (pos = list_entry(pos->member.next, typeof(*pos), member); \
379 &pos->member != (head); \
380 pos = list_entry(pos->member.next, typeof(*pos), member))
381
382/**
383 * list_for_each_entry_from - iterate over list of given type from the current point
384 * @pos: the type * to use as a loop cursor.
385 * @head: the head for your list.
386 * @member: the name of the list_struct within the struct.
387 *
388 * Iterate over list of given type, continuing from current position.
389 */
390#define list_for_each_entry_from(pos, head, member) \
391 for (; &pos->member != (head); \
392 pos = list_entry(pos->member.next, typeof(*pos), member))
393
394/**
395 * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
396 * @pos: the type * to use as a loop cursor.
397 * @n: another type * to use as temporary storage
398 * @head: the head for your list.
399 * @member: the name of the list_struct within the struct.
400 */
401#define list_for_each_entry_safe(pos, n, head, member) \
402 for (pos = list_entry((head)->next, typeof(*pos), member), \
403 n = list_entry(pos->member.next, typeof(*pos), member); \
404 &pos->member != (head); \
405 pos = n, n = list_entry(n->member.next, typeof(*n), member))
406
407/**
408 * list_for_each_entry_safe_continue
409 * @pos: the type * to use as a loop cursor.
410 * @n: another type * to use as temporary storage
411 * @head: the head for your list.
412 * @member: the name of the list_struct within the struct.
413 *
414 * Iterate over list of given type, continuing after current point,
415 * safe against removal of list entry.
416 */
417#define list_for_each_entry_safe_continue(pos, n, head, member) \
418 for (pos = list_entry(pos->member.next, typeof(*pos), member), \
419 n = list_entry(pos->member.next, typeof(*pos), member); \
420 &pos->member != (head); \
421 pos = n, n = list_entry(n->member.next, typeof(*n), member))
422
423/**
424 * list_for_each_entry_safe_from
425 * @pos: the type * to use as a loop cursor.
426 * @n: another type * to use as temporary storage
427 * @head: the head for your list.
428 * @member: the name of the list_struct within the struct.
429 *
430 * Iterate over list of given type from current point, safe against
431 * removal of list entry.
432 */
433#define list_for_each_entry_safe_from(pos, n, head, member) \
434 for (n = list_entry(pos->member.next, typeof(*pos), member); \
435 &pos->member != (head); \
436 pos = n, n = list_entry(n->member.next, typeof(*n), member))
437
438/**
439 * list_for_each_entry_safe_reverse
440 * @pos: the type * to use as a loop cursor.
441 * @n: another type * to use as temporary storage
442 * @head: the head for your list.
443 * @member: the name of the list_struct within the struct.
444 *
445 * Iterate backwards over list of given type, safe against removal
446 * of list entry.
447 */
448#define list_for_each_entry_safe_reverse(pos, n, head, member) \
449 for (pos = list_entry((head)->prev, typeof(*pos), member), \
450 n = list_entry(pos->member.prev, typeof(*pos), member); \
451 &pos->member != (head); \
452 pos = n, n = list_entry(n->member.prev, typeof(*n), member))
453
454/*
455 * Double linked lists with a single pointer list head.
456 * Mostly useful for hash tables where the two pointer list head is
457 * too wasteful.
458 * You lose the ability to access the tail in O(1).
459 */
460
461struct hlist_head {
462 struct hlist_node *first;
463};
464
465struct hlist_node {
466 struct hlist_node *next, **pprev;
467};
468
469#define HLIST_HEAD_INIT { .first = NULL }
470#define HLIST_HEAD(name) struct hlist_head name = { .first = NULL }
471#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
472static inline void INIT_HLIST_NODE(struct hlist_node *h)
473{
474 h->next = NULL;
475 h->pprev = NULL;
476}
477
478static inline int hlist_unhashed(const struct hlist_node *h)
479{
480 return !h->pprev;
481}
482
483static inline int hlist_empty(const struct hlist_head *h)
484{
485 return !h->first;
486}
487
488static inline void __hlist_del(struct hlist_node *n)
489{
490 struct hlist_node *next = n->next;
491 struct hlist_node **pprev = n->pprev;
492 *pprev = next;
493 if (next)
494 next->pprev = pprev;
495}
496
497static inline void hlist_del(struct hlist_node *n)
498{
499 __hlist_del(n);
500 n->next = LIST_POISON1;
501 n->pprev = LIST_POISON2;
502}
503
504static inline void hlist_del_init(struct hlist_node *n)
505{
506 if (!hlist_unhashed(n)) {
507 __hlist_del(n);
508 INIT_HLIST_NODE(n);
509 }
510}
511
512static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
513{
514 struct hlist_node *first = h->first;
515 n->next = first;
516 if (first)
517 first->pprev = &n->next;
518 h->first = n;
519 n->pprev = &h->first;
520}
521
522/* next must be != NULL */
523static inline void hlist_add_before(struct hlist_node *n,
524 struct hlist_node *next)
525{
526 n->pprev = next->pprev;
527 n->next = next;
528 next->pprev = &n->next;
529 *(n->pprev) = n;
530}
531
532static inline void hlist_add_after(struct hlist_node *n,
533 struct hlist_node *next)
534{
535 next->next = n->next;
536 n->next = next;
537 next->pprev = &n->next;
538
539 if(next->next)
540 next->next->pprev = &next->next;
541}
542
543#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
544
545#define hlist_for_each(pos, head) \
546 for (pos = (head)->first; pos; \
547 pos = pos->next)
548
549#define hlist_for_each_safe(pos, n, head) \
550 for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \
551 pos = n)
552
553/**
554 * hlist_for_each_entry - iterate over list of given type
555 * @tpos: the type * to use as a loop cursor.
556 * @pos: the &struct hlist_node to use as a loop cursor.
557 * @head: the head for your list.
558 * @member: the name of the hlist_node within the struct.
559 */
560#define hlist_for_each_entry(tpos, pos, head, member) \
561 for (pos = (head)->first; \
562 pos && \
563 ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
564 pos = pos->next)
565
566/**
567 * hlist_for_each_entry_continue - iterate over a hlist continuing after current point
568 * @tpos: the type * to use as a loop cursor.
569 * @pos: the &struct hlist_node to use as a loop cursor.
570 * @member: the name of the hlist_node within the struct.
571 */
572#define hlist_for_each_entry_continue(tpos, pos, member) \
573 for (pos = (pos)->next; \
574 pos && \
575 ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
576 pos = pos->next)
577
578/**
579 * hlist_for_each_entry_from - iterate over a hlist continuing from current point
580 * @tpos: the type * to use as a loop cursor.
581 * @pos: the &struct hlist_node to use as a loop cursor.
582 * @member: the name of the hlist_node within the struct.
583 */
584#define hlist_for_each_entry_from(tpos, pos, member) \
585 for (; pos && \
586 ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
587 pos = pos->next)
588
589/**
590 * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry
591 * @tpos: the type * to use as a loop cursor.
592 * @pos: the &struct hlist_node to use as a loop cursor.
593 * @n: another &struct hlist_node to use as temporary storage
594 * @head: the head for your list.
595 * @member: the name of the hlist_node within the struct.
596 */
597#define hlist_for_each_entry_safe(tpos, pos, n, head, member) \
598 for (pos = (head)->first; \
599 pos && ({ n = pos->next; 1; }) && \
600 ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
601 pos = n)
602
603#endif
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
new file mode 100644
index 00000000000..3a7eb6ec0ee
--- /dev/null
+++ b/tools/perf/util/map.c
@@ -0,0 +1,682 @@
1#include "symbol.h"
2#include <errno.h>
3#include <limits.h>
4#include <stdlib.h>
5#include <string.h>
6#include <stdio.h>
7#include <unistd.h>
8#include "map.h"
9
10const char *map_type__name[MAP__NR_TYPES] = {
11 [MAP__FUNCTION] = "Functions",
12 [MAP__VARIABLE] = "Variables",
13};
14
15static inline int is_anon_memory(const char *filename)
16{
17 return strcmp(filename, "//anon") == 0;
18}
19
20void map__init(struct map *self, enum map_type type,
21 u64 start, u64 end, u64 pgoff, struct dso *dso)
22{
23 self->type = type;
24 self->start = start;
25 self->end = end;
26 self->pgoff = pgoff;
27 self->dso = dso;
28 self->map_ip = map__map_ip;
29 self->unmap_ip = map__unmap_ip;
30 RB_CLEAR_NODE(&self->rb_node);
31 self->groups = NULL;
32 self->referenced = false;
33}
34
35struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,
36 u64 pgoff, u32 pid, char *filename,
37 enum map_type type)
38{
39 struct map *self = malloc(sizeof(*self));
40
41 if (self != NULL) {
42 char newfilename[PATH_MAX];
43 struct dso *dso;
44 int anon;
45
46 anon = is_anon_memory(filename);
47
48 if (anon) {
49 snprintf(newfilename, sizeof(newfilename), "/tmp/perf-%d.map", pid);
50 filename = newfilename;
51 }
52
53 dso = __dsos__findnew(dsos__list, filename);
54 if (dso == NULL)
55 goto out_delete;
56
57 map__init(self, type, start, start + len, pgoff, dso);
58
59 if (anon) {
60set_identity:
61 self->map_ip = self->unmap_ip = identity__map_ip;
62 } else if (strcmp(filename, "[vdso]") == 0) {
63 dso__set_loaded(dso, self->type);
64 goto set_identity;
65 }
66 }
67 return self;
68out_delete:
69 free(self);
70 return NULL;
71}
72
73void map__delete(struct map *self)
74{
75 free(self);
76}
77
78void map__fixup_start(struct map *self)
79{
80 struct rb_root *symbols = &self->dso->symbols[self->type];
81 struct rb_node *nd = rb_first(symbols);
82 if (nd != NULL) {
83 struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
84 self->start = sym->start;
85 }
86}
87
88void map__fixup_end(struct map *self)
89{
90 struct rb_root *symbols = &self->dso->symbols[self->type];
91 struct rb_node *nd = rb_last(symbols);
92 if (nd != NULL) {
93 struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
94 self->end = sym->end;
95 }
96}
97
98#define DSO__DELETED "(deleted)"
99
100int map__load(struct map *self, symbol_filter_t filter)
101{
102 const char *name = self->dso->long_name;
103 int nr;
104
105 if (dso__loaded(self->dso, self->type))
106 return 0;
107
108 nr = dso__load(self->dso, self, filter);
109 if (nr < 0) {
110 if (self->dso->has_build_id) {
111 char sbuild_id[BUILD_ID_SIZE * 2 + 1];
112
113 build_id__sprintf(self->dso->build_id,
114 sizeof(self->dso->build_id),
115 sbuild_id);
116 pr_warning("%s with build id %s not found",
117 name, sbuild_id);
118 } else
119 pr_warning("Failed to open %s", name);
120
121 pr_warning(", continuing without symbols\n");
122 return -1;
123 } else if (nr == 0) {
124 const size_t len = strlen(name);
125 const size_t real_len = len - sizeof(DSO__DELETED);
126
127 if (len > sizeof(DSO__DELETED) &&
128 strcmp(name + real_len + 1, DSO__DELETED) == 0) {
129 pr_warning("%.*s was updated, restart the long "
130 "running apps that use it!\n",
131 (int)real_len, name);
132 } else {
133 pr_warning("no symbols found in %s, maybe install "
134 "a debug package?\n", name);
135 }
136
137 return -1;
138 }
139 /*
140 * Only applies to the kernel, as its symtabs aren't relative like the
141 * module ones.
142 */
143 if (self->dso->kernel)
144 map__reloc_vmlinux(self);
145
146 return 0;
147}
148
149struct symbol *map__find_symbol(struct map *self, u64 addr,
150 symbol_filter_t filter)
151{
152 if (map__load(self, filter) < 0)
153 return NULL;
154
155 return dso__find_symbol(self->dso, self->type, addr);
156}
157
158struct symbol *map__find_symbol_by_name(struct map *self, const char *name,
159 symbol_filter_t filter)
160{
161 if (map__load(self, filter) < 0)
162 return NULL;
163
164 if (!dso__sorted_by_name(self->dso, self->type))
165 dso__sort_by_name(self->dso, self->type);
166
167 return dso__find_symbol_by_name(self->dso, self->type, name);
168}
169
170struct map *map__clone(struct map *self)
171{
172 struct map *map = malloc(sizeof(*self));
173
174 if (!map)
175 return NULL;
176
177 memcpy(map, self, sizeof(*self));
178
179 return map;
180}
181
182int map__overlap(struct map *l, struct map *r)
183{
184 if (l->start > r->start) {
185 struct map *t = l;
186 l = r;
187 r = t;
188 }
189
190 if (l->end > r->start)
191 return 1;
192
193 return 0;
194}
195
196size_t map__fprintf(struct map *self, FILE *fp)
197{
198 return fprintf(fp, " %Lx-%Lx %Lx %s\n",
199 self->start, self->end, self->pgoff, self->dso->name);
200}
201
202/*
203 * objdump wants/reports absolute IPs for ET_EXEC, and RIPs for ET_DYN.
204 * map->dso->adjust_symbols==1 for ET_EXEC-like cases.
205 */
206u64 map__rip_2objdump(struct map *map, u64 rip)
207{
208 u64 addr = map->dso->adjust_symbols ?
209 map->unmap_ip(map, rip) : /* RIP -> IP */
210 rip;
211 return addr;
212}
213
214u64 map__objdump_2ip(struct map *map, u64 addr)
215{
216 u64 ip = map->dso->adjust_symbols ?
217 addr :
218 map->unmap_ip(map, addr); /* RIP -> IP */
219 return ip;
220}
221
222void map_groups__init(struct map_groups *self)
223{
224 int i;
225 for (i = 0; i < MAP__NR_TYPES; ++i) {
226 self->maps[i] = RB_ROOT;
227 INIT_LIST_HEAD(&self->removed_maps[i]);
228 }
229 self->machine = NULL;
230}
231
232static void maps__delete(struct rb_root *self)
233{
234 struct rb_node *next = rb_first(self);
235
236 while (next) {
237 struct map *pos = rb_entry(next, struct map, rb_node);
238
239 next = rb_next(&pos->rb_node);
240 rb_erase(&pos->rb_node, self);
241 map__delete(pos);
242 }
243}
244
245static void maps__delete_removed(struct list_head *self)
246{
247 struct map *pos, *n;
248
249 list_for_each_entry_safe(pos, n, self, node) {
250 list_del(&pos->node);
251 map__delete(pos);
252 }
253}
254
255void map_groups__exit(struct map_groups *self)
256{
257 int i;
258
259 for (i = 0; i < MAP__NR_TYPES; ++i) {
260 maps__delete(&self->maps[i]);
261 maps__delete_removed(&self->removed_maps[i]);
262 }
263}
264
265void map_groups__flush(struct map_groups *self)
266{
267 int type;
268
269 for (type = 0; type < MAP__NR_TYPES; type++) {
270 struct rb_root *root = &self->maps[type];
271 struct rb_node *next = rb_first(root);
272
273 while (next) {
274 struct map *pos = rb_entry(next, struct map, rb_node);
275 next = rb_next(&pos->rb_node);
276 rb_erase(&pos->rb_node, root);
277 /*
278 * We may have references to this map, for
279 * instance in some hist_entry instances, so
280 * just move them to a separate list.
281 */
282 list_add_tail(&pos->node, &self->removed_maps[pos->type]);
283 }
284 }
285}
286
287struct symbol *map_groups__find_symbol(struct map_groups *self,
288 enum map_type type, u64 addr,
289 struct map **mapp,
290 symbol_filter_t filter)
291{
292 struct map *map = map_groups__find(self, type, addr);
293
294 if (map != NULL) {
295 if (mapp != NULL)
296 *mapp = map;
297 return map__find_symbol(map, map->map_ip(map, addr), filter);
298 }
299
300 return NULL;
301}
302
303struct symbol *map_groups__find_symbol_by_name(struct map_groups *self,
304 enum map_type type,
305 const char *name,
306 struct map **mapp,
307 symbol_filter_t filter)
308{
309 struct rb_node *nd;
310
311 for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) {
312 struct map *pos = rb_entry(nd, struct map, rb_node);
313 struct symbol *sym = map__find_symbol_by_name(pos, name, filter);
314
315 if (sym == NULL)
316 continue;
317 if (mapp != NULL)
318 *mapp = pos;
319 return sym;
320 }
321
322 return NULL;
323}
324
325size_t __map_groups__fprintf_maps(struct map_groups *self,
326 enum map_type type, int verbose, FILE *fp)
327{
328 size_t printed = fprintf(fp, "%s:\n", map_type__name[type]);
329 struct rb_node *nd;
330
331 for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) {
332 struct map *pos = rb_entry(nd, struct map, rb_node);
333 printed += fprintf(fp, "Map:");
334 printed += map__fprintf(pos, fp);
335 if (verbose > 2) {
336 printed += dso__fprintf(pos->dso, type, fp);
337 printed += fprintf(fp, "--\n");
338 }
339 }
340
341 return printed;
342}
343
344size_t map_groups__fprintf_maps(struct map_groups *self, int verbose, FILE *fp)
345{
346 size_t printed = 0, i;
347 for (i = 0; i < MAP__NR_TYPES; ++i)
348 printed += __map_groups__fprintf_maps(self, i, verbose, fp);
349 return printed;
350}
351
352static size_t __map_groups__fprintf_removed_maps(struct map_groups *self,
353 enum map_type type,
354 int verbose, FILE *fp)
355{
356 struct map *pos;
357 size_t printed = 0;
358
359 list_for_each_entry(pos, &self->removed_maps[type], node) {
360 printed += fprintf(fp, "Map:");
361 printed += map__fprintf(pos, fp);
362 if (verbose > 1) {
363 printed += dso__fprintf(pos->dso, type, fp);
364 printed += fprintf(fp, "--\n");
365 }
366 }
367 return printed;
368}
369
370static size_t map_groups__fprintf_removed_maps(struct map_groups *self,
371 int verbose, FILE *fp)
372{
373 size_t printed = 0, i;
374 for (i = 0; i < MAP__NR_TYPES; ++i)
375 printed += __map_groups__fprintf_removed_maps(self, i, verbose, fp);
376 return printed;
377}
378
379size_t map_groups__fprintf(struct map_groups *self, int verbose, FILE *fp)
380{
381 size_t printed = map_groups__fprintf_maps(self, verbose, fp);
382 printed += fprintf(fp, "Removed maps:\n");
383 return printed + map_groups__fprintf_removed_maps(self, verbose, fp);
384}
385
386int map_groups__fixup_overlappings(struct map_groups *self, struct map *map,
387 int verbose, FILE *fp)
388{
389 struct rb_root *root = &self->maps[map->type];
390 struct rb_node *next = rb_first(root);
391 int err = 0;
392
393 while (next) {
394 struct map *pos = rb_entry(next, struct map, rb_node);
395 next = rb_next(&pos->rb_node);
396
397 if (!map__overlap(pos, map))
398 continue;
399
400 if (verbose >= 2) {
401 fputs("overlapping maps:\n", fp);
402 map__fprintf(map, fp);
403 map__fprintf(pos, fp);
404 }
405
406 rb_erase(&pos->rb_node, root);
407 /*
408 * Now check if we need to create new maps for areas not
409 * overlapped by the new map:
410 */
411 if (map->start > pos->start) {
412 struct map *before = map__clone(pos);
413
414 if (before == NULL) {
415 err = -ENOMEM;
416 goto move_map;
417 }
418
419 before->end = map->start - 1;
420 map_groups__insert(self, before);
421 if (verbose >= 2)
422 map__fprintf(before, fp);
423 }
424
425 if (map->end < pos->end) {
426 struct map *after = map__clone(pos);
427
428 if (after == NULL) {
429 err = -ENOMEM;
430 goto move_map;
431 }
432
433 after->start = map->end + 1;
434 map_groups__insert(self, after);
435 if (verbose >= 2)
436 map__fprintf(after, fp);
437 }
438move_map:
439 /*
440 * If we have references, just move them to a separate list.
441 */
442 if (pos->referenced)
443 list_add_tail(&pos->node, &self->removed_maps[map->type]);
444 else
445 map__delete(pos);
446
447 if (err)
448 return err;
449 }
450
451 return 0;
452}
453
454/*
455 * XXX This should not really _copy_ te maps, but refcount them.
456 */
457int map_groups__clone(struct map_groups *self,
458 struct map_groups *parent, enum map_type type)
459{
460 struct rb_node *nd;
461 for (nd = rb_first(&parent->maps[type]); nd; nd = rb_next(nd)) {
462 struct map *map = rb_entry(nd, struct map, rb_node);
463 struct map *new = map__clone(map);
464 if (new == NULL)
465 return -ENOMEM;
466 map_groups__insert(self, new);
467 }
468 return 0;
469}
470
471static u64 map__reloc_map_ip(struct map *map, u64 ip)
472{
473 return ip + (s64)map->pgoff;
474}
475
476static u64 map__reloc_unmap_ip(struct map *map, u64 ip)
477{
478 return ip - (s64)map->pgoff;
479}
480
481void map__reloc_vmlinux(struct map *self)
482{
483 struct kmap *kmap = map__kmap(self);
484 s64 reloc;
485
486 if (!kmap->ref_reloc_sym || !kmap->ref_reloc_sym->unrelocated_addr)
487 return;
488
489 reloc = (kmap->ref_reloc_sym->unrelocated_addr -
490 kmap->ref_reloc_sym->addr);
491
492 if (!reloc)
493 return;
494
495 self->map_ip = map__reloc_map_ip;
496 self->unmap_ip = map__reloc_unmap_ip;
497 self->pgoff = reloc;
498}
499
500void maps__insert(struct rb_root *maps, struct map *map)
501{
502 struct rb_node **p = &maps->rb_node;
503 struct rb_node *parent = NULL;
504 const u64 ip = map->start;
505 struct map *m;
506
507 while (*p != NULL) {
508 parent = *p;
509 m = rb_entry(parent, struct map, rb_node);
510 if (ip < m->start)
511 p = &(*p)->rb_left;
512 else
513 p = &(*p)->rb_right;
514 }
515
516 rb_link_node(&map->rb_node, parent, p);
517 rb_insert_color(&map->rb_node, maps);
518}
519
520void maps__remove(struct rb_root *self, struct map *map)
521{
522 rb_erase(&map->rb_node, self);
523}
524
525struct map *maps__find(struct rb_root *maps, u64 ip)
526{
527 struct rb_node **p = &maps->rb_node;
528 struct rb_node *parent = NULL;
529 struct map *m;
530
531 while (*p != NULL) {
532 parent = *p;
533 m = rb_entry(parent, struct map, rb_node);
534 if (ip < m->start)
535 p = &(*p)->rb_left;
536 else if (ip > m->end)
537 p = &(*p)->rb_right;
538 else
539 return m;
540 }
541
542 return NULL;
543}
544
545int machine__init(struct machine *self, const char *root_dir, pid_t pid)
546{
547 map_groups__init(&self->kmaps);
548 RB_CLEAR_NODE(&self->rb_node);
549 INIT_LIST_HEAD(&self->user_dsos);
550 INIT_LIST_HEAD(&self->kernel_dsos);
551
552 self->kmaps.machine = self;
553 self->pid = pid;
554 self->root_dir = strdup(root_dir);
555 return self->root_dir == NULL ? -ENOMEM : 0;
556}
557
558static void dsos__delete(struct list_head *self)
559{
560 struct dso *pos, *n;
561
562 list_for_each_entry_safe(pos, n, self, node) {
563 list_del(&pos->node);
564 dso__delete(pos);
565 }
566}
567
568void machine__exit(struct machine *self)
569{
570 map_groups__exit(&self->kmaps);
571 dsos__delete(&self->user_dsos);
572 dsos__delete(&self->kernel_dsos);
573 free(self->root_dir);
574 self->root_dir = NULL;
575}
576
577void machine__delete(struct machine *self)
578{
579 machine__exit(self);
580 free(self);
581}
582
583struct machine *machines__add(struct rb_root *self, pid_t pid,
584 const char *root_dir)
585{
586 struct rb_node **p = &self->rb_node;
587 struct rb_node *parent = NULL;
588 struct machine *pos, *machine = malloc(sizeof(*machine));
589
590 if (!machine)
591 return NULL;
592
593 if (machine__init(machine, root_dir, pid) != 0) {
594 free(machine);
595 return NULL;
596 }
597
598 while (*p != NULL) {
599 parent = *p;
600 pos = rb_entry(parent, struct machine, rb_node);
601 if (pid < pos->pid)
602 p = &(*p)->rb_left;
603 else
604 p = &(*p)->rb_right;
605 }
606
607 rb_link_node(&machine->rb_node, parent, p);
608 rb_insert_color(&machine->rb_node, self);
609
610 return machine;
611}
612
613struct machine *machines__find(struct rb_root *self, pid_t pid)
614{
615 struct rb_node **p = &self->rb_node;
616 struct rb_node *parent = NULL;
617 struct machine *machine;
618 struct machine *default_machine = NULL;
619
620 while (*p != NULL) {
621 parent = *p;
622 machine = rb_entry(parent, struct machine, rb_node);
623 if (pid < machine->pid)
624 p = &(*p)->rb_left;
625 else if (pid > machine->pid)
626 p = &(*p)->rb_right;
627 else
628 return machine;
629 if (!machine->pid)
630 default_machine = machine;
631 }
632
633 return default_machine;
634}
635
636struct machine *machines__findnew(struct rb_root *self, pid_t pid)
637{
638 char path[PATH_MAX];
639 const char *root_dir;
640 struct machine *machine = machines__find(self, pid);
641
642 if (!machine || machine->pid != pid) {
643 if (pid == HOST_KERNEL_ID || pid == DEFAULT_GUEST_KERNEL_ID)
644 root_dir = "";
645 else {
646 if (!symbol_conf.guestmount)
647 goto out;
648 sprintf(path, "%s/%d", symbol_conf.guestmount, pid);
649 if (access(path, R_OK)) {
650 pr_err("Can't access file %s\n", path);
651 goto out;
652 }
653 root_dir = path;
654 }
655 machine = machines__add(self, pid, root_dir);
656 }
657
658out:
659 return machine;
660}
661
662void machines__process(struct rb_root *self, machine__process_t process, void *data)
663{
664 struct rb_node *nd;
665
666 for (nd = rb_first(self); nd; nd = rb_next(nd)) {
667 struct machine *pos = rb_entry(nd, struct machine, rb_node);
668 process(pos, data);
669 }
670}
671
672char *machine__mmap_name(struct machine *self, char *bf, size_t size)
673{
674 if (machine__is_host(self))
675 snprintf(bf, size, "[%s]", "kernel.kallsyms");
676 else if (machine__is_default_guest(self))
677 snprintf(bf, size, "[%s]", "guest.kernel.kallsyms");
678 else
679 snprintf(bf, size, "[%s.%d]", "guest.kernel.kallsyms", self->pid);
680
681 return bf;
682}
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h
new file mode 100644
index 00000000000..b397c038372
--- /dev/null
+++ b/tools/perf/util/map.h
@@ -0,0 +1,237 @@
1#ifndef __PERF_MAP_H
2#define __PERF_MAP_H
3
4#include <linux/compiler.h>
5#include <linux/list.h>
6#include <linux/rbtree.h>
7#include <stdio.h>
8#include <stdbool.h>
9#include "types.h"
10
11enum map_type {
12 MAP__FUNCTION = 0,
13 MAP__VARIABLE,
14};
15
16#define MAP__NR_TYPES (MAP__VARIABLE + 1)
17
18extern const char *map_type__name[MAP__NR_TYPES];
19
20struct dso;
21struct ref_reloc_sym;
22struct map_groups;
23struct machine;
24
25struct map {
26 union {
27 struct rb_node rb_node;
28 struct list_head node;
29 };
30 u64 start;
31 u64 end;
32 u8 /* enum map_type */ type;
33 bool referenced;
34 u32 priv;
35 u64 pgoff;
36
37 /* ip -> dso rip */
38 u64 (*map_ip)(struct map *, u64);
39 /* dso rip -> ip */
40 u64 (*unmap_ip)(struct map *, u64);
41
42 struct dso *dso;
43 struct map_groups *groups;
44};
45
46struct kmap {
47 struct ref_reloc_sym *ref_reloc_sym;
48 struct map_groups *kmaps;
49};
50
51struct map_groups {
52 struct rb_root maps[MAP__NR_TYPES];
53 struct list_head removed_maps[MAP__NR_TYPES];
54 struct machine *machine;
55};
56
57/* Native host kernel uses -1 as pid index in machine */
58#define HOST_KERNEL_ID (-1)
59#define DEFAULT_GUEST_KERNEL_ID (0)
60
61struct machine {
62 struct rb_node rb_node;
63 pid_t pid;
64 char *root_dir;
65 struct list_head user_dsos;
66 struct list_head kernel_dsos;
67 struct map_groups kmaps;
68 struct map *vmlinux_maps[MAP__NR_TYPES];
69};
70
71static inline
72struct map *machine__kernel_map(struct machine *self, enum map_type type)
73{
74 return self->vmlinux_maps[type];
75}
76
77static inline struct kmap *map__kmap(struct map *self)
78{
79 return (struct kmap *)(self + 1);
80}
81
82static inline u64 map__map_ip(struct map *map, u64 ip)
83{
84 return ip - map->start + map->pgoff;
85}
86
87static inline u64 map__unmap_ip(struct map *map, u64 ip)
88{
89 return ip + map->start - map->pgoff;
90}
91
92static inline u64 identity__map_ip(struct map *map __used, u64 ip)
93{
94 return ip;
95}
96
97
98/* rip/ip <-> addr suitable for passing to `objdump --start-address=` */
99u64 map__rip_2objdump(struct map *map, u64 rip);
100u64 map__objdump_2ip(struct map *map, u64 addr);
101
102struct symbol;
103
104typedef int (*symbol_filter_t)(struct map *map, struct symbol *sym);
105
106void map__init(struct map *self, enum map_type type,
107 u64 start, u64 end, u64 pgoff, struct dso *dso);
108struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,
109 u64 pgoff, u32 pid, char *filename,
110 enum map_type type);
111void map__delete(struct map *self);
112struct map *map__clone(struct map *self);
113int map__overlap(struct map *l, struct map *r);
114size_t map__fprintf(struct map *self, FILE *fp);
115
116int map__load(struct map *self, symbol_filter_t filter);
117struct symbol *map__find_symbol(struct map *self,
118 u64 addr, symbol_filter_t filter);
119struct symbol *map__find_symbol_by_name(struct map *self, const char *name,
120 symbol_filter_t filter);
121void map__fixup_start(struct map *self);
122void map__fixup_end(struct map *self);
123
124void map__reloc_vmlinux(struct map *self);
125
126size_t __map_groups__fprintf_maps(struct map_groups *self,
127 enum map_type type, int verbose, FILE *fp);
128void maps__insert(struct rb_root *maps, struct map *map);
129void maps__remove(struct rb_root *self, struct map *map);
130struct map *maps__find(struct rb_root *maps, u64 addr);
131void map_groups__init(struct map_groups *self);
132void map_groups__exit(struct map_groups *self);
133int map_groups__clone(struct map_groups *self,
134 struct map_groups *parent, enum map_type type);
135size_t map_groups__fprintf(struct map_groups *self, int verbose, FILE *fp);
136size_t map_groups__fprintf_maps(struct map_groups *self, int verbose, FILE *fp);
137
138typedef void (*machine__process_t)(struct machine *self, void *data);
139
140void machines__process(struct rb_root *self, machine__process_t process, void *data);
141struct machine *machines__add(struct rb_root *self, pid_t pid,
142 const char *root_dir);
143struct machine *machines__find_host(struct rb_root *self);
144struct machine *machines__find(struct rb_root *self, pid_t pid);
145struct machine *machines__findnew(struct rb_root *self, pid_t pid);
146char *machine__mmap_name(struct machine *self, char *bf, size_t size);
147int machine__init(struct machine *self, const char *root_dir, pid_t pid);
148void machine__exit(struct machine *self);
149void machine__delete(struct machine *self);
150
151/*
152 * Default guest kernel is defined by parameter --guestkallsyms
153 * and --guestmodules
154 */
155static inline bool machine__is_default_guest(struct machine *self)
156{
157 return self ? self->pid == DEFAULT_GUEST_KERNEL_ID : false;
158}
159
160static inline bool machine__is_host(struct machine *self)
161{
162 return self ? self->pid == HOST_KERNEL_ID : false;
163}
164
165static inline void map_groups__insert(struct map_groups *self, struct map *map)
166{
167 maps__insert(&self->maps[map->type], map);
168 map->groups = self;
169}
170
171static inline void map_groups__remove(struct map_groups *self, struct map *map)
172{
173 maps__remove(&self->maps[map->type], map);
174}
175
176static inline struct map *map_groups__find(struct map_groups *self,
177 enum map_type type, u64 addr)
178{
179 return maps__find(&self->maps[type], addr);
180}
181
182struct symbol *map_groups__find_symbol(struct map_groups *self,
183 enum map_type type, u64 addr,
184 struct map **mapp,
185 symbol_filter_t filter);
186
187struct symbol *map_groups__find_symbol_by_name(struct map_groups *self,
188 enum map_type type,
189 const char *name,
190 struct map **mapp,
191 symbol_filter_t filter);
192
193static inline
194struct symbol *machine__find_kernel_symbol(struct machine *self,
195 enum map_type type, u64 addr,
196 struct map **mapp,
197 symbol_filter_t filter)
198{
199 return map_groups__find_symbol(&self->kmaps, type, addr, mapp, filter);
200}
201
202static inline
203struct symbol *machine__find_kernel_function(struct machine *self, u64 addr,
204 struct map **mapp,
205 symbol_filter_t filter)
206{
207 return machine__find_kernel_symbol(self, MAP__FUNCTION, addr, mapp, filter);
208}
209
210static inline
211struct symbol *map_groups__find_function_by_name(struct map_groups *self,
212 const char *name, struct map **mapp,
213 symbol_filter_t filter)
214{
215 return map_groups__find_symbol_by_name(self, MAP__FUNCTION, name, mapp, filter);
216}
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
228int map_groups__fixup_overlappings(struct map_groups *self, struct map *map,
229 int verbose, FILE *fp);
230
231struct map *map_groups__find_by_name(struct map_groups *self,
232 enum map_type type, const char *name);
233struct map *machine__new_module(struct machine *self, u64 start, const char *filename);
234
235void map_groups__flush(struct map_groups *self);
236
237#endif /* __PERF_MAP_H */
diff --git a/tools/perf/util/pager.c b/tools/perf/util/pager.c
index a28bccae545..1915de20dca 100644
--- a/tools/perf/util/pager.c
+++ b/tools/perf/util/pager.c
@@ -9,7 +9,6 @@
9 9
10static int spawned_pager; 10static int spawned_pager;
11 11
12#ifndef __MINGW32__
13static void pager_preexec(void) 12static void pager_preexec(void)
14{ 13{
15 /* 14 /*
@@ -24,7 +23,6 @@ static void pager_preexec(void)
24 23
25 setenv("LESS", "FRSX", 0); 24 setenv("LESS", "FRSX", 0);
26} 25}
27#endif
28 26
29static const char *pager_argv[] = { "sh", "-c", NULL, NULL }; 27static const char *pager_argv[] = { "sh", "-c", NULL, NULL };
30static struct child_process pager_process; 28static struct child_process pager_process;
@@ -70,9 +68,8 @@ void setup_pager(void)
70 pager_argv[2] = pager; 68 pager_argv[2] = pager;
71 pager_process.argv = pager_argv; 69 pager_process.argv = pager_argv;
72 pager_process.in = -1; 70 pager_process.in = -1;
73#ifndef __MINGW32__
74 pager_process.preexec_cb = pager_preexec; 71 pager_process.preexec_cb = pager_preexec;
75#endif 72
76 if (start_command(&pager_process)) 73 if (start_command(&pager_process))
77 return; 74 return;
78 75
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 35d04da38d6..5cb6f4bde90 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -1,58 +1,67 @@
1 1#include "../../../include/linux/hw_breakpoint.h"
2#include "../perf.h"
3#include "util.h" 2#include "util.h"
3#include "../perf.h"
4#include "evsel.h"
4#include "parse-options.h" 5#include "parse-options.h"
5#include "parse-events.h" 6#include "parse-events.h"
6#include "exec_cmd.h" 7#include "exec_cmd.h"
7#include "string.h" 8#include "string.h"
9#include "symbol.h"
10#include "cache.h"
11#include "header.h"
12#include "debugfs.h"
8 13
9extern char *strcasestr(const char *haystack, const char *needle); 14int nr_counters;
10
11int nr_counters;
12 15
13struct perf_counter_attr attrs[MAX_COUNTERS]; 16LIST_HEAD(evsel_list);
14 17
15struct event_symbol { 18struct event_symbol {
16 u8 type; 19 u8 type;
17 u64 config; 20 u64 config;
18 char *symbol; 21 const char *symbol;
22 const char *alias;
19}; 23};
20 24
21#define C(x, y) .type = PERF_TYPE_##x, .config = PERF_COUNT_##y 25enum event_result {
22#define CR(x, y) .type = PERF_TYPE_##x, .config = y 26 EVT_FAILED,
27 EVT_HANDLED,
28 EVT_HANDLED_ALL
29};
30
31char debugfs_path[MAXPATHLEN];
32
33#define CHW(x) .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_##x
34#define CSW(x) .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_##x
23 35
24static struct event_symbol event_symbols[] = { 36static struct event_symbol event_symbols[] = {
25 { C(HARDWARE, HW_CPU_CYCLES), "cpu-cycles", }, 37 { CHW(CPU_CYCLES), "cpu-cycles", "cycles" },
26 { C(HARDWARE, HW_CPU_CYCLES), "cycles", }, 38 { CHW(INSTRUCTIONS), "instructions", "" },
27 { C(HARDWARE, HW_INSTRUCTIONS), "instructions", }, 39 { CHW(CACHE_REFERENCES), "cache-references", "" },
28 { C(HARDWARE, HW_CACHE_REFERENCES), "cache-references", }, 40 { CHW(CACHE_MISSES), "cache-misses", "" },
29 { C(HARDWARE, HW_CACHE_MISSES), "cache-misses", }, 41 { CHW(BRANCH_INSTRUCTIONS), "branch-instructions", "branches" },
30 { C(HARDWARE, HW_BRANCH_INSTRUCTIONS),"branch-instructions", }, 42 { CHW(BRANCH_MISSES), "branch-misses", "" },
31 { C(HARDWARE, HW_BRANCH_INSTRUCTIONS),"branches", }, 43 { CHW(BUS_CYCLES), "bus-cycles", "" },
32 { C(HARDWARE, HW_BRANCH_MISSES), "branch-misses", }, 44
33 { C(HARDWARE, HW_BUS_CYCLES), "bus-cycles", }, 45 { CSW(CPU_CLOCK), "cpu-clock", "" },
34 46 { CSW(TASK_CLOCK), "task-clock", "" },
35 { C(SOFTWARE, SW_CPU_CLOCK), "cpu-clock", }, 47 { CSW(PAGE_FAULTS), "page-faults", "faults" },
36 { C(SOFTWARE, SW_TASK_CLOCK), "task-clock", }, 48 { CSW(PAGE_FAULTS_MIN), "minor-faults", "" },
37 { C(SOFTWARE, SW_PAGE_FAULTS), "page-faults", }, 49 { CSW(PAGE_FAULTS_MAJ), "major-faults", "" },
38 { C(SOFTWARE, SW_PAGE_FAULTS), "faults", }, 50 { CSW(CONTEXT_SWITCHES), "context-switches", "cs" },
39 { C(SOFTWARE, SW_PAGE_FAULTS_MIN), "minor-faults", }, 51 { CSW(CPU_MIGRATIONS), "cpu-migrations", "migrations" },
40 { C(SOFTWARE, SW_PAGE_FAULTS_MAJ), "major-faults", }, 52 { CSW(ALIGNMENT_FAULTS), "alignment-faults", "" },
41 { C(SOFTWARE, SW_CONTEXT_SWITCHES), "context-switches", }, 53 { CSW(EMULATION_FAULTS), "emulation-faults", "" },
42 { C(SOFTWARE, SW_CONTEXT_SWITCHES), "cs", },
43 { C(SOFTWARE, SW_CPU_MIGRATIONS), "cpu-migrations", },
44 { C(SOFTWARE, SW_CPU_MIGRATIONS), "migrations", },
45}; 54};
46 55
47#define __PERF_COUNTER_FIELD(config, name) \ 56#define __PERF_EVENT_FIELD(config, name) \
48 ((config & PERF_COUNTER_##name##_MASK) >> PERF_COUNTER_##name##_SHIFT) 57 ((config & PERF_EVENT_##name##_MASK) >> PERF_EVENT_##name##_SHIFT)
49 58
50#define PERF_COUNTER_RAW(config) __PERF_COUNTER_FIELD(config, RAW) 59#define PERF_EVENT_RAW(config) __PERF_EVENT_FIELD(config, RAW)
51#define PERF_COUNTER_CONFIG(config) __PERF_COUNTER_FIELD(config, CONFIG) 60#define PERF_EVENT_CONFIG(config) __PERF_EVENT_FIELD(config, CONFIG)
52#define PERF_COUNTER_TYPE(config) __PERF_COUNTER_FIELD(config, TYPE) 61#define PERF_EVENT_TYPE(config) __PERF_EVENT_FIELD(config, TYPE)
53#define PERF_COUNTER_ID(config) __PERF_COUNTER_FIELD(config, EVENT) 62#define PERF_EVENT_ID(config) __PERF_EVENT_FIELD(config, EVENT)
54 63
55static char *hw_event_names[] = { 64static const char *hw_event_names[] = {
56 "cycles", 65 "cycles",
57 "instructions", 66 "instructions",
58 "cache-references", 67 "cache-references",
@@ -62,7 +71,7 @@ static char *hw_event_names[] = {
62 "bus-cycles", 71 "bus-cycles",
63}; 72};
64 73
65static char *sw_event_names[] = { 74static const char *sw_event_names[] = {
66 "cpu-clock-msecs", 75 "cpu-clock-msecs",
67 "task-clock-msecs", 76 "task-clock-msecs",
68 "page-faults", 77 "page-faults",
@@ -70,37 +79,206 @@ static char *sw_event_names[] = {
70 "CPU-migrations", 79 "CPU-migrations",
71 "minor-faults", 80 "minor-faults",
72 "major-faults", 81 "major-faults",
82 "alignment-faults",
83 "emulation-faults",
73}; 84};
74 85
75#define MAX_ALIASES 8 86#define MAX_ALIASES 8
76 87
77static char *hw_cache [][MAX_ALIASES] = { 88static const char *hw_cache[][MAX_ALIASES] = {
78 { "L1-data" , "l1-d", "l1d" }, 89 { "L1-dcache", "l1-d", "l1d", "L1-data", },
79 { "L1-instruction" , "l1-i", "l1i" }, 90 { "L1-icache", "l1-i", "l1i", "L1-instruction", },
80 { "L2" , "l2" }, 91 { "LLC", "L2" },
81 { "Data-TLB" , "dtlb", "d-tlb" }, 92 { "dTLB", "d-tlb", "Data-TLB", },
82 { "Instruction-TLB" , "itlb", "i-tlb" }, 93 { "iTLB", "i-tlb", "Instruction-TLB", },
83 { "Branch" , "bpu" , "btb", "bpc" }, 94 { "branch", "branches", "bpu", "btb", "bpc", },
84}; 95};
85 96
86static char *hw_cache_op [][MAX_ALIASES] = { 97static const char *hw_cache_op[][MAX_ALIASES] = {
87 { "Load" , "read" }, 98 { "load", "loads", "read", },
88 { "Store" , "write" }, 99 { "store", "stores", "write", },
89 { "Prefetch" , "speculative-read", "speculative-load" }, 100 { "prefetch", "prefetches", "speculative-read", "speculative-load", },
90}; 101};
91 102
92static char *hw_cache_result [][MAX_ALIASES] = { 103static const char *hw_cache_result[][MAX_ALIASES] = {
93 { "Reference" , "ops", "access" }, 104 { "refs", "Reference", "ops", "access", },
94 { "Miss" }, 105 { "misses", "miss", },
95}; 106};
96 107
97char *event_name(int counter) 108#define C(x) PERF_COUNT_HW_CACHE_##x
109#define CACHE_READ (1 << C(OP_READ))
110#define CACHE_WRITE (1 << C(OP_WRITE))
111#define CACHE_PREFETCH (1 << C(OP_PREFETCH))
112#define COP(x) (1 << x)
113
114/*
115 * cache operartion stat
116 * L1I : Read and prefetch only
117 * ITLB and BPU : Read-only
118 */
119static unsigned long hw_cache_stat[C(MAX)] = {
120 [C(L1D)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH),
121 [C(L1I)] = (CACHE_READ | CACHE_PREFETCH),
122 [C(LL)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH),
123 [C(DTLB)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH),
124 [C(ITLB)] = (CACHE_READ),
125 [C(BPU)] = (CACHE_READ),
126};
127
128#define for_each_subsystem(sys_dir, sys_dirent, sys_next) \
129 while (!readdir_r(sys_dir, &sys_dirent, &sys_next) && sys_next) \
130 if (sys_dirent.d_type == DT_DIR && \
131 (strcmp(sys_dirent.d_name, ".")) && \
132 (strcmp(sys_dirent.d_name, "..")))
133
134static int tp_event_has_id(struct dirent *sys_dir, struct dirent *evt_dir)
135{
136 char evt_path[MAXPATHLEN];
137 int fd;
138
139 snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", debugfs_path,
140 sys_dir->d_name, evt_dir->d_name);
141 fd = open(evt_path, O_RDONLY);
142 if (fd < 0)
143 return -EINVAL;
144 close(fd);
145
146 return 0;
147}
148
149#define for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) \
150 while (!readdir_r(evt_dir, &evt_dirent, &evt_next) && evt_next) \
151 if (evt_dirent.d_type == DT_DIR && \
152 (strcmp(evt_dirent.d_name, ".")) && \
153 (strcmp(evt_dirent.d_name, "..")) && \
154 (!tp_event_has_id(&sys_dirent, &evt_dirent)))
155
156#define MAX_EVENT_LENGTH 512
157
158
159struct tracepoint_path *tracepoint_id_to_path(u64 config)
160{
161 struct tracepoint_path *path = NULL;
162 DIR *sys_dir, *evt_dir;
163 struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent;
164 char id_buf[4];
165 int fd;
166 u64 id;
167 char evt_path[MAXPATHLEN];
168 char dir_path[MAXPATHLEN];
169
170 if (debugfs_valid_mountpoint(debugfs_path))
171 return NULL;
172
173 sys_dir = opendir(debugfs_path);
174 if (!sys_dir)
175 return NULL;
176
177 for_each_subsystem(sys_dir, sys_dirent, sys_next) {
178
179 snprintf(dir_path, MAXPATHLEN, "%s/%s", debugfs_path,
180 sys_dirent.d_name);
181 evt_dir = opendir(dir_path);
182 if (!evt_dir)
183 continue;
184
185 for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) {
186
187 snprintf(evt_path, MAXPATHLEN, "%s/%s/id", dir_path,
188 evt_dirent.d_name);
189 fd = open(evt_path, O_RDONLY);
190 if (fd < 0)
191 continue;
192 if (read(fd, id_buf, sizeof(id_buf)) < 0) {
193 close(fd);
194 continue;
195 }
196 close(fd);
197 id = atoll(id_buf);
198 if (id == config) {
199 closedir(evt_dir);
200 closedir(sys_dir);
201 path = zalloc(sizeof(*path));
202 path->system = malloc(MAX_EVENT_LENGTH);
203 if (!path->system) {
204 free(path);
205 return NULL;
206 }
207 path->name = malloc(MAX_EVENT_LENGTH);
208 if (!path->name) {
209 free(path->system);
210 free(path);
211 return NULL;
212 }
213 strncpy(path->system, sys_dirent.d_name,
214 MAX_EVENT_LENGTH);
215 strncpy(path->name, evt_dirent.d_name,
216 MAX_EVENT_LENGTH);
217 return path;
218 }
219 }
220 closedir(evt_dir);
221 }
222
223 closedir(sys_dir);
224 return NULL;
225}
226
227#define TP_PATH_LEN (MAX_EVENT_LENGTH * 2 + 1)
228static const char *tracepoint_id_to_name(u64 config)
229{
230 static char buf[TP_PATH_LEN];
231 struct tracepoint_path *path;
232
233 path = tracepoint_id_to_path(config);
234 if (path) {
235 snprintf(buf, TP_PATH_LEN, "%s:%s", path->system, path->name);
236 free(path->name);
237 free(path->system);
238 free(path);
239 } else
240 snprintf(buf, TP_PATH_LEN, "%s:%s", "unknown", "unknown");
241
242 return buf;
243}
244
245static int is_cache_op_valid(u8 cache_type, u8 cache_op)
246{
247 if (hw_cache_stat[cache_type] & COP(cache_op))
248 return 1; /* valid */
249 else
250 return 0; /* invalid */
251}
252
253static char *event_cache_name(u8 cache_type, u8 cache_op, u8 cache_result)
254{
255 static char name[50];
256
257 if (cache_result) {
258 sprintf(name, "%s-%s-%s", hw_cache[cache_type][0],
259 hw_cache_op[cache_op][0],
260 hw_cache_result[cache_result][0]);
261 } else {
262 sprintf(name, "%s-%s", hw_cache[cache_type][0],
263 hw_cache_op[cache_op][1]);
264 }
265
266 return name;
267}
268
269const char *event_name(struct perf_evsel *evsel)
270{
271 u64 config = evsel->attr.config;
272 int type = evsel->attr.type;
273
274 return __event_name(type, config);
275}
276
277const char *__event_name(int type, u64 config)
98{ 278{
99 u64 config = attrs[counter].config;
100 int type = attrs[counter].type;
101 static char buf[32]; 279 static char buf[32];
102 280
103 if (attrs[counter].type == PERF_TYPE_RAW) { 281 if (type == PERF_TYPE_RAW) {
104 sprintf(buf, "raw 0x%llx", config); 282 sprintf(buf, "raw 0x%llx", config);
105 return buf; 283 return buf;
106 } 284 }
@@ -113,7 +291,6 @@ char *event_name(int counter)
113 291
114 case PERF_TYPE_HW_CACHE: { 292 case PERF_TYPE_HW_CACHE: {
115 u8 cache_type, cache_op, cache_result; 293 u8 cache_type, cache_op, cache_result;
116 static char name[100];
117 294
118 cache_type = (config >> 0) & 0xff; 295 cache_type = (config >> 0) & 0xff;
119 if (cache_type > PERF_COUNT_HW_CACHE_MAX) 296 if (cache_type > PERF_COUNT_HW_CACHE_MAX)
@@ -127,12 +304,10 @@ char *event_name(int counter)
127 if (cache_result > PERF_COUNT_HW_CACHE_RESULT_MAX) 304 if (cache_result > PERF_COUNT_HW_CACHE_RESULT_MAX)
128 return "unknown-ext-hardware-cache-result"; 305 return "unknown-ext-hardware-cache-result";
129 306
130 sprintf(name, "%s-Cache-%s-%ses", 307 if (!is_cache_op_valid(cache_type, cache_op))
131 hw_cache[cache_type][0], 308 return "invalid-cache";
132 hw_cache_op[cache_op][0],
133 hw_cache_result[cache_result][0]);
134 309
135 return name; 310 return event_cache_name(cache_type, cache_op, cache_result);
136 } 311 }
137 312
138 case PERF_TYPE_SOFTWARE: 313 case PERF_TYPE_SOFTWARE:
@@ -140,6 +315,9 @@ char *event_name(int counter)
140 return sw_event_names[config]; 315 return sw_event_names[config];
141 return "unknown-software"; 316 return "unknown-software";
142 317
318 case PERF_TYPE_TRACEPOINT:
319 return tracepoint_id_to_name(config);
320
143 default: 321 default:
144 break; 322 break;
145 } 323 }
@@ -147,43 +325,74 @@ char *event_name(int counter)
147 return "unknown"; 325 return "unknown";
148} 326}
149 327
150static int parse_aliases(const char *str, char *names[][MAX_ALIASES], int size) 328static int parse_aliases(const char **str, const char *names[][MAX_ALIASES], int size)
151{ 329{
152 int i, j; 330 int i, j;
331 int n, longest = -1;
153 332
154 for (i = 0; i < size; i++) { 333 for (i = 0; i < size; i++) {
155 for (j = 0; j < MAX_ALIASES; j++) { 334 for (j = 0; j < MAX_ALIASES && names[i][j]; j++) {
156 if (!names[i][j]) 335 n = strlen(names[i][j]);
157 break; 336 if (n > longest && !strncasecmp(*str, names[i][j], n))
158 if (strcasestr(str, names[i][j])) 337 longest = n;
159 return i; 338 }
339 if (longest > 0) {
340 *str += longest;
341 return i;
160 } 342 }
161 } 343 }
162 344
163 return -1; 345 return -1;
164} 346}
165 347
166static int parse_generic_hw_symbols(const char *str, struct perf_counter_attr *attr) 348static enum event_result
349parse_generic_hw_event(const char **str, struct perf_event_attr *attr)
167{ 350{
168 int cache_type = -1, cache_op = 0, cache_result = 0; 351 const char *s = *str;
352 int cache_type = -1, cache_op = -1, cache_result = -1;
169 353
170 cache_type = parse_aliases(str, hw_cache, PERF_COUNT_HW_CACHE_MAX); 354 cache_type = parse_aliases(&s, hw_cache, PERF_COUNT_HW_CACHE_MAX);
171 /* 355 /*
172 * No fallback - if we cannot get a clear cache type 356 * No fallback - if we cannot get a clear cache type
173 * then bail out: 357 * then bail out:
174 */ 358 */
175 if (cache_type == -1) 359 if (cache_type == -1)
176 return -EINVAL; 360 return EVT_FAILED;
361
362 while ((cache_op == -1 || cache_result == -1) && *s == '-') {
363 ++s;
364
365 if (cache_op == -1) {
366 cache_op = parse_aliases(&s, hw_cache_op,
367 PERF_COUNT_HW_CACHE_OP_MAX);
368 if (cache_op >= 0) {
369 if (!is_cache_op_valid(cache_type, cache_op))
370 return 0;
371 continue;
372 }
373 }
374
375 if (cache_result == -1) {
376 cache_result = parse_aliases(&s, hw_cache_result,
377 PERF_COUNT_HW_CACHE_RESULT_MAX);
378 if (cache_result >= 0)
379 continue;
380 }
381
382 /*
383 * Can't parse this as a cache op or result, so back up
384 * to the '-'.
385 */
386 --s;
387 break;
388 }
177 389
178 cache_op = parse_aliases(str, hw_cache_op, PERF_COUNT_HW_CACHE_OP_MAX);
179 /* 390 /*
180 * Fall back to reads: 391 * Fall back to reads:
181 */ 392 */
182 if (cache_op == -1) 393 if (cache_op == -1)
183 cache_op = PERF_COUNT_HW_CACHE_OP_READ; 394 cache_op = PERF_COUNT_HW_CACHE_OP_READ;
184 395
185 cache_result = parse_aliases(str, hw_cache_result,
186 PERF_COUNT_HW_CACHE_RESULT_MAX);
187 /* 396 /*
188 * Fall back to accesses: 397 * Fall back to accesses:
189 */ 398 */
@@ -193,124 +402,641 @@ static int parse_generic_hw_symbols(const char *str, struct perf_counter_attr *a
193 attr->config = cache_type | (cache_op << 8) | (cache_result << 16); 402 attr->config = cache_type | (cache_op << 8) | (cache_result << 16);
194 attr->type = PERF_TYPE_HW_CACHE; 403 attr->type = PERF_TYPE_HW_CACHE;
195 404
196 return 0; 405 *str = s;
406 return EVT_HANDLED;
197} 407}
198 408
199/* 409static enum event_result
200 * Each event can have multiple symbolic names. 410parse_single_tracepoint_event(char *sys_name,
201 * Symbolic names are (almost) exactly matched. 411 const char *evt_name,
202 */ 412 unsigned int evt_length,
203static int parse_event_symbols(const char *str, struct perf_counter_attr *attr) 413 struct perf_event_attr *attr,
414 const char **strp)
204{ 415{
205 u64 config, id; 416 char evt_path[MAXPATHLEN];
206 int type; 417 char id_buf[4];
207 unsigned int i; 418 u64 id;
208 const char *sep, *pstr; 419 int fd;
209 420
210 if (str[0] == 'r' && hex2u64(str + 1, &config) > 0) { 421 snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", debugfs_path,
211 attr->type = PERF_TYPE_RAW; 422 sys_name, evt_name);
212 attr->config = config; 423
424 fd = open(evt_path, O_RDONLY);
425 if (fd < 0)
426 return EVT_FAILED;
427
428 if (read(fd, id_buf, sizeof(id_buf)) < 0) {
429 close(fd);
430 return EVT_FAILED;
431 }
432
433 close(fd);
434 id = atoll(id_buf);
435 attr->config = id;
436 attr->type = PERF_TYPE_TRACEPOINT;
437 *strp += strlen(sys_name) + evt_length + 1; /* + 1 for the ':' */
438
439 attr->sample_type |= PERF_SAMPLE_RAW;
440 attr->sample_type |= PERF_SAMPLE_TIME;
441 attr->sample_type |= PERF_SAMPLE_CPU;
442
443 attr->sample_period = 1;
444
445
446 return EVT_HANDLED;
447}
448
449/* sys + ':' + event + ':' + flags*/
450#define MAX_EVOPT_LEN (MAX_EVENT_LENGTH * 2 + 2 + 128)
451static enum event_result
452parse_multiple_tracepoint_event(char *sys_name, const char *evt_exp,
453 char *flags)
454{
455 char evt_path[MAXPATHLEN];
456 struct dirent *evt_ent;
457 DIR *evt_dir;
458
459 snprintf(evt_path, MAXPATHLEN, "%s/%s", debugfs_path, sys_name);
460 evt_dir = opendir(evt_path);
461
462 if (!evt_dir) {
463 perror("Can't open event dir");
464 return EVT_FAILED;
465 }
466
467 while ((evt_ent = readdir(evt_dir))) {
468 char event_opt[MAX_EVOPT_LEN + 1];
469 int len;
470
471 if (!strcmp(evt_ent->d_name, ".")
472 || !strcmp(evt_ent->d_name, "..")
473 || !strcmp(evt_ent->d_name, "enable")
474 || !strcmp(evt_ent->d_name, "filter"))
475 continue;
476
477 if (!strglobmatch(evt_ent->d_name, evt_exp))
478 continue;
479
480 len = snprintf(event_opt, MAX_EVOPT_LEN, "%s:%s%s%s", sys_name,
481 evt_ent->d_name, flags ? ":" : "",
482 flags ?: "");
483 if (len < 0)
484 return EVT_FAILED;
213 485
486 if (parse_events(NULL, event_opt, 0))
487 return EVT_FAILED;
488 }
489
490 return EVT_HANDLED_ALL;
491}
492
493static int store_event_type(const char *orgname)
494{
495 char filename[PATH_MAX], *c;
496 FILE *file;
497 int id, n;
498
499 sprintf(filename, "%s/", debugfs_path);
500 strncat(filename, orgname, strlen(orgname));
501 strcat(filename, "/id");
502
503 c = strchr(filename, ':');
504 if (c)
505 *c = '/';
506
507 file = fopen(filename, "r");
508 if (!file)
214 return 0; 509 return 0;
510 n = fscanf(file, "%i", &id);
511 fclose(file);
512 if (n < 1) {
513 pr_err("cannot store event ID\n");
514 return -EINVAL;
215 } 515 }
516 return perf_header__push_event(id, orgname);
517}
216 518
217 pstr = str; 519static enum event_result parse_tracepoint_event(const char **strp,
218 sep = strchr(pstr, ':'); 520 struct perf_event_attr *attr)
219 if (sep) { 521{
220 type = atoi(pstr); 522 const char *evt_name;
221 pstr = sep + 1; 523 char *flags = NULL, *comma_loc;
222 id = atoi(pstr); 524 char sys_name[MAX_EVENT_LENGTH];
223 sep = strchr(pstr, ':'); 525 unsigned int sys_length, evt_length;
224 if (sep) {
225 pstr = sep + 1;
226 if (strchr(pstr, 'k'))
227 attr->exclude_user = 1;
228 if (strchr(pstr, 'u'))
229 attr->exclude_kernel = 1;
230 }
231 attr->type = type;
232 attr->config = id;
233 526
527 if (debugfs_valid_mountpoint(debugfs_path))
234 return 0; 528 return 0;
529
530 evt_name = strchr(*strp, ':');
531 if (!evt_name)
532 return EVT_FAILED;
533
534 sys_length = evt_name - *strp;
535 if (sys_length >= MAX_EVENT_LENGTH)
536 return 0;
537
538 strncpy(sys_name, *strp, sys_length);
539 sys_name[sys_length] = '\0';
540 evt_name = evt_name + 1;
541
542 comma_loc = strchr(evt_name, ',');
543 if (comma_loc) {
544 /* take the event name up to the comma */
545 evt_name = strndup(evt_name, comma_loc - evt_name);
546 }
547 flags = strchr(evt_name, ':');
548 if (flags) {
549 /* split it out: */
550 evt_name = strndup(evt_name, flags - evt_name);
551 flags++;
235 } 552 }
236 553
237 for (i = 0; i < ARRAY_SIZE(event_symbols); i++) { 554 evt_length = strlen(evt_name);
238 if (!strncmp(str, event_symbols[i].symbol, 555 if (evt_length >= MAX_EVENT_LENGTH)
239 strlen(event_symbols[i].symbol))) { 556 return EVT_FAILED;
557 if (strpbrk(evt_name, "*?")) {
558 *strp += strlen(sys_name) + evt_length;
559 return parse_multiple_tracepoint_event(sys_name, evt_name,
560 flags);
561 } else {
562 if (store_event_type(evt_name) < 0)
563 return EVT_FAILED;
564
565 return parse_single_tracepoint_event(sys_name, evt_name,
566 evt_length, attr, strp);
567 }
568}
569
570static enum event_result
571parse_breakpoint_type(const char *type, const char **strp,
572 struct perf_event_attr *attr)
573{
574 int i;
575
576 for (i = 0; i < 3; i++) {
577 if (!type[i])
578 break;
579
580 switch (type[i]) {
581 case 'r':
582 attr->bp_type |= HW_BREAKPOINT_R;
583 break;
584 case 'w':
585 attr->bp_type |= HW_BREAKPOINT_W;
586 break;
587 case 'x':
588 attr->bp_type |= HW_BREAKPOINT_X;
589 break;
590 default:
591 return EVT_FAILED;
592 }
593 }
594 if (!attr->bp_type) /* Default */
595 attr->bp_type = HW_BREAKPOINT_R | HW_BREAKPOINT_W;
596
597 *strp = type + i;
598
599 return EVT_HANDLED;
600}
240 601
602static enum event_result
603parse_breakpoint_event(const char **strp, struct perf_event_attr *attr)
604{
605 const char *target;
606 const char *type;
607 char *endaddr;
608 u64 addr;
609 enum event_result err;
610
611 target = strchr(*strp, ':');
612 if (!target)
613 return EVT_FAILED;
614
615 if (strncmp(*strp, "mem", target - *strp) != 0)
616 return EVT_FAILED;
617
618 target++;
619
620 addr = strtoull(target, &endaddr, 0);
621 if (target == endaddr)
622 return EVT_FAILED;
623
624 attr->bp_addr = addr;
625 *strp = endaddr;
626
627 type = strchr(target, ':');
628
629 /* If no type is defined, just rw as default */
630 if (!type) {
631 attr->bp_type = HW_BREAKPOINT_R | HW_BREAKPOINT_W;
632 } else {
633 err = parse_breakpoint_type(++type, strp, attr);
634 if (err == EVT_FAILED)
635 return EVT_FAILED;
636 }
637
638 /*
639 * We should find a nice way to override the access length
640 * Provide some defaults for now
641 */
642 if (attr->bp_type == HW_BREAKPOINT_X)
643 attr->bp_len = sizeof(long);
644 else
645 attr->bp_len = HW_BREAKPOINT_LEN_4;
646
647 attr->type = PERF_TYPE_BREAKPOINT;
648
649 return EVT_HANDLED;
650}
651
652static int check_events(const char *str, unsigned int i)
653{
654 int n;
655
656 n = strlen(event_symbols[i].symbol);
657 if (!strncmp(str, event_symbols[i].symbol, n))
658 return n;
659
660 n = strlen(event_symbols[i].alias);
661 if (n)
662 if (!strncmp(str, event_symbols[i].alias, n))
663 return n;
664 return 0;
665}
666
667static enum event_result
668parse_symbolic_event(const char **strp, struct perf_event_attr *attr)
669{
670 const char *str = *strp;
671 unsigned int i;
672 int n;
673
674 for (i = 0; i < ARRAY_SIZE(event_symbols); i++) {
675 n = check_events(str, i);
676 if (n > 0) {
241 attr->type = event_symbols[i].type; 677 attr->type = event_symbols[i].type;
242 attr->config = event_symbols[i].config; 678 attr->config = event_symbols[i].config;
679 *strp = str + n;
680 return EVT_HANDLED;
681 }
682 }
683 return EVT_FAILED;
684}
685
686static enum event_result
687parse_raw_event(const char **strp, struct perf_event_attr *attr)
688{
689 const char *str = *strp;
690 u64 config;
691 int n;
243 692
244 return 0; 693 if (*str != 'r')
694 return EVT_FAILED;
695 n = hex2u64(str + 1, &config);
696 if (n > 0) {
697 *strp = str + n + 1;
698 attr->type = PERF_TYPE_RAW;
699 attr->config = config;
700 return EVT_HANDLED;
701 }
702 return EVT_FAILED;
703}
704
705static enum event_result
706parse_numeric_event(const char **strp, struct perf_event_attr *attr)
707{
708 const char *str = *strp;
709 char *endp;
710 unsigned long type;
711 u64 config;
712
713 type = strtoul(str, &endp, 0);
714 if (endp > str && type < PERF_TYPE_MAX && *endp == ':') {
715 str = endp + 1;
716 config = strtoul(str, &endp, 0);
717 if (endp > str) {
718 attr->type = type;
719 attr->config = config;
720 *strp = endp;
721 return EVT_HANDLED;
245 } 722 }
246 } 723 }
724 return EVT_FAILED;
725}
726
727static enum event_result
728parse_event_modifier(const char **strp, struct perf_event_attr *attr)
729{
730 const char *str = *strp;
731 int exclude = 0;
732 int eu = 0, ek = 0, eh = 0, precise = 0;
733
734 if (*str++ != ':')
735 return 0;
736 while (*str) {
737 if (*str == 'u') {
738 if (!exclude)
739 exclude = eu = ek = eh = 1;
740 eu = 0;
741 } else if (*str == 'k') {
742 if (!exclude)
743 exclude = eu = ek = eh = 1;
744 ek = 0;
745 } else if (*str == 'h') {
746 if (!exclude)
747 exclude = eu = ek = eh = 1;
748 eh = 0;
749 } else if (*str == 'p') {
750 precise++;
751 } else
752 break;
247 753
248 return parse_generic_hw_symbols(str, attr); 754 ++str;
755 }
756 if (str >= *strp + 2) {
757 *strp = str;
758 attr->exclude_user = eu;
759 attr->exclude_kernel = ek;
760 attr->exclude_hv = eh;
761 attr->precise_ip = precise;
762 return 1;
763 }
764 return 0;
249} 765}
250 766
251int parse_events(const struct option *opt, const char *str, int unset) 767/*
768 * Each event can have multiple symbolic names.
769 * Symbolic names are (almost) exactly matched.
770 */
771static enum event_result
772parse_event_symbols(const char **str, struct perf_event_attr *attr)
252{ 773{
253 struct perf_counter_attr attr; 774 enum event_result ret;
254 int ret;
255 775
256 memset(&attr, 0, sizeof(attr)); 776 ret = parse_tracepoint_event(str, attr);
257again: 777 if (ret != EVT_FAILED)
258 if (nr_counters == MAX_COUNTERS) 778 goto modifier;
259 return -1; 779
780 ret = parse_raw_event(str, attr);
781 if (ret != EVT_FAILED)
782 goto modifier;
783
784 ret = parse_numeric_event(str, attr);
785 if (ret != EVT_FAILED)
786 goto modifier;
787
788 ret = parse_symbolic_event(str, attr);
789 if (ret != EVT_FAILED)
790 goto modifier;
791
792 ret = parse_generic_hw_event(str, attr);
793 if (ret != EVT_FAILED)
794 goto modifier;
795
796 ret = parse_breakpoint_event(str, attr);
797 if (ret != EVT_FAILED)
798 goto modifier;
799
800 fprintf(stderr, "invalid or unsupported event: '%s'\n", *str);
801 fprintf(stderr, "Run 'perf list' for a list of valid events\n");
802 return EVT_FAILED;
803
804modifier:
805 parse_event_modifier(str, attr);
806
807 return ret;
808}
809
810int parse_events(const struct option *opt __used, const char *str, int unset __used)
811{
812 struct perf_event_attr attr;
813 enum event_result ret;
260 814
261 ret = parse_event_symbols(str, &attr); 815 for (;;) {
262 if (ret < 0) 816 memset(&attr, 0, sizeof(attr));
263 return ret; 817 ret = parse_event_symbols(&str, &attr);
818 if (ret == EVT_FAILED)
819 return -1;
264 820
265 attrs[nr_counters] = attr; 821 if (!(*str == 0 || *str == ',' || isspace(*str)))
266 nr_counters++; 822 return -1;
267 823
268 str = strstr(str, ","); 824 if (ret != EVT_HANDLED_ALL) {
269 if (str) { 825 struct perf_evsel *evsel;
270 str++; 826 evsel = perf_evsel__new(&attr,
271 goto again; 827 nr_counters);
828 if (evsel == NULL)
829 return -1;
830 list_add_tail(&evsel->node, &evsel_list);
831 ++nr_counters;
832 }
833
834 if (*str == 0)
835 break;
836 if (*str == ',')
837 ++str;
838 while (isspace(*str))
839 ++str;
840 }
841
842 return 0;
843}
844
845int parse_filter(const struct option *opt __used, const char *str,
846 int unset __used)
847{
848 struct perf_evsel *last = NULL;
849
850 if (!list_empty(&evsel_list))
851 last = list_entry(evsel_list.prev, struct perf_evsel, node);
852
853 if (last == NULL || last->attr.type != PERF_TYPE_TRACEPOINT) {
854 fprintf(stderr,
855 "-F option should follow a -e tracepoint option\n");
856 return -1;
857 }
858
859 last->filter = strdup(str);
860 if (last->filter == NULL) {
861 fprintf(stderr, "not enough memory to hold filter string\n");
862 return -1;
272 } 863 }
273 864
274 return 0; 865 return 0;
275} 866}
276 867
277static const char * const event_type_descriptors[] = { 868static const char * const event_type_descriptors[] = {
278 "",
279 "Hardware event", 869 "Hardware event",
280 "Software event", 870 "Software event",
281 "Tracepoint event", 871 "Tracepoint event",
282 "Hardware cache event", 872 "Hardware cache event",
873 "Raw hardware event descriptor",
874 "Hardware breakpoint",
283}; 875};
284 876
285/* 877/*
878 * Print the events from <debugfs_mount_point>/tracing/events
879 */
880
881static void print_tracepoint_events(void)
882{
883 DIR *sys_dir, *evt_dir;
884 struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent;
885 char evt_path[MAXPATHLEN];
886 char dir_path[MAXPATHLEN];
887
888 if (debugfs_valid_mountpoint(debugfs_path))
889 return;
890
891 sys_dir = opendir(debugfs_path);
892 if (!sys_dir)
893 return;
894
895 for_each_subsystem(sys_dir, sys_dirent, sys_next) {
896
897 snprintf(dir_path, MAXPATHLEN, "%s/%s", debugfs_path,
898 sys_dirent.d_name);
899 evt_dir = opendir(dir_path);
900 if (!evt_dir)
901 continue;
902
903 for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) {
904 snprintf(evt_path, MAXPATHLEN, "%s:%s",
905 sys_dirent.d_name, evt_dirent.d_name);
906 printf(" %-42s [%s]\n", evt_path,
907 event_type_descriptors[PERF_TYPE_TRACEPOINT]);
908 }
909 closedir(evt_dir);
910 }
911 closedir(sys_dir);
912}
913
914/*
915 * Check whether event is in <debugfs_mount_point>/tracing/events
916 */
917
918int is_valid_tracepoint(const char *event_string)
919{
920 DIR *sys_dir, *evt_dir;
921 struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent;
922 char evt_path[MAXPATHLEN];
923 char dir_path[MAXPATHLEN];
924
925 if (debugfs_valid_mountpoint(debugfs_path))
926 return 0;
927
928 sys_dir = opendir(debugfs_path);
929 if (!sys_dir)
930 return 0;
931
932 for_each_subsystem(sys_dir, sys_dirent, sys_next) {
933
934 snprintf(dir_path, MAXPATHLEN, "%s/%s", debugfs_path,
935 sys_dirent.d_name);
936 evt_dir = opendir(dir_path);
937 if (!evt_dir)
938 continue;
939
940 for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) {
941 snprintf(evt_path, MAXPATHLEN, "%s:%s",
942 sys_dirent.d_name, evt_dirent.d_name);
943 if (!strcmp(evt_path, event_string)) {
944 closedir(evt_dir);
945 closedir(sys_dir);
946 return 1;
947 }
948 }
949 closedir(evt_dir);
950 }
951 closedir(sys_dir);
952 return 0;
953}
954
955/*
286 * Print the help text for the event symbols: 956 * Print the help text for the event symbols:
287 */ 957 */
288void print_events(void) 958void print_events(void)
289{ 959{
290 struct event_symbol *syms = event_symbols; 960 struct event_symbol *syms = event_symbols;
291 unsigned int i, type, prev_type = -1; 961 unsigned int i, type, op, prev_type = -1;
962 char name[40];
292 963
293 fprintf(stderr, "\n"); 964 printf("\n");
294 fprintf(stderr, "List of pre-defined events (to be used in -e):\n"); 965 printf("List of pre-defined events (to be used in -e):\n");
295 966
296 for (i = 0; i < ARRAY_SIZE(event_symbols); i++, syms++) { 967 for (i = 0; i < ARRAY_SIZE(event_symbols); i++, syms++) {
297 type = syms->type + 1; 968 type = syms->type;
298 if (type > ARRAY_SIZE(event_type_descriptors))
299 type = 0;
300 969
301 if (type != prev_type) 970 if (type != prev_type)
302 fprintf(stderr, "\n"); 971 printf("\n");
303 972
304 fprintf(stderr, " %-30s [%s]\n", syms->symbol, 973 if (strlen(syms->alias))
974 sprintf(name, "%s OR %s", syms->symbol, syms->alias);
975 else
976 strcpy(name, syms->symbol);
977 printf(" %-42s [%s]\n", name,
305 event_type_descriptors[type]); 978 event_type_descriptors[type]);
306 979
307 prev_type = type; 980 prev_type = type;
308 } 981 }
309 982
310 fprintf(stderr, "\n"); 983 printf("\n");
311 fprintf(stderr, " %-30s [raw hardware event descriptor]\n", 984 for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) {
312 "rNNN"); 985 for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) {
313 fprintf(stderr, "\n"); 986 /* skip invalid cache type */
987 if (!is_cache_op_valid(type, op))
988 continue;
989
990 for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) {
991 printf(" %-42s [%s]\n",
992 event_cache_name(type, op, i),
993 event_type_descriptors[PERF_TYPE_HW_CACHE]);
994 }
995 }
996 }
997
998 printf("\n");
999 printf(" %-42s [%s]\n",
1000 "rNNN (see 'perf list --help' on how to encode it)",
1001 event_type_descriptors[PERF_TYPE_RAW]);
1002 printf("\n");
1003
1004 printf(" %-42s [%s]\n",
1005 "mem:<addr>[:access]",
1006 event_type_descriptors[PERF_TYPE_BREAKPOINT]);
1007 printf("\n");
1008
1009 print_tracepoint_events();
314 1010
315 exit(129); 1011 exit(129);
316} 1012}
1013
1014int perf_evsel_list__create_default(void)
1015{
1016 struct perf_evsel *evsel;
1017 struct perf_event_attr attr;
1018
1019 memset(&attr, 0, sizeof(attr));
1020 attr.type = PERF_TYPE_HARDWARE;
1021 attr.config = PERF_COUNT_HW_CPU_CYCLES;
1022
1023 evsel = perf_evsel__new(&attr, 0);
1024
1025 if (evsel == NULL)
1026 return -ENOMEM;
1027
1028 list_add(&evsel->node, &evsel_list);
1029 ++nr_counters;
1030 return 0;
1031}
1032
1033void perf_evsel_list__delete(void)
1034{
1035 struct perf_evsel *pos, *n;
1036
1037 list_for_each_entry_safe(pos, n, &evsel_list, node) {
1038 list_del_init(&pos->node);
1039 perf_evsel__delete(pos);
1040 }
1041 nr_counters = 0;
1042}
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h
index e3d552908e6..b82cafb8377 100644
--- a/tools/perf/util/parse-events.h
+++ b/tools/perf/util/parse-events.h
@@ -1,17 +1,44 @@
1 1#ifndef __PERF_PARSE_EVENTS_H
2#define __PERF_PARSE_EVENTS_H
2/* 3/*
3 * Parse symbolic events/counts passed in as options: 4 * Parse symbolic events/counts passed in as options:
4 */ 5 */
5 6
6extern int nr_counters; 7#include "../../../include/linux/perf_event.h"
8
9struct list_head;
10struct perf_evsel;
11
12extern struct list_head evsel_list;
13
14int perf_evsel_list__create_default(void);
15void perf_evsel_list__delete(void);
7 16
8extern struct perf_counter_attr attrs[MAX_COUNTERS]; 17struct option;
9 18
10extern char *event_name(int ctr); 19struct tracepoint_path {
20 char *system;
21 char *name;
22 struct tracepoint_path *next;
23};
24
25extern struct tracepoint_path *tracepoint_id_to_path(u64 config);
26extern bool have_tracepoints(struct list_head *evsel_list);
27
28extern int nr_counters;
29
30const char *event_name(struct perf_evsel *event);
31extern const char *__event_name(int type, u64 config);
11 32
12extern int parse_events(const struct option *opt, const char *str, int unset); 33extern int parse_events(const struct option *opt, const char *str, int unset);
34extern int parse_filter(const struct option *opt, const char *str, int unset);
13 35
14#define EVENTS_HELP_MAX (128*1024) 36#define EVENTS_HELP_MAX (128*1024)
15 37
16extern void print_events(void); 38extern void print_events(void);
39extern int is_valid_tracepoint(const char *event_string);
40
41extern char debugfs_path[];
42extern int valid_debugfs_mount(const char *debugfs);
17 43
44#endif /* __PERF_PARSE_EVENTS_H */
diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c
index b3affb1658d..99d02aa57db 100644
--- a/tools/perf/util/parse-options.c
+++ b/tools/perf/util/parse-options.c
@@ -20,7 +20,8 @@ static int get_arg(struct parse_opt_ctx_t *p, const struct option *opt,
20 if (p->opt) { 20 if (p->opt) {
21 *arg = p->opt; 21 *arg = p->opt;
22 p->opt = NULL; 22 p->opt = NULL;
23 } else if (p->argc == 1 && (opt->flags & PARSE_OPT_LASTARG_DEFAULT)) { 23 } else if ((opt->flags & PARSE_OPT_LASTARG_DEFAULT) && (p->argc == 1 ||
24 **(p->argv + 1) == '-')) {
24 *arg = (const char *)opt->defval; 25 *arg = (const char *)opt->defval;
25 } else if (p->argc > 1) { 26 } else if (p->argc > 1) {
26 p->argc--; 27 p->argc--;
@@ -48,10 +49,19 @@ static int get_value(struct parse_opt_ctx_t *p,
48 break; 49 break;
49 /* FALLTHROUGH */ 50 /* FALLTHROUGH */
50 case OPTION_BOOLEAN: 51 case OPTION_BOOLEAN:
52 case OPTION_INCR:
51 case OPTION_BIT: 53 case OPTION_BIT:
52 case OPTION_SET_INT: 54 case OPTION_SET_UINT:
53 case OPTION_SET_PTR: 55 case OPTION_SET_PTR:
54 return opterror(opt, "takes no value", flags); 56 return opterror(opt, "takes no value", flags);
57 case OPTION_END:
58 case OPTION_ARGUMENT:
59 case OPTION_GROUP:
60 case OPTION_STRING:
61 case OPTION_INTEGER:
62 case OPTION_UINTEGER:
63 case OPTION_LONG:
64 case OPTION_U64:
55 default: 65 default:
56 break; 66 break;
57 } 67 }
@@ -66,11 +76,15 @@ static int get_value(struct parse_opt_ctx_t *p,
66 return 0; 76 return 0;
67 77
68 case OPTION_BOOLEAN: 78 case OPTION_BOOLEAN:
79 *(bool *)opt->value = unset ? false : true;
80 return 0;
81
82 case OPTION_INCR:
69 *(int *)opt->value = unset ? 0 : *(int *)opt->value + 1; 83 *(int *)opt->value = unset ? 0 : *(int *)opt->value + 1;
70 return 0; 84 return 0;
71 85
72 case OPTION_SET_INT: 86 case OPTION_SET_UINT:
73 *(int *)opt->value = unset ? 0 : opt->defval; 87 *(unsigned int *)opt->value = unset ? 0 : opt->defval;
74 return 0; 88 return 0;
75 89
76 case OPTION_SET_PTR: 90 case OPTION_SET_PTR:
@@ -113,6 +127,22 @@ static int get_value(struct parse_opt_ctx_t *p,
113 return opterror(opt, "expects a numerical value", flags); 127 return opterror(opt, "expects a numerical value", flags);
114 return 0; 128 return 0;
115 129
130 case OPTION_UINTEGER:
131 if (unset) {
132 *(unsigned int *)opt->value = 0;
133 return 0;
134 }
135 if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
136 *(unsigned int *)opt->value = opt->defval;
137 return 0;
138 }
139 if (get_arg(p, opt, flags, &arg))
140 return -1;
141 *(unsigned int *)opt->value = strtol(arg, (char **)&s, 10);
142 if (*s)
143 return opterror(opt, "expects a numerical value", flags);
144 return 0;
145
116 case OPTION_LONG: 146 case OPTION_LONG:
117 if (unset) { 147 if (unset) {
118 *(long *)opt->value = 0; 148 *(long *)opt->value = 0;
@@ -129,6 +159,25 @@ static int get_value(struct parse_opt_ctx_t *p,
129 return opterror(opt, "expects a numerical value", flags); 159 return opterror(opt, "expects a numerical value", flags);
130 return 0; 160 return 0;
131 161
162 case OPTION_U64:
163 if (unset) {
164 *(u64 *)opt->value = 0;
165 return 0;
166 }
167 if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
168 *(u64 *)opt->value = opt->defval;
169 return 0;
170 }
171 if (get_arg(p, opt, flags, &arg))
172 return -1;
173 *(u64 *)opt->value = strtoull(arg, (char **)&s, 10);
174 if (*s)
175 return opterror(opt, "expects a numerical value", flags);
176 return 0;
177
178 case OPTION_END:
179 case OPTION_ARGUMENT:
180 case OPTION_GROUP:
132 default: 181 default:
133 die("should not happen, someone must be hit on the forehead"); 182 die("should not happen, someone must be hit on the forehead");
134 } 183 }
@@ -295,6 +344,8 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
295 return parse_options_usage(usagestr, options); 344 return parse_options_usage(usagestr, options);
296 case -2: 345 case -2:
297 goto unknown; 346 goto unknown;
347 default:
348 break;
298 } 349 }
299 if (ctx->opt) 350 if (ctx->opt)
300 check_typos(arg + 1, options); 351 check_typos(arg + 1, options);
@@ -313,6 +364,8 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
313 ctx->argv[0] = strdup(ctx->opt - 1); 364 ctx->argv[0] = strdup(ctx->opt - 1);
314 *(char *)ctx->argv[0] = '-'; 365 *(char *)ctx->argv[0] = '-';
315 goto unknown; 366 goto unknown;
367 default:
368 break;
316 } 369 }
317 } 370 }
318 continue; 371 continue;
@@ -335,6 +388,8 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
335 return parse_options_usage(usagestr, options); 388 return parse_options_usage(usagestr, options);
336 case -2: 389 case -2:
337 goto unknown; 390 goto unknown;
391 default:
392 break;
338 } 393 }
339 continue; 394 continue;
340unknown: 395unknown:
@@ -414,6 +469,9 @@ int usage_with_options_internal(const char * const *usagestr,
414 pos = fprintf(stderr, " "); 469 pos = fprintf(stderr, " ");
415 if (opts->short_name) 470 if (opts->short_name)
416 pos += fprintf(stderr, "-%c", opts->short_name); 471 pos += fprintf(stderr, "-%c", opts->short_name);
472 else
473 pos += fprintf(stderr, " ");
474
417 if (opts->long_name && opts->short_name) 475 if (opts->long_name && opts->short_name)
418 pos += fprintf(stderr, ", "); 476 pos += fprintf(stderr, ", ");
419 if (opts->long_name) 477 if (opts->long_name)
@@ -422,7 +480,10 @@ int usage_with_options_internal(const char * const *usagestr,
422 switch (opts->type) { 480 switch (opts->type) {
423 case OPTION_ARGUMENT: 481 case OPTION_ARGUMENT:
424 break; 482 break;
483 case OPTION_LONG:
484 case OPTION_U64:
425 case OPTION_INTEGER: 485 case OPTION_INTEGER:
486 case OPTION_UINTEGER:
426 if (opts->flags & PARSE_OPT_OPTARG) 487 if (opts->flags & PARSE_OPT_OPTARG)
427 if (opts->long_name) 488 if (opts->long_name)
428 pos += fprintf(stderr, "[=<n>]"); 489 pos += fprintf(stderr, "[=<n>]");
@@ -454,7 +515,14 @@ int usage_with_options_internal(const char * const *usagestr,
454 pos += fprintf(stderr, " ..."); 515 pos += fprintf(stderr, " ...");
455 } 516 }
456 break; 517 break;
457 default: /* OPTION_{BIT,BOOLEAN,SET_INT,SET_PTR} */ 518 default: /* OPTION_{BIT,BOOLEAN,SET_UINT,SET_PTR} */
519 case OPTION_END:
520 case OPTION_GROUP:
521 case OPTION_BIT:
522 case OPTION_BOOLEAN:
523 case OPTION_INCR:
524 case OPTION_SET_UINT:
525 case OPTION_SET_PTR:
458 break; 526 break;
459 } 527 }
460 528
@@ -474,6 +542,7 @@ int usage_with_options_internal(const char * const *usagestr,
474void usage_with_options(const char * const *usagestr, 542void usage_with_options(const char * const *usagestr,
475 const struct option *opts) 543 const struct option *opts)
476{ 544{
545 exit_browser(false);
477 usage_with_options_internal(usagestr, opts, 0); 546 usage_with_options_internal(usagestr, opts, 0);
478 exit(129); 547 exit(129);
479} 548}
@@ -485,7 +554,7 @@ int parse_options_usage(const char * const *usagestr,
485} 554}
486 555
487 556
488int parse_opt_verbosity_cb(const struct option *opt, const char *arg, 557int parse_opt_verbosity_cb(const struct option *opt, const char *arg __used,
489 int unset) 558 int unset)
490{ 559{
491 int *target = opt->value; 560 int *target = opt->value;
diff --git a/tools/perf/util/parse-options.h b/tools/perf/util/parse-options.h
index a1039a6ce0e..abc31a1dac1 100644
--- a/tools/perf/util/parse-options.h
+++ b/tools/perf/util/parse-options.h
@@ -1,5 +1,8 @@
1#ifndef PARSE_OPTIONS_H 1#ifndef __PERF_PARSE_OPTIONS_H
2#define PARSE_OPTIONS_H 2#define __PERF_PARSE_OPTIONS_H
3
4#include <linux/kernel.h>
5#include <stdbool.h>
3 6
4enum parse_opt_type { 7enum parse_opt_type {
5 /* special types */ 8 /* special types */
@@ -8,14 +11,17 @@ enum parse_opt_type {
8 OPTION_GROUP, 11 OPTION_GROUP,
9 /* options with no arguments */ 12 /* options with no arguments */
10 OPTION_BIT, 13 OPTION_BIT,
11 OPTION_BOOLEAN, /* _INCR would have been a better name */ 14 OPTION_BOOLEAN,
12 OPTION_SET_INT, 15 OPTION_INCR,
16 OPTION_SET_UINT,
13 OPTION_SET_PTR, 17 OPTION_SET_PTR,
14 /* options with arguments (usually) */ 18 /* options with arguments (usually) */
15 OPTION_STRING, 19 OPTION_STRING,
16 OPTION_INTEGER, 20 OPTION_INTEGER,
17 OPTION_LONG, 21 OPTION_LONG,
18 OPTION_CALLBACK, 22 OPTION_CALLBACK,
23 OPTION_U64,
24 OPTION_UINTEGER,
19}; 25};
20 26
21enum parse_opt_flags { 27enum parse_opt_flags {
@@ -73,7 +79,7 @@ typedef int parse_opt_cb(const struct option *, const char *arg, int unset);
73 * 79 *
74 * `defval`:: 80 * `defval`::
75 * default value to fill (*->value) with for PARSE_OPT_OPTARG. 81 * default value to fill (*->value) with for PARSE_OPT_OPTARG.
76 * OPTION_{BIT,SET_INT,SET_PTR} store the {mask,integer,pointer} to put in 82 * OPTION_{BIT,SET_UINT,SET_PTR} store the {mask,integer,pointer} to put in
77 * the value when met. 83 * the value when met.
78 * CALLBACKS can use it like they want. 84 * CALLBACKS can use it like they want.
79 */ 85 */
@@ -90,21 +96,33 @@ struct option {
90 intptr_t defval; 96 intptr_t defval;
91}; 97};
92 98
93#define OPT_END() { OPTION_END } 99#define check_vtype(v, type) ( BUILD_BUG_ON_ZERO(!__builtin_types_compatible_p(typeof(v), type)) + v )
94#define OPT_ARGUMENT(l, h) { OPTION_ARGUMENT, 0, (l), NULL, NULL, (h) } 100
95#define OPT_GROUP(h) { OPTION_GROUP, 0, NULL, NULL, NULL, (h) } 101#define OPT_END() { .type = OPTION_END }
96#define OPT_BIT(s, l, v, h, b) { OPTION_BIT, (s), (l), (v), NULL, (h), 0, NULL, (b) } 102#define OPT_ARGUMENT(l, h) { .type = OPTION_ARGUMENT, .long_name = (l), .help = (h) }
97#define OPT_BOOLEAN(s, l, v, h) { OPTION_BOOLEAN, (s), (l), (v), NULL, (h) } 103#define OPT_GROUP(h) { .type = OPTION_GROUP, .help = (h) }
98#define OPT_SET_INT(s, l, v, h, i) { OPTION_SET_INT, (s), (l), (v), NULL, (h), 0, NULL, (i) } 104#define OPT_BIT(s, l, v, h, b) { .type = OPTION_BIT, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h), .defval = (b) }
99#define OPT_SET_PTR(s, l, v, h, p) { OPTION_SET_PTR, (s), (l), (v), NULL, (h), 0, NULL, (p) } 105#define OPT_BOOLEAN(s, l, v, h) { .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), .value = check_vtype(v, bool *), .help = (h) }
100#define OPT_INTEGER(s, l, v, h) { OPTION_INTEGER, (s), (l), (v), NULL, (h) } 106#define OPT_INCR(s, l, v, h) { .type = OPTION_INCR, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h) }
101#define OPT_LONG(s, l, v, h) { OPTION_LONG, (s), (l), (v), NULL, (h) } 107#define OPT_SET_UINT(s, l, v, h, i) { .type = OPTION_SET_UINT, .short_name = (s), .long_name = (l), .value = check_vtype(v, unsigned int *), .help = (h), .defval = (i) }
102#define OPT_STRING(s, l, v, a, h) { OPTION_STRING, (s), (l), (v), (a), (h) } 108#define OPT_SET_PTR(s, l, v, h, p) { .type = OPTION_SET_PTR, .short_name = (s), .long_name = (l), .value = (v), .help = (h), .defval = (p) }
109#define OPT_INTEGER(s, l, v, h) { .type = OPTION_INTEGER, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h) }
110#define OPT_UINTEGER(s, l, v, h) { .type = OPTION_UINTEGER, .short_name = (s), .long_name = (l), .value = check_vtype(v, unsigned int *), .help = (h) }
111#define OPT_LONG(s, l, v, h) { .type = OPTION_LONG, .short_name = (s), .long_name = (l), .value = check_vtype(v, long *), .help = (h) }
112#define OPT_U64(s, l, v, h) { .type = OPTION_U64, .short_name = (s), .long_name = (l), .value = check_vtype(v, u64 *), .help = (h) }
113#define OPT_STRING(s, l, v, a, h) { .type = OPTION_STRING, .short_name = (s), .long_name = (l), .value = check_vtype(v, const char **), (a), .help = (h) }
103#define OPT_DATE(s, l, v, h) \ 114#define OPT_DATE(s, l, v, h) \
104 { OPTION_CALLBACK, (s), (l), (v), "time",(h), 0, \ 115 { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), .argh = "time", .help = (h), .callback = parse_opt_approxidate_cb }
105 parse_opt_approxidate_cb }
106#define OPT_CALLBACK(s, l, v, a, h, f) \ 116#define OPT_CALLBACK(s, l, v, a, h, f) \
107 { OPTION_CALLBACK, (s), (l), (v), (a), (h), 0, (f) } 117 { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h), .callback = (f) }
118#define OPT_CALLBACK_NOOPT(s, l, v, a, h, f) \
119 { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h), .callback = (f), .flags = PARSE_OPT_NOARG }
120#define OPT_CALLBACK_DEFAULT(s, l, v, a, h, f, d) \
121 { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h), .callback = (f), .defval = (intptr_t)d, .flags = PARSE_OPT_LASTARG_DEFAULT }
122#define OPT_CALLBACK_DEFAULT_NOOPT(s, l, v, a, h, f, d) \
123 { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l),\
124 .value = (v), (a), .help = (h), .callback = (f), .defval = (intptr_t)d,\
125 .flags = PARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NOARG}
108 126
109/* parse_options() will filter out the processed options and leave the 127/* parse_options() will filter out the processed options and leave the
110 * non-option argments in argv[]. 128 * non-option argments in argv[].
@@ -171,4 +189,4 @@ extern int parse_opt_verbosity_cb(const struct option *, const char *, int);
171 189
172extern const char *parse_options_fix_filename(const char *prefix, const char *file); 190extern const char *parse_options_fix_filename(const char *prefix, const char *file);
173 191
174#endif 192#endif /* __PERF_PARSE_OPTIONS_H */
diff --git a/tools/perf/util/path.c b/tools/perf/util/path.c
index a501a40dd2c..bd749771142 100644
--- a/tools/perf/util/path.c
+++ b/tools/perf/util/path.c
@@ -17,11 +17,12 @@ static char bad_path[] = "/bad-path/";
17 * Two hacks: 17 * Two hacks:
18 */ 18 */
19 19
20static char *get_perf_dir(void) 20static const char *get_perf_dir(void)
21{ 21{
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,13 +34,14 @@ 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{
40 static char pathname_array[4][PATH_MAX]; 41 static char pathname_array[4][PATH_MAX];
41 static int index; 42 static int idx;
42 return pathname_array[3 & ++index]; 43
44 return pathname_array[3 & ++idx];
43} 45}
44 46
45static char *cleanup_path(char *path) 47static char *cleanup_path(char *path)
@@ -53,21 +55,6 @@ static char *cleanup_path(char *path)
53 return path; 55 return path;
54} 56}
55 57
56char *mksnpath(char *buf, size_t n, const char *fmt, ...)
57{
58 va_list args;
59 unsigned len;
60
61 va_start(args, fmt);
62 len = vsnprintf(buf, n, fmt, args);
63 va_end(args);
64 if (len >= n) {
65 strlcpy(buf, bad_path, n);
66 return buf;
67 }
68 return cleanup_path(buf);
69}
70
71static char *perf_vsnpath(char *buf, size_t n, const char *fmt, va_list args) 58static char *perf_vsnpath(char *buf, size_t n, const char *fmt, va_list args)
72{ 59{
73 const char *perf_dir = get_perf_dir(); 60 const char *perf_dir = get_perf_dir();
@@ -88,15 +75,6 @@ bad:
88 return buf; 75 return buf;
89} 76}
90 77
91char *perf_snpath(char *buf, size_t n, const char *fmt, ...)
92{
93 va_list args;
94 va_start(args, fmt);
95 (void)perf_vsnpath(buf, n, fmt, args);
96 va_end(args);
97 return buf;
98}
99
100char *perf_pathdup(const char *fmt, ...) 78char *perf_pathdup(const char *fmt, ...)
101{ 79{
102 char path[PATH_MAX]; 80 char path[PATH_MAX];
@@ -142,180 +120,6 @@ char *perf_path(const char *fmt, ...)
142 return cleanup_path(pathname); 120 return cleanup_path(pathname);
143} 121}
144 122
145
146/* perf_mkstemp() - create tmp file honoring TMPDIR variable */
147int perf_mkstemp(char *path, size_t len, const char *template)
148{
149 const char *tmp;
150 size_t n;
151
152 tmp = getenv("TMPDIR");
153 if (!tmp)
154 tmp = "/tmp";
155 n = snprintf(path, len, "%s/%s", tmp, template);
156 if (len <= n) {
157 errno = ENAMETOOLONG;
158 return -1;
159 }
160 return mkstemp(path);
161}
162
163
164const char *make_relative_path(const char *abs, const char *base)
165{
166 static char buf[PATH_MAX + 1];
167 int baselen;
168 if (!base)
169 return abs;
170 baselen = strlen(base);
171 if (prefixcmp(abs, base))
172 return abs;
173 if (abs[baselen] == '/')
174 baselen++;
175 else if (base[baselen - 1] != '/')
176 return abs;
177 strcpy(buf, abs + baselen);
178 return buf;
179}
180
181/*
182 * It is okay if dst == src, but they should not overlap otherwise.
183 *
184 * Performs the following normalizations on src, storing the result in dst:
185 * - Ensures that components are separated by '/' (Windows only)
186 * - Squashes sequences of '/'.
187 * - Removes "." components.
188 * - Removes ".." components, and the components the precede them.
189 * Returns failure (non-zero) if a ".." component appears as first path
190 * component anytime during the normalization. Otherwise, returns success (0).
191 *
192 * Note that this function is purely textual. It does not follow symlinks,
193 * verify the existence of the path, or make any system calls.
194 */
195int normalize_path_copy(char *dst, const char *src)
196{
197 char *dst0;
198
199 if (has_dos_drive_prefix(src)) {
200 *dst++ = *src++;
201 *dst++ = *src++;
202 }
203 dst0 = dst;
204
205 if (is_dir_sep(*src)) {
206 *dst++ = '/';
207 while (is_dir_sep(*src))
208 src++;
209 }
210
211 for (;;) {
212 char c = *src;
213
214 /*
215 * A path component that begins with . could be
216 * special:
217 * (1) "." and ends -- ignore and terminate.
218 * (2) "./" -- ignore them, eat slash and continue.
219 * (3) ".." and ends -- strip one and terminate.
220 * (4) "../" -- strip one, eat slash and continue.
221 */
222 if (c == '.') {
223 if (!src[1]) {
224 /* (1) */
225 src++;
226 } else if (is_dir_sep(src[1])) {
227 /* (2) */
228 src += 2;
229 while (is_dir_sep(*src))
230 src++;
231 continue;
232 } else if (src[1] == '.') {
233 if (!src[2]) {
234 /* (3) */
235 src += 2;
236 goto up_one;
237 } else if (is_dir_sep(src[2])) {
238 /* (4) */
239 src += 3;
240 while (is_dir_sep(*src))
241 src++;
242 goto up_one;
243 }
244 }
245 }
246
247 /* copy up to the next '/', and eat all '/' */
248 while ((c = *src++) != '\0' && !is_dir_sep(c))
249 *dst++ = c;
250 if (is_dir_sep(c)) {
251 *dst++ = '/';
252 while (is_dir_sep(c))
253 c = *src++;
254 src--;
255 } else if (!c)
256 break;
257 continue;
258
259 up_one:
260 /*
261 * dst0..dst is prefix portion, and dst[-1] is '/';
262 * go up one level.
263 */
264 dst--; /* go to trailing '/' */
265 if (dst <= dst0)
266 return -1;
267 /* Windows: dst[-1] cannot be backslash anymore */
268 while (dst0 < dst && dst[-1] != '/')
269 dst--;
270 }
271 *dst = '\0';
272 return 0;
273}
274
275/*
276 * path = Canonical absolute path
277 * prefix_list = Colon-separated list of absolute paths
278 *
279 * Determines, for each path in prefix_list, whether the "prefix" really
280 * is an ancestor directory of path. Returns the length of the longest
281 * ancestor directory, excluding any trailing slashes, or -1 if no prefix
282 * is an ancestor. (Note that this means 0 is returned if prefix_list is
283 * "/".) "/foo" is not considered an ancestor of "/foobar". Directories
284 * are not considered to be their own ancestors. path must be in a
285 * canonical form: empty components, or "." or ".." components are not
286 * allowed. prefix_list may be null, which is like "".
287 */
288int longest_ancestor_length(const char *path, const char *prefix_list)
289{
290 char buf[PATH_MAX+1];
291 const char *ceil, *colon;
292 int len, max_len = -1;
293
294 if (prefix_list == NULL || !strcmp(path, "/"))
295 return -1;
296
297 for (colon = ceil = prefix_list; *colon; ceil = colon+1) {
298 for (colon = ceil; *colon && *colon != PATH_SEP; colon++);
299 len = colon - ceil;
300 if (len == 0 || len > PATH_MAX || !is_absolute_path(ceil))
301 continue;
302 strlcpy(buf, ceil, len+1);
303 if (normalize_path_copy(buf, buf) < 0)
304 continue;
305 len = strlen(buf);
306 if (len > 0 && buf[len-1] == '/')
307 buf[--len] = '\0';
308
309 if (!strncmp(path, buf, len) &&
310 path[len] == '/' &&
311 len > max_len) {
312 max_len = len;
313 }
314 }
315
316 return max_len;
317}
318
319/* strip arbitrary amount of directory separators at end of path */ 123/* strip arbitrary amount of directory separators at end of path */
320static inline int chomp_trailing_dir_sep(const char *path, int len) 124static inline int chomp_trailing_dir_sep(const char *path, int len)
321{ 125{
@@ -349,5 +153,5 @@ char *strip_path_suffix(const char *path, const char *suffix)
349 153
350 if (path_len && !is_dir_sep(path[path_len - 1])) 154 if (path_len && !is_dir_sep(path[path_len - 1]))
351 return NULL; 155 return NULL;
352 return xstrndup(path, chomp_trailing_dir_sep(path, path_len)); 156 return strndup(path, chomp_trailing_dir_sep(path, path_len));
353} 157}
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
new file mode 100644
index 00000000000..128aaab0aed
--- /dev/null
+++ b/tools/perf/util/probe-event.c
@@ -0,0 +1,1915 @@
1/*
2 * probe-event.c : perf-probe definition to probe_events format converter
3 *
4 * Written by Masami Hiramatsu <mhiramat@redhat.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 *
20 */
21
22#define _GNU_SOURCE
23#include <sys/utsname.h>
24#include <sys/types.h>
25#include <sys/stat.h>
26#include <fcntl.h>
27#include <errno.h>
28#include <stdio.h>
29#include <unistd.h>
30#include <stdlib.h>
31#include <string.h>
32#include <stdarg.h>
33#include <limits.h>
34
35#undef _GNU_SOURCE
36#include "util.h"
37#include "event.h"
38#include "string.h"
39#include "strlist.h"
40#include "debug.h"
41#include "cache.h"
42#include "color.h"
43#include "symbol.h"
44#include "thread.h"
45#include "debugfs.h"
46#include "trace-event.h" /* For __unused */
47#include "probe-event.h"
48#include "probe-finder.h"
49
50#define MAX_CMDLEN 256
51#define MAX_PROBE_ARGS 128
52#define PERFPROBE_GROUP "probe"
53
54bool probe_event_dry_run; /* Dry run flag */
55
56#define semantic_error(msg ...) pr_err("Semantic error :" msg)
57
58/* If there is no space to write, returns -E2BIG. */
59static int e_snprintf(char *str, size_t size, const char *format, ...)
60 __attribute__((format(printf, 3, 4)));
61
62static int e_snprintf(char *str, size_t size, const char *format, ...)
63{
64 int ret;
65 va_list ap;
66 va_start(ap, format);
67 ret = vsnprintf(str, size, format, ap);
68 va_end(ap);
69 if (ret >= (int)size)
70 ret = -E2BIG;
71 return ret;
72}
73
74static char *synthesize_perf_probe_point(struct perf_probe_point *pp);
75static struct machine machine;
76
77/* Initialize symbol maps and path of vmlinux/modules */
78static int init_vmlinux(void)
79{
80 int ret;
81
82 symbol_conf.sort_by_name = true;
83 if (symbol_conf.vmlinux_name == NULL)
84 symbol_conf.try_vmlinux_path = true;
85 else
86 pr_debug("Use vmlinux: %s\n", symbol_conf.vmlinux_name);
87 ret = symbol__init();
88 if (ret < 0) {
89 pr_debug("Failed to init symbol map.\n");
90 goto out;
91 }
92
93 ret = machine__init(&machine, "", HOST_KERNEL_ID);
94 if (ret < 0)
95 goto out;
96
97 if (machine__create_kernel_maps(&machine) < 0) {
98 pr_debug("machine__create_kernel_maps() failed.\n");
99 goto out;
100 }
101out:
102 if (ret < 0)
103 pr_warning("Failed to init vmlinux path.\n");
104 return ret;
105}
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
147#ifdef DWARF_SUPPORT
148static int open_vmlinux(const char *module)
149{
150 const char *path = kernel_get_module_path(module);
151 if (!path) {
152 pr_err("Failed to find path of %s module.\n",
153 module ?: "kernel");
154 return -ENOENT;
155 }
156 pr_debug("Try to open %s\n", path);
157 return open(path, O_RDONLY);
158}
159
160/*
161 * Convert trace point to probe point with debuginfo
162 * Currently only handles kprobes.
163 */
164static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
165 struct perf_probe_point *pp)
166{
167 struct symbol *sym;
168 struct map *map;
169 u64 addr;
170 int ret = -ENOENT;
171
172 sym = __find_kernel_function_by_name(tp->symbol, &map);
173 if (sym) {
174 addr = map->unmap_ip(map, sym->start + tp->offset);
175 pr_debug("try to find %s+%ld@%llx\n", tp->symbol,
176 tp->offset, addr);
177 ret = find_perf_probe_point((unsigned long)addr, pp);
178 }
179 if (ret <= 0) {
180 pr_debug("Failed to find corresponding probes from "
181 "debuginfo. Use kprobe event information.\n");
182 pp->function = strdup(tp->symbol);
183 if (pp->function == NULL)
184 return -ENOMEM;
185 pp->offset = tp->offset;
186 }
187 pp->retprobe = tp->retprobe;
188
189 return 0;
190}
191
192/* Try to find perf_probe_event with debuginfo */
193static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
194 struct probe_trace_event **tevs,
195 int max_tevs, const char *module)
196{
197 bool need_dwarf = perf_probe_event_need_dwarf(pev);
198 int fd, ntevs;
199
200 fd = open_vmlinux(module);
201 if (fd < 0) {
202 if (need_dwarf) {
203 pr_warning("Failed to open debuginfo file.\n");
204 return fd;
205 }
206 pr_debug("Could not open vmlinux. Try to use symbols.\n");
207 return 0;
208 }
209
210 /* Searching trace events corresponding to probe event */
211 ntevs = find_probe_trace_events(fd, pev, tevs, max_tevs);
212 close(fd);
213
214 if (ntevs > 0) { /* Succeeded to find trace events */
215 pr_debug("find %d probe_trace_events.\n", ntevs);
216 return ntevs;
217 }
218
219 if (ntevs == 0) { /* No error but failed to find probe point. */
220 pr_warning("Probe point '%s' not found.\n",
221 synthesize_perf_probe_point(&pev->point));
222 return -ENOENT;
223 }
224 /* Error path : ntevs < 0 */
225 pr_debug("An error occurred in debuginfo analysis (%d).\n", ntevs);
226 if (ntevs == -EBADF) {
227 pr_warning("Warning: No dwarf info found in the vmlinux - "
228 "please rebuild kernel with CONFIG_DEBUG_INFO=y.\n");
229 if (!need_dwarf) {
230 pr_debug("Trying to use symbols.\n");
231 return 0;
232 }
233 }
234 return ntevs;
235}
236
237/*
238 * Find a src file from a DWARF tag path. Prepend optional source path prefix
239 * and chop off leading directories that do not exist. Result is passed back as
240 * a newly allocated path on success.
241 * Return 0 if file was found and readable, -errno otherwise.
242 */
243static int get_real_path(const char *raw_path, const char *comp_dir,
244 char **new_path)
245{
246 const char *prefix = symbol_conf.source_prefix;
247
248 if (!prefix) {
249 if (raw_path[0] != '/' && comp_dir)
250 /* If not an absolute path, try to use comp_dir */
251 prefix = comp_dir;
252 else {
253 if (access(raw_path, R_OK) == 0) {
254 *new_path = strdup(raw_path);
255 return 0;
256 } else
257 return -errno;
258 }
259 }
260
261 *new_path = malloc((strlen(prefix) + strlen(raw_path) + 2));
262 if (!*new_path)
263 return -ENOMEM;
264
265 for (;;) {
266 sprintf(*new_path, "%s/%s", prefix, raw_path);
267
268 if (access(*new_path, R_OK) == 0)
269 return 0;
270
271 if (!symbol_conf.source_prefix)
272 /* In case of searching comp_dir, don't retry */
273 return -errno;
274
275 switch (errno) {
276 case ENAMETOOLONG:
277 case ENOENT:
278 case EROFS:
279 case EFAULT:
280 raw_path = strchr(++raw_path, '/');
281 if (!raw_path) {
282 free(*new_path);
283 *new_path = NULL;
284 return -ENOENT;
285 }
286 continue;
287
288 default:
289 free(*new_path);
290 *new_path = NULL;
291 return -errno;
292 }
293 }
294}
295
296#define LINEBUF_SIZE 256
297#define NR_ADDITIONAL_LINES 2
298
299static int __show_one_line(FILE *fp, int l, bool skip, bool show_num)
300{
301 char buf[LINEBUF_SIZE];
302 const char *color = show_num ? "" : PERF_COLOR_BLUE;
303 const char *prefix = NULL;
304
305 do {
306 if (fgets(buf, LINEBUF_SIZE, fp) == NULL)
307 goto error;
308 if (skip)
309 continue;
310 if (!prefix) {
311 prefix = show_num ? "%7d " : " ";
312 color_fprintf(stdout, color, prefix, l);
313 }
314 color_fprintf(stdout, color, "%s", buf);
315
316 } while (strchr(buf, '\n') == NULL);
317
318 return 1;
319error:
320 if (ferror(fp)) {
321 pr_warning("File read error: %s\n", strerror(errno));
322 return -1;
323 }
324 return 0;
325}
326
327static int _show_one_line(FILE *fp, int l, bool skip, bool show_num)
328{
329 int rv = __show_one_line(fp, l, skip, show_num);
330 if (rv == 0) {
331 pr_warning("Source file is shorter than expected.\n");
332 rv = -1;
333 }
334 return rv;
335}
336
337#define show_one_line_with_num(f,l) _show_one_line(f,l,false,true)
338#define show_one_line(f,l) _show_one_line(f,l,false,false)
339#define skip_one_line(f,l) _show_one_line(f,l,true,false)
340#define show_one_line_or_eof(f,l) __show_one_line(f,l,false,false)
341
342/*
343 * Show line-range always requires debuginfo to find source file and
344 * line number.
345 */
346int show_line_range(struct line_range *lr, const char *module)
347{
348 int l = 1;
349 struct line_node *ln;
350 FILE *fp;
351 int fd, ret;
352 char *tmp;
353
354 /* Search a line range */
355 ret = init_vmlinux();
356 if (ret < 0)
357 return ret;
358
359 fd = open_vmlinux(module);
360 if (fd < 0) {
361 pr_warning("Failed to open debuginfo file.\n");
362 return fd;
363 }
364
365 ret = find_line_range(fd, lr);
366 close(fd);
367 if (ret == 0) {
368 pr_warning("Specified source line is not found.\n");
369 return -ENOENT;
370 } else if (ret < 0) {
371 pr_warning("Debuginfo analysis failed. (%d)\n", ret);
372 return ret;
373 }
374
375 /* Convert source file path */
376 tmp = lr->path;
377 ret = get_real_path(tmp, lr->comp_dir, &lr->path);
378 free(tmp); /* Free old path */
379 if (ret < 0) {
380 pr_warning("Failed to find source file. (%d)\n", ret);
381 return ret;
382 }
383
384 setup_pager();
385
386 if (lr->function)
387 fprintf(stdout, "<%s:%d>\n", lr->function,
388 lr->start - lr->offset);
389 else
390 fprintf(stdout, "<%s:%d>\n", lr->path, lr->start);
391
392 fp = fopen(lr->path, "r");
393 if (fp == NULL) {
394 pr_warning("Failed to open %s: %s\n", lr->path,
395 strerror(errno));
396 return -errno;
397 }
398 /* Skip to starting line number */
399 while (l < lr->start) {
400 ret = skip_one_line(fp, l++);
401 if (ret < 0)
402 goto end;
403 }
404
405 list_for_each_entry(ln, &lr->line_list, list) {
406 for (; ln->line > l; l++) {
407 ret = show_one_line(fp, l - lr->offset);
408 if (ret < 0)
409 goto end;
410 }
411 ret = show_one_line_with_num(fp, l++ - lr->offset);
412 if (ret < 0)
413 goto end;
414 }
415
416 if (lr->end == INT_MAX)
417 lr->end = l + NR_ADDITIONAL_LINES;
418 while (l <= lr->end) {
419 ret = show_one_line_or_eof(fp, l++ - lr->offset);
420 if (ret <= 0)
421 break;
422 }
423end:
424 fclose(fp);
425 return ret;
426}
427
428static int show_available_vars_at(int fd, struct perf_probe_event *pev,
429 int max_vls, bool externs)
430{
431 char *buf;
432 int ret, i;
433 struct str_node *node;
434 struct variable_list *vls = NULL, *vl;
435
436 buf = synthesize_perf_probe_point(&pev->point);
437 if (!buf)
438 return -EINVAL;
439 pr_debug("Searching variables at %s\n", buf);
440
441 ret = find_available_vars_at(fd, pev, &vls, max_vls, externs);
442 if (ret > 0) {
443 /* Some variables were found */
444 fprintf(stdout, "Available variables at %s\n", buf);
445 for (i = 0; i < ret; i++) {
446 vl = &vls[i];
447 /*
448 * A probe point might be converted to
449 * several trace points.
450 */
451 fprintf(stdout, "\t@<%s+%lu>\n", vl->point.symbol,
452 vl->point.offset);
453 free(vl->point.symbol);
454 if (vl->vars) {
455 strlist__for_each(node, vl->vars)
456 fprintf(stdout, "\t\t%s\n", node->s);
457 strlist__delete(vl->vars);
458 } else
459 fprintf(stdout, "(No variables)\n");
460 }
461 free(vls);
462 } else
463 pr_err("Failed to find variables at %s (%d)\n", buf, ret);
464
465 free(buf);
466 return ret;
467}
468
469/* Show available variables on given probe point */
470int show_available_vars(struct perf_probe_event *pevs, int npevs,
471 int max_vls, const char *module, bool externs)
472{
473 int i, fd, ret = 0;
474
475 ret = init_vmlinux();
476 if (ret < 0)
477 return ret;
478
479 fd = open_vmlinux(module);
480 if (fd < 0) {
481 pr_warning("Failed to open debug information file.\n");
482 return fd;
483 }
484
485 setup_pager();
486
487 for (i = 0; i < npevs && ret >= 0; i++)
488 ret = show_available_vars_at(fd, &pevs[i], max_vls, externs);
489
490 close(fd);
491 return ret;
492}
493
494#else /* !DWARF_SUPPORT */
495
496static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
497 struct perf_probe_point *pp)
498{
499 struct symbol *sym;
500
501 sym = __find_kernel_function_by_name(tp->symbol, NULL);
502 if (!sym) {
503 pr_err("Failed to find symbol %s in kernel.\n", tp->symbol);
504 return -ENOENT;
505 }
506 pp->function = strdup(tp->symbol);
507 if (pp->function == NULL)
508 return -ENOMEM;
509 pp->offset = tp->offset;
510 pp->retprobe = tp->retprobe;
511
512 return 0;
513}
514
515static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
516 struct probe_trace_event **tevs __unused,
517 int max_tevs __unused, const char *mod __unused)
518{
519 if (perf_probe_event_need_dwarf(pev)) {
520 pr_warning("Debuginfo-analysis is not supported.\n");
521 return -ENOSYS;
522 }
523 return 0;
524}
525
526int show_line_range(struct line_range *lr __unused, const char *module __unused)
527{
528 pr_warning("Debuginfo-analysis is not supported.\n");
529 return -ENOSYS;
530}
531
532int show_available_vars(struct perf_probe_event *pevs __unused,
533 int npevs __unused, int max_vls __unused,
534 const char *module __unused, bool externs __unused)
535{
536 pr_warning("Debuginfo-analysis is not supported.\n");
537 return -ENOSYS;
538}
539#endif
540
541static int parse_line_num(char **ptr, int *val, const char *what)
542{
543 const char *start = *ptr;
544
545 errno = 0;
546 *val = strtol(*ptr, ptr, 0);
547 if (errno || *ptr == start) {
548 semantic_error("'%s' is not a valid number.\n", what);
549 return -EINVAL;
550 }
551 return 0;
552}
553
554/*
555 * Stuff 'lr' according to the line range described by 'arg'.
556 * The line range syntax is described by:
557 *
558 * SRC[:SLN[+NUM|-ELN]]
559 * FNC[:SLN[+NUM|-ELN]]
560 */
561int parse_line_range_desc(const char *arg, struct line_range *lr)
562{
563 char *range, *name = strdup(arg);
564 int err;
565
566 if (!name)
567 return -ENOMEM;
568
569 lr->start = 0;
570 lr->end = INT_MAX;
571
572 range = strchr(name, ':');
573 if (range) {
574 *range++ = '\0';
575
576 err = parse_line_num(&range, &lr->start, "start line");
577 if (err)
578 goto err;
579
580 if (*range == '+' || *range == '-') {
581 const char c = *range++;
582
583 err = parse_line_num(&range, &lr->end, "end line");
584 if (err)
585 goto err;
586
587 if (c == '+') {
588 lr->end += lr->start;
589 /*
590 * Adjust the number of lines here.
591 * If the number of lines == 1, the
592 * the end of line should be equal to
593 * the start of line.
594 */
595 lr->end--;
596 }
597 }
598
599 pr_debug("Line range is %d to %d\n", lr->start, lr->end);
600
601 err = -EINVAL;
602 if (lr->start > lr->end) {
603 semantic_error("Start line must be smaller"
604 " than end line.\n");
605 goto err;
606 }
607 if (*range != '\0') {
608 semantic_error("Tailing with invalid str '%s'.\n", range);
609 goto err;
610 }
611 }
612
613 if (strchr(name, '.'))
614 lr->file = name;
615 else
616 lr->function = name;
617
618 return 0;
619err:
620 free(name);
621 return err;
622}
623
624/* Check the name is good for event/group */
625static bool check_event_name(const char *name)
626{
627 if (!isalpha(*name) && *name != '_')
628 return false;
629 while (*++name != '\0') {
630 if (!isalpha(*name) && !isdigit(*name) && *name != '_')
631 return false;
632 }
633 return true;
634}
635
636/* Parse probepoint definition. */
637static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
638{
639 struct perf_probe_point *pp = &pev->point;
640 char *ptr, *tmp;
641 char c, nc = 0;
642 /*
643 * <Syntax>
644 * perf probe [EVENT=]SRC[:LN|;PTN]
645 * perf probe [EVENT=]FUNC[@SRC][+OFFS|%return|:LN|;PAT]
646 *
647 * TODO:Group name support
648 */
649
650 ptr = strpbrk(arg, ";=@+%");
651 if (ptr && *ptr == '=') { /* Event name */
652 *ptr = '\0';
653 tmp = ptr + 1;
654 if (strchr(arg, ':')) {
655 semantic_error("Group name is not supported yet.\n");
656 return -ENOTSUP;
657 }
658 if (!check_event_name(arg)) {
659 semantic_error("%s is bad for event name -it must "
660 "follow C symbol-naming rule.\n", arg);
661 return -EINVAL;
662 }
663 pev->event = strdup(arg);
664 if (pev->event == NULL)
665 return -ENOMEM;
666 pev->group = NULL;
667 arg = tmp;
668 }
669
670 ptr = strpbrk(arg, ";:+@%");
671 if (ptr) {
672 nc = *ptr;
673 *ptr++ = '\0';
674 }
675
676 tmp = strdup(arg);
677 if (tmp == NULL)
678 return -ENOMEM;
679
680 /* Check arg is function or file and copy it */
681 if (strchr(tmp, '.')) /* File */
682 pp->file = tmp;
683 else /* Function */
684 pp->function = tmp;
685
686 /* Parse other options */
687 while (ptr) {
688 arg = ptr;
689 c = nc;
690 if (c == ';') { /* Lazy pattern must be the last part */
691 pp->lazy_line = strdup(arg);
692 if (pp->lazy_line == NULL)
693 return -ENOMEM;
694 break;
695 }
696 ptr = strpbrk(arg, ";:+@%");
697 if (ptr) {
698 nc = *ptr;
699 *ptr++ = '\0';
700 }
701 switch (c) {
702 case ':': /* Line number */
703 pp->line = strtoul(arg, &tmp, 0);
704 if (*tmp != '\0') {
705 semantic_error("There is non-digit char"
706 " in line number.\n");
707 return -EINVAL;
708 }
709 break;
710 case '+': /* Byte offset from a symbol */
711 pp->offset = strtoul(arg, &tmp, 0);
712 if (*tmp != '\0') {
713 semantic_error("There is non-digit character"
714 " in offset.\n");
715 return -EINVAL;
716 }
717 break;
718 case '@': /* File name */
719 if (pp->file) {
720 semantic_error("SRC@SRC is not allowed.\n");
721 return -EINVAL;
722 }
723 pp->file = strdup(arg);
724 if (pp->file == NULL)
725 return -ENOMEM;
726 break;
727 case '%': /* Probe places */
728 if (strcmp(arg, "return") == 0) {
729 pp->retprobe = 1;
730 } else { /* Others not supported yet */
731 semantic_error("%%%s is not supported.\n", arg);
732 return -ENOTSUP;
733 }
734 break;
735 default: /* Buggy case */
736 pr_err("This program has a bug at %s:%d.\n",
737 __FILE__, __LINE__);
738 return -ENOTSUP;
739 break;
740 }
741 }
742
743 /* Exclusion check */
744 if (pp->lazy_line && pp->line) {
745 semantic_error("Lazy pattern can't be used with"
746 " line number.\n");
747 return -EINVAL;
748 }
749
750 if (pp->lazy_line && pp->offset) {
751 semantic_error("Lazy pattern can't be used with offset.\n");
752 return -EINVAL;
753 }
754
755 if (pp->line && pp->offset) {
756 semantic_error("Offset can't be used with line number.\n");
757 return -EINVAL;
758 }
759
760 if (!pp->line && !pp->lazy_line && pp->file && !pp->function) {
761 semantic_error("File always requires line number or "
762 "lazy pattern.\n");
763 return -EINVAL;
764 }
765
766 if (pp->offset && !pp->function) {
767 semantic_error("Offset requires an entry function.\n");
768 return -EINVAL;
769 }
770
771 if (pp->retprobe && !pp->function) {
772 semantic_error("Return probe requires an entry function.\n");
773 return -EINVAL;
774 }
775
776 if ((pp->offset || pp->line || pp->lazy_line) && pp->retprobe) {
777 semantic_error("Offset/Line/Lazy pattern can't be used with "
778 "return probe.\n");
779 return -EINVAL;
780 }
781
782 pr_debug("symbol:%s file:%s line:%d offset:%lu return:%d lazy:%s\n",
783 pp->function, pp->file, pp->line, pp->offset, pp->retprobe,
784 pp->lazy_line);
785 return 0;
786}
787
788/* Parse perf-probe event argument */
789static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg)
790{
791 char *tmp, *goodname;
792 struct perf_probe_arg_field **fieldp;
793
794 pr_debug("parsing arg: %s into ", str);
795
796 tmp = strchr(str, '=');
797 if (tmp) {
798 arg->name = strndup(str, tmp - str);
799 if (arg->name == NULL)
800 return -ENOMEM;
801 pr_debug("name:%s ", arg->name);
802 str = tmp + 1;
803 }
804
805 tmp = strchr(str, ':');
806 if (tmp) { /* Type setting */
807 *tmp = '\0';
808 arg->type = strdup(tmp + 1);
809 if (arg->type == NULL)
810 return -ENOMEM;
811 pr_debug("type:%s ", arg->type);
812 }
813
814 tmp = strpbrk(str, "-.[");
815 if (!is_c_varname(str) || !tmp) {
816 /* A variable, register, symbol or special value */
817 arg->var = strdup(str);
818 if (arg->var == NULL)
819 return -ENOMEM;
820 pr_debug("%s\n", arg->var);
821 return 0;
822 }
823
824 /* Structure fields or array element */
825 arg->var = strndup(str, tmp - str);
826 if (arg->var == NULL)
827 return -ENOMEM;
828 goodname = arg->var;
829 pr_debug("%s, ", arg->var);
830 fieldp = &arg->field;
831
832 do {
833 *fieldp = zalloc(sizeof(struct perf_probe_arg_field));
834 if (*fieldp == NULL)
835 return -ENOMEM;
836 if (*tmp == '[') { /* Array */
837 str = tmp;
838 (*fieldp)->index = strtol(str + 1, &tmp, 0);
839 (*fieldp)->ref = true;
840 if (*tmp != ']' || tmp == str + 1) {
841 semantic_error("Array index must be a"
842 " number.\n");
843 return -EINVAL;
844 }
845 tmp++;
846 if (*tmp == '\0')
847 tmp = NULL;
848 } else { /* Structure */
849 if (*tmp == '.') {
850 str = tmp + 1;
851 (*fieldp)->ref = false;
852 } else if (tmp[1] == '>') {
853 str = tmp + 2;
854 (*fieldp)->ref = true;
855 } else {
856 semantic_error("Argument parse error: %s\n",
857 str);
858 return -EINVAL;
859 }
860 tmp = strpbrk(str, "-.[");
861 }
862 if (tmp) {
863 (*fieldp)->name = strndup(str, tmp - str);
864 if ((*fieldp)->name == NULL)
865 return -ENOMEM;
866 if (*str != '[')
867 goodname = (*fieldp)->name;
868 pr_debug("%s(%d), ", (*fieldp)->name, (*fieldp)->ref);
869 fieldp = &(*fieldp)->next;
870 }
871 } while (tmp);
872 (*fieldp)->name = strdup(str);
873 if ((*fieldp)->name == NULL)
874 return -ENOMEM;
875 if (*str != '[')
876 goodname = (*fieldp)->name;
877 pr_debug("%s(%d)\n", (*fieldp)->name, (*fieldp)->ref);
878
879 /* If no name is specified, set the last field name (not array index)*/
880 if (!arg->name) {
881 arg->name = strdup(goodname);
882 if (arg->name == NULL)
883 return -ENOMEM;
884 }
885 return 0;
886}
887
888/* Parse perf-probe event command */
889int parse_perf_probe_command(const char *cmd, struct perf_probe_event *pev)
890{
891 char **argv;
892 int argc, i, ret = 0;
893
894 argv = argv_split(cmd, &argc);
895 if (!argv) {
896 pr_debug("Failed to split arguments.\n");
897 return -ENOMEM;
898 }
899 if (argc - 1 > MAX_PROBE_ARGS) {
900 semantic_error("Too many probe arguments (%d).\n", argc - 1);
901 ret = -ERANGE;
902 goto out;
903 }
904 /* Parse probe point */
905 ret = parse_perf_probe_point(argv[0], pev);
906 if (ret < 0)
907 goto out;
908
909 /* Copy arguments and ensure return probe has no C argument */
910 pev->nargs = argc - 1;
911 pev->args = zalloc(sizeof(struct perf_probe_arg) * pev->nargs);
912 if (pev->args == NULL) {
913 ret = -ENOMEM;
914 goto out;
915 }
916 for (i = 0; i < pev->nargs && ret >= 0; i++) {
917 ret = parse_perf_probe_arg(argv[i + 1], &pev->args[i]);
918 if (ret >= 0 &&
919 is_c_varname(pev->args[i].var) && pev->point.retprobe) {
920 semantic_error("You can't specify local variable for"
921 " kretprobe.\n");
922 ret = -EINVAL;
923 }
924 }
925out:
926 argv_free(argv);
927
928 return ret;
929}
930
931/* Return true if this perf_probe_event requires debuginfo */
932bool perf_probe_event_need_dwarf(struct perf_probe_event *pev)
933{
934 int i;
935
936 if (pev->point.file || pev->point.line || pev->point.lazy_line)
937 return true;
938
939 for (i = 0; i < pev->nargs; i++)
940 if (is_c_varname(pev->args[i].var))
941 return true;
942
943 return false;
944}
945
946/* Parse probe_events event into struct probe_point */
947static int parse_probe_trace_command(const char *cmd,
948 struct probe_trace_event *tev)
949{
950 struct probe_trace_point *tp = &tev->point;
951 char pr;
952 char *p;
953 int ret, i, argc;
954 char **argv;
955
956 pr_debug("Parsing probe_events: %s\n", cmd);
957 argv = argv_split(cmd, &argc);
958 if (!argv) {
959 pr_debug("Failed to split arguments.\n");
960 return -ENOMEM;
961 }
962 if (argc < 2) {
963 semantic_error("Too few probe arguments.\n");
964 ret = -ERANGE;
965 goto out;
966 }
967
968 /* Scan event and group name. */
969 ret = sscanf(argv[0], "%c:%a[^/ \t]/%a[^ \t]",
970 &pr, (float *)(void *)&tev->group,
971 (float *)(void *)&tev->event);
972 if (ret != 3) {
973 semantic_error("Failed to parse event name: %s\n", argv[0]);
974 ret = -EINVAL;
975 goto out;
976 }
977 pr_debug("Group:%s Event:%s probe:%c\n", tev->group, tev->event, pr);
978
979 tp->retprobe = (pr == 'r');
980
981 /* Scan function name and offset */
982 ret = sscanf(argv[1], "%a[^+]+%lu", (float *)(void *)&tp->symbol,
983 &tp->offset);
984 if (ret == 1)
985 tp->offset = 0;
986
987 tev->nargs = argc - 2;
988 tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs);
989 if (tev->args == NULL) {
990 ret = -ENOMEM;
991 goto out;
992 }
993 for (i = 0; i < tev->nargs; i++) {
994 p = strchr(argv[i + 2], '=');
995 if (p) /* We don't need which register is assigned. */
996 *p++ = '\0';
997 else
998 p = argv[i + 2];
999 tev->args[i].name = strdup(argv[i + 2]);
1000 /* TODO: parse regs and offset */
1001 tev->args[i].value = strdup(p);
1002 if (tev->args[i].name == NULL || tev->args[i].value == NULL) {
1003 ret = -ENOMEM;
1004 goto out;
1005 }
1006 }
1007 ret = 0;
1008out:
1009 argv_free(argv);
1010 return ret;
1011}
1012
1013/* Compose only probe arg */
1014int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, size_t len)
1015{
1016 struct perf_probe_arg_field *field = pa->field;
1017 int ret;
1018 char *tmp = buf;
1019
1020 if (pa->name && pa->var)
1021 ret = e_snprintf(tmp, len, "%s=%s", pa->name, pa->var);
1022 else
1023 ret = e_snprintf(tmp, len, "%s", pa->name ? pa->name : pa->var);
1024 if (ret <= 0)
1025 goto error;
1026 tmp += ret;
1027 len -= ret;
1028
1029 while (field) {
1030 if (field->name[0] == '[')
1031 ret = e_snprintf(tmp, len, "%s", field->name);
1032 else
1033 ret = e_snprintf(tmp, len, "%s%s",
1034 field->ref ? "->" : ".", field->name);
1035 if (ret <= 0)
1036 goto error;
1037 tmp += ret;
1038 len -= ret;
1039 field = field->next;
1040 }
1041
1042 if (pa->type) {
1043 ret = e_snprintf(tmp, len, ":%s", pa->type);
1044 if (ret <= 0)
1045 goto error;
1046 tmp += ret;
1047 len -= ret;
1048 }
1049
1050 return tmp - buf;
1051error:
1052 pr_debug("Failed to synthesize perf probe argument: %s\n",
1053 strerror(-ret));
1054 return ret;
1055}
1056
1057/* Compose only probe point (not argument) */
1058static char *synthesize_perf_probe_point(struct perf_probe_point *pp)
1059{
1060 char *buf, *tmp;
1061 char offs[32] = "", line[32] = "", file[32] = "";
1062 int ret, len;
1063
1064 buf = zalloc(MAX_CMDLEN);
1065 if (buf == NULL) {
1066 ret = -ENOMEM;
1067 goto error;
1068 }
1069 if (pp->offset) {
1070 ret = e_snprintf(offs, 32, "+%lu", pp->offset);
1071 if (ret <= 0)
1072 goto error;
1073 }
1074 if (pp->line) {
1075 ret = e_snprintf(line, 32, ":%d", pp->line);
1076 if (ret <= 0)
1077 goto error;
1078 }
1079 if (pp->file) {
1080 tmp = pp->file;
1081 len = strlen(tmp);
1082 if (len > 30) {
1083 tmp = strchr(pp->file + len - 30, '/');
1084 tmp = tmp ? tmp + 1 : pp->file + len - 30;
1085 }
1086 ret = e_snprintf(file, 32, "@%s", tmp);
1087 if (ret <= 0)
1088 goto error;
1089 }
1090
1091 if (pp->function)
1092 ret = e_snprintf(buf, MAX_CMDLEN, "%s%s%s%s%s", pp->function,
1093 offs, pp->retprobe ? "%return" : "", line,
1094 file);
1095 else
1096 ret = e_snprintf(buf, MAX_CMDLEN, "%s%s", file, line);
1097 if (ret <= 0)
1098 goto error;
1099
1100 return buf;
1101error:
1102 pr_debug("Failed to synthesize perf probe point: %s\n",
1103 strerror(-ret));
1104 if (buf)
1105 free(buf);
1106 return NULL;
1107}
1108
1109#if 0
1110char *synthesize_perf_probe_command(struct perf_probe_event *pev)
1111{
1112 char *buf;
1113 int i, len, ret;
1114
1115 buf = synthesize_perf_probe_point(&pev->point);
1116 if (!buf)
1117 return NULL;
1118
1119 len = strlen(buf);
1120 for (i = 0; i < pev->nargs; i++) {
1121 ret = e_snprintf(&buf[len], MAX_CMDLEN - len, " %s",
1122 pev->args[i].name);
1123 if (ret <= 0) {
1124 free(buf);
1125 return NULL;
1126 }
1127 len += ret;
1128 }
1129
1130 return buf;
1131}
1132#endif
1133
1134static int __synthesize_probe_trace_arg_ref(struct probe_trace_arg_ref *ref,
1135 char **buf, size_t *buflen,
1136 int depth)
1137{
1138 int ret;
1139 if (ref->next) {
1140 depth = __synthesize_probe_trace_arg_ref(ref->next, buf,
1141 buflen, depth + 1);
1142 if (depth < 0)
1143 goto out;
1144 }
1145
1146 ret = e_snprintf(*buf, *buflen, "%+ld(", ref->offset);
1147 if (ret < 0)
1148 depth = ret;
1149 else {
1150 *buf += ret;
1151 *buflen -= ret;
1152 }
1153out:
1154 return depth;
1155
1156}
1157
1158static int synthesize_probe_trace_arg(struct probe_trace_arg *arg,
1159 char *buf, size_t buflen)
1160{
1161 struct probe_trace_arg_ref *ref = arg->ref;
1162 int ret, depth = 0;
1163 char *tmp = buf;
1164
1165 /* Argument name or separator */
1166 if (arg->name)
1167 ret = e_snprintf(buf, buflen, " %s=", arg->name);
1168 else
1169 ret = e_snprintf(buf, buflen, " ");
1170 if (ret < 0)
1171 return ret;
1172 buf += ret;
1173 buflen -= ret;
1174
1175 /* Special case: @XXX */
1176 if (arg->value[0] == '@' && arg->ref)
1177 ref = ref->next;
1178
1179 /* Dereferencing arguments */
1180 if (ref) {
1181 depth = __synthesize_probe_trace_arg_ref(ref, &buf,
1182 &buflen, 1);
1183 if (depth < 0)
1184 return depth;
1185 }
1186
1187 /* Print argument value */
1188 if (arg->value[0] == '@' && arg->ref)
1189 ret = e_snprintf(buf, buflen, "%s%+ld", arg->value,
1190 arg->ref->offset);
1191 else
1192 ret = e_snprintf(buf, buflen, "%s", arg->value);
1193 if (ret < 0)
1194 return ret;
1195 buf += ret;
1196 buflen -= ret;
1197
1198 /* Closing */
1199 while (depth--) {
1200 ret = e_snprintf(buf, buflen, ")");
1201 if (ret < 0)
1202 return ret;
1203 buf += ret;
1204 buflen -= ret;
1205 }
1206 /* Print argument type */
1207 if (arg->type) {
1208 ret = e_snprintf(buf, buflen, ":%s", arg->type);
1209 if (ret <= 0)
1210 return ret;
1211 buf += ret;
1212 }
1213
1214 return buf - tmp;
1215}
1216
1217char *synthesize_probe_trace_command(struct probe_trace_event *tev)
1218{
1219 struct probe_trace_point *tp = &tev->point;
1220 char *buf;
1221 int i, len, ret;
1222
1223 buf = zalloc(MAX_CMDLEN);
1224 if (buf == NULL)
1225 return NULL;
1226
1227 len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s+%lu",
1228 tp->retprobe ? 'r' : 'p',
1229 tev->group, tev->event,
1230 tp->symbol, tp->offset);
1231 if (len <= 0)
1232 goto error;
1233
1234 for (i = 0; i < tev->nargs; i++) {
1235 ret = synthesize_probe_trace_arg(&tev->args[i], buf + len,
1236 MAX_CMDLEN - len);
1237 if (ret <= 0)
1238 goto error;
1239 len += ret;
1240 }
1241
1242 return buf;
1243error:
1244 free(buf);
1245 return NULL;
1246}
1247
1248static int convert_to_perf_probe_event(struct probe_trace_event *tev,
1249 struct perf_probe_event *pev)
1250{
1251 char buf[64] = "";
1252 int i, ret;
1253
1254 /* Convert event/group name */
1255 pev->event = strdup(tev->event);
1256 pev->group = strdup(tev->group);
1257 if (pev->event == NULL || pev->group == NULL)
1258 return -ENOMEM;
1259
1260 /* Convert trace_point to probe_point */
1261 ret = kprobe_convert_to_perf_probe(&tev->point, &pev->point);
1262 if (ret < 0)
1263 return ret;
1264
1265 /* Convert trace_arg to probe_arg */
1266 pev->nargs = tev->nargs;
1267 pev->args = zalloc(sizeof(struct perf_probe_arg) * pev->nargs);
1268 if (pev->args == NULL)
1269 return -ENOMEM;
1270 for (i = 0; i < tev->nargs && ret >= 0; i++) {
1271 if (tev->args[i].name)
1272 pev->args[i].name = strdup(tev->args[i].name);
1273 else {
1274 ret = synthesize_probe_trace_arg(&tev->args[i],
1275 buf, 64);
1276 pev->args[i].name = strdup(buf);
1277 }
1278 if (pev->args[i].name == NULL && ret >= 0)
1279 ret = -ENOMEM;
1280 }
1281
1282 if (ret < 0)
1283 clear_perf_probe_event(pev);
1284
1285 return ret;
1286}
1287
1288void clear_perf_probe_event(struct perf_probe_event *pev)
1289{
1290 struct perf_probe_point *pp = &pev->point;
1291 struct perf_probe_arg_field *field, *next;
1292 int i;
1293
1294 if (pev->event)
1295 free(pev->event);
1296 if (pev->group)
1297 free(pev->group);
1298 if (pp->file)
1299 free(pp->file);
1300 if (pp->function)
1301 free(pp->function);
1302 if (pp->lazy_line)
1303 free(pp->lazy_line);
1304 for (i = 0; i < pev->nargs; i++) {
1305 if (pev->args[i].name)
1306 free(pev->args[i].name);
1307 if (pev->args[i].var)
1308 free(pev->args[i].var);
1309 if (pev->args[i].type)
1310 free(pev->args[i].type);
1311 field = pev->args[i].field;
1312 while (field) {
1313 next = field->next;
1314 if (field->name)
1315 free(field->name);
1316 free(field);
1317 field = next;
1318 }
1319 }
1320 if (pev->args)
1321 free(pev->args);
1322 memset(pev, 0, sizeof(*pev));
1323}
1324
1325static void clear_probe_trace_event(struct probe_trace_event *tev)
1326{
1327 struct probe_trace_arg_ref *ref, *next;
1328 int i;
1329
1330 if (tev->event)
1331 free(tev->event);
1332 if (tev->group)
1333 free(tev->group);
1334 if (tev->point.symbol)
1335 free(tev->point.symbol);
1336 for (i = 0; i < tev->nargs; i++) {
1337 if (tev->args[i].name)
1338 free(tev->args[i].name);
1339 if (tev->args[i].value)
1340 free(tev->args[i].value);
1341 if (tev->args[i].type)
1342 free(tev->args[i].type);
1343 ref = tev->args[i].ref;
1344 while (ref) {
1345 next = ref->next;
1346 free(ref);
1347 ref = next;
1348 }
1349 }
1350 if (tev->args)
1351 free(tev->args);
1352 memset(tev, 0, sizeof(*tev));
1353}
1354
1355static int open_kprobe_events(bool readwrite)
1356{
1357 char buf[PATH_MAX];
1358 const char *__debugfs;
1359 int ret;
1360
1361 __debugfs = debugfs_find_mountpoint();
1362 if (__debugfs == NULL) {
1363 pr_warning("Debugfs is not mounted.\n");
1364 return -ENOENT;
1365 }
1366
1367 ret = e_snprintf(buf, PATH_MAX, "%stracing/kprobe_events", __debugfs);
1368 if (ret >= 0) {
1369 pr_debug("Opening %s write=%d\n", buf, readwrite);
1370 if (readwrite && !probe_event_dry_run)
1371 ret = open(buf, O_RDWR, O_APPEND);
1372 else
1373 ret = open(buf, O_RDONLY, 0);
1374 }
1375
1376 if (ret < 0) {
1377 if (errno == ENOENT)
1378 pr_warning("kprobe_events file does not exist - please"
1379 " rebuild kernel with CONFIG_KPROBE_EVENT.\n");
1380 else
1381 pr_warning("Failed to open kprobe_events file: %s\n",
1382 strerror(errno));
1383 }
1384 return ret;
1385}
1386
1387/* Get raw string list of current kprobe_events */
1388static struct strlist *get_probe_trace_command_rawlist(int fd)
1389{
1390 int ret, idx;
1391 FILE *fp;
1392 char buf[MAX_CMDLEN];
1393 char *p;
1394 struct strlist *sl;
1395
1396 sl = strlist__new(true, NULL);
1397
1398 fp = fdopen(dup(fd), "r");
1399 while (!feof(fp)) {
1400 p = fgets(buf, MAX_CMDLEN, fp);
1401 if (!p)
1402 break;
1403
1404 idx = strlen(p) - 1;
1405 if (p[idx] == '\n')
1406 p[idx] = '\0';
1407 ret = strlist__add(sl, buf);
1408 if (ret < 0) {
1409 pr_debug("strlist__add failed: %s\n", strerror(-ret));
1410 strlist__delete(sl);
1411 return NULL;
1412 }
1413 }
1414 fclose(fp);
1415
1416 return sl;
1417}
1418
1419/* Show an event */
1420static int show_perf_probe_event(struct perf_probe_event *pev)
1421{
1422 int i, ret;
1423 char buf[128];
1424 char *place;
1425
1426 /* Synthesize only event probe point */
1427 place = synthesize_perf_probe_point(&pev->point);
1428 if (!place)
1429 return -EINVAL;
1430
1431 ret = e_snprintf(buf, 128, "%s:%s", pev->group, pev->event);
1432 if (ret < 0)
1433 return ret;
1434
1435 printf(" %-20s (on %s", buf, place);
1436
1437 if (pev->nargs > 0) {
1438 printf(" with");
1439 for (i = 0; i < pev->nargs; i++) {
1440 ret = synthesize_perf_probe_arg(&pev->args[i],
1441 buf, 128);
1442 if (ret < 0)
1443 break;
1444 printf(" %s", buf);
1445 }
1446 }
1447 printf(")\n");
1448 free(place);
1449 return ret;
1450}
1451
1452/* List up current perf-probe events */
1453int show_perf_probe_events(void)
1454{
1455 int fd, ret;
1456 struct probe_trace_event tev;
1457 struct perf_probe_event pev;
1458 struct strlist *rawlist;
1459 struct str_node *ent;
1460
1461 setup_pager();
1462 ret = init_vmlinux();
1463 if (ret < 0)
1464 return ret;
1465
1466 memset(&tev, 0, sizeof(tev));
1467 memset(&pev, 0, sizeof(pev));
1468
1469 fd = open_kprobe_events(false);
1470 if (fd < 0)
1471 return fd;
1472
1473 rawlist = get_probe_trace_command_rawlist(fd);
1474 close(fd);
1475 if (!rawlist)
1476 return -ENOENT;
1477
1478 strlist__for_each(ent, rawlist) {
1479 ret = parse_probe_trace_command(ent->s, &tev);
1480 if (ret >= 0) {
1481 ret = convert_to_perf_probe_event(&tev, &pev);
1482 if (ret >= 0)
1483 ret = show_perf_probe_event(&pev);
1484 }
1485 clear_perf_probe_event(&pev);
1486 clear_probe_trace_event(&tev);
1487 if (ret < 0)
1488 break;
1489 }
1490 strlist__delete(rawlist);
1491
1492 return ret;
1493}
1494
1495/* Get current perf-probe event names */
1496static struct strlist *get_probe_trace_event_names(int fd, bool include_group)
1497{
1498 char buf[128];
1499 struct strlist *sl, *rawlist;
1500 struct str_node *ent;
1501 struct probe_trace_event tev;
1502 int ret = 0;
1503
1504 memset(&tev, 0, sizeof(tev));
1505 rawlist = get_probe_trace_command_rawlist(fd);
1506 sl = strlist__new(true, NULL);
1507 strlist__for_each(ent, rawlist) {
1508 ret = parse_probe_trace_command(ent->s, &tev);
1509 if (ret < 0)
1510 break;
1511 if (include_group) {
1512 ret = e_snprintf(buf, 128, "%s:%s", tev.group,
1513 tev.event);
1514 if (ret >= 0)
1515 ret = strlist__add(sl, buf);
1516 } else
1517 ret = strlist__add(sl, tev.event);
1518 clear_probe_trace_event(&tev);
1519 if (ret < 0)
1520 break;
1521 }
1522 strlist__delete(rawlist);
1523
1524 if (ret < 0) {
1525 strlist__delete(sl);
1526 return NULL;
1527 }
1528 return sl;
1529}
1530
1531static int write_probe_trace_event(int fd, struct probe_trace_event *tev)
1532{
1533 int ret = 0;
1534 char *buf = synthesize_probe_trace_command(tev);
1535
1536 if (!buf) {
1537 pr_debug("Failed to synthesize probe trace event.\n");
1538 return -EINVAL;
1539 }
1540
1541 pr_debug("Writing event: %s\n", buf);
1542 if (!probe_event_dry_run) {
1543 ret = write(fd, buf, strlen(buf));
1544 if (ret <= 0)
1545 pr_warning("Failed to write event: %s\n",
1546 strerror(errno));
1547 }
1548 free(buf);
1549 return ret;
1550}
1551
1552static int get_new_event_name(char *buf, size_t len, const char *base,
1553 struct strlist *namelist, bool allow_suffix)
1554{
1555 int i, ret;
1556
1557 /* Try no suffix */
1558 ret = e_snprintf(buf, len, "%s", base);
1559 if (ret < 0) {
1560 pr_debug("snprintf() failed: %s\n", strerror(-ret));
1561 return ret;
1562 }
1563 if (!strlist__has_entry(namelist, buf))
1564 return 0;
1565
1566 if (!allow_suffix) {
1567 pr_warning("Error: event \"%s\" already exists. "
1568 "(Use -f to force duplicates.)\n", base);
1569 return -EEXIST;
1570 }
1571
1572 /* Try to add suffix */
1573 for (i = 1; i < MAX_EVENT_INDEX; i++) {
1574 ret = e_snprintf(buf, len, "%s_%d", base, i);
1575 if (ret < 0) {
1576 pr_debug("snprintf() failed: %s\n", strerror(-ret));
1577 return ret;
1578 }
1579 if (!strlist__has_entry(namelist, buf))
1580 break;
1581 }
1582 if (i == MAX_EVENT_INDEX) {
1583 pr_warning("Too many events are on the same function.\n");
1584 ret = -ERANGE;
1585 }
1586
1587 return ret;
1588}
1589
1590static int __add_probe_trace_events(struct perf_probe_event *pev,
1591 struct probe_trace_event *tevs,
1592 int ntevs, bool allow_suffix)
1593{
1594 int i, fd, ret;
1595 struct probe_trace_event *tev = NULL;
1596 char buf[64];
1597 const char *event, *group;
1598 struct strlist *namelist;
1599
1600 fd = open_kprobe_events(true);
1601 if (fd < 0)
1602 return fd;
1603 /* Get current event names */
1604 namelist = get_probe_trace_event_names(fd, false);
1605 if (!namelist) {
1606 pr_debug("Failed to get current event list.\n");
1607 return -EIO;
1608 }
1609
1610 ret = 0;
1611 printf("Add new event%s\n", (ntevs > 1) ? "s:" : ":");
1612 for (i = 0; i < ntevs; i++) {
1613 tev = &tevs[i];
1614 if (pev->event)
1615 event = pev->event;
1616 else
1617 if (pev->point.function)
1618 event = pev->point.function;
1619 else
1620 event = tev->point.symbol;
1621 if (pev->group)
1622 group = pev->group;
1623 else
1624 group = PERFPROBE_GROUP;
1625
1626 /* Get an unused new event name */
1627 ret = get_new_event_name(buf, 64, event,
1628 namelist, allow_suffix);
1629 if (ret < 0)
1630 break;
1631 event = buf;
1632
1633 tev->event = strdup(event);
1634 tev->group = strdup(group);
1635 if (tev->event == NULL || tev->group == NULL) {
1636 ret = -ENOMEM;
1637 break;
1638 }
1639 ret = write_probe_trace_event(fd, tev);
1640 if (ret < 0)
1641 break;
1642 /* Add added event name to namelist */
1643 strlist__add(namelist, event);
1644
1645 /* Trick here - save current event/group */
1646 event = pev->event;
1647 group = pev->group;
1648 pev->event = tev->event;
1649 pev->group = tev->group;
1650 show_perf_probe_event(pev);
1651 /* Trick here - restore current event/group */
1652 pev->event = (char *)event;
1653 pev->group = (char *)group;
1654
1655 /*
1656 * Probes after the first probe which comes from same
1657 * user input are always allowed to add suffix, because
1658 * there might be several addresses corresponding to
1659 * one code line.
1660 */
1661 allow_suffix = true;
1662 }
1663
1664 if (ret >= 0) {
1665 /* Show how to use the event. */
1666 printf("\nYou can now use it on all perf tools, such as:\n\n");
1667 printf("\tperf record -e %s:%s -aR sleep 1\n\n", tev->group,
1668 tev->event);
1669 }
1670
1671 strlist__delete(namelist);
1672 close(fd);
1673 return ret;
1674}
1675
1676static int convert_to_probe_trace_events(struct perf_probe_event *pev,
1677 struct probe_trace_event **tevs,
1678 int max_tevs, const char *module)
1679{
1680 struct symbol *sym;
1681 int ret = 0, i;
1682 struct probe_trace_event *tev;
1683
1684 /* Convert perf_probe_event with debuginfo */
1685 ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, module);
1686 if (ret != 0)
1687 return ret;
1688
1689 /* Allocate trace event buffer */
1690 tev = *tevs = zalloc(sizeof(struct probe_trace_event));
1691 if (tev == NULL)
1692 return -ENOMEM;
1693
1694 /* Copy parameters */
1695 tev->point.symbol = strdup(pev->point.function);
1696 if (tev->point.symbol == NULL) {
1697 ret = -ENOMEM;
1698 goto error;
1699 }
1700 tev->point.offset = pev->point.offset;
1701 tev->point.retprobe = pev->point.retprobe;
1702 tev->nargs = pev->nargs;
1703 if (tev->nargs) {
1704 tev->args = zalloc(sizeof(struct probe_trace_arg)
1705 * tev->nargs);
1706 if (tev->args == NULL) {
1707 ret = -ENOMEM;
1708 goto error;
1709 }
1710 for (i = 0; i < tev->nargs; i++) {
1711 if (pev->args[i].name) {
1712 tev->args[i].name = strdup(pev->args[i].name);
1713 if (tev->args[i].name == NULL) {
1714 ret = -ENOMEM;
1715 goto error;
1716 }
1717 }
1718 tev->args[i].value = strdup(pev->args[i].var);
1719 if (tev->args[i].value == NULL) {
1720 ret = -ENOMEM;
1721 goto error;
1722 }
1723 if (pev->args[i].type) {
1724 tev->args[i].type = strdup(pev->args[i].type);
1725 if (tev->args[i].type == NULL) {
1726 ret = -ENOMEM;
1727 goto error;
1728 }
1729 }
1730 }
1731 }
1732
1733 /* Currently just checking function name from symbol map */
1734 sym = __find_kernel_function_by_name(tev->point.symbol, NULL);
1735 if (!sym) {
1736 pr_warning("Kernel symbol \'%s\' not found.\n",
1737 tev->point.symbol);
1738 ret = -ENOENT;
1739 goto error;
1740 }
1741
1742 return 1;
1743error:
1744 clear_probe_trace_event(tev);
1745 free(tev);
1746 *tevs = NULL;
1747 return ret;
1748}
1749
1750struct __event_package {
1751 struct perf_probe_event *pev;
1752 struct probe_trace_event *tevs;
1753 int ntevs;
1754};
1755
1756int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
1757 int max_tevs, const char *module, bool force_add)
1758{
1759 int i, j, ret;
1760 struct __event_package *pkgs;
1761
1762 pkgs = zalloc(sizeof(struct __event_package) * npevs);
1763 if (pkgs == NULL)
1764 return -ENOMEM;
1765
1766 /* Init vmlinux path */
1767 ret = init_vmlinux();
1768 if (ret < 0) {
1769 free(pkgs);
1770 return ret;
1771 }
1772
1773 /* Loop 1: convert all events */
1774 for (i = 0; i < npevs; i++) {
1775 pkgs[i].pev = &pevs[i];
1776 /* Convert with or without debuginfo */
1777 ret = convert_to_probe_trace_events(pkgs[i].pev,
1778 &pkgs[i].tevs,
1779 max_tevs,
1780 module);
1781 if (ret < 0)
1782 goto end;
1783 pkgs[i].ntevs = ret;
1784 }
1785
1786 /* Loop 2: add all events */
1787 for (i = 0; i < npevs && ret >= 0; i++)
1788 ret = __add_probe_trace_events(pkgs[i].pev, pkgs[i].tevs,
1789 pkgs[i].ntevs, force_add);
1790end:
1791 /* Loop 3: cleanup and free trace events */
1792 for (i = 0; i < npevs; i++) {
1793 for (j = 0; j < pkgs[i].ntevs; j++)
1794 clear_probe_trace_event(&pkgs[i].tevs[j]);
1795 free(pkgs[i].tevs);
1796 }
1797 free(pkgs);
1798
1799 return ret;
1800}
1801
1802static int __del_trace_probe_event(int fd, struct str_node *ent)
1803{
1804 char *p;
1805 char buf[128];
1806 int ret;
1807
1808 /* Convert from perf-probe event to trace-probe event */
1809 ret = e_snprintf(buf, 128, "-:%s", ent->s);
1810 if (ret < 0)
1811 goto error;
1812
1813 p = strchr(buf + 2, ':');
1814 if (!p) {
1815 pr_debug("Internal error: %s should have ':' but not.\n",
1816 ent->s);
1817 ret = -ENOTSUP;
1818 goto error;
1819 }
1820 *p = '/';
1821
1822 pr_debug("Writing event: %s\n", buf);
1823 ret = write(fd, buf, strlen(buf));
1824 if (ret < 0)
1825 goto error;
1826
1827 printf("Remove event: %s\n", ent->s);
1828 return 0;
1829error:
1830 pr_warning("Failed to delete event: %s\n", strerror(-ret));
1831 return ret;
1832}
1833
1834static int del_trace_probe_event(int fd, const char *group,
1835 const char *event, struct strlist *namelist)
1836{
1837 char buf[128];
1838 struct str_node *ent, *n;
1839 int found = 0, ret = 0;
1840
1841 ret = e_snprintf(buf, 128, "%s:%s", group, event);
1842 if (ret < 0) {
1843 pr_err("Failed to copy event.\n");
1844 return ret;
1845 }
1846
1847 if (strpbrk(buf, "*?")) { /* Glob-exp */
1848 strlist__for_each_safe(ent, n, namelist)
1849 if (strglobmatch(ent->s, buf)) {
1850 found++;
1851 ret = __del_trace_probe_event(fd, ent);
1852 if (ret < 0)
1853 break;
1854 strlist__remove(namelist, ent);
1855 }
1856 } else {
1857 ent = strlist__find(namelist, buf);
1858 if (ent) {
1859 found++;
1860 ret = __del_trace_probe_event(fd, ent);
1861 if (ret >= 0)
1862 strlist__remove(namelist, ent);
1863 }
1864 }
1865 if (found == 0 && ret >= 0)
1866 pr_info("Info: Event \"%s\" does not exist.\n", buf);
1867
1868 return ret;
1869}
1870
1871int del_perf_probe_events(struct strlist *dellist)
1872{
1873 int fd, ret = 0;
1874 const char *group, *event;
1875 char *p, *str;
1876 struct str_node *ent;
1877 struct strlist *namelist;
1878
1879 fd = open_kprobe_events(true);
1880 if (fd < 0)
1881 return fd;
1882
1883 /* Get current event names */
1884 namelist = get_probe_trace_event_names(fd, true);
1885 if (namelist == NULL)
1886 return -EINVAL;
1887
1888 strlist__for_each(ent, dellist) {
1889 str = strdup(ent->s);
1890 if (str == NULL) {
1891 ret = -ENOMEM;
1892 break;
1893 }
1894 pr_debug("Parsing: %s\n", str);
1895 p = strchr(str, ':');
1896 if (p) {
1897 group = str;
1898 *p = '\0';
1899 event = p + 1;
1900 } else {
1901 group = "*";
1902 event = str;
1903 }
1904 pr_debug("Group: %s, Event: %s\n", group, event);
1905 ret = del_trace_probe_event(fd, group, event, namelist);
1906 free(str);
1907 if (ret < 0)
1908 break;
1909 }
1910 strlist__delete(namelist);
1911 close(fd);
1912
1913 return ret;
1914}
1915
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
new file mode 100644
index 00000000000..5accbedfea3
--- /dev/null
+++ b/tools/perf/util/probe-event.h
@@ -0,0 +1,135 @@
1#ifndef _PROBE_EVENT_H
2#define _PROBE_EVENT_H
3
4#include <stdbool.h>
5#include "strlist.h"
6
7extern bool probe_event_dry_run;
8
9/* kprobe-tracer tracing point */
10struct probe_trace_point {
11 char *symbol; /* Base symbol */
12 unsigned long offset; /* Offset from symbol */
13 bool retprobe; /* Return probe flag */
14};
15
16/* probe-tracer tracing argument referencing offset */
17struct probe_trace_arg_ref {
18 struct probe_trace_arg_ref *next; /* Next reference */
19 long offset; /* Offset value */
20};
21
22/* kprobe-tracer tracing argument */
23struct probe_trace_arg {
24 char *name; /* Argument name */
25 char *value; /* Base value */
26 char *type; /* Type name */
27 struct probe_trace_arg_ref *ref; /* Referencing offset */
28};
29
30/* kprobe-tracer tracing event (point + arg) */
31struct probe_trace_event {
32 char *event; /* Event name */
33 char *group; /* Group name */
34 struct probe_trace_point point; /* Trace point */
35 int nargs; /* Number of args */
36 struct probe_trace_arg *args; /* Arguments */
37};
38
39/* Perf probe probing point */
40struct perf_probe_point {
41 char *file; /* File path */
42 char *function; /* Function name */
43 int line; /* Line number */
44 bool retprobe; /* Return probe flag */
45 char *lazy_line; /* Lazy matching pattern */
46 unsigned long offset; /* Offset from function entry */
47};
48
49/* Perf probe probing argument field chain */
50struct perf_probe_arg_field {
51 struct perf_probe_arg_field *next; /* Next field */
52 char *name; /* Name of the field */
53 long index; /* Array index number */
54 bool ref; /* Referencing flag */
55};
56
57/* Perf probe probing argument */
58struct perf_probe_arg {
59 char *name; /* Argument name */
60 char *var; /* Variable name */
61 char *type; /* Type name */
62 struct perf_probe_arg_field *field; /* Structure fields */
63};
64
65/* Perf probe probing event (point + arg) */
66struct perf_probe_event {
67 char *event; /* Event name */
68 char *group; /* Group name */
69 struct perf_probe_point point; /* Probe point */
70 int nargs; /* Number of arguments */
71 struct perf_probe_arg *args; /* Arguments */
72};
73
74
75/* Line number container */
76struct line_node {
77 struct list_head list;
78 int line;
79};
80
81/* Line range */
82struct line_range {
83 char *file; /* File name */
84 char *function; /* Function name */
85 int start; /* Start line number */
86 int end; /* End line number */
87 int offset; /* Start line offset */
88 char *path; /* Real path name */
89 char *comp_dir; /* Compile directory */
90 struct list_head line_list; /* Visible lines */
91};
92
93/* List of variables */
94struct variable_list {
95 struct probe_trace_point point; /* Actual probepoint */
96 struct strlist *vars; /* Available variables */
97};
98
99/* Command string to events */
100extern int parse_perf_probe_command(const char *cmd,
101 struct perf_probe_event *pev);
102
103/* Events to command string */
104extern char *synthesize_perf_probe_command(struct perf_probe_event *pev);
105extern char *synthesize_probe_trace_command(struct probe_trace_event *tev);
106extern int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf,
107 size_t len);
108
109/* Check the perf_probe_event needs debuginfo */
110extern bool perf_probe_event_need_dwarf(struct perf_probe_event *pev);
111
112/* Release event contents */
113extern void clear_perf_probe_event(struct perf_probe_event *pev);
114
115/* Command string to line-range */
116extern int parse_line_range_desc(const char *cmd, struct line_range *lr);
117
118/* Internal use: Return kernel/module path */
119extern const char *kernel_get_module_path(const char *module);
120
121extern int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
122 int max_probe_points, const char *module,
123 bool force_add);
124extern int del_perf_probe_events(struct strlist *dellist);
125extern int show_perf_probe_events(void);
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);
130
131
132/* Maximum index number of event-name postfix */
133#define MAX_EVENT_INDEX 1024
134
135#endif /*_PROBE_EVENT_H */
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
new file mode 100644
index 00000000000..ab83b6ac5d6
--- /dev/null
+++ b/tools/perf/util/probe-finder.c
@@ -0,0 +1,1861 @@
1/*
2 * probe-finder.c : C expression to kprobe event converter
3 *
4 * Written by Masami Hiramatsu <mhiramat@redhat.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 *
20 */
21
22#include <sys/utsname.h>
23#include <sys/types.h>
24#include <sys/stat.h>
25#include <fcntl.h>
26#include <errno.h>
27#include <stdio.h>
28#include <unistd.h>
29#include <getopt.h>
30#include <stdlib.h>
31#include <string.h>
32#include <stdarg.h>
33#include <ctype.h>
34#include <dwarf-regs.h>
35
36#include "event.h"
37#include "debug.h"
38#include "util.h"
39#include "symbol.h"
40#include "probe-finder.h"
41
42/* Kprobe tracer basic type is up to u64 */
43#define MAX_BASIC_TYPE_BITS 64
44
45/*
46 * Compare the tail of two strings.
47 * Return 0 if whole of either string is same as another's tail part.
48 */
49static int strtailcmp(const char *s1, const char *s2)
50{
51 int i1 = strlen(s1);
52 int i2 = strlen(s2);
53 while (--i1 >= 0 && --i2 >= 0) {
54 if (s1[i1] != s2[i2])
55 return s1[i1] - s2[i2];
56 }
57 return 0;
58}
59
60/* Line number list operations */
61
62/* Add a line to line number list */
63static int line_list__add_line(struct list_head *head, int line)
64{
65 struct line_node *ln;
66 struct list_head *p;
67
68 /* Reverse search, because new line will be the last one */
69 list_for_each_entry_reverse(ln, head, list) {
70 if (ln->line < line) {
71 p = &ln->list;
72 goto found;
73 } else if (ln->line == line) /* Already exist */
74 return 1;
75 }
76 /* List is empty, or the smallest entry */
77 p = head;
78found:
79 pr_debug("line list: add a line %u\n", line);
80 ln = zalloc(sizeof(struct line_node));
81 if (ln == NULL)
82 return -ENOMEM;
83 ln->line = line;
84 INIT_LIST_HEAD(&ln->list);
85 list_add(&ln->list, p);
86 return 0;
87}
88
89/* Check if the line in line number list */
90static int line_list__has_line(struct list_head *head, int line)
91{
92 struct line_node *ln;
93
94 /* Reverse search, because new line will be the last one */
95 list_for_each_entry(ln, head, list)
96 if (ln->line == line)
97 return 1;
98
99 return 0;
100}
101
102/* Init line number list */
103static void line_list__init(struct list_head *head)
104{
105 INIT_LIST_HEAD(head);
106}
107
108/* Free line number list */
109static void line_list__free(struct list_head *head)
110{
111 struct line_node *ln;
112 while (!list_empty(head)) {
113 ln = list_first_entry(head, struct line_node, list);
114 list_del(&ln->list);
115 free(ln);
116 }
117}
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
239/* Dwarf wrappers */
240
241/* Find the realpath of the target file. */
242static const char *cu_find_realpath(Dwarf_Die *cu_die, const char *fname)
243{
244 Dwarf_Files *files;
245 size_t nfiles, i;
246 const char *src = NULL;
247 int ret;
248
249 if (!fname)
250 return NULL;
251
252 ret = dwarf_getsrcfiles(cu_die, &files, &nfiles);
253 if (ret != 0)
254 return NULL;
255
256 for (i = 0; i < nfiles; i++) {
257 src = dwarf_filesrc(files, i, NULL, NULL);
258 if (strtailcmp(src, fname) == 0)
259 break;
260 }
261 if (i == nfiles)
262 return NULL;
263 return src;
264}
265
266/* Get DW_AT_comp_dir (should be NULL with older gcc) */
267static const char *cu_get_comp_dir(Dwarf_Die *cu_die)
268{
269 Dwarf_Attribute attr;
270 if (dwarf_attr(cu_die, DW_AT_comp_dir, &attr) == NULL)
271 return NULL;
272 return dwarf_formstring(&attr);
273}
274
275/* Compare diename and tname */
276static bool die_compare_name(Dwarf_Die *dw_die, const char *tname)
277{
278 const char *name;
279 name = dwarf_diename(dw_die);
280 return name ? (strcmp(tname, name) == 0) : false;
281}
282
283/* Get type die */
284static Dwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
285{
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{
298 int tag;
299
300 do {
301 vr_die = die_get_type(vr_die, die_mem);
302 if (!vr_die)
303 break;
304 tag = dwarf_tag(vr_die);
305 } while (tag == DW_TAG_const_type ||
306 tag == DW_TAG_restrict_type ||
307 tag == DW_TAG_volatile_type ||
308 tag == DW_TAG_shared_type);
309
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;
321}
322
323static bool die_is_signed_type(Dwarf_Die *tp_die)
324{
325 Dwarf_Attribute attr;
326 Dwarf_Word ret;
327
328 if (dwarf_attr(tp_die, DW_AT_encoding, &attr) == NULL ||
329 dwarf_formudata(&attr, &ret) != 0)
330 return false;
331
332 return (ret == DW_ATE_signed_char || ret == DW_ATE_signed ||
333 ret == DW_ATE_signed_fixed);
334}
335
336static int die_get_byte_size(Dwarf_Die *tp_die)
337{
338 Dwarf_Attribute attr;
339 Dwarf_Word ret;
340
341 if (dwarf_attr(tp_die, DW_AT_byte_size, &attr) == NULL ||
342 dwarf_formudata(&attr, &ret) != 0)
343 return 0;
344
345 return (int)ret;
346}
347
348/* Get data_member_location offset */
349static int die_get_data_member_location(Dwarf_Die *mb_die, Dwarf_Word *offs)
350{
351 Dwarf_Attribute attr;
352 Dwarf_Op *expr;
353 size_t nexpr;
354 int ret;
355
356 if (dwarf_attr(mb_die, DW_AT_data_member_location, &attr) == NULL)
357 return -ENOENT;
358
359 if (dwarf_formudata(&attr, offs) != 0) {
360 /* DW_AT_data_member_location should be DW_OP_plus_uconst */
361 ret = dwarf_getlocation(&attr, &expr, &nexpr);
362 if (ret < 0 || nexpr == 0)
363 return -ENOENT;
364
365 if (expr[0].atom != DW_OP_plus_uconst || nexpr != 1) {
366 pr_debug("Unable to get offset:Unexpected OP %x (%zd)\n",
367 expr[0].atom, nexpr);
368 return -ENOTSUP;
369 }
370 *offs = (Dwarf_Word)expr[0].number;
371 }
372 return 0;
373}
374
375/* Return values for die_find callbacks */
376enum {
377 DIE_FIND_CB_FOUND = 0, /* End of Search */
378 DIE_FIND_CB_CHILD = 1, /* Search only children */
379 DIE_FIND_CB_SIBLING = 2, /* Search only siblings */
380 DIE_FIND_CB_CONTINUE = 3, /* Search children and siblings */
381};
382
383/* Search a child die */
384static Dwarf_Die *die_find_child(Dwarf_Die *rt_die,
385 int (*callback)(Dwarf_Die *, void *),
386 void *data, Dwarf_Die *die_mem)
387{
388 Dwarf_Die child_die;
389 int ret;
390
391 ret = dwarf_child(rt_die, die_mem);
392 if (ret != 0)
393 return NULL;
394
395 do {
396 ret = callback(die_mem, data);
397 if (ret == DIE_FIND_CB_FOUND)
398 return die_mem;
399
400 if ((ret & DIE_FIND_CB_CHILD) &&
401 die_find_child(die_mem, callback, data, &child_die)) {
402 memcpy(die_mem, &child_die, sizeof(Dwarf_Die));
403 return die_mem;
404 }
405 } while ((ret & DIE_FIND_CB_SIBLING) &&
406 dwarf_siblingof(die_mem, die_mem) == 0);
407
408 return NULL;
409}
410
411struct __addr_die_search_param {
412 Dwarf_Addr addr;
413 Dwarf_Die *die_mem;
414};
415
416static int __die_search_func_cb(Dwarf_Die *fn_die, void *data)
417{
418 struct __addr_die_search_param *ad = data;
419
420 if (dwarf_tag(fn_die) == DW_TAG_subprogram &&
421 dwarf_haspc(fn_die, ad->addr)) {
422 memcpy(ad->die_mem, fn_die, sizeof(Dwarf_Die));
423 return DWARF_CB_ABORT;
424 }
425 return DWARF_CB_OK;
426}
427
428/* Search a real subprogram including this line, */
429static Dwarf_Die *die_find_real_subprogram(Dwarf_Die *cu_die, Dwarf_Addr addr,
430 Dwarf_Die *die_mem)
431{
432 struct __addr_die_search_param ad;
433 ad.addr = addr;
434 ad.die_mem = die_mem;
435 /* dwarf_getscopes can't find subprogram. */
436 if (!dwarf_getfuncs(cu_die, __die_search_func_cb, &ad, 0))
437 return NULL;
438 else
439 return die_mem;
440}
441
442/* die_find callback for inline function search */
443static int __die_find_inline_cb(Dwarf_Die *die_mem, void *data)
444{
445 Dwarf_Addr *addr = data;
446
447 if (dwarf_tag(die_mem) == DW_TAG_inlined_subroutine &&
448 dwarf_haspc(die_mem, *addr))
449 return DIE_FIND_CB_FOUND;
450
451 return DIE_FIND_CB_CONTINUE;
452}
453
454/* Similar to dwarf_getfuncs, but returns inlined_subroutine if exists. */
455static Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr,
456 Dwarf_Die *die_mem)
457{
458 return die_find_child(sp_die, __die_find_inline_cb, &addr, die_mem);
459}
460
461struct __find_variable_param {
462 const char *name;
463 Dwarf_Addr addr;
464};
465
466static int __die_find_variable_cb(Dwarf_Die *die_mem, void *data)
467{
468 struct __find_variable_param *fvp = data;
469 int tag;
470
471 tag = dwarf_tag(die_mem);
472 if ((tag == DW_TAG_formal_parameter ||
473 tag == DW_TAG_variable) &&
474 die_compare_name(die_mem, fvp->name))
475 return DIE_FIND_CB_FOUND;
476
477 if (dwarf_haspc(die_mem, fvp->addr))
478 return DIE_FIND_CB_CONTINUE;
479 else
480 return DIE_FIND_CB_SIBLING;
481}
482
483/* Find a variable called 'name' at given address */
484static Dwarf_Die *die_find_variable_at(Dwarf_Die *sp_die, const char *name,
485 Dwarf_Addr addr, Dwarf_Die *die_mem)
486{
487 struct __find_variable_param fvp = { .name = name, .addr = addr};
488
489 return die_find_child(sp_die, __die_find_variable_cb, (void *)&fvp,
490 die_mem);
491}
492
493static int __die_find_member_cb(Dwarf_Die *die_mem, void *data)
494{
495 const char *name = data;
496
497 if ((dwarf_tag(die_mem) == DW_TAG_member) &&
498 die_compare_name(die_mem, name))
499 return DIE_FIND_CB_FOUND;
500
501 return DIE_FIND_CB_SIBLING;
502}
503
504/* Find a member called 'name' */
505static Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name,
506 Dwarf_Die *die_mem)
507{
508 return die_find_child(st_die, __die_find_member_cb, (void *)name,
509 die_mem);
510}
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
566/*
567 * Probe finder related functions
568 */
569
570static struct probe_trace_arg_ref *alloc_trace_arg_ref(long offs)
571{
572 struct probe_trace_arg_ref *ref;
573 ref = zalloc(sizeof(struct probe_trace_arg_ref));
574 if (ref != NULL)
575 ref->offset = offs;
576 return ref;
577}
578
579/*
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)
586{
587 Dwarf_Attribute attr;
588 Dwarf_Op *op;
589 size_t nops;
590 unsigned int regn;
591 Dwarf_Word offs = 0;
592 bool ref = false;
593 const char *regs;
594 int ret;
595
596 if (dwarf_attr(vr_die, DW_AT_external, &attr) != NULL)
597 goto static_var;
598
599 /* TODO: handle more than 1 exprs */
600 if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL ||
601 dwarf_getlocation_addr(&attr, addr, &op, &nops, 1) <= 0 ||
602 nops == 0) {
603 /* TODO: Support const_value */
604 return -ENOENT;
605 }
606
607 if (op->atom == DW_OP_addr) {
608static_var:
609 if (!tvar)
610 return 0;
611 /* Static variables on memory (not stack), make @varname */
612 ret = strlen(dwarf_diename(vr_die));
613 tvar->value = zalloc(ret + 2);
614 if (tvar->value == NULL)
615 return -ENOMEM;
616 snprintf(tvar->value, ret + 2, "@%s", dwarf_diename(vr_die));
617 tvar->ref = alloc_trace_arg_ref((long)offs);
618 if (tvar->ref == NULL)
619 return -ENOMEM;
620 return 0;
621 }
622
623 /* If this is based on frame buffer, set the offset */
624 if (op->atom == DW_OP_fbreg) {
625 if (fb_ops == NULL)
626 return -ENOTSUP;
627 ref = true;
628 offs = op->number;
629 op = &fb_ops[0];
630 }
631
632 if (op->atom >= DW_OP_breg0 && op->atom <= DW_OP_breg31) {
633 regn = op->atom - DW_OP_breg0;
634 offs += op->number;
635 ref = true;
636 } else if (op->atom >= DW_OP_reg0 && op->atom <= DW_OP_reg31) {
637 regn = op->atom - DW_OP_reg0;
638 } else if (op->atom == DW_OP_bregx) {
639 regn = op->number;
640 offs += op->number2;
641 ref = true;
642 } else if (op->atom == DW_OP_regx) {
643 regn = op->number;
644 } else {
645 pr_debug("DW_OP %x is not supported.\n", op->atom);
646 return -ENOTSUP;
647 }
648
649 if (!tvar)
650 return 0;
651
652 regs = get_arch_regstr(regn);
653 if (!regs) {
654 /* This should be a bug in DWARF or this tool */
655 pr_warning("Mapping for the register number %u "
656 "missing on this architecture.\n", regn);
657 return -ERANGE;
658 }
659
660 tvar->value = strdup(regs);
661 if (tvar->value == NULL)
662 return -ENOMEM;
663
664 if (ref) {
665 tvar->ref = alloc_trace_arg_ref((long)offs);
666 if (tvar->ref == NULL)
667 return -ENOMEM;
668 }
669 return 0;
670}
671
672static int convert_variable_type(Dwarf_Die *vr_die,
673 struct probe_trace_arg *tvar,
674 const char *cast)
675{
676 struct probe_trace_arg_ref **ref_ptr = &tvar->ref;
677 Dwarf_Die type;
678 char buf[16];
679 int ret;
680
681 /* TODO: check all types */
682 if (cast && strcmp(cast, "string") != 0) {
683 /* Non string type is OK */
684 tvar->type = strdup(cast);
685 return (tvar->type == NULL) ? -ENOMEM : 0;
686 }
687
688 if (die_get_real_type(vr_die, &type) == NULL) {
689 pr_warning("Failed to get a type information of %s.\n",
690 dwarf_diename(vr_die));
691 return -ENOENT;
692 }
693
694 pr_debug("%s type is %s.\n",
695 dwarf_diename(vr_die), dwarf_diename(&type));
696
697 if (cast && strcmp(cast, "string") == 0) { /* String type */
698 ret = dwarf_tag(&type);
699 if (ret != DW_TAG_pointer_type &&
700 ret != DW_TAG_array_type) {
701 pr_warning("Failed to cast into string: "
702 "%s(%s) is not a pointer nor array.\n",
703 dwarf_diename(vr_die), dwarf_diename(&type));
704 return -EINVAL;
705 }
706 if (ret == DW_TAG_pointer_type) {
707 if (die_get_real_type(&type, &type) == NULL) {
708 pr_warning("Failed to get a type"
709 " information.\n");
710 return -ENOENT;
711 }
712 while (*ref_ptr)
713 ref_ptr = &(*ref_ptr)->next;
714 /* Add new reference with offset +0 */
715 *ref_ptr = zalloc(sizeof(struct probe_trace_arg_ref));
716 if (*ref_ptr == NULL) {
717 pr_warning("Out of memory error\n");
718 return -ENOMEM;
719 }
720 }
721 if (!die_compare_name(&type, "char") &&
722 !die_compare_name(&type, "unsigned char")) {
723 pr_warning("Failed to cast into string: "
724 "%s is not (unsigned) char *.\n",
725 dwarf_diename(vr_die));
726 return -EINVAL;
727 }
728 tvar->type = strdup(cast);
729 return (tvar->type == NULL) ? -ENOMEM : 0;
730 }
731
732 ret = die_get_byte_size(&type) * 8;
733 if (ret) {
734 /* Check the bitwidth */
735 if (ret > MAX_BASIC_TYPE_BITS) {
736 pr_info("%s exceeds max-bitwidth."
737 " Cut down to %d bits.\n",
738 dwarf_diename(&type), MAX_BASIC_TYPE_BITS);
739 ret = MAX_BASIC_TYPE_BITS;
740 }
741
742 ret = snprintf(buf, 16, "%c%d",
743 die_is_signed_type(&type) ? 's' : 'u', ret);
744 if (ret < 0 || ret >= 16) {
745 if (ret >= 16)
746 ret = -E2BIG;
747 pr_warning("Failed to convert variable type: %s\n",
748 strerror(-ret));
749 return ret;
750 }
751 tvar->type = strdup(buf);
752 if (tvar->type == NULL)
753 return -ENOMEM;
754 }
755 return 0;
756}
757
758static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
759 struct perf_probe_arg_field *field,
760 struct probe_trace_arg_ref **ref_ptr,
761 Dwarf_Die *die_mem)
762{
763 struct probe_trace_arg_ref *ref = *ref_ptr;
764 Dwarf_Die type;
765 Dwarf_Word offs;
766 int ret, tag;
767
768 pr_debug("converting %s in %s\n", field->name, varname);
769 if (die_get_real_type(vr_die, &type) == NULL) {
770 pr_warning("Failed to get the type of %s.\n", varname);
771 return -ENOENT;
772 }
773 pr_debug2("Var real type: (%x)\n", (unsigned)dwarf_dieoffset(&type));
774 tag = dwarf_tag(&type);
775
776 if (field->name[0] == '[' &&
777 (tag == DW_TAG_array_type || tag == DW_TAG_pointer_type)) {
778 if (field->next)
779 /* Save original type for next field */
780 memcpy(die_mem, &type, sizeof(*die_mem));
781 /* Get the type of this array */
782 if (die_get_real_type(&type, &type) == NULL) {
783 pr_warning("Failed to get the type of %s.\n", varname);
784 return -ENOENT;
785 }
786 pr_debug2("Array real type: (%x)\n",
787 (unsigned)dwarf_dieoffset(&type));
788 if (tag == DW_TAG_pointer_type) {
789 ref = zalloc(sizeof(struct probe_trace_arg_ref));
790 if (ref == NULL)
791 return -ENOMEM;
792 if (*ref_ptr)
793 (*ref_ptr)->next = ref;
794 else
795 *ref_ptr = ref;
796 }
797 ref->offset += die_get_byte_size(&type) * field->index;
798 if (!field->next)
799 /* Save vr_die for converting types */
800 memcpy(die_mem, vr_die, sizeof(*die_mem));
801 goto next;
802 } else if (tag == DW_TAG_pointer_type) {
803 /* Check the pointer and dereference */
804 if (!field->ref) {
805 pr_err("Semantic error: %s must be referred by '->'\n",
806 field->name);
807 return -EINVAL;
808 }
809 /* Get the type pointed by this pointer */
810 if (die_get_real_type(&type, &type) == NULL) {
811 pr_warning("Failed to get the type of %s.\n", varname);
812 return -ENOENT;
813 }
814 /* Verify it is a data structure */
815 if (dwarf_tag(&type) != DW_TAG_structure_type) {
816 pr_warning("%s is not a data structure.\n", varname);
817 return -EINVAL;
818 }
819
820 ref = zalloc(sizeof(struct probe_trace_arg_ref));
821 if (ref == NULL)
822 return -ENOMEM;
823 if (*ref_ptr)
824 (*ref_ptr)->next = ref;
825 else
826 *ref_ptr = ref;
827 } else {
828 /* Verify it is a data structure */
829 if (tag != DW_TAG_structure_type) {
830 pr_warning("%s is not a data structure.\n", varname);
831 return -EINVAL;
832 }
833 if (field->name[0] == '[') {
834 pr_err("Semantic error: %s is not a pointor"
835 " nor array.\n", varname);
836 return -EINVAL;
837 }
838 if (field->ref) {
839 pr_err("Semantic error: %s must be referred by '.'\n",
840 field->name);
841 return -EINVAL;
842 }
843 if (!ref) {
844 pr_warning("Structure on a register is not "
845 "supported yet.\n");
846 return -ENOTSUP;
847 }
848 }
849
850 if (die_find_member(&type, field->name, die_mem) == NULL) {
851 pr_warning("%s(tyep:%s) has no member %s.\n", varname,
852 dwarf_diename(&type), field->name);
853 return -EINVAL;
854 }
855
856 /* Get the offset of the field */
857 ret = die_get_data_member_location(die_mem, &offs);
858 if (ret < 0) {
859 pr_warning("Failed to get the offset of %s.\n", field->name);
860 return ret;
861 }
862 ref->offset += (long)offs;
863
864next:
865 /* Converting next field */
866 if (field->next)
867 return convert_variable_fields(die_mem, field->name,
868 field->next, &ref, die_mem);
869 else
870 return 0;
871}
872
873/* Show a variables in kprobe event format */
874static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)
875{
876 Dwarf_Die die_mem;
877 int ret;
878
879 pr_debug("Converting variable %s into trace event.\n",
880 dwarf_diename(vr_die));
881
882 ret = convert_variable_location(vr_die, pf->addr, pf->fb_ops,
883 pf->tvar);
884 if (ret == -ENOENT)
885 pr_err("Failed to find the location of %s at this address.\n"
886 " Perhaps, it has been optimized out.\n", pf->pvar->var);
887 else if (ret == -ENOTSUP)
888 pr_err("Sorry, we don't support this variable location yet.\n");
889 else if (pf->pvar->field) {
890 ret = convert_variable_fields(vr_die, pf->pvar->var,
891 pf->pvar->field, &pf->tvar->ref,
892 &die_mem);
893 vr_die = &die_mem;
894 }
895 if (ret == 0)
896 ret = convert_variable_type(vr_die, pf->tvar, pf->pvar->type);
897 /* *expr will be cached in libdw. Don't free it. */
898 return ret;
899}
900
901/* Find a variable in a subprogram die */
902static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf)
903{
904 Dwarf_Die vr_die, *scopes;
905 char buf[32], *ptr;
906 int ret, nscopes;
907
908 if (!is_c_varname(pf->pvar->var)) {
909 /* Copy raw parameters */
910 pf->tvar->value = strdup(pf->pvar->var);
911 if (pf->tvar->value == NULL)
912 return -ENOMEM;
913 if (pf->pvar->type) {
914 pf->tvar->type = strdup(pf->pvar->type);
915 if (pf->tvar->type == NULL)
916 return -ENOMEM;
917 }
918 if (pf->pvar->name) {
919 pf->tvar->name = strdup(pf->pvar->name);
920 if (pf->tvar->name == NULL)
921 return -ENOMEM;
922 } else
923 pf->tvar->name = NULL;
924 return 0;
925 }
926
927 if (pf->pvar->name)
928 pf->tvar->name = strdup(pf->pvar->name);
929 else {
930 ret = synthesize_perf_probe_arg(pf->pvar, buf, 32);
931 if (ret < 0)
932 return ret;
933 ptr = strchr(buf, ':'); /* Change type separator to _ */
934 if (ptr)
935 *ptr = '_';
936 pf->tvar->name = strdup(buf);
937 }
938 if (pf->tvar->name == NULL)
939 return -ENOMEM;
940
941 pr_debug("Searching '%s' variable in context.\n",
942 pf->pvar->var);
943 /* Search child die for local variables and parameters. */
944 if (die_find_variable_at(sp_die, pf->pvar->var, pf->addr, &vr_die))
945 ret = convert_variable(&vr_die, pf);
946 else {
947 /* Search upper class */
948 nscopes = dwarf_getscopes_die(sp_die, &scopes);
949 while (nscopes-- > 1) {
950 pr_debug("Searching variables in %s\n",
951 dwarf_diename(&scopes[nscopes]));
952 /* We should check this scope, so give dummy address */
953 if (die_find_variable_at(&scopes[nscopes],
954 pf->pvar->var, 0,
955 &vr_die)) {
956 ret = convert_variable(&vr_die, pf);
957 goto found;
958 }
959 }
960 if (scopes)
961 free(scopes);
962 ret = -ENOENT;
963 }
964found:
965 if (ret < 0)
966 pr_warning("Failed to find '%s' in this function.\n",
967 pf->pvar->var);
968 return ret;
969}
970
971/* Convert subprogram DIE to trace point */
972static int convert_to_trace_point(Dwarf_Die *sp_die, Dwarf_Addr paddr,
973 bool retprobe, struct probe_trace_point *tp)
974{
975 Dwarf_Addr eaddr;
976 const char *name;
977
978 /* Copy the name of probe point */
979 name = dwarf_diename(sp_die);
980 if (name) {
981 if (dwarf_entrypc(sp_die, &eaddr) != 0) {
982 pr_warning("Failed to get entry address of %s\n",
983 dwarf_diename(sp_die));
984 return -ENOENT;
985 }
986 tp->symbol = strdup(name);
987 if (tp->symbol == NULL)
988 return -ENOMEM;
989 tp->offset = (unsigned long)(paddr - eaddr);
990 } else
991 /* This function has no name. */
992 tp->offset = (unsigned long)paddr;
993
994 /* Return probe must be on the head of a subprogram */
995 if (retprobe) {
996 if (eaddr != paddr) {
997 pr_warning("Return probe must be on the head of"
998 " a real function.\n");
999 return -EINVAL;
1000 }
1001 tp->retprobe = true;
1002 }
1003
1004 return 0;
1005}
1006
1007/* Call probe_finder callback with real subprogram DIE */
1008static int call_probe_finder(Dwarf_Die *sp_die, struct probe_finder *pf)
1009{
1010 Dwarf_Die die_mem;
1011 Dwarf_Attribute fb_attr;
1012 size_t nops;
1013 int ret;
1014
1015 /* If no real subprogram, find a real one */
1016 if (!sp_die || dwarf_tag(sp_die) != DW_TAG_subprogram) {
1017 sp_die = die_find_real_subprogram(&pf->cu_die,
1018 pf->addr, &die_mem);
1019 if (!sp_die) {
1020 pr_warning("Failed to find probe point in any "
1021 "functions.\n");
1022 return -ENOENT;
1023 }
1024 }
1025
1026 /* Get the frame base attribute/ops */
1027 dwarf_attr(sp_die, DW_AT_frame_base, &fb_attr);
1028 ret = dwarf_getlocation_addr(&fb_attr, pf->addr, &pf->fb_ops, &nops, 1);
1029 if (ret <= 0 || nops == 0) {
1030 pf->fb_ops = NULL;
1031#if _ELFUTILS_PREREQ(0, 142)
1032 } else if (nops == 1 && pf->fb_ops[0].atom == DW_OP_call_frame_cfa &&
1033 pf->cfi != NULL) {
1034 Dwarf_Frame *frame;
1035 if (dwarf_cfi_addrframe(pf->cfi, pf->addr, &frame) != 0 ||
1036 dwarf_frame_cfa(frame, &pf->fb_ops, &nops) != 0) {
1037 pr_warning("Failed to get call frame on 0x%jx\n",
1038 (uintmax_t)pf->addr);
1039 return -ENOENT;
1040 }
1041#endif
1042 }
1043
1044 /* Call finder's callback handler */
1045 ret = pf->callback(sp_die, pf);
1046
1047 /* *pf->fb_ops will be cached in libdw. Don't free it. */
1048 pf->fb_ops = NULL;
1049
1050 return ret;
1051}
1052
1053/* Find probe point from its line number */
1054static int find_probe_point_by_line(struct probe_finder *pf)
1055{
1056 Dwarf_Lines *lines;
1057 Dwarf_Line *line;
1058 size_t nlines, i;
1059 Dwarf_Addr addr;
1060 int lineno;
1061 int ret = 0;
1062
1063 if (dwarf_getsrclines(&pf->cu_die, &lines, &nlines) != 0) {
1064 pr_warning("No source lines found.\n");
1065 return -ENOENT;
1066 }
1067
1068 for (i = 0; i < nlines && ret == 0; i++) {
1069 line = dwarf_onesrcline(lines, i);
1070 if (dwarf_lineno(line, &lineno) != 0 ||
1071 lineno != pf->lno)
1072 continue;
1073
1074 /* TODO: Get fileno from line, but how? */
1075 if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0)
1076 continue;
1077
1078 if (dwarf_lineaddr(line, &addr) != 0) {
1079 pr_warning("Failed to get the address of the line.\n");
1080 return -ENOENT;
1081 }
1082 pr_debug("Probe line found: line[%d]:%d addr:0x%jx\n",
1083 (int)i, lineno, (uintmax_t)addr);
1084 pf->addr = addr;
1085
1086 ret = call_probe_finder(NULL, pf);
1087 /* Continuing, because target line might be inlined. */
1088 }
1089 return ret;
1090}
1091
1092/* Find lines which match lazy pattern */
1093static int find_lazy_match_lines(struct list_head *head,
1094 const char *fname, const char *pat)
1095{
1096 char *fbuf, *p1, *p2;
1097 int fd, line, nlines = -1;
1098 struct stat st;
1099
1100 fd = open(fname, O_RDONLY);
1101 if (fd < 0) {
1102 pr_warning("Failed to open %s: %s\n", fname, strerror(-fd));
1103 return -errno;
1104 }
1105
1106 if (fstat(fd, &st) < 0) {
1107 pr_warning("Failed to get the size of %s: %s\n",
1108 fname, strerror(errno));
1109 nlines = -errno;
1110 goto out_close;
1111 }
1112
1113 nlines = -ENOMEM;
1114 fbuf = malloc(st.st_size + 2);
1115 if (fbuf == NULL)
1116 goto out_close;
1117 if (read(fd, fbuf, st.st_size) < 0) {
1118 pr_warning("Failed to read %s: %s\n", fname, strerror(errno));
1119 nlines = -errno;
1120 goto out_free_fbuf;
1121 }
1122 fbuf[st.st_size] = '\n'; /* Dummy line */
1123 fbuf[st.st_size + 1] = '\0';
1124 p1 = fbuf;
1125 line = 1;
1126 nlines = 0;
1127 while ((p2 = strchr(p1, '\n')) != NULL) {
1128 *p2 = '\0';
1129 if (strlazymatch(p1, pat)) {
1130 line_list__add_line(head, line);
1131 nlines++;
1132 }
1133 line++;
1134 p1 = p2 + 1;
1135 }
1136out_free_fbuf:
1137 free(fbuf);
1138out_close:
1139 close(fd);
1140 return nlines;
1141}
1142
1143/* Find probe points from lazy pattern */
1144static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf)
1145{
1146 Dwarf_Lines *lines;
1147 Dwarf_Line *line;
1148 size_t nlines, i;
1149 Dwarf_Addr addr;
1150 Dwarf_Die die_mem;
1151 int lineno;
1152 int ret = 0;
1153
1154 if (list_empty(&pf->lcache)) {
1155 /* Matching lazy line pattern */
1156 ret = find_lazy_match_lines(&pf->lcache, pf->fname,
1157 pf->pev->point.lazy_line);
1158 if (ret == 0) {
1159 pr_debug("No matched lines found in %s.\n", pf->fname);
1160 return 0;
1161 } else if (ret < 0)
1162 return ret;
1163 }
1164
1165 if (dwarf_getsrclines(&pf->cu_die, &lines, &nlines) != 0) {
1166 pr_warning("No source lines found.\n");
1167 return -ENOENT;
1168 }
1169
1170 for (i = 0; i < nlines && ret >= 0; i++) {
1171 line = dwarf_onesrcline(lines, i);
1172
1173 if (dwarf_lineno(line, &lineno) != 0 ||
1174 !line_list__has_line(&pf->lcache, lineno))
1175 continue;
1176
1177 /* TODO: Get fileno from line, but how? */
1178 if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0)
1179 continue;
1180
1181 if (dwarf_lineaddr(line, &addr) != 0) {
1182 pr_debug("Failed to get the address of line %d.\n",
1183 lineno);
1184 continue;
1185 }
1186 if (sp_die) {
1187 /* Address filtering 1: does sp_die include addr? */
1188 if (!dwarf_haspc(sp_die, addr))
1189 continue;
1190 /* Address filtering 2: No child include addr? */
1191 if (die_find_inlinefunc(sp_die, addr, &die_mem))
1192 continue;
1193 }
1194
1195 pr_debug("Probe line found: line[%d]:%d addr:0x%llx\n",
1196 (int)i, lineno, (unsigned long long)addr);
1197 pf->addr = addr;
1198
1199 ret = call_probe_finder(sp_die, pf);
1200 /* Continuing, because target line might be inlined. */
1201 }
1202 /* TODO: deallocate lines, but how? */
1203 return ret;
1204}
1205
1206/* Callback parameter with return value */
1207struct dwarf_callback_param {
1208 void *data;
1209 int retval;
1210};
1211
1212static int probe_point_inline_cb(Dwarf_Die *in_die, void *data)
1213{
1214 struct dwarf_callback_param *param = data;
1215 struct probe_finder *pf = param->data;
1216 struct perf_probe_point *pp = &pf->pev->point;
1217 Dwarf_Addr addr;
1218
1219 if (pp->lazy_line)
1220 param->retval = find_probe_point_lazy(in_die, pf);
1221 else {
1222 /* Get probe address */
1223 if (dwarf_entrypc(in_die, &addr) != 0) {
1224 pr_warning("Failed to get entry address of %s.\n",
1225 dwarf_diename(in_die));
1226 param->retval = -ENOENT;
1227 return DWARF_CB_ABORT;
1228 }
1229 pf->addr = addr;
1230 pf->addr += pp->offset;
1231 pr_debug("found inline addr: 0x%jx\n",
1232 (uintmax_t)pf->addr);
1233
1234 param->retval = call_probe_finder(in_die, pf);
1235 if (param->retval < 0)
1236 return DWARF_CB_ABORT;
1237 }
1238
1239 return DWARF_CB_OK;
1240}
1241
1242/* Search function from function name */
1243static int probe_point_search_cb(Dwarf_Die *sp_die, void *data)
1244{
1245 struct dwarf_callback_param *param = data;
1246 struct probe_finder *pf = param->data;
1247 struct perf_probe_point *pp = &pf->pev->point;
1248
1249 /* Check tag and diename */
1250 if (dwarf_tag(sp_die) != DW_TAG_subprogram ||
1251 !die_compare_name(sp_die, pp->function))
1252 return DWARF_CB_OK;
1253
1254 pf->fname = dwarf_decl_file(sp_die);
1255 if (pp->line) { /* Function relative line */
1256 dwarf_decl_line(sp_die, &pf->lno);
1257 pf->lno += pp->line;
1258 param->retval = find_probe_point_by_line(pf);
1259 } else if (!dwarf_func_inline(sp_die)) {
1260 /* Real function */
1261 if (pp->lazy_line)
1262 param->retval = find_probe_point_lazy(sp_die, pf);
1263 else {
1264 if (dwarf_entrypc(sp_die, &pf->addr) != 0) {
1265 pr_warning("Failed to get entry address of "
1266 "%s.\n", dwarf_diename(sp_die));
1267 param->retval = -ENOENT;
1268 return DWARF_CB_ABORT;
1269 }
1270 pf->addr += pp->offset;
1271 /* TODO: Check the address in this function */
1272 param->retval = call_probe_finder(sp_die, pf);
1273 }
1274 } else {
1275 struct dwarf_callback_param _param = {.data = (void *)pf,
1276 .retval = 0};
1277 /* Inlined function: search instances */
1278 dwarf_func_inline_instances(sp_die, probe_point_inline_cb,
1279 &_param);
1280 param->retval = _param.retval;
1281 }
1282
1283 return DWARF_CB_ABORT; /* Exit; no same symbol in this CU. */
1284}
1285
1286static int find_probe_point_by_func(struct probe_finder *pf)
1287{
1288 struct dwarf_callback_param _param = {.data = (void *)pf,
1289 .retval = 0};
1290 dwarf_getfuncs(&pf->cu_die, probe_point_search_cb, &_param, 0);
1291 return _param.retval;
1292}
1293
1294/* Find probe points from debuginfo */
1295static int find_probes(int fd, struct probe_finder *pf)
1296{
1297 struct perf_probe_point *pp = &pf->pev->point;
1298 Dwarf_Off off, noff;
1299 size_t cuhl;
1300 Dwarf_Die *diep;
1301 Dwarf *dbg = NULL;
1302 Dwfl *dwfl;
1303 Dwarf_Addr bias; /* Currently ignored */
1304 int ret = 0;
1305
1306 dbg = dwfl_init_offline_dwarf(fd, &dwfl, &bias);
1307 if (!dbg) {
1308 pr_warning("No debug information found in the vmlinux - "
1309 "please rebuild with CONFIG_DEBUG_INFO=y.\n");
1310 return -EBADF;
1311 }
1312
1313#if _ELFUTILS_PREREQ(0, 142)
1314 /* Get the call frame information from this dwarf */
1315 pf->cfi = dwarf_getcfi(dbg);
1316#endif
1317
1318 off = 0;
1319 line_list__init(&pf->lcache);
1320 /* Loop on CUs (Compilation Unit) */
1321 while (!dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL) &&
1322 ret >= 0) {
1323 /* Get the DIE(Debugging Information Entry) of this CU */
1324 diep = dwarf_offdie(dbg, off + cuhl, &pf->cu_die);
1325 if (!diep)
1326 continue;
1327
1328 /* Check if target file is included. */
1329 if (pp->file)
1330 pf->fname = cu_find_realpath(&pf->cu_die, pp->file);
1331 else
1332 pf->fname = NULL;
1333
1334 if (!pp->file || pf->fname) {
1335 if (pp->function)
1336 ret = find_probe_point_by_func(pf);
1337 else if (pp->lazy_line)
1338 ret = find_probe_point_lazy(NULL, pf);
1339 else {
1340 pf->lno = pp->line;
1341 ret = find_probe_point_by_line(pf);
1342 }
1343 }
1344 off = noff;
1345 }
1346 line_list__free(&pf->lcache);
1347 if (dwfl)
1348 dwfl_end(dwfl);
1349
1350 return ret;
1351}
1352
1353/* Add a found probe point into trace event list */
1354static int add_probe_trace_event(Dwarf_Die *sp_die, struct probe_finder *pf)
1355{
1356 struct trace_event_finder *tf =
1357 container_of(pf, struct trace_event_finder, pf);
1358 struct probe_trace_event *tev;
1359 int ret, i;
1360
1361 /* Check number of tevs */
1362 if (tf->ntevs == tf->max_tevs) {
1363 pr_warning("Too many( > %d) probe point found.\n",
1364 tf->max_tevs);
1365 return -ERANGE;
1366 }
1367 tev = &tf->tevs[tf->ntevs++];
1368
1369 ret = convert_to_trace_point(sp_die, pf->addr, pf->pev->point.retprobe,
1370 &tev->point);
1371 if (ret < 0)
1372 return ret;
1373
1374 pr_debug("Probe point found: %s+%lu\n", tev->point.symbol,
1375 tev->point.offset);
1376
1377 /* Find each argument */
1378 tev->nargs = pf->pev->nargs;
1379 tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs);
1380 if (tev->args == NULL)
1381 return -ENOMEM;
1382 for (i = 0; i < pf->pev->nargs; i++) {
1383 pf->pvar = &pf->pev->args[i];
1384 pf->tvar = &tev->args[i];
1385 ret = find_variable(sp_die, pf);
1386 if (ret != 0)
1387 return ret;
1388 }
1389
1390 return 0;
1391}
1392
1393/* Find probe_trace_events specified by perf_probe_event from debuginfo */
1394int find_probe_trace_events(int fd, struct perf_probe_event *pev,
1395 struct probe_trace_event **tevs, int max_tevs)
1396{
1397 struct trace_event_finder tf = {
1398 .pf = {.pev = pev, .callback = add_probe_trace_event},
1399 .max_tevs = max_tevs};
1400 int ret;
1401
1402 /* Allocate result tevs array */
1403 *tevs = zalloc(sizeof(struct probe_trace_event) * max_tevs);
1404 if (*tevs == NULL)
1405 return -ENOMEM;
1406
1407 tf.tevs = *tevs;
1408 tf.ntevs = 0;
1409
1410 ret = find_probes(fd, &tf.pf);
1411 if (ret < 0) {
1412 free(*tevs);
1413 *tevs = NULL;
1414 return ret;
1415 }
1416
1417 return (ret < 0) ? ret : tf.ntevs;
1418}
1419
1420#define MAX_VAR_LEN 64
1421
1422/* Collect available variables in this scope */
1423static int collect_variables_cb(Dwarf_Die *die_mem, void *data)
1424{
1425 struct available_var_finder *af = data;
1426 struct variable_list *vl;
1427 char buf[MAX_VAR_LEN];
1428 int tag, ret;
1429
1430 vl = &af->vls[af->nvls - 1];
1431
1432 tag = dwarf_tag(die_mem);
1433 if (tag == DW_TAG_formal_parameter ||
1434 tag == DW_TAG_variable) {
1435 ret = convert_variable_location(die_mem, af->pf.addr,
1436 af->pf.fb_ops, NULL);
1437 if (ret == 0) {
1438 ret = die_get_varname(die_mem, buf, MAX_VAR_LEN);
1439 pr_debug2("Add new var: %s\n", buf);
1440 if (ret > 0)
1441 strlist__add(vl->vars, buf);
1442 }
1443 }
1444
1445 if (af->child && dwarf_haspc(die_mem, af->pf.addr))
1446 return DIE_FIND_CB_CONTINUE;
1447 else
1448 return DIE_FIND_CB_SIBLING;
1449}
1450
1451/* Add a found vars into available variables list */
1452static int add_available_vars(Dwarf_Die *sp_die, struct probe_finder *pf)
1453{
1454 struct available_var_finder *af =
1455 container_of(pf, struct available_var_finder, pf);
1456 struct variable_list *vl;
1457 Dwarf_Die die_mem, *scopes = NULL;
1458 int ret, nscopes;
1459
1460 /* Check number of tevs */
1461 if (af->nvls == af->max_vls) {
1462 pr_warning("Too many( > %d) probe point found.\n", af->max_vls);
1463 return -ERANGE;
1464 }
1465 vl = &af->vls[af->nvls++];
1466
1467 ret = convert_to_trace_point(sp_die, pf->addr, pf->pev->point.retprobe,
1468 &vl->point);
1469 if (ret < 0)
1470 return ret;
1471
1472 pr_debug("Probe point found: %s+%lu\n", vl->point.symbol,
1473 vl->point.offset);
1474
1475 /* Find local variables */
1476 vl->vars = strlist__new(true, NULL);
1477 if (vl->vars == NULL)
1478 return -ENOMEM;
1479 af->child = true;
1480 die_find_child(sp_die, collect_variables_cb, (void *)af, &die_mem);
1481
1482 /* Find external variables */
1483 if (!af->externs)
1484 goto out;
1485 /* Don't need to search child DIE for externs. */
1486 af->child = false;
1487 nscopes = dwarf_getscopes_die(sp_die, &scopes);
1488 while (nscopes-- > 1)
1489 die_find_child(&scopes[nscopes], collect_variables_cb,
1490 (void *)af, &die_mem);
1491 if (scopes)
1492 free(scopes);
1493
1494out:
1495 if (strlist__empty(vl->vars)) {
1496 strlist__delete(vl->vars);
1497 vl->vars = NULL;
1498 }
1499
1500 return ret;
1501}
1502
1503/* Find available variables at given probe point */
1504int find_available_vars_at(int fd, struct perf_probe_event *pev,
1505 struct variable_list **vls, int max_vls,
1506 bool externs)
1507{
1508 struct available_var_finder af = {
1509 .pf = {.pev = pev, .callback = add_available_vars},
1510 .max_vls = max_vls, .externs = externs};
1511 int ret;
1512
1513 /* Allocate result vls array */
1514 *vls = zalloc(sizeof(struct variable_list) * max_vls);
1515 if (*vls == NULL)
1516 return -ENOMEM;
1517
1518 af.vls = *vls;
1519 af.nvls = 0;
1520
1521 ret = find_probes(fd, &af.pf);
1522 if (ret < 0) {
1523 /* Free vlist for error */
1524 while (af.nvls--) {
1525 if (af.vls[af.nvls].point.symbol)
1526 free(af.vls[af.nvls].point.symbol);
1527 if (af.vls[af.nvls].vars)
1528 strlist__delete(af.vls[af.nvls].vars);
1529 }
1530 free(af.vls);
1531 *vls = NULL;
1532 return ret;
1533 }
1534
1535 return (ret < 0) ? ret : af.nvls;
1536}
1537
1538/* Reverse search */
1539int find_perf_probe_point(unsigned long addr, struct perf_probe_point *ppt)
1540{
1541 Dwarf_Die cudie, spdie, indie;
1542 Dwarf *dbg = NULL;
1543 Dwfl *dwfl = NULL;
1544 Dwarf_Line *line;
1545 Dwarf_Addr laddr, eaddr, bias = 0;
1546 const char *tmp;
1547 int lineno, ret = 0;
1548 bool found = false;
1549
1550 /* Open the live linux kernel */
1551 dbg = dwfl_init_live_kernel_dwarf(addr, &dwfl, &bias);
1552 if (!dbg) {
1553 pr_warning("No debug information found in the vmlinux - "
1554 "please rebuild with CONFIG_DEBUG_INFO=y.\n");
1555 ret = -EINVAL;
1556 goto end;
1557 }
1558
1559 /* Adjust address with bias */
1560 addr += bias;
1561 /* Find cu die */
1562 if (!dwarf_addrdie(dbg, (Dwarf_Addr)addr - bias, &cudie)) {
1563 pr_warning("Failed to find debug information for address %lx\n",
1564 addr);
1565 ret = -EINVAL;
1566 goto end;
1567 }
1568
1569 /* Find a corresponding line */
1570 line = dwarf_getsrc_die(&cudie, (Dwarf_Addr)addr);
1571 if (line) {
1572 if (dwarf_lineaddr(line, &laddr) == 0 &&
1573 (Dwarf_Addr)addr == laddr &&
1574 dwarf_lineno(line, &lineno) == 0) {
1575 tmp = dwarf_linesrc(line, NULL, NULL);
1576 if (tmp) {
1577 ppt->line = lineno;
1578 ppt->file = strdup(tmp);
1579 if (ppt->file == NULL) {
1580 ret = -ENOMEM;
1581 goto end;
1582 }
1583 found = true;
1584 }
1585 }
1586 }
1587
1588 /* Find a corresponding function */
1589 if (die_find_real_subprogram(&cudie, (Dwarf_Addr)addr, &spdie)) {
1590 tmp = dwarf_diename(&spdie);
1591 if (!tmp || dwarf_entrypc(&spdie, &eaddr) != 0)
1592 goto end;
1593
1594 if (ppt->line) {
1595 if (die_find_inlinefunc(&spdie, (Dwarf_Addr)addr,
1596 &indie)) {
1597 /* addr in an inline function */
1598 tmp = dwarf_diename(&indie);
1599 if (!tmp)
1600 goto end;
1601 ret = dwarf_decl_line(&indie, &lineno);
1602 } else {
1603 if (eaddr == addr) { /* Function entry */
1604 lineno = ppt->line;
1605 ret = 0;
1606 } else
1607 ret = dwarf_decl_line(&spdie, &lineno);
1608 }
1609 if (ret == 0) {
1610 /* Make a relative line number */
1611 ppt->line -= lineno;
1612 goto found;
1613 }
1614 }
1615 /* We don't have a line number, let's use offset */
1616 ppt->offset = addr - (unsigned long)eaddr;
1617found:
1618 ppt->function = strdup(tmp);
1619 if (ppt->function == NULL) {
1620 ret = -ENOMEM;
1621 goto end;
1622 }
1623 found = true;
1624 }
1625
1626end:
1627 if (dwfl)
1628 dwfl_end(dwfl);
1629 if (ret >= 0)
1630 ret = found ? 1 : 0;
1631 return ret;
1632}
1633
1634/* Add a line and store the src path */
1635static int line_range_add_line(const char *src, unsigned int lineno,
1636 struct line_range *lr)
1637{
1638 /* Copy source path */
1639 if (!lr->path) {
1640 lr->path = strdup(src);
1641 if (lr->path == NULL)
1642 return -ENOMEM;
1643 }
1644 return line_list__add_line(&lr->line_list, lineno);
1645}
1646
1647/* Search function declaration lines */
1648static int line_range_funcdecl_cb(Dwarf_Die *sp_die, void *data)
1649{
1650 struct dwarf_callback_param *param = data;
1651 struct line_finder *lf = param->data;
1652 const char *src;
1653 int lineno;
1654
1655 src = dwarf_decl_file(sp_die);
1656 if (src && strtailcmp(src, lf->fname) != 0)
1657 return DWARF_CB_OK;
1658
1659 if (dwarf_decl_line(sp_die, &lineno) != 0 ||
1660 (lf->lno_s > lineno || lf->lno_e < lineno))
1661 return DWARF_CB_OK;
1662
1663 param->retval = line_range_add_line(src, lineno, lf->lr);
1664 if (param->retval < 0)
1665 return DWARF_CB_ABORT;
1666 return DWARF_CB_OK;
1667}
1668
1669static int find_line_range_func_decl_lines(struct line_finder *lf)
1670{
1671 struct dwarf_callback_param param = {.data = (void *)lf, .retval = 0};
1672 dwarf_getfuncs(&lf->cu_die, line_range_funcdecl_cb, &param, 0);
1673 return param.retval;
1674}
1675
1676/* Find line range from its line number */
1677static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf)
1678{
1679 Dwarf_Lines *lines;
1680 Dwarf_Line *line;
1681 size_t nlines, i;
1682 Dwarf_Addr addr;
1683 int lineno, ret = 0;
1684 const char *src;
1685 Dwarf_Die die_mem;
1686
1687 line_list__init(&lf->lr->line_list);
1688 if (dwarf_getsrclines(&lf->cu_die, &lines, &nlines) != 0) {
1689 pr_warning("No source lines found.\n");
1690 return -ENOENT;
1691 }
1692
1693 /* Search probable lines on lines list */
1694 for (i = 0; i < nlines; i++) {
1695 line = dwarf_onesrcline(lines, i);
1696 if (dwarf_lineno(line, &lineno) != 0 ||
1697 (lf->lno_s > lineno || lf->lno_e < lineno))
1698 continue;
1699
1700 if (sp_die) {
1701 /* Address filtering 1: does sp_die include addr? */
1702 if (dwarf_lineaddr(line, &addr) != 0 ||
1703 !dwarf_haspc(sp_die, addr))
1704 continue;
1705
1706 /* Address filtering 2: No child include addr? */
1707 if (die_find_inlinefunc(sp_die, addr, &die_mem))
1708 continue;
1709 }
1710
1711 /* TODO: Get fileno from line, but how? */
1712 src = dwarf_linesrc(line, NULL, NULL);
1713 if (strtailcmp(src, lf->fname) != 0)
1714 continue;
1715
1716 ret = line_range_add_line(src, lineno, lf->lr);
1717 if (ret < 0)
1718 return ret;
1719 }
1720
1721 /*
1722 * Dwarf lines doesn't include function declarations. We have to
1723 * check functions list or given function.
1724 */
1725 if (sp_die) {
1726 src = dwarf_decl_file(sp_die);
1727 if (src && dwarf_decl_line(sp_die, &lineno) == 0 &&
1728 (lf->lno_s <= lineno && lf->lno_e >= lineno))
1729 ret = line_range_add_line(src, lineno, lf->lr);
1730 } else
1731 ret = find_line_range_func_decl_lines(lf);
1732
1733 /* Update status */
1734 if (ret >= 0)
1735 if (!list_empty(&lf->lr->line_list))
1736 ret = lf->found = 1;
1737 else
1738 ret = 0; /* Lines are not found */
1739 else {
1740 free(lf->lr->path);
1741 lf->lr->path = NULL;
1742 }
1743 return ret;
1744}
1745
1746static int line_range_inline_cb(Dwarf_Die *in_die, void *data)
1747{
1748 struct dwarf_callback_param *param = data;
1749
1750 param->retval = find_line_range_by_line(in_die, param->data);
1751 return DWARF_CB_ABORT; /* No need to find other instances */
1752}
1753
1754/* Search function from function name */
1755static int line_range_search_cb(Dwarf_Die *sp_die, void *data)
1756{
1757 struct dwarf_callback_param *param = data;
1758 struct line_finder *lf = param->data;
1759 struct line_range *lr = lf->lr;
1760
1761 pr_debug("find (%llx) %s\n",
1762 (unsigned long long)dwarf_dieoffset(sp_die),
1763 dwarf_diename(sp_die));
1764 if (dwarf_tag(sp_die) == DW_TAG_subprogram &&
1765 die_compare_name(sp_die, lr->function)) {
1766 lf->fname = dwarf_decl_file(sp_die);
1767 dwarf_decl_line(sp_die, &lr->offset);
1768 pr_debug("fname: %s, lineno:%d\n", lf->fname, lr->offset);
1769 lf->lno_s = lr->offset + lr->start;
1770 if (lf->lno_s < 0) /* Overflow */
1771 lf->lno_s = INT_MAX;
1772 lf->lno_e = lr->offset + lr->end;
1773 if (lf->lno_e < 0) /* Overflow */
1774 lf->lno_e = INT_MAX;
1775 pr_debug("New line range: %d to %d\n", lf->lno_s, lf->lno_e);
1776 lr->start = lf->lno_s;
1777 lr->end = lf->lno_e;
1778 if (dwarf_func_inline(sp_die)) {
1779 struct dwarf_callback_param _param;
1780 _param.data = (void *)lf;
1781 _param.retval = 0;
1782 dwarf_func_inline_instances(sp_die,
1783 line_range_inline_cb,
1784 &_param);
1785 param->retval = _param.retval;
1786 } else
1787 param->retval = find_line_range_by_line(sp_die, lf);
1788 return DWARF_CB_ABORT;
1789 }
1790 return DWARF_CB_OK;
1791}
1792
1793static int find_line_range_by_func(struct line_finder *lf)
1794{
1795 struct dwarf_callback_param param = {.data = (void *)lf, .retval = 0};
1796 dwarf_getfuncs(&lf->cu_die, line_range_search_cb, &param, 0);
1797 return param.retval;
1798}
1799
1800int find_line_range(int fd, struct line_range *lr)
1801{
1802 struct line_finder lf = {.lr = lr, .found = 0};
1803 int ret = 0;
1804 Dwarf_Off off = 0, noff;
1805 size_t cuhl;
1806 Dwarf_Die *diep;
1807 Dwarf *dbg = NULL;
1808 Dwfl *dwfl;
1809 Dwarf_Addr bias; /* Currently ignored */
1810 const char *comp_dir;
1811
1812 dbg = dwfl_init_offline_dwarf(fd, &dwfl, &bias);
1813 if (!dbg) {
1814 pr_warning("No debug information found in the vmlinux - "
1815 "please rebuild with CONFIG_DEBUG_INFO=y.\n");
1816 return -EBADF;
1817 }
1818
1819 /* Loop on CUs (Compilation Unit) */
1820 while (!lf.found && ret >= 0) {
1821 if (dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL) != 0)
1822 break;
1823
1824 /* Get the DIE(Debugging Information Entry) of this CU */
1825 diep = dwarf_offdie(dbg, off + cuhl, &lf.cu_die);
1826 if (!diep)
1827 continue;
1828
1829 /* Check if target file is included. */
1830 if (lr->file)
1831 lf.fname = cu_find_realpath(&lf.cu_die, lr->file);
1832 else
1833 lf.fname = 0;
1834
1835 if (!lr->file || lf.fname) {
1836 if (lr->function)
1837 ret = find_line_range_by_func(&lf);
1838 else {
1839 lf.lno_s = lr->start;
1840 lf.lno_e = lr->end;
1841 ret = find_line_range_by_line(NULL, &lf);
1842 }
1843 }
1844 off = noff;
1845 }
1846
1847 /* Store comp_dir */
1848 if (lf.found) {
1849 comp_dir = cu_get_comp_dir(&lf.cu_die);
1850 if (comp_dir) {
1851 lr->comp_dir = strdup(comp_dir);
1852 if (!lr->comp_dir)
1853 ret = -ENOMEM;
1854 }
1855 }
1856
1857 pr_debug("path: %s\n", lr->path);
1858 dwfl_end(dwfl);
1859 return (ret < 0) ? ret : lf.found;
1860}
1861
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h
new file mode 100644
index 00000000000..beaefc3c122
--- /dev/null
+++ b/tools/perf/util/probe-finder.h
@@ -0,0 +1,91 @@
1#ifndef _PROBE_FINDER_H
2#define _PROBE_FINDER_H
3
4#include <stdbool.h>
5#include "util.h"
6#include "probe-event.h"
7
8#define MAX_PATH_LEN 256
9#define MAX_PROBE_BUFFER 1024
10#define MAX_PROBES 128
11
12static inline int is_c_varname(const char *name)
13{
14 /* TODO */
15 return isalpha(name[0]) || name[0] == '_';
16}
17
18#ifdef DWARF_SUPPORT
19/* Find probe_trace_events specified by perf_probe_event from debuginfo */
20extern int find_probe_trace_events(int fd, struct perf_probe_event *pev,
21 struct probe_trace_event **tevs,
22 int max_tevs);
23
24/* Find a perf_probe_point from debuginfo */
25extern int find_perf_probe_point(unsigned long addr,
26 struct perf_probe_point *ppt);
27
28/* Find a line range */
29extern int find_line_range(int fd, struct line_range *lr);
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
36#include <dwarf.h>
37#include <elfutils/libdw.h>
38#include <elfutils/libdwfl.h>
39#include <elfutils/version.h>
40
41struct probe_finder {
42 struct perf_probe_event *pev; /* Target probe event */
43
44 /* Callback when a probe point is found */
45 int (*callback)(Dwarf_Die *sp_die, struct probe_finder *pf);
46
47 /* For function searching */
48 int lno; /* Line number */
49 Dwarf_Addr addr; /* Address */
50 const char *fname; /* Real file name */
51 Dwarf_Die cu_die; /* Current CU */
52 struct list_head lcache; /* Line cache for lazy match */
53
54 /* For variable searching */
55#if _ELFUTILS_PREREQ(0, 142)
56 Dwarf_CFI *cfi; /* Call Frame Information */
57#endif
58 Dwarf_Op *fb_ops; /* Frame base attribute */
59 struct perf_probe_arg *pvar; /* Current target variable */
60 struct probe_trace_arg *tvar; /* Current result variable */
61};
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
79struct line_finder {
80 struct line_range *lr; /* Target line range */
81
82 const char *fname; /* File name */
83 int lno_s; /* Start line number */
84 int lno_e; /* End line number */
85 Dwarf_Die cu_die; /* Current CU */
86 int found;
87};
88
89#endif /* DWARF_SUPPORT */
90
91#endif /*_PROBE_FINDER_H */
diff --git a/tools/perf/util/pstack.c b/tools/perf/util/pstack.c
new file mode 100644
index 00000000000..13d36faf64e
--- /dev/null
+++ b/tools/perf/util/pstack.c
@@ -0,0 +1,75 @@
1/*
2 * Simple pointer stack
3 *
4 * (c) 2010 Arnaldo Carvalho de Melo <acme@redhat.com>
5 */
6
7#include "util.h"
8#include "pstack.h"
9#include <linux/kernel.h>
10#include <stdlib.h>
11
12struct pstack {
13 unsigned short top;
14 unsigned short max_nr_entries;
15 void *entries[0];
16};
17
18struct pstack *pstack__new(unsigned short max_nr_entries)
19{
20 struct pstack *self = zalloc((sizeof(*self) +
21 max_nr_entries * sizeof(void *)));
22 if (self != NULL)
23 self->max_nr_entries = max_nr_entries;
24 return self;
25}
26
27void pstack__delete(struct pstack *self)
28{
29 free(self);
30}
31
32bool pstack__empty(const struct pstack *self)
33{
34 return self->top == 0;
35}
36
37void pstack__remove(struct pstack *self, void *key)
38{
39 unsigned short i = self->top, last_index = self->top - 1;
40
41 while (i-- != 0) {
42 if (self->entries[i] == key) {
43 if (i < last_index)
44 memmove(self->entries + i,
45 self->entries + i + 1,
46 (last_index - i) * sizeof(void *));
47 --self->top;
48 return;
49 }
50 }
51 pr_err("%s: %p not on the pstack!\n", __func__, key);
52}
53
54void pstack__push(struct pstack *self, void *key)
55{
56 if (self->top == self->max_nr_entries) {
57 pr_err("%s: top=%d, overflow!\n", __func__, self->top);
58 return;
59 }
60 self->entries[self->top++] = key;
61}
62
63void *pstack__pop(struct pstack *self)
64{
65 void *ret;
66
67 if (self->top == 0) {
68 pr_err("%s: underflow!\n", __func__);
69 return NULL;
70 }
71
72 ret = self->entries[--self->top];
73 self->entries[self->top] = NULL;
74 return ret;
75}
diff --git a/tools/perf/util/pstack.h b/tools/perf/util/pstack.h
new file mode 100644
index 00000000000..4cedea59f51
--- /dev/null
+++ b/tools/perf/util/pstack.h
@@ -0,0 +1,14 @@
1#ifndef _PERF_PSTACK_
2#define _PERF_PSTACK_
3
4#include <stdbool.h>
5
6struct pstack;
7struct pstack *pstack__new(unsigned short max_nr_entries);
8void pstack__delete(struct pstack *self);
9bool pstack__empty(const struct pstack *self);
10void pstack__remove(struct pstack *self, void *key);
11void pstack__push(struct pstack *self, void *key);
12void *pstack__pop(struct pstack *self);
13
14#endif /* _PERF_PSTACK_ */
diff --git a/tools/perf/util/quote.c b/tools/perf/util/quote.c
index f18c5212bc9..01f03242b86 100644
--- a/tools/perf/util/quote.c
+++ b/tools/perf/util/quote.c
@@ -1,8 +1,6 @@
1#include "cache.h" 1#include "cache.h"
2#include "quote.h" 2#include "quote.h"
3 3
4int quote_path_fully = 1;
5
6/* Help to copy the thing properly quoted for the shell safety. 4/* Help to copy the thing properly quoted for the shell safety.
7 * any single quote is replaced with '\'', any exclamation point 5 * any single quote is replaced with '\'', any exclamation point
8 * is replaced with '\!', and the whole thing is enclosed in a 6 * is replaced with '\!', and the whole thing is enclosed in a
@@ -19,7 +17,7 @@ static inline int need_bs_quote(char c)
19 return (c == '\'' || c == '!'); 17 return (c == '\'' || c == '!');
20} 18}
21 19
22void sq_quote_buf(struct strbuf *dst, const char *src) 20static void sq_quote_buf(struct strbuf *dst, const char *src)
23{ 21{
24 char *to_free = NULL; 22 char *to_free = NULL;
25 23
@@ -41,23 +39,6 @@ void sq_quote_buf(struct strbuf *dst, const char *src)
41 free(to_free); 39 free(to_free);
42} 40}
43 41
44void sq_quote_print(FILE *stream, const char *src)
45{
46 char c;
47
48 fputc('\'', stream);
49 while ((c = *src++)) {
50 if (need_bs_quote(c)) {
51 fputs("'\\", stream);
52 fputc(c, stream);
53 fputc('\'', stream);
54 } else {
55 fputc(c, stream);
56 }
57 }
58 fputc('\'', stream);
59}
60
61void sq_quote_argv(struct strbuf *dst, const char** argv, size_t maxlen) 42void sq_quote_argv(struct strbuf *dst, const char** argv, size_t maxlen)
62{ 43{
63 int i; 44 int i;
@@ -71,411 +52,3 @@ void sq_quote_argv(struct strbuf *dst, const char** argv, size_t maxlen)
71 die("Too many or long arguments"); 52 die("Too many or long arguments");
72 } 53 }
73} 54}
74
75char *sq_dequote_step(char *arg, char **next)
76{
77 char *dst = arg;
78 char *src = arg;
79 char c;
80
81 if (*src != '\'')
82 return NULL;
83 for (;;) {
84 c = *++src;
85 if (!c)
86 return NULL;
87 if (c != '\'') {
88 *dst++ = c;
89 continue;
90 }
91 /* We stepped out of sq */
92 switch (*++src) {
93 case '\0':
94 *dst = 0;
95 if (next)
96 *next = NULL;
97 return arg;
98 case '\\':
99 c = *++src;
100 if (need_bs_quote(c) && *++src == '\'') {
101 *dst++ = c;
102 continue;
103 }
104 /* Fallthrough */
105 default:
106 if (!next || !isspace(*src))
107 return NULL;
108 do {
109 c = *++src;
110 } while (isspace(c));
111 *dst = 0;
112 *next = src;
113 return arg;
114 }
115 }
116}
117
118char *sq_dequote(char *arg)
119{
120 return sq_dequote_step(arg, NULL);
121}
122
123int sq_dequote_to_argv(char *arg, const char ***argv, int *nr, int *alloc)
124{
125 char *next = arg;
126
127 if (!*arg)
128 return 0;
129 do {
130 char *dequoted = sq_dequote_step(next, &next);
131 if (!dequoted)
132 return -1;
133 ALLOC_GROW(*argv, *nr + 1, *alloc);
134 (*argv)[(*nr)++] = dequoted;
135 } while (next);
136
137 return 0;
138}
139
140/* 1 means: quote as octal
141 * 0 means: quote as octal if (quote_path_fully)
142 * -1 means: never quote
143 * c: quote as "\\c"
144 */
145#define X8(x) x, x, x, x, x, x, x, x
146#define X16(x) X8(x), X8(x)
147static signed char const sq_lookup[256] = {
148 /* 0 1 2 3 4 5 6 7 */
149 /* 0x00 */ 1, 1, 1, 1, 1, 1, 1, 'a',
150 /* 0x08 */ 'b', 't', 'n', 'v', 'f', 'r', 1, 1,
151 /* 0x10 */ X16(1),
152 /* 0x20 */ -1, -1, '"', -1, -1, -1, -1, -1,
153 /* 0x28 */ X16(-1), X16(-1), X16(-1),
154 /* 0x58 */ -1, -1, -1, -1,'\\', -1, -1, -1,
155 /* 0x60 */ X16(-1), X8(-1),
156 /* 0x78 */ -1, -1, -1, -1, -1, -1, -1, 1,
157 /* 0x80 */ /* set to 0 */
158};
159
160static inline int sq_must_quote(char c)
161{
162 return sq_lookup[(unsigned char)c] + quote_path_fully > 0;
163}
164
165/* returns the longest prefix not needing a quote up to maxlen if positive.
166 This stops at the first \0 because it's marked as a character needing an
167 escape */
168static size_t next_quote_pos(const char *s, ssize_t maxlen)
169{
170 size_t len;
171 if (maxlen < 0) {
172 for (len = 0; !sq_must_quote(s[len]); len++);
173 } else {
174 for (len = 0; len < maxlen && !sq_must_quote(s[len]); len++);
175 }
176 return len;
177}
178
179/*
180 * C-style name quoting.
181 *
182 * (1) if sb and fp are both NULL, inspect the input name and counts the
183 * number of bytes that are needed to hold c_style quoted version of name,
184 * counting the double quotes around it but not terminating NUL, and
185 * returns it.
186 * However, if name does not need c_style quoting, it returns 0.
187 *
188 * (2) if sb or fp are not NULL, it emits the c_style quoted version
189 * of name, enclosed with double quotes if asked and needed only.
190 * Return value is the same as in (1).
191 */
192static size_t quote_c_style_counted(const char *name, ssize_t maxlen,
193 struct strbuf *sb, FILE *fp, int no_dq)
194{
195#undef EMIT
196#define EMIT(c) \
197 do { \
198 if (sb) strbuf_addch(sb, (c)); \
199 if (fp) fputc((c), fp); \
200 count++; \
201 } while (0)
202#define EMITBUF(s, l) \
203 do { \
204 int __ret; \
205 if (sb) strbuf_add(sb, (s), (l)); \
206 if (fp) __ret = fwrite((s), (l), 1, fp); \
207 count += (l); \
208 } while (0)
209
210 size_t len, count = 0;
211 const char *p = name;
212
213 for (;;) {
214 int ch;
215
216 len = next_quote_pos(p, maxlen);
217 if (len == maxlen || !p[len])
218 break;
219
220 if (!no_dq && p == name)
221 EMIT('"');
222
223 EMITBUF(p, len);
224 EMIT('\\');
225 p += len;
226 ch = (unsigned char)*p++;
227 if (sq_lookup[ch] >= ' ') {
228 EMIT(sq_lookup[ch]);
229 } else {
230 EMIT(((ch >> 6) & 03) + '0');
231 EMIT(((ch >> 3) & 07) + '0');
232 EMIT(((ch >> 0) & 07) + '0');
233 }
234 }
235
236 EMITBUF(p, len);
237 if (p == name) /* no ending quote needed */
238 return 0;
239
240 if (!no_dq)
241 EMIT('"');
242 return count;
243}
244
245size_t quote_c_style(const char *name, struct strbuf *sb, FILE *fp, int nodq)
246{
247 return quote_c_style_counted(name, -1, sb, fp, nodq);
248}
249
250void quote_two_c_style(struct strbuf *sb, const char *prefix, const char *path, int nodq)
251{
252 if (quote_c_style(prefix, NULL, NULL, 0) ||
253 quote_c_style(path, NULL, NULL, 0)) {
254 if (!nodq)
255 strbuf_addch(sb, '"');
256 quote_c_style(prefix, sb, NULL, 1);
257 quote_c_style(path, sb, NULL, 1);
258 if (!nodq)
259 strbuf_addch(sb, '"');
260 } else {
261 strbuf_addstr(sb, prefix);
262 strbuf_addstr(sb, path);
263 }
264}
265
266void write_name_quoted(const char *name, FILE *fp, int terminator)
267{
268 if (terminator) {
269 quote_c_style(name, NULL, fp, 0);
270 } else {
271 fputs(name, fp);
272 }
273 fputc(terminator, fp);
274}
275
276extern void write_name_quotedpfx(const char *pfx, size_t pfxlen,
277 const char *name, FILE *fp, int terminator)
278{
279 int needquote = 0;
280
281 if (terminator) {
282 needquote = next_quote_pos(pfx, pfxlen) < pfxlen
283 || name[next_quote_pos(name, -1)];
284 }
285 if (needquote) {
286 fputc('"', fp);
287 quote_c_style_counted(pfx, pfxlen, NULL, fp, 1);
288 quote_c_style(name, NULL, fp, 1);
289 fputc('"', fp);
290 } else {
291 int ret;
292
293 ret = fwrite(pfx, pfxlen, 1, fp);
294 fputs(name, fp);
295 }
296 fputc(terminator, fp);
297}
298
299/* quote path as relative to the given prefix */
300char *quote_path_relative(const char *in, int len,
301 struct strbuf *out, const char *prefix)
302{
303 int needquote;
304
305 if (len < 0)
306 len = strlen(in);
307
308 /* "../" prefix itself does not need quoting, but "in" might. */
309 needquote = next_quote_pos(in, len) < len;
310 strbuf_setlen(out, 0);
311 strbuf_grow(out, len);
312
313 if (needquote)
314 strbuf_addch(out, '"');
315 if (prefix) {
316 int off = 0;
317 while (prefix[off] && off < len && prefix[off] == in[off])
318 if (prefix[off] == '/') {
319 prefix += off + 1;
320 in += off + 1;
321 len -= off + 1;
322 off = 0;
323 } else
324 off++;
325
326 for (; *prefix; prefix++)
327 if (*prefix == '/')
328 strbuf_addstr(out, "../");
329 }
330
331 quote_c_style_counted (in, len, out, NULL, 1);
332
333 if (needquote)
334 strbuf_addch(out, '"');
335 if (!out->len)
336 strbuf_addstr(out, "./");
337
338 return out->buf;
339}
340
341/*
342 * C-style name unquoting.
343 *
344 * Quoted should point at the opening double quote.
345 * + Returns 0 if it was able to unquote the string properly, and appends the
346 * result in the strbuf `sb'.
347 * + Returns -1 in case of error, and doesn't touch the strbuf. Though note
348 * that this function will allocate memory in the strbuf, so calling
349 * strbuf_release is mandatory whichever result unquote_c_style returns.
350 *
351 * Updates endp pointer to point at one past the ending double quote if given.
352 */
353int unquote_c_style(struct strbuf *sb, const char *quoted, const char **endp)
354{
355 size_t oldlen = sb->len, len;
356 int ch, ac;
357
358 if (*quoted++ != '"')
359 return -1;
360
361 for (;;) {
362 len = strcspn(quoted, "\"\\");
363 strbuf_add(sb, quoted, len);
364 quoted += len;
365
366 switch (*quoted++) {
367 case '"':
368 if (endp)
369 *endp = quoted;
370 return 0;
371 case '\\':
372 break;
373 default:
374 goto error;
375 }
376
377 switch ((ch = *quoted++)) {
378 case 'a': ch = '\a'; break;
379 case 'b': ch = '\b'; break;
380 case 'f': ch = '\f'; break;
381 case 'n': ch = '\n'; break;
382 case 'r': ch = '\r'; break;
383 case 't': ch = '\t'; break;
384 case 'v': ch = '\v'; break;
385
386 case '\\': case '"':
387 break; /* verbatim */
388
389 /* octal values with first digit over 4 overflow */
390 case '0': case '1': case '2': case '3':
391 ac = ((ch - '0') << 6);
392 if ((ch = *quoted++) < '0' || '7' < ch)
393 goto error;
394 ac |= ((ch - '0') << 3);
395 if ((ch = *quoted++) < '0' || '7' < ch)
396 goto error;
397 ac |= (ch - '0');
398 ch = ac;
399 break;
400 default:
401 goto error;
402 }
403 strbuf_addch(sb, ch);
404 }
405
406 error:
407 strbuf_setlen(sb, oldlen);
408 return -1;
409}
410
411/* quoting as a string literal for other languages */
412
413void perl_quote_print(FILE *stream, const char *src)
414{
415 const char sq = '\'';
416 const char bq = '\\';
417 char c;
418
419 fputc(sq, stream);
420 while ((c = *src++)) {
421 if (c == sq || c == bq)
422 fputc(bq, stream);
423 fputc(c, stream);
424 }
425 fputc(sq, stream);
426}
427
428void python_quote_print(FILE *stream, const char *src)
429{
430 const char sq = '\'';
431 const char bq = '\\';
432 const char nl = '\n';
433 char c;
434
435 fputc(sq, stream);
436 while ((c = *src++)) {
437 if (c == nl) {
438 fputc(bq, stream);
439 fputc('n', stream);
440 continue;
441 }
442 if (c == sq || c == bq)
443 fputc(bq, stream);
444 fputc(c, stream);
445 }
446 fputc(sq, stream);
447}
448
449void tcl_quote_print(FILE *stream, const char *src)
450{
451 char c;
452
453 fputc('"', stream);
454 while ((c = *src++)) {
455 switch (c) {
456 case '[': case ']':
457 case '{': case '}':
458 case '$': case '\\': case '"':
459 fputc('\\', stream);
460 default:
461 fputc(c, stream);
462 break;
463 case '\f':
464 fputs("\\f", stream);
465 break;
466 case '\r':
467 fputs("\\r", stream);
468 break;
469 case '\n':
470 fputs("\\n", stream);
471 break;
472 case '\t':
473 fputs("\\t", stream);
474 break;
475 case '\v':
476 fputs("\\v", stream);
477 break;
478 }
479 }
480 fputc('"', stream);
481}
diff --git a/tools/perf/util/quote.h b/tools/perf/util/quote.h
index 5dfad89816d..172889ea234 100644
--- a/tools/perf/util/quote.h
+++ b/tools/perf/util/quote.h
@@ -1,5 +1,5 @@
1#ifndef QUOTE_H 1#ifndef __PERF_QUOTE_H
2#define QUOTE_H 2#define __PERF_QUOTE_H
3 3
4#include <stddef.h> 4#include <stddef.h>
5#include <stdio.h> 5#include <stdio.h>
@@ -22,47 +22,8 @@
22 * 22 *
23 * Note that the above examples leak memory! Remember to free result from 23 * Note that the above examples leak memory! Remember to free result from
24 * sq_quote() in a real application. 24 * sq_quote() in a real application.
25 *
26 * sq_quote_buf() writes to an existing buffer of specified size; it
27 * will return the number of characters that would have been written
28 * excluding the final null regardless of the buffer size.
29 */ 25 */
30 26
31extern void sq_quote_print(FILE *stream, const char *src);
32
33extern void sq_quote_buf(struct strbuf *, const char *src);
34extern void sq_quote_argv(struct strbuf *, const char **argv, size_t maxlen); 27extern void sq_quote_argv(struct strbuf *, const char **argv, size_t maxlen);
35 28
36/* This unwraps what sq_quote() produces in place, but returns 29#endif /* __PERF_QUOTE_H */
37 * NULL if the input does not look like what sq_quote would have
38 * produced.
39 */
40extern char *sq_dequote(char *);
41
42/*
43 * Same as the above, but can be used to unwrap many arguments in the
44 * same string separated by space. "next" is changed to point to the
45 * next argument that should be passed as first parameter. When there
46 * is no more argument to be dequoted, "next" is updated to point to NULL.
47 */
48extern char *sq_dequote_step(char *arg, char **next);
49extern int sq_dequote_to_argv(char *arg, const char ***argv, int *nr, int *alloc);
50
51extern int unquote_c_style(struct strbuf *, const char *quoted, const char **endp);
52extern size_t quote_c_style(const char *name, struct strbuf *, FILE *, int no_dq);
53extern void quote_two_c_style(struct strbuf *, const char *, const char *, int);
54
55extern void write_name_quoted(const char *name, FILE *, int terminator);
56extern void write_name_quotedpfx(const char *pfx, size_t pfxlen,
57 const char *name, FILE *, int terminator);
58
59/* quote path as relative to the given prefix */
60char *quote_path_relative(const char *in, int len,
61 struct strbuf *out, const char *prefix);
62
63/* quoting as a string literal for other languages */
64extern void perl_quote_print(FILE *stream, const char *src);
65extern void python_quote_print(FILE *stream, const char *src);
66extern void tcl_quote_print(FILE *stream, const char *src);
67
68#endif
diff --git a/tools/perf/util/rbtree.c b/tools/perf/util/rbtree.c
deleted file mode 100644
index b15ba9c7cb3..00000000000
--- a/tools/perf/util/rbtree.c
+++ /dev/null
@@ -1,383 +0,0 @@
1/*
2 Red Black Trees
3 (C) 1999 Andrea Arcangeli <andrea@suse.de>
4 (C) 2002 David Woodhouse <dwmw2@infradead.org>
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
20 linux/lib/rbtree.c
21*/
22
23#include "rbtree.h"
24
25static void __rb_rotate_left(struct rb_node *node, struct rb_root *root)
26{
27 struct rb_node *right = node->rb_right;
28 struct rb_node *parent = rb_parent(node);
29
30 if ((node->rb_right = right->rb_left))
31 rb_set_parent(right->rb_left, node);
32 right->rb_left = node;
33
34 rb_set_parent(right, parent);
35
36 if (parent)
37 {
38 if (node == parent->rb_left)
39 parent->rb_left = right;
40 else
41 parent->rb_right = right;
42 }
43 else
44 root->rb_node = right;
45 rb_set_parent(node, right);
46}
47
48static void __rb_rotate_right(struct rb_node *node, struct rb_root *root)
49{
50 struct rb_node *left = node->rb_left;
51 struct rb_node *parent = rb_parent(node);
52
53 if ((node->rb_left = left->rb_right))
54 rb_set_parent(left->rb_right, node);
55 left->rb_right = node;
56
57 rb_set_parent(left, parent);
58
59 if (parent)
60 {
61 if (node == parent->rb_right)
62 parent->rb_right = left;
63 else
64 parent->rb_left = left;
65 }
66 else
67 root->rb_node = left;
68 rb_set_parent(node, left);
69}
70
71void rb_insert_color(struct rb_node *node, struct rb_root *root)
72{
73 struct rb_node *parent, *gparent;
74
75 while ((parent = rb_parent(node)) && rb_is_red(parent))
76 {
77 gparent = rb_parent(parent);
78
79 if (parent == gparent->rb_left)
80 {
81 {
82 register struct rb_node *uncle = gparent->rb_right;
83 if (uncle && rb_is_red(uncle))
84 {
85 rb_set_black(uncle);
86 rb_set_black(parent);
87 rb_set_red(gparent);
88 node = gparent;
89 continue;
90 }
91 }
92
93 if (parent->rb_right == node)
94 {
95 register struct rb_node *tmp;
96 __rb_rotate_left(parent, root);
97 tmp = parent;
98 parent = node;
99 node = tmp;
100 }
101
102 rb_set_black(parent);
103 rb_set_red(gparent);
104 __rb_rotate_right(gparent, root);
105 } else {
106 {
107 register struct rb_node *uncle = gparent->rb_left;
108 if (uncle && rb_is_red(uncle))
109 {
110 rb_set_black(uncle);
111 rb_set_black(parent);
112 rb_set_red(gparent);
113 node = gparent;
114 continue;
115 }
116 }
117
118 if (parent->rb_left == node)
119 {
120 register struct rb_node *tmp;
121 __rb_rotate_right(parent, root);
122 tmp = parent;
123 parent = node;
124 node = tmp;
125 }
126
127 rb_set_black(parent);
128 rb_set_red(gparent);
129 __rb_rotate_left(gparent, root);
130 }
131 }
132
133 rb_set_black(root->rb_node);
134}
135
136static void __rb_erase_color(struct rb_node *node, struct rb_node *parent,
137 struct rb_root *root)
138{
139 struct rb_node *other;
140
141 while ((!node || rb_is_black(node)) && node != root->rb_node)
142 {
143 if (parent->rb_left == node)
144 {
145 other = parent->rb_right;
146 if (rb_is_red(other))
147 {
148 rb_set_black(other);
149 rb_set_red(parent);
150 __rb_rotate_left(parent, root);
151 other = parent->rb_right;
152 }
153 if ((!other->rb_left || rb_is_black(other->rb_left)) &&
154 (!other->rb_right || rb_is_black(other->rb_right)))
155 {
156 rb_set_red(other);
157 node = parent;
158 parent = rb_parent(node);
159 }
160 else
161 {
162 if (!other->rb_right || rb_is_black(other->rb_right))
163 {
164 rb_set_black(other->rb_left);
165 rb_set_red(other);
166 __rb_rotate_right(other, root);
167 other = parent->rb_right;
168 }
169 rb_set_color(other, rb_color(parent));
170 rb_set_black(parent);
171 rb_set_black(other->rb_right);
172 __rb_rotate_left(parent, root);
173 node = root->rb_node;
174 break;
175 }
176 }
177 else
178 {
179 other = parent->rb_left;
180 if (rb_is_red(other))
181 {
182 rb_set_black(other);
183 rb_set_red(parent);
184 __rb_rotate_right(parent, root);
185 other = parent->rb_left;
186 }
187 if ((!other->rb_left || rb_is_black(other->rb_left)) &&
188 (!other->rb_right || rb_is_black(other->rb_right)))
189 {
190 rb_set_red(other);
191 node = parent;
192 parent = rb_parent(node);
193 }
194 else
195 {
196 if (!other->rb_left || rb_is_black(other->rb_left))
197 {
198 rb_set_black(other->rb_right);
199 rb_set_red(other);
200 __rb_rotate_left(other, root);
201 other = parent->rb_left;
202 }
203 rb_set_color(other, rb_color(parent));
204 rb_set_black(parent);
205 rb_set_black(other->rb_left);
206 __rb_rotate_right(parent, root);
207 node = root->rb_node;
208 break;
209 }
210 }
211 }
212 if (node)
213 rb_set_black(node);
214}
215
216void rb_erase(struct rb_node *node, struct rb_root *root)
217{
218 struct rb_node *child, *parent;
219 int color;
220
221 if (!node->rb_left)
222 child = node->rb_right;
223 else if (!node->rb_right)
224 child = node->rb_left;
225 else
226 {
227 struct rb_node *old = node, *left;
228
229 node = node->rb_right;
230 while ((left = node->rb_left) != NULL)
231 node = left;
232 child = node->rb_right;
233 parent = rb_parent(node);
234 color = rb_color(node);
235
236 if (child)
237 rb_set_parent(child, parent);
238 if (parent == old) {
239 parent->rb_right = child;
240 parent = node;
241 } else
242 parent->rb_left = child;
243
244 node->rb_parent_color = old->rb_parent_color;
245 node->rb_right = old->rb_right;
246 node->rb_left = old->rb_left;
247
248 if (rb_parent(old))
249 {
250 if (rb_parent(old)->rb_left == old)
251 rb_parent(old)->rb_left = node;
252 else
253 rb_parent(old)->rb_right = node;
254 } else
255 root->rb_node = node;
256
257 rb_set_parent(old->rb_left, node);
258 if (old->rb_right)
259 rb_set_parent(old->rb_right, node);
260 goto color;
261 }
262
263 parent = rb_parent(node);
264 color = rb_color(node);
265
266 if (child)
267 rb_set_parent(child, parent);
268 if (parent)
269 {
270 if (parent->rb_left == node)
271 parent->rb_left = child;
272 else
273 parent->rb_right = child;
274 }
275 else
276 root->rb_node = child;
277
278 color:
279 if (color == RB_BLACK)
280 __rb_erase_color(child, parent, root);
281}
282
283/*
284 * This function returns the first node (in sort order) of the tree.
285 */
286struct rb_node *rb_first(const struct rb_root *root)
287{
288 struct rb_node *n;
289
290 n = root->rb_node;
291 if (!n)
292 return NULL;
293 while (n->rb_left)
294 n = n->rb_left;
295 return n;
296}
297
298struct rb_node *rb_last(const struct rb_root *root)
299{
300 struct rb_node *n;
301
302 n = root->rb_node;
303 if (!n)
304 return NULL;
305 while (n->rb_right)
306 n = n->rb_right;
307 return n;
308}
309
310struct rb_node *rb_next(const struct rb_node *node)
311{
312 struct rb_node *parent;
313
314 if (rb_parent(node) == node)
315 return NULL;
316
317 /* If we have a right-hand child, go down and then left as far
318 as we can. */
319 if (node->rb_right) {
320 node = node->rb_right;
321 while (node->rb_left)
322 node=node->rb_left;
323 return (struct rb_node *)node;
324 }
325
326 /* No right-hand children. Everything down and left is
327 smaller than us, so any 'next' node must be in the general
328 direction of our parent. Go up the tree; any time the
329 ancestor is a right-hand child of its parent, keep going
330 up. First time it's a left-hand child of its parent, said
331 parent is our 'next' node. */
332 while ((parent = rb_parent(node)) && node == parent->rb_right)
333 node = parent;
334
335 return parent;
336}
337
338struct rb_node *rb_prev(const struct rb_node *node)
339{
340 struct rb_node *parent;
341
342 if (rb_parent(node) == node)
343 return NULL;
344
345 /* If we have a left-hand child, go down and then right as far
346 as we can. */
347 if (node->rb_left) {
348 node = node->rb_left;
349 while (node->rb_right)
350 node=node->rb_right;
351 return (struct rb_node *)node;
352 }
353
354 /* No left-hand children. Go up till we find an ancestor which
355 is a right-hand child of its parent */
356 while ((parent = rb_parent(node)) && node == parent->rb_left)
357 node = parent;
358
359 return parent;
360}
361
362void rb_replace_node(struct rb_node *victim, struct rb_node *new,
363 struct rb_root *root)
364{
365 struct rb_node *parent = rb_parent(victim);
366
367 /* Set the surrounding nodes to point to the replacement */
368 if (parent) {
369 if (victim == parent->rb_left)
370 parent->rb_left = new;
371 else
372 parent->rb_right = new;
373 } else {
374 root->rb_node = new;
375 }
376 if (victim->rb_left)
377 rb_set_parent(victim->rb_left, new);
378 if (victim->rb_right)
379 rb_set_parent(victim->rb_right, new);
380
381 /* Copy the pointers/colour from the victim to the replacement */
382 *new = *victim;
383}
diff --git a/tools/perf/util/rbtree.h b/tools/perf/util/rbtree.h
deleted file mode 100644
index 6bdc488a47f..00000000000
--- a/tools/perf/util/rbtree.h
+++ /dev/null
@@ -1,171 +0,0 @@
1/*
2 Red Black Trees
3 (C) 1999 Andrea Arcangeli <andrea@suse.de>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19 linux/include/linux/rbtree.h
20
21 To use rbtrees you'll have to implement your own insert and search cores.
22 This will avoid us to use callbacks and to drop drammatically performances.
23 I know it's not the cleaner way, but in C (not in C++) to get
24 performances and genericity...
25
26 Some example of insert and search follows here. The search is a plain
27 normal search over an ordered tree. The insert instead must be implemented
28 int two steps: as first thing the code must insert the element in
29 order as a red leaf in the tree, then the support library function
30 rb_insert_color() must be called. Such function will do the
31 not trivial work to rebalance the rbtree if necessary.
32
33-----------------------------------------------------------------------
34static inline struct page * rb_search_page_cache(struct inode * inode,
35 unsigned long offset)
36{
37 struct rb_node * n = inode->i_rb_page_cache.rb_node;
38 struct page * page;
39
40 while (n)
41 {
42 page = rb_entry(n, struct page, rb_page_cache);
43
44 if (offset < page->offset)
45 n = n->rb_left;
46 else if (offset > page->offset)
47 n = n->rb_right;
48 else
49 return page;
50 }
51 return NULL;
52}
53
54static inline struct page * __rb_insert_page_cache(struct inode * inode,
55 unsigned long offset,
56 struct rb_node * node)
57{
58 struct rb_node ** p = &inode->i_rb_page_cache.rb_node;
59 struct rb_node * parent = NULL;
60 struct page * page;
61
62 while (*p)
63 {
64 parent = *p;
65 page = rb_entry(parent, struct page, rb_page_cache);
66
67 if (offset < page->offset)
68 p = &(*p)->rb_left;
69 else if (offset > page->offset)
70 p = &(*p)->rb_right;
71 else
72 return page;
73 }
74
75 rb_link_node(node, parent, p);
76
77 return NULL;
78}
79
80static inline struct page * rb_insert_page_cache(struct inode * inode,
81 unsigned long offset,
82 struct rb_node * node)
83{
84 struct page * ret;
85 if ((ret = __rb_insert_page_cache(inode, offset, node)))
86 goto out;
87 rb_insert_color(node, &inode->i_rb_page_cache);
88 out:
89 return ret;
90}
91-----------------------------------------------------------------------
92*/
93
94#ifndef _LINUX_RBTREE_H
95#define _LINUX_RBTREE_H
96
97#include <stddef.h>
98
99/**
100 * container_of - cast a member of a structure out to the containing structure
101 * @ptr: the pointer to the member.
102 * @type: the type of the container struct this is embedded in.
103 * @member: the name of the member within the struct.
104 *
105 */
106#define container_of(ptr, type, member) ({ \
107 const typeof( ((type *)0)->member ) *__mptr = (ptr); \
108 (type *)( (char *)__mptr - offsetof(type,member) );})
109
110struct rb_node
111{
112 unsigned long rb_parent_color;
113#define RB_RED 0
114#define RB_BLACK 1
115 struct rb_node *rb_right;
116 struct rb_node *rb_left;
117} __attribute__((aligned(sizeof(long))));
118 /* The alignment might seem pointless, but allegedly CRIS needs it */
119
120struct rb_root
121{
122 struct rb_node *rb_node;
123};
124
125
126#define rb_parent(r) ((struct rb_node *)((r)->rb_parent_color & ~3))
127#define rb_color(r) ((r)->rb_parent_color & 1)
128#define rb_is_red(r) (!rb_color(r))
129#define rb_is_black(r) rb_color(r)
130#define rb_set_red(r) do { (r)->rb_parent_color &= ~1; } while (0)
131#define rb_set_black(r) do { (r)->rb_parent_color |= 1; } while (0)
132
133static inline void rb_set_parent(struct rb_node *rb, struct rb_node *p)
134{
135 rb->rb_parent_color = (rb->rb_parent_color & 3) | (unsigned long)p;
136}
137static inline void rb_set_color(struct rb_node *rb, int color)
138{
139 rb->rb_parent_color = (rb->rb_parent_color & ~1) | color;
140}
141
142#define RB_ROOT (struct rb_root) { NULL, }
143#define rb_entry(ptr, type, member) container_of(ptr, type, member)
144
145#define RB_EMPTY_ROOT(root) ((root)->rb_node == NULL)
146#define RB_EMPTY_NODE(node) (rb_parent(node) == node)
147#define RB_CLEAR_NODE(node) (rb_set_parent(node, node))
148
149extern void rb_insert_color(struct rb_node *, struct rb_root *);
150extern void rb_erase(struct rb_node *, struct rb_root *);
151
152/* Find logical next and previous nodes in a tree */
153extern struct rb_node *rb_next(const struct rb_node *);
154extern struct rb_node *rb_prev(const struct rb_node *);
155extern struct rb_node *rb_first(const struct rb_root *);
156extern struct rb_node *rb_last(const struct rb_root *);
157
158/* Fast replacement of a single node without remove/rebalance/add/rebalance */
159extern void rb_replace_node(struct rb_node *victim, struct rb_node *new,
160 struct rb_root *root);
161
162static inline void rb_link_node(struct rb_node * node, struct rb_node * parent,
163 struct rb_node ** rb_link)
164{
165 node->rb_parent_color = (unsigned long )parent;
166 node->rb_left = node->rb_right = NULL;
167
168 *rb_link = node;
169}
170
171#endif /* _LINUX_RBTREE_H */
diff --git a/tools/perf/util/run-command.c b/tools/perf/util/run-command.c
index b2f5e854f40..da8e9b285f5 100644
--- a/tools/perf/util/run-command.c
+++ b/tools/perf/util/run-command.c
@@ -65,7 +65,6 @@ int start_command(struct child_process *cmd)
65 cmd->err = fderr[0]; 65 cmd->err = fderr[0];
66 } 66 }
67 67
68#ifndef __MINGW32__
69 fflush(NULL); 68 fflush(NULL);
70 cmd->pid = fork(); 69 cmd->pid = fork();
71 if (!cmd->pid) { 70 if (!cmd->pid) {
@@ -118,71 +117,6 @@ int start_command(struct child_process *cmd)
118 } 117 }
119 exit(127); 118 exit(127);
120 } 119 }
121#else
122 int s0 = -1, s1 = -1, s2 = -1; /* backups of stdin, stdout, stderr */
123 const char **sargv = cmd->argv;
124 char **env = environ;
125
126 if (cmd->no_stdin) {
127 s0 = dup(0);
128 dup_devnull(0);
129 } else if (need_in) {
130 s0 = dup(0);
131 dup2(fdin[0], 0);
132 } else if (cmd->in) {
133 s0 = dup(0);
134 dup2(cmd->in, 0);
135 }
136
137 if (cmd->no_stderr) {
138 s2 = dup(2);
139 dup_devnull(2);
140 } else if (need_err) {
141 s2 = dup(2);
142 dup2(fderr[1], 2);
143 }
144
145 if (cmd->no_stdout) {
146 s1 = dup(1);
147 dup_devnull(1);
148 } else if (cmd->stdout_to_stderr) {
149 s1 = dup(1);
150 dup2(2, 1);
151 } else if (need_out) {
152 s1 = dup(1);
153 dup2(fdout[1], 1);
154 } else if (cmd->out > 1) {
155 s1 = dup(1);
156 dup2(cmd->out, 1);
157 }
158
159 if (cmd->dir)
160 die("chdir in start_command() not implemented");
161 if (cmd->env) {
162 env = copy_environ();
163 for (; *cmd->env; cmd->env++)
164 env = env_setenv(env, *cmd->env);
165 }
166
167 if (cmd->perf_cmd) {
168 cmd->argv = prepare_perf_cmd(cmd->argv);
169 }
170
171 cmd->pid = mingw_spawnvpe(cmd->argv[0], cmd->argv, env);
172
173 if (cmd->env)
174 free_environ(env);
175 if (cmd->perf_cmd)
176 free(cmd->argv);
177
178 cmd->argv = sargv;
179 if (s0 >= 0)
180 dup2(s0, 0), close(s0);
181 if (s1 >= 0)
182 dup2(s1, 1), close(s1);
183 if (s2 >= 0)
184 dup2(s2, 2), close(s2);
185#endif
186 120
187 if (cmd->pid < 0) { 121 if (cmd->pid < 0) {
188 int err = errno; 122 int err = errno;
@@ -278,118 +212,3 @@ int run_command_v_opt(const char **argv, int opt)
278 prepare_run_command_v_opt(&cmd, argv, opt); 212 prepare_run_command_v_opt(&cmd, argv, opt);
279 return run_command(&cmd); 213 return run_command(&cmd);
280} 214}
281
282int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const char *const *env)
283{
284 struct child_process cmd;
285 prepare_run_command_v_opt(&cmd, argv, opt);
286 cmd.dir = dir;
287 cmd.env = env;
288 return run_command(&cmd);
289}
290
291#ifdef __MINGW32__
292static __stdcall unsigned run_thread(void *data)
293{
294 struct async *async = data;
295 return async->proc(async->fd_for_proc, async->data);
296}
297#endif
298
299int start_async(struct async *async)
300{
301 int pipe_out[2];
302
303 if (pipe(pipe_out) < 0)
304 return error("cannot create pipe: %s", strerror(errno));
305 async->out = pipe_out[0];
306
307#ifndef __MINGW32__
308 /* Flush stdio before fork() to avoid cloning buffers */
309 fflush(NULL);
310
311 async->pid = fork();
312 if (async->pid < 0) {
313 error("fork (async) failed: %s", strerror(errno));
314 close_pair(pipe_out);
315 return -1;
316 }
317 if (!async->pid) {
318 close(pipe_out[0]);
319 exit(!!async->proc(pipe_out[1], async->data));
320 }
321 close(pipe_out[1]);
322#else
323 async->fd_for_proc = pipe_out[1];
324 async->tid = (HANDLE) _beginthreadex(NULL, 0, run_thread, async, 0, NULL);
325 if (!async->tid) {
326 error("cannot create thread: %s", strerror(errno));
327 close_pair(pipe_out);
328 return -1;
329 }
330#endif
331 return 0;
332}
333
334int finish_async(struct async *async)
335{
336#ifndef __MINGW32__
337 int ret = 0;
338
339 if (wait_or_whine(async->pid))
340 ret = error("waitpid (async) failed");
341#else
342 DWORD ret = 0;
343 if (WaitForSingleObject(async->tid, INFINITE) != WAIT_OBJECT_0)
344 ret = error("waiting for thread failed: %lu", GetLastError());
345 else if (!GetExitCodeThread(async->tid, &ret))
346 ret = error("cannot get thread exit code: %lu", GetLastError());
347 CloseHandle(async->tid);
348#endif
349 return ret;
350}
351
352int run_hook(const char *index_file, const char *name, ...)
353{
354 struct child_process hook;
355 const char **argv = NULL, *env[2];
356 char index[PATH_MAX];
357 va_list args;
358 int ret;
359 size_t i = 0, alloc = 0;
360
361 if (access(perf_path("hooks/%s", name), X_OK) < 0)
362 return 0;
363
364 va_start(args, name);
365 ALLOC_GROW(argv, i + 1, alloc);
366 argv[i++] = perf_path("hooks/%s", name);
367 while (argv[i-1]) {
368 ALLOC_GROW(argv, i + 1, alloc);
369 argv[i++] = va_arg(args, const char *);
370 }
371 va_end(args);
372
373 memset(&hook, 0, sizeof(hook));
374 hook.argv = argv;
375 hook.no_stdin = 1;
376 hook.stdout_to_stderr = 1;
377 if (index_file) {
378 snprintf(index, sizeof(index), "PERF_INDEX_FILE=%s", index_file);
379 env[0] = index;
380 env[1] = NULL;
381 hook.env = env;
382 }
383
384 ret = start_command(&hook);
385 free(argv);
386 if (ret) {
387 warning("Could not spawn %s", argv[0]);
388 return ret;
389 }
390 ret = finish_command(&hook);
391 if (ret == -ERR_RUN_COMMAND_WAITPID_SIGNAL)
392 warning("%s exited due to uncaught signal", argv[0]);
393
394 return ret;
395}
diff --git a/tools/perf/util/run-command.h b/tools/perf/util/run-command.h
index 328289f2366..1ef264d5069 100644
--- a/tools/perf/util/run-command.h
+++ b/tools/perf/util/run-command.h
@@ -1,5 +1,5 @@
1#ifndef RUN_COMMAND_H 1#ifndef __PERF_RUN_COMMAND_H
2#define RUN_COMMAND_H 2#define __PERF_RUN_COMMAND_H
3 3
4enum { 4enum {
5 ERR_RUN_COMMAND_FORK = 10000, 5 ERR_RUN_COMMAND_FORK = 10000,
@@ -50,44 +50,9 @@ int start_command(struct child_process *);
50int finish_command(struct child_process *); 50int finish_command(struct child_process *);
51int run_command(struct child_process *); 51int run_command(struct child_process *);
52 52
53extern int run_hook(const char *index_file, const char *name, ...);
54
55#define RUN_COMMAND_NO_STDIN 1 53#define RUN_COMMAND_NO_STDIN 1
56#define RUN_PERF_CMD 2 /*If this is to be perf sub-command */ 54#define RUN_PERF_CMD 2 /*If this is to be perf sub-command */
57#define RUN_COMMAND_STDOUT_TO_STDERR 4 55#define RUN_COMMAND_STDOUT_TO_STDERR 4
58int run_command_v_opt(const char **argv, int opt); 56int run_command_v_opt(const char **argv, int opt);
59 57
60/* 58#endif /* __PERF_RUN_COMMAND_H */
61 * env (the environment) is to be formatted like environ: "VAR=VALUE".
62 * To unset an environment variable use just "VAR".
63 */
64int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const char *const *env);
65
66/*
67 * The purpose of the following functions is to feed a pipe by running
68 * a function asynchronously and providing output that the caller reads.
69 *
70 * It is expected that no synchronization and mutual exclusion between
71 * the caller and the feed function is necessary so that the function
72 * can run in a thread without interfering with the caller.
73 */
74struct async {
75 /*
76 * proc writes to fd and closes it;
77 * returns 0 on success, non-zero on failure
78 */
79 int (*proc)(int fd, void *data);
80 void *data;
81 int out; /* caller reads from here and closes it */
82#ifndef __MINGW32__
83 pid_t pid;
84#else
85 HANDLE tid;
86 int fd_for_proc;
87#endif
88};
89
90int start_async(struct async *async);
91int finish_async(struct async *async);
92
93#endif
diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c
new file mode 100644
index 00000000000..93680818e24
--- /dev/null
+++ b/tools/perf/util/scripting-engines/trace-event-perl.c
@@ -0,0 +1,565 @@
1/*
2 * trace-event-perl. Feed perf script events to an embedded Perl interpreter.
3 *
4 * Copyright (C) 2009 Tom Zanussi <tzanussi@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20 */
21
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <ctype.h>
26#include <errno.h>
27
28#include "../../perf.h"
29#include "../util.h"
30#include "../trace-event.h"
31
32#include <EXTERN.h>
33#include <perl.h>
34
35void boot_Perf__Trace__Context(pTHX_ CV *cv);
36void boot_DynaLoader(pTHX_ CV *cv);
37typedef PerlInterpreter * INTERP;
38
39void xs_init(pTHX);
40
41void xs_init(pTHX)
42{
43 const char *file = __FILE__;
44 dXSUB_SYS;
45
46 newXS("Perf::Trace::Context::bootstrap", boot_Perf__Trace__Context,
47 file);
48 newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, file);
49}
50
51INTERP my_perl;
52
53#define FTRACE_MAX_EVENT \
54 ((1 << (sizeof(unsigned short) * 8)) - 1)
55
56struct event *events[FTRACE_MAX_EVENT];
57
58extern struct scripting_context *scripting_context;
59
60static char *cur_field_name;
61static int zero_flag_atom;
62
63static void define_symbolic_value(const char *ev_name,
64 const char *field_name,
65 const char *field_value,
66 const char *field_str)
67{
68 unsigned long long value;
69 dSP;
70
71 value = eval_flag(field_value);
72
73 ENTER;
74 SAVETMPS;
75 PUSHMARK(SP);
76
77 XPUSHs(sv_2mortal(newSVpv(ev_name, 0)));
78 XPUSHs(sv_2mortal(newSVpv(field_name, 0)));
79 XPUSHs(sv_2mortal(newSVuv(value)));
80 XPUSHs(sv_2mortal(newSVpv(field_str, 0)));
81
82 PUTBACK;
83 if (get_cv("main::define_symbolic_value", 0))
84 call_pv("main::define_symbolic_value", G_SCALAR);
85 SPAGAIN;
86 PUTBACK;
87 FREETMPS;
88 LEAVE;
89}
90
91static void define_symbolic_values(struct print_flag_sym *field,
92 const char *ev_name,
93 const char *field_name)
94{
95 define_symbolic_value(ev_name, field_name, field->value, field->str);
96 if (field->next)
97 define_symbolic_values(field->next, ev_name, field_name);
98}
99
100static void define_symbolic_field(const char *ev_name,
101 const char *field_name)
102{
103 dSP;
104
105 ENTER;
106 SAVETMPS;
107 PUSHMARK(SP);
108
109 XPUSHs(sv_2mortal(newSVpv(ev_name, 0)));
110 XPUSHs(sv_2mortal(newSVpv(field_name, 0)));
111
112 PUTBACK;
113 if (get_cv("main::define_symbolic_field", 0))
114 call_pv("main::define_symbolic_field", G_SCALAR);
115 SPAGAIN;
116 PUTBACK;
117 FREETMPS;
118 LEAVE;
119}
120
121static void define_flag_value(const char *ev_name,
122 const char *field_name,
123 const char *field_value,
124 const char *field_str)
125{
126 unsigned long long value;
127 dSP;
128
129 value = eval_flag(field_value);
130
131 ENTER;
132 SAVETMPS;
133 PUSHMARK(SP);
134
135 XPUSHs(sv_2mortal(newSVpv(ev_name, 0)));
136 XPUSHs(sv_2mortal(newSVpv(field_name, 0)));
137 XPUSHs(sv_2mortal(newSVuv(value)));
138 XPUSHs(sv_2mortal(newSVpv(field_str, 0)));
139
140 PUTBACK;
141 if (get_cv("main::define_flag_value", 0))
142 call_pv("main::define_flag_value", G_SCALAR);
143 SPAGAIN;
144 PUTBACK;
145 FREETMPS;
146 LEAVE;
147}
148
149static void define_flag_values(struct print_flag_sym *field,
150 const char *ev_name,
151 const char *field_name)
152{
153 define_flag_value(ev_name, field_name, field->value, field->str);
154 if (field->next)
155 define_flag_values(field->next, ev_name, field_name);
156}
157
158static void define_flag_field(const char *ev_name,
159 const char *field_name,
160 const char *delim)
161{
162 dSP;
163
164 ENTER;
165 SAVETMPS;
166 PUSHMARK(SP);
167
168 XPUSHs(sv_2mortal(newSVpv(ev_name, 0)));
169 XPUSHs(sv_2mortal(newSVpv(field_name, 0)));
170 XPUSHs(sv_2mortal(newSVpv(delim, 0)));
171
172 PUTBACK;
173 if (get_cv("main::define_flag_field", 0))
174 call_pv("main::define_flag_field", G_SCALAR);
175 SPAGAIN;
176 PUTBACK;
177 FREETMPS;
178 LEAVE;
179}
180
181static void define_event_symbols(struct event *event,
182 const char *ev_name,
183 struct print_arg *args)
184{
185 switch (args->type) {
186 case PRINT_NULL:
187 break;
188 case PRINT_ATOM:
189 define_flag_value(ev_name, cur_field_name, "0",
190 args->atom.atom);
191 zero_flag_atom = 0;
192 break;
193 case PRINT_FIELD:
194 if (cur_field_name)
195 free(cur_field_name);
196 cur_field_name = strdup(args->field.name);
197 break;
198 case PRINT_FLAGS:
199 define_event_symbols(event, ev_name, args->flags.field);
200 define_flag_field(ev_name, cur_field_name, args->flags.delim);
201 define_flag_values(args->flags.flags, ev_name, cur_field_name);
202 break;
203 case PRINT_SYMBOL:
204 define_event_symbols(event, ev_name, args->symbol.field);
205 define_symbolic_field(ev_name, cur_field_name);
206 define_symbolic_values(args->symbol.symbols, ev_name,
207 cur_field_name);
208 break;
209 case PRINT_STRING:
210 break;
211 case PRINT_TYPE:
212 define_event_symbols(event, ev_name, args->typecast.item);
213 break;
214 case PRINT_OP:
215 if (strcmp(args->op.op, ":") == 0)
216 zero_flag_atom = 1;
217 define_event_symbols(event, ev_name, args->op.left);
218 define_event_symbols(event, ev_name, args->op.right);
219 break;
220 default:
221 /* we should warn... */
222 return;
223 }
224
225 if (args->next)
226 define_event_symbols(event, ev_name, args->next);
227}
228
229static inline struct event *find_cache_event(int type)
230{
231 static char ev_name[256];
232 struct event *event;
233
234 if (events[type])
235 return events[type];
236
237 events[type] = event = trace_find_event(type);
238 if (!event)
239 return NULL;
240
241 sprintf(ev_name, "%s::%s", event->system, event->name);
242
243 define_event_symbols(event, ev_name, event->print_fmt.args);
244
245 return event;
246}
247
248static void perl_process_event(int cpu, void *data,
249 int size __unused,
250 unsigned long long nsecs, char *comm)
251{
252 struct format_field *field;
253 static char handler[256];
254 unsigned long long val;
255 unsigned long s, ns;
256 struct event *event;
257 int type;
258 int pid;
259
260 dSP;
261
262 type = trace_parse_common_type(data);
263
264 event = find_cache_event(type);
265 if (!event)
266 die("ug! no event found for type %d", type);
267
268 pid = trace_parse_common_pid(data);
269
270 sprintf(handler, "%s::%s", event->system, event->name);
271
272 s = nsecs / NSECS_PER_SEC;
273 ns = nsecs - s * NSECS_PER_SEC;
274
275 scripting_context->event_data = data;
276
277 ENTER;
278 SAVETMPS;
279 PUSHMARK(SP);
280
281 XPUSHs(sv_2mortal(newSVpv(handler, 0)));
282 XPUSHs(sv_2mortal(newSViv(PTR2IV(scripting_context))));
283 XPUSHs(sv_2mortal(newSVuv(cpu)));
284 XPUSHs(sv_2mortal(newSVuv(s)));
285 XPUSHs(sv_2mortal(newSVuv(ns)));
286 XPUSHs(sv_2mortal(newSViv(pid)));
287 XPUSHs(sv_2mortal(newSVpv(comm, 0)));
288
289 /* common fields other than pid can be accessed via xsub fns */
290
291 for (field = event->format.fields; field; field = field->next) {
292 if (field->flags & FIELD_IS_STRING) {
293 int offset;
294 if (field->flags & FIELD_IS_DYNAMIC) {
295 offset = *(int *)(data + field->offset);
296 offset &= 0xffff;
297 } else
298 offset = field->offset;
299 XPUSHs(sv_2mortal(newSVpv((char *)data + offset, 0)));
300 } else { /* FIELD_IS_NUMERIC */
301 val = read_size(data + field->offset, field->size);
302 if (field->flags & FIELD_IS_SIGNED) {
303 XPUSHs(sv_2mortal(newSViv(val)));
304 } else {
305 XPUSHs(sv_2mortal(newSVuv(val)));
306 }
307 }
308 }
309
310 PUTBACK;
311
312 if (get_cv(handler, 0))
313 call_pv(handler, G_SCALAR);
314 else if (get_cv("main::trace_unhandled", 0)) {
315 XPUSHs(sv_2mortal(newSVpv(handler, 0)));
316 XPUSHs(sv_2mortal(newSViv(PTR2IV(scripting_context))));
317 XPUSHs(sv_2mortal(newSVuv(cpu)));
318 XPUSHs(sv_2mortal(newSVuv(nsecs)));
319 XPUSHs(sv_2mortal(newSViv(pid)));
320 XPUSHs(sv_2mortal(newSVpv(comm, 0)));
321 call_pv("main::trace_unhandled", G_SCALAR);
322 }
323 SPAGAIN;
324 PUTBACK;
325 FREETMPS;
326 LEAVE;
327}
328
329static void run_start_sub(void)
330{
331 dSP; /* access to Perl stack */
332 PUSHMARK(SP);
333
334 if (get_cv("main::trace_begin", 0))
335 call_pv("main::trace_begin", G_DISCARD | G_NOARGS);
336}
337
338/*
339 * Start trace script
340 */
341static int perl_start_script(const char *script, int argc, const char **argv)
342{
343 const char **command_line;
344 int i, err = 0;
345
346 command_line = malloc((argc + 2) * sizeof(const char *));
347 command_line[0] = "";
348 command_line[1] = script;
349 for (i = 2; i < argc + 2; i++)
350 command_line[i] = argv[i - 2];
351
352 my_perl = perl_alloc();
353 perl_construct(my_perl);
354
355 if (perl_parse(my_perl, xs_init, argc + 2, (char **)command_line,
356 (char **)NULL)) {
357 err = -1;
358 goto error;
359 }
360
361 if (perl_run(my_perl)) {
362 err = -1;
363 goto error;
364 }
365
366 if (SvTRUE(ERRSV)) {
367 err = -1;
368 goto error;
369 }
370
371 run_start_sub();
372
373 free(command_line);
374 return 0;
375error:
376 perl_free(my_perl);
377 free(command_line);
378
379 return err;
380}
381
382/*
383 * Stop trace script
384 */
385static int perl_stop_script(void)
386{
387 dSP; /* access to Perl stack */
388 PUSHMARK(SP);
389
390 if (get_cv("main::trace_end", 0))
391 call_pv("main::trace_end", G_DISCARD | G_NOARGS);
392
393 perl_destruct(my_perl);
394 perl_free(my_perl);
395
396 return 0;
397}
398
399static int perl_generate_script(const char *outfile)
400{
401 struct event *event = NULL;
402 struct format_field *f;
403 char fname[PATH_MAX];
404 int not_first, count;
405 FILE *ofp;
406
407 sprintf(fname, "%s.pl", outfile);
408 ofp = fopen(fname, "w");
409 if (ofp == NULL) {
410 fprintf(stderr, "couldn't open %s\n", fname);
411 return -1;
412 }
413
414 fprintf(ofp, "# perf script event handlers, "
415 "generated by perf script -g perl\n");
416
417 fprintf(ofp, "# Licensed under the terms of the GNU GPL"
418 " License version 2\n\n");
419
420 fprintf(ofp, "# The common_* event handler fields are the most useful "
421 "fields common to\n");
422
423 fprintf(ofp, "# all events. They don't necessarily correspond to "
424 "the 'common_*' fields\n");
425
426 fprintf(ofp, "# in the format files. Those fields not available as "
427 "handler params can\n");
428
429 fprintf(ofp, "# be retrieved using Perl functions of the form "
430 "common_*($context).\n");
431
432 fprintf(ofp, "# See Context.pm for the list of available "
433 "functions.\n\n");
434
435 fprintf(ofp, "use lib \"$ENV{'PERF_EXEC_PATH'}/scripts/perl/"
436 "Perf-Trace-Util/lib\";\n");
437
438 fprintf(ofp, "use lib \"./Perf-Trace-Util/lib\";\n");
439 fprintf(ofp, "use Perf::Trace::Core;\n");
440 fprintf(ofp, "use Perf::Trace::Context;\n");
441 fprintf(ofp, "use Perf::Trace::Util;\n\n");
442
443 fprintf(ofp, "sub trace_begin\n{\n\t# optional\n}\n\n");
444 fprintf(ofp, "sub trace_end\n{\n\t# optional\n}\n\n");
445
446 while ((event = trace_find_next_event(event))) {
447 fprintf(ofp, "sub %s::%s\n{\n", event->system, event->name);
448 fprintf(ofp, "\tmy (");
449
450 fprintf(ofp, "$event_name, ");
451 fprintf(ofp, "$context, ");
452 fprintf(ofp, "$common_cpu, ");
453 fprintf(ofp, "$common_secs, ");
454 fprintf(ofp, "$common_nsecs,\n");
455 fprintf(ofp, "\t $common_pid, ");
456 fprintf(ofp, "$common_comm,\n\t ");
457
458 not_first = 0;
459 count = 0;
460
461 for (f = event->format.fields; f; f = f->next) {
462 if (not_first++)
463 fprintf(ofp, ", ");
464 if (++count % 5 == 0)
465 fprintf(ofp, "\n\t ");
466
467 fprintf(ofp, "$%s", f->name);
468 }
469 fprintf(ofp, ") = @_;\n\n");
470
471 fprintf(ofp, "\tprint_header($event_name, $common_cpu, "
472 "$common_secs, $common_nsecs,\n\t "
473 "$common_pid, $common_comm);\n\n");
474
475 fprintf(ofp, "\tprintf(\"");
476
477 not_first = 0;
478 count = 0;
479
480 for (f = event->format.fields; f; f = f->next) {
481 if (not_first++)
482 fprintf(ofp, ", ");
483 if (count && count % 4 == 0) {
484 fprintf(ofp, "\".\n\t \"");
485 }
486 count++;
487
488 fprintf(ofp, "%s=", f->name);
489 if (f->flags & FIELD_IS_STRING ||
490 f->flags & FIELD_IS_FLAG ||
491 f->flags & FIELD_IS_SYMBOLIC)
492 fprintf(ofp, "%%s");
493 else if (f->flags & FIELD_IS_SIGNED)
494 fprintf(ofp, "%%d");
495 else
496 fprintf(ofp, "%%u");
497 }
498
499 fprintf(ofp, "\\n\",\n\t ");
500
501 not_first = 0;
502 count = 0;
503
504 for (f = event->format.fields; f; f = f->next) {
505 if (not_first++)
506 fprintf(ofp, ", ");
507
508 if (++count % 5 == 0)
509 fprintf(ofp, "\n\t ");
510
511 if (f->flags & FIELD_IS_FLAG) {
512 if ((count - 1) % 5 != 0) {
513 fprintf(ofp, "\n\t ");
514 count = 4;
515 }
516 fprintf(ofp, "flag_str(\"");
517 fprintf(ofp, "%s::%s\", ", event->system,
518 event->name);
519 fprintf(ofp, "\"%s\", $%s)", f->name,
520 f->name);
521 } else if (f->flags & FIELD_IS_SYMBOLIC) {
522 if ((count - 1) % 5 != 0) {
523 fprintf(ofp, "\n\t ");
524 count = 4;
525 }
526 fprintf(ofp, "symbol_str(\"");
527 fprintf(ofp, "%s::%s\", ", event->system,
528 event->name);
529 fprintf(ofp, "\"%s\", $%s)", f->name,
530 f->name);
531 } else
532 fprintf(ofp, "$%s", f->name);
533 }
534
535 fprintf(ofp, ");\n");
536 fprintf(ofp, "}\n\n");
537 }
538
539 fprintf(ofp, "sub trace_unhandled\n{\n\tmy ($event_name, $context, "
540 "$common_cpu, $common_secs, $common_nsecs,\n\t "
541 "$common_pid, $common_comm) = @_;\n\n");
542
543 fprintf(ofp, "\tprint_header($event_name, $common_cpu, "
544 "$common_secs, $common_nsecs,\n\t $common_pid, "
545 "$common_comm);\n}\n\n");
546
547 fprintf(ofp, "sub print_header\n{\n"
548 "\tmy ($event_name, $cpu, $secs, $nsecs, $pid, $comm) = @_;\n\n"
549 "\tprintf(\"%%-20s %%5u %%05u.%%09u %%8u %%-20s \",\n\t "
550 "$event_name, $cpu, $secs, $nsecs, $pid, $comm);\n}");
551
552 fclose(ofp);
553
554 fprintf(stderr, "generated Perl script: %s\n", fname);
555
556 return 0;
557}
558
559struct scripting_ops perl_scripting_ops = {
560 .name = "Perl",
561 .start_script = perl_start_script,
562 .stop_script = perl_stop_script,
563 .process_event = perl_process_event,
564 .generate_script = perl_generate_script,
565};
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c
new file mode 100644
index 00000000000..c6d99334bdf
--- /dev/null
+++ b/tools/perf/util/scripting-engines/trace-event-python.c
@@ -0,0 +1,594 @@
1/*
2 * trace-event-python. Feed trace events to an embedded Python interpreter.
3 *
4 * Copyright (C) 2010 Tom Zanussi <tzanussi@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20 */
21
22#include <Python.h>
23
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27#include <ctype.h>
28#include <errno.h>
29
30#include "../../perf.h"
31#include "../util.h"
32#include "../trace-event.h"
33
34PyMODINIT_FUNC initperf_trace_context(void);
35
36#define FTRACE_MAX_EVENT \
37 ((1 << (sizeof(unsigned short) * 8)) - 1)
38
39struct event *events[FTRACE_MAX_EVENT];
40
41#define MAX_FIELDS 64
42#define N_COMMON_FIELDS 7
43
44extern struct scripting_context *scripting_context;
45
46static char *cur_field_name;
47static int zero_flag_atom;
48
49static PyObject *main_module, *main_dict;
50
51static void handler_call_die(const char *handler_name)
52{
53 PyErr_Print();
54 Py_FatalError("problem in Python trace event handler");
55}
56
57static void define_value(enum print_arg_type field_type,
58 const char *ev_name,
59 const char *field_name,
60 const char *field_value,
61 const char *field_str)
62{
63 const char *handler_name = "define_flag_value";
64 PyObject *handler, *t, *retval;
65 unsigned long long value;
66 unsigned n = 0;
67
68 if (field_type == PRINT_SYMBOL)
69 handler_name = "define_symbolic_value";
70
71 t = PyTuple_New(4);
72 if (!t)
73 Py_FatalError("couldn't create Python tuple");
74
75 value = eval_flag(field_value);
76
77 PyTuple_SetItem(t, n++, PyString_FromString(ev_name));
78 PyTuple_SetItem(t, n++, PyString_FromString(field_name));
79 PyTuple_SetItem(t, n++, PyInt_FromLong(value));
80 PyTuple_SetItem(t, n++, PyString_FromString(field_str));
81
82 handler = PyDict_GetItemString(main_dict, handler_name);
83 if (handler && PyCallable_Check(handler)) {
84 retval = PyObject_CallObject(handler, t);
85 if (retval == NULL)
86 handler_call_die(handler_name);
87 }
88
89 Py_DECREF(t);
90}
91
92static void define_values(enum print_arg_type field_type,
93 struct print_flag_sym *field,
94 const char *ev_name,
95 const char *field_name)
96{
97 define_value(field_type, ev_name, field_name, field->value,
98 field->str);
99
100 if (field->next)
101 define_values(field_type, field->next, ev_name, field_name);
102}
103
104static void define_field(enum print_arg_type field_type,
105 const char *ev_name,
106 const char *field_name,
107 const char *delim)
108{
109 const char *handler_name = "define_flag_field";
110 PyObject *handler, *t, *retval;
111 unsigned n = 0;
112
113 if (field_type == PRINT_SYMBOL)
114 handler_name = "define_symbolic_field";
115
116 if (field_type == PRINT_FLAGS)
117 t = PyTuple_New(3);
118 else
119 t = PyTuple_New(2);
120 if (!t)
121 Py_FatalError("couldn't create Python tuple");
122
123 PyTuple_SetItem(t, n++, PyString_FromString(ev_name));
124 PyTuple_SetItem(t, n++, PyString_FromString(field_name));
125 if (field_type == PRINT_FLAGS)
126 PyTuple_SetItem(t, n++, PyString_FromString(delim));
127
128 handler = PyDict_GetItemString(main_dict, handler_name);
129 if (handler && PyCallable_Check(handler)) {
130 retval = PyObject_CallObject(handler, t);
131 if (retval == NULL)
132 handler_call_die(handler_name);
133 }
134
135 Py_DECREF(t);
136}
137
138static void define_event_symbols(struct event *event,
139 const char *ev_name,
140 struct print_arg *args)
141{
142 switch (args->type) {
143 case PRINT_NULL:
144 break;
145 case PRINT_ATOM:
146 define_value(PRINT_FLAGS, ev_name, cur_field_name, "0",
147 args->atom.atom);
148 zero_flag_atom = 0;
149 break;
150 case PRINT_FIELD:
151 if (cur_field_name)
152 free(cur_field_name);
153 cur_field_name = strdup(args->field.name);
154 break;
155 case PRINT_FLAGS:
156 define_event_symbols(event, ev_name, args->flags.field);
157 define_field(PRINT_FLAGS, ev_name, cur_field_name,
158 args->flags.delim);
159 define_values(PRINT_FLAGS, args->flags.flags, ev_name,
160 cur_field_name);
161 break;
162 case PRINT_SYMBOL:
163 define_event_symbols(event, ev_name, args->symbol.field);
164 define_field(PRINT_SYMBOL, ev_name, cur_field_name, NULL);
165 define_values(PRINT_SYMBOL, args->symbol.symbols, ev_name,
166 cur_field_name);
167 break;
168 case PRINT_STRING:
169 break;
170 case PRINT_TYPE:
171 define_event_symbols(event, ev_name, args->typecast.item);
172 break;
173 case PRINT_OP:
174 if (strcmp(args->op.op, ":") == 0)
175 zero_flag_atom = 1;
176 define_event_symbols(event, ev_name, args->op.left);
177 define_event_symbols(event, ev_name, args->op.right);
178 break;
179 default:
180 /* we should warn... */
181 return;
182 }
183
184 if (args->next)
185 define_event_symbols(event, ev_name, args->next);
186}
187
188static inline struct event *find_cache_event(int type)
189{
190 static char ev_name[256];
191 struct event *event;
192
193 if (events[type])
194 return events[type];
195
196 events[type] = event = trace_find_event(type);
197 if (!event)
198 return NULL;
199
200 sprintf(ev_name, "%s__%s", event->system, event->name);
201
202 define_event_symbols(event, ev_name, event->print_fmt.args);
203
204 return event;
205}
206
207static void python_process_event(int cpu, void *data,
208 int size __unused,
209 unsigned long long nsecs, char *comm)
210{
211 PyObject *handler, *retval, *context, *t, *obj, *dict = NULL;
212 static char handler_name[256];
213 struct format_field *field;
214 unsigned long long val;
215 unsigned long s, ns;
216 struct event *event;
217 unsigned n = 0;
218 int type;
219 int pid;
220
221 t = PyTuple_New(MAX_FIELDS);
222 if (!t)
223 Py_FatalError("couldn't create Python tuple");
224
225 type = trace_parse_common_type(data);
226
227 event = find_cache_event(type);
228 if (!event)
229 die("ug! no event found for type %d", type);
230
231 pid = trace_parse_common_pid(data);
232
233 sprintf(handler_name, "%s__%s", event->system, event->name);
234
235 handler = PyDict_GetItemString(main_dict, handler_name);
236 if (handler && !PyCallable_Check(handler))
237 handler = NULL;
238 if (!handler) {
239 dict = PyDict_New();
240 if (!dict)
241 Py_FatalError("couldn't create Python dict");
242 }
243 s = nsecs / NSECS_PER_SEC;
244 ns = nsecs - s * NSECS_PER_SEC;
245
246 scripting_context->event_data = data;
247
248 context = PyCObject_FromVoidPtr(scripting_context, NULL);
249
250 PyTuple_SetItem(t, n++, PyString_FromString(handler_name));
251 PyTuple_SetItem(t, n++,
252 PyCObject_FromVoidPtr(scripting_context, NULL));
253
254 if (handler) {
255 PyTuple_SetItem(t, n++, PyInt_FromLong(cpu));
256 PyTuple_SetItem(t, n++, PyInt_FromLong(s));
257 PyTuple_SetItem(t, n++, PyInt_FromLong(ns));
258 PyTuple_SetItem(t, n++, PyInt_FromLong(pid));
259 PyTuple_SetItem(t, n++, PyString_FromString(comm));
260 } else {
261 PyDict_SetItemString(dict, "common_cpu", PyInt_FromLong(cpu));
262 PyDict_SetItemString(dict, "common_s", PyInt_FromLong(s));
263 PyDict_SetItemString(dict, "common_ns", PyInt_FromLong(ns));
264 PyDict_SetItemString(dict, "common_pid", PyInt_FromLong(pid));
265 PyDict_SetItemString(dict, "common_comm", PyString_FromString(comm));
266 }
267 for (field = event->format.fields; field; field = field->next) {
268 if (field->flags & FIELD_IS_STRING) {
269 int offset;
270 if (field->flags & FIELD_IS_DYNAMIC) {
271 offset = *(int *)(data + field->offset);
272 offset &= 0xffff;
273 } else
274 offset = field->offset;
275 obj = PyString_FromString((char *)data + offset);
276 } else { /* FIELD_IS_NUMERIC */
277 val = read_size(data + field->offset, field->size);
278 if (field->flags & FIELD_IS_SIGNED) {
279 if ((long long)val >= LONG_MIN &&
280 (long long)val <= LONG_MAX)
281 obj = PyInt_FromLong(val);
282 else
283 obj = PyLong_FromLongLong(val);
284 } else {
285 if (val <= LONG_MAX)
286 obj = PyInt_FromLong(val);
287 else
288 obj = PyLong_FromUnsignedLongLong(val);
289 }
290 }
291 if (handler)
292 PyTuple_SetItem(t, n++, obj);
293 else
294 PyDict_SetItemString(dict, field->name, obj);
295
296 }
297 if (!handler)
298 PyTuple_SetItem(t, n++, dict);
299
300 if (_PyTuple_Resize(&t, n) == -1)
301 Py_FatalError("error resizing Python tuple");
302
303 if (handler) {
304 retval = PyObject_CallObject(handler, t);
305 if (retval == NULL)
306 handler_call_die(handler_name);
307 } else {
308 handler = PyDict_GetItemString(main_dict, "trace_unhandled");
309 if (handler && PyCallable_Check(handler)) {
310
311 retval = PyObject_CallObject(handler, t);
312 if (retval == NULL)
313 handler_call_die("trace_unhandled");
314 }
315 Py_DECREF(dict);
316 }
317
318 Py_DECREF(t);
319}
320
321static int run_start_sub(void)
322{
323 PyObject *handler, *retval;
324 int err = 0;
325
326 main_module = PyImport_AddModule("__main__");
327 if (main_module == NULL)
328 return -1;
329 Py_INCREF(main_module);
330
331 main_dict = PyModule_GetDict(main_module);
332 if (main_dict == NULL) {
333 err = -1;
334 goto error;
335 }
336 Py_INCREF(main_dict);
337
338 handler = PyDict_GetItemString(main_dict, "trace_begin");
339 if (handler == NULL || !PyCallable_Check(handler))
340 goto out;
341
342 retval = PyObject_CallObject(handler, NULL);
343 if (retval == NULL)
344 handler_call_die("trace_begin");
345
346 Py_DECREF(retval);
347 return err;
348error:
349 Py_XDECREF(main_dict);
350 Py_XDECREF(main_module);
351out:
352 return err;
353}
354
355/*
356 * Start trace script
357 */
358static int python_start_script(const char *script, int argc, const char **argv)
359{
360 const char **command_line;
361 char buf[PATH_MAX];
362 int i, err = 0;
363 FILE *fp;
364
365 command_line = malloc((argc + 1) * sizeof(const char *));
366 command_line[0] = script;
367 for (i = 1; i < argc + 1; i++)
368 command_line[i] = argv[i - 1];
369
370 Py_Initialize();
371
372 initperf_trace_context();
373
374 PySys_SetArgv(argc + 1, (char **)command_line);
375
376 fp = fopen(script, "r");
377 if (!fp) {
378 sprintf(buf, "Can't open python script \"%s\"", script);
379 perror(buf);
380 err = -1;
381 goto error;
382 }
383
384 err = PyRun_SimpleFile(fp, script);
385 if (err) {
386 fprintf(stderr, "Error running python script %s\n", script);
387 goto error;
388 }
389
390 err = run_start_sub();
391 if (err) {
392 fprintf(stderr, "Error starting python script %s\n", script);
393 goto error;
394 }
395
396 free(command_line);
397
398 return err;
399error:
400 Py_Finalize();
401 free(command_line);
402
403 return err;
404}
405
406/*
407 * Stop trace script
408 */
409static int python_stop_script(void)
410{
411 PyObject *handler, *retval;
412 int err = 0;
413
414 handler = PyDict_GetItemString(main_dict, "trace_end");
415 if (handler == NULL || !PyCallable_Check(handler))
416 goto out;
417
418 retval = PyObject_CallObject(handler, NULL);
419 if (retval == NULL)
420 handler_call_die("trace_end");
421 else
422 Py_DECREF(retval);
423out:
424 Py_XDECREF(main_dict);
425 Py_XDECREF(main_module);
426 Py_Finalize();
427
428 return err;
429}
430
431static int python_generate_script(const char *outfile)
432{
433 struct event *event = NULL;
434 struct format_field *f;
435 char fname[PATH_MAX];
436 int not_first, count;
437 FILE *ofp;
438
439 sprintf(fname, "%s.py", outfile);
440 ofp = fopen(fname, "w");
441 if (ofp == NULL) {
442 fprintf(stderr, "couldn't open %s\n", fname);
443 return -1;
444 }
445 fprintf(ofp, "# perf script event handlers, "
446 "generated by perf script -g python\n");
447
448 fprintf(ofp, "# Licensed under the terms of the GNU GPL"
449 " License version 2\n\n");
450
451 fprintf(ofp, "# The common_* event handler fields are the most useful "
452 "fields common to\n");
453
454 fprintf(ofp, "# all events. They don't necessarily correspond to "
455 "the 'common_*' fields\n");
456
457 fprintf(ofp, "# in the format files. Those fields not available as "
458 "handler params can\n");
459
460 fprintf(ofp, "# be retrieved using Python functions of the form "
461 "common_*(context).\n");
462
463 fprintf(ofp, "# See the perf-trace-python Documentation for the list "
464 "of available functions.\n\n");
465
466 fprintf(ofp, "import os\n");
467 fprintf(ofp, "import sys\n\n");
468
469 fprintf(ofp, "sys.path.append(os.environ['PERF_EXEC_PATH'] + \\\n");
470 fprintf(ofp, "\t'/scripts/python/Perf-Trace-Util/lib/Perf/Trace')\n");
471 fprintf(ofp, "\nfrom perf_trace_context import *\n");
472 fprintf(ofp, "from Core import *\n\n\n");
473
474 fprintf(ofp, "def trace_begin():\n");
475 fprintf(ofp, "\tprint \"in trace_begin\"\n\n");
476
477 fprintf(ofp, "def trace_end():\n");
478 fprintf(ofp, "\tprint \"in trace_end\"\n\n");
479
480 while ((event = trace_find_next_event(event))) {
481 fprintf(ofp, "def %s__%s(", event->system, event->name);
482 fprintf(ofp, "event_name, ");
483 fprintf(ofp, "context, ");
484 fprintf(ofp, "common_cpu,\n");
485 fprintf(ofp, "\tcommon_secs, ");
486 fprintf(ofp, "common_nsecs, ");
487 fprintf(ofp, "common_pid, ");
488 fprintf(ofp, "common_comm,\n\t");
489
490 not_first = 0;
491 count = 0;
492
493 for (f = event->format.fields; f; f = f->next) {
494 if (not_first++)
495 fprintf(ofp, ", ");
496 if (++count % 5 == 0)
497 fprintf(ofp, "\n\t");
498
499 fprintf(ofp, "%s", f->name);
500 }
501 fprintf(ofp, "):\n");
502
503 fprintf(ofp, "\t\tprint_header(event_name, common_cpu, "
504 "common_secs, common_nsecs,\n\t\t\t"
505 "common_pid, common_comm)\n\n");
506
507 fprintf(ofp, "\t\tprint \"");
508
509 not_first = 0;
510 count = 0;
511
512 for (f = event->format.fields; f; f = f->next) {
513 if (not_first++)
514 fprintf(ofp, ", ");
515 if (count && count % 3 == 0) {
516 fprintf(ofp, "\" \\\n\t\t\"");
517 }
518 count++;
519
520 fprintf(ofp, "%s=", f->name);
521 if (f->flags & FIELD_IS_STRING ||
522 f->flags & FIELD_IS_FLAG ||
523 f->flags & FIELD_IS_SYMBOLIC)
524 fprintf(ofp, "%%s");
525 else if (f->flags & FIELD_IS_SIGNED)
526 fprintf(ofp, "%%d");
527 else
528 fprintf(ofp, "%%u");
529 }
530
531 fprintf(ofp, "\\n\" %% \\\n\t\t(");
532
533 not_first = 0;
534 count = 0;
535
536 for (f = event->format.fields; f; f = f->next) {
537 if (not_first++)
538 fprintf(ofp, ", ");
539
540 if (++count % 5 == 0)
541 fprintf(ofp, "\n\t\t");
542
543 if (f->flags & FIELD_IS_FLAG) {
544 if ((count - 1) % 5 != 0) {
545 fprintf(ofp, "\n\t\t");
546 count = 4;
547 }
548 fprintf(ofp, "flag_str(\"");
549 fprintf(ofp, "%s__%s\", ", event->system,
550 event->name);
551 fprintf(ofp, "\"%s\", %s)", f->name,
552 f->name);
553 } else if (f->flags & FIELD_IS_SYMBOLIC) {
554 if ((count - 1) % 5 != 0) {
555 fprintf(ofp, "\n\t\t");
556 count = 4;
557 }
558 fprintf(ofp, "symbol_str(\"");
559 fprintf(ofp, "%s__%s\", ", event->system,
560 event->name);
561 fprintf(ofp, "\"%s\", %s)", f->name,
562 f->name);
563 } else
564 fprintf(ofp, "%s", f->name);
565 }
566
567 fprintf(ofp, "),\n\n");
568 }
569
570 fprintf(ofp, "def trace_unhandled(event_name, context, "
571 "event_fields_dict):\n");
572
573 fprintf(ofp, "\t\tprint ' '.join(['%%s=%%s'%%(k,str(v))"
574 "for k,v in sorted(event_fields_dict.items())])\n\n");
575
576 fprintf(ofp, "def print_header("
577 "event_name, cpu, secs, nsecs, pid, comm):\n"
578 "\tprint \"%%-20s %%5u %%05u.%%09u %%8u %%-20s \" %% \\\n\t"
579 "(event_name, cpu, secs, nsecs, pid, comm),\n");
580
581 fclose(ofp);
582
583 fprintf(stderr, "generated Python script: %s\n", fname);
584
585 return 0;
586}
587
588struct scripting_ops python_scripting_ops = {
589 .name = "Python",
590 .start_script = python_start_script,
591 .stop_script = python_stop_script,
592 .process_event = python_process_event,
593 .generate_script = python_generate_script,
594};
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
new file mode 100644
index 00000000000..313dac2d94c
--- /dev/null
+++ b/tools/perf/util/session.c
@@ -0,0 +1,1134 @@
1#define _FILE_OFFSET_BITS 64
2
3#include <linux/kernel.h>
4
5#include <byteswap.h>
6#include <unistd.h>
7#include <sys/types.h>
8#include <sys/mman.h>
9
10#include "session.h"
11#include "sort.h"
12#include "util.h"
13
14static int perf_session__open(struct perf_session *self, bool force)
15{
16 struct stat input_stat;
17
18 if (!strcmp(self->filename, "-")) {
19 self->fd_pipe = true;
20 self->fd = STDIN_FILENO;
21
22 if (perf_header__read(self, self->fd) < 0)
23 pr_err("incompatible file format");
24
25 return 0;
26 }
27
28 self->fd = open(self->filename, O_RDONLY);
29 if (self->fd < 0) {
30 int err = errno;
31
32 pr_err("failed to open %s: %s", self->filename, strerror(err));
33 if (err == ENOENT && !strcmp(self->filename, "perf.data"))
34 pr_err(" (try 'perf record' first)");
35 pr_err("\n");
36 return -errno;
37 }
38
39 if (fstat(self->fd, &input_stat) < 0)
40 goto out_close;
41
42 if (!force && input_stat.st_uid && (input_stat.st_uid != geteuid())) {
43 pr_err("file %s not owned by current user or root\n",
44 self->filename);
45 goto out_close;
46 }
47
48 if (!input_stat.st_size) {
49 pr_info("zero-sized file (%s), nothing to do!\n",
50 self->filename);
51 goto out_close;
52 }
53
54 if (perf_header__read(self, self->fd) < 0) {
55 pr_err("incompatible file format");
56 goto out_close;
57 }
58
59 self->size = input_stat.st_size;
60 return 0;
61
62out_close:
63 close(self->fd);
64 self->fd = -1;
65 return -1;
66}
67
68static void perf_session__id_header_size(struct perf_session *session)
69{
70 struct sample_data *data;
71 u64 sample_type = session->sample_type;
72 u16 size = 0;
73
74 if (!session->sample_id_all)
75 goto out;
76
77 if (sample_type & PERF_SAMPLE_TID)
78 size += sizeof(data->tid) * 2;
79
80 if (sample_type & PERF_SAMPLE_TIME)
81 size += sizeof(data->time);
82
83 if (sample_type & PERF_SAMPLE_ID)
84 size += sizeof(data->id);
85
86 if (sample_type & PERF_SAMPLE_STREAM_ID)
87 size += sizeof(data->stream_id);
88
89 if (sample_type & PERF_SAMPLE_CPU)
90 size += sizeof(data->cpu) * 2;
91out:
92 session->id_hdr_size = size;
93}
94
95void perf_session__set_sample_id_all(struct perf_session *session, bool value)
96{
97 session->sample_id_all = value;
98 perf_session__id_header_size(session);
99}
100
101void perf_session__set_sample_type(struct perf_session *session, u64 type)
102{
103 session->sample_type = type;
104}
105
106void perf_session__update_sample_type(struct perf_session *self)
107{
108 self->sample_type = perf_header__sample_type(&self->header);
109 self->sample_id_all = perf_header__sample_id_all(&self->header);
110 perf_session__id_header_size(self);
111}
112
113int perf_session__create_kernel_maps(struct perf_session *self)
114{
115 int ret = machine__create_kernel_maps(&self->host_machine);
116
117 if (ret >= 0)
118 ret = machines__create_guest_kernel_maps(&self->machines);
119 return ret;
120}
121
122static void perf_session__destroy_kernel_maps(struct perf_session *self)
123{
124 machine__destroy_kernel_maps(&self->host_machine);
125 machines__destroy_guest_kernel_maps(&self->machines);
126}
127
128struct perf_session *perf_session__new(const char *filename, int mode,
129 bool force, bool repipe,
130 struct perf_event_ops *ops)
131{
132 size_t len = filename ? strlen(filename) + 1 : 0;
133 struct perf_session *self = zalloc(sizeof(*self) + len);
134
135 if (self == NULL)
136 goto out;
137
138 if (perf_header__init(&self->header) < 0)
139 goto out_free;
140
141 memcpy(self->filename, filename, len);
142 self->threads = RB_ROOT;
143 INIT_LIST_HEAD(&self->dead_threads);
144 self->hists_tree = RB_ROOT;
145 self->last_match = NULL;
146 /*
147 * On 64bit we can mmap the data file in one go. No need for tiny mmap
148 * slices. On 32bit we use 32MB.
149 */
150#if BITS_PER_LONG == 64
151 self->mmap_window = ULLONG_MAX;
152#else
153 self->mmap_window = 32 * 1024 * 1024ULL;
154#endif
155 self->machines = RB_ROOT;
156 self->repipe = repipe;
157 INIT_LIST_HEAD(&self->ordered_samples.samples);
158 INIT_LIST_HEAD(&self->ordered_samples.sample_cache);
159 INIT_LIST_HEAD(&self->ordered_samples.to_free);
160 machine__init(&self->host_machine, "", HOST_KERNEL_ID);
161
162 if (mode == O_RDONLY) {
163 if (perf_session__open(self, force) < 0)
164 goto out_delete;
165 } else if (mode == O_WRONLY) {
166 /*
167 * In O_RDONLY mode this will be performed when reading the
168 * kernel MMAP event, in event__process_mmap().
169 */
170 if (perf_session__create_kernel_maps(self) < 0)
171 goto out_delete;
172 }
173
174 perf_session__update_sample_type(self);
175
176 if (ops && ops->ordering_requires_timestamps &&
177 ops->ordered_samples && !self->sample_id_all) {
178 dump_printf("WARNING: No sample_id_all support, falling back to unordered processing\n");
179 ops->ordered_samples = false;
180 }
181
182out:
183 return self;
184out_free:
185 free(self);
186 return NULL;
187out_delete:
188 perf_session__delete(self);
189 return NULL;
190}
191
192static void perf_session__delete_dead_threads(struct perf_session *self)
193{
194 struct thread *n, *t;
195
196 list_for_each_entry_safe(t, n, &self->dead_threads, node) {
197 list_del(&t->node);
198 thread__delete(t);
199 }
200}
201
202static void perf_session__delete_threads(struct perf_session *self)
203{
204 struct rb_node *nd = rb_first(&self->threads);
205
206 while (nd) {
207 struct thread *t = rb_entry(nd, struct thread, rb_node);
208
209 rb_erase(&t->rb_node, &self->threads);
210 nd = rb_next(nd);
211 thread__delete(t);
212 }
213}
214
215void perf_session__delete(struct perf_session *self)
216{
217 perf_header__exit(&self->header);
218 perf_session__destroy_kernel_maps(self);
219 perf_session__delete_dead_threads(self);
220 perf_session__delete_threads(self);
221 machine__exit(&self->host_machine);
222 close(self->fd);
223 free(self);
224}
225
226void perf_session__remove_thread(struct perf_session *self, struct thread *th)
227{
228 self->last_match = NULL;
229 rb_erase(&th->rb_node, &self->threads);
230 /*
231 * We may have references to this thread, for instance in some hist_entry
232 * instances, so just move them to a separate list.
233 */
234 list_add_tail(&th->node, &self->dead_threads);
235}
236
237static bool symbol__match_parent_regex(struct symbol *sym)
238{
239 if (sym->name && !regexec(&parent_regex, sym->name, 0, NULL, 0))
240 return 1;
241
242 return 0;
243}
244
245struct map_symbol *perf_session__resolve_callchain(struct perf_session *self,
246 struct thread *thread,
247 struct ip_callchain *chain,
248 struct symbol **parent)
249{
250 u8 cpumode = PERF_RECORD_MISC_USER;
251 unsigned int i;
252 struct map_symbol *syms = calloc(chain->nr, sizeof(*syms));
253
254 if (!syms)
255 return NULL;
256
257 for (i = 0; i < chain->nr; i++) {
258 u64 ip = chain->ips[i];
259 struct addr_location al;
260
261 if (ip >= PERF_CONTEXT_MAX) {
262 switch (ip) {
263 case PERF_CONTEXT_HV:
264 cpumode = PERF_RECORD_MISC_HYPERVISOR; break;
265 case PERF_CONTEXT_KERNEL:
266 cpumode = PERF_RECORD_MISC_KERNEL; break;
267 case PERF_CONTEXT_USER:
268 cpumode = PERF_RECORD_MISC_USER; break;
269 default:
270 break;
271 }
272 continue;
273 }
274
275 al.filtered = false;
276 thread__find_addr_location(thread, self, cpumode,
277 MAP__FUNCTION, thread->pid, ip, &al, NULL);
278 if (al.sym != NULL) {
279 if (sort__has_parent && !*parent &&
280 symbol__match_parent_regex(al.sym))
281 *parent = al.sym;
282 if (!symbol_conf.use_callchain)
283 break;
284 syms[i].map = al.map;
285 syms[i].sym = al.sym;
286 }
287 }
288
289 return syms;
290}
291
292static int process_event_synth_stub(event_t *event __used,
293 struct perf_session *session __used)
294{
295 dump_printf(": unhandled!\n");
296 return 0;
297}
298
299static int process_event_stub(event_t *event __used,
300 struct sample_data *sample __used,
301 struct perf_session *session __used)
302{
303 dump_printf(": unhandled!\n");
304 return 0;
305}
306
307static int process_finished_round_stub(event_t *event __used,
308 struct perf_session *session __used,
309 struct perf_event_ops *ops __used)
310{
311 dump_printf(": unhandled!\n");
312 return 0;
313}
314
315static int process_finished_round(event_t *event,
316 struct perf_session *session,
317 struct perf_event_ops *ops);
318
319static void perf_event_ops__fill_defaults(struct perf_event_ops *handler)
320{
321 if (handler->sample == NULL)
322 handler->sample = process_event_stub;
323 if (handler->mmap == NULL)
324 handler->mmap = process_event_stub;
325 if (handler->comm == NULL)
326 handler->comm = process_event_stub;
327 if (handler->fork == NULL)
328 handler->fork = process_event_stub;
329 if (handler->exit == NULL)
330 handler->exit = process_event_stub;
331 if (handler->lost == NULL)
332 handler->lost = event__process_lost;
333 if (handler->read == NULL)
334 handler->read = process_event_stub;
335 if (handler->throttle == NULL)
336 handler->throttle = process_event_stub;
337 if (handler->unthrottle == NULL)
338 handler->unthrottle = process_event_stub;
339 if (handler->attr == NULL)
340 handler->attr = process_event_synth_stub;
341 if (handler->event_type == NULL)
342 handler->event_type = process_event_synth_stub;
343 if (handler->tracing_data == NULL)
344 handler->tracing_data = process_event_synth_stub;
345 if (handler->build_id == NULL)
346 handler->build_id = process_event_synth_stub;
347 if (handler->finished_round == NULL) {
348 if (handler->ordered_samples)
349 handler->finished_round = process_finished_round;
350 else
351 handler->finished_round = process_finished_round_stub;
352 }
353}
354
355void mem_bswap_64(void *src, int byte_size)
356{
357 u64 *m = src;
358
359 while (byte_size > 0) {
360 *m = bswap_64(*m);
361 byte_size -= sizeof(u64);
362 ++m;
363 }
364}
365
366static void event__all64_swap(event_t *self)
367{
368 struct perf_event_header *hdr = &self->header;
369 mem_bswap_64(hdr + 1, self->header.size - sizeof(*hdr));
370}
371
372static void event__comm_swap(event_t *self)
373{
374 self->comm.pid = bswap_32(self->comm.pid);
375 self->comm.tid = bswap_32(self->comm.tid);
376}
377
378static void event__mmap_swap(event_t *self)
379{
380 self->mmap.pid = bswap_32(self->mmap.pid);
381 self->mmap.tid = bswap_32(self->mmap.tid);
382 self->mmap.start = bswap_64(self->mmap.start);
383 self->mmap.len = bswap_64(self->mmap.len);
384 self->mmap.pgoff = bswap_64(self->mmap.pgoff);
385}
386
387static void event__task_swap(event_t *self)
388{
389 self->fork.pid = bswap_32(self->fork.pid);
390 self->fork.tid = bswap_32(self->fork.tid);
391 self->fork.ppid = bswap_32(self->fork.ppid);
392 self->fork.ptid = bswap_32(self->fork.ptid);
393 self->fork.time = bswap_64(self->fork.time);
394}
395
396static void event__read_swap(event_t *self)
397{
398 self->read.pid = bswap_32(self->read.pid);
399 self->read.tid = bswap_32(self->read.tid);
400 self->read.value = bswap_64(self->read.value);
401 self->read.time_enabled = bswap_64(self->read.time_enabled);
402 self->read.time_running = bswap_64(self->read.time_running);
403 self->read.id = bswap_64(self->read.id);
404}
405
406static void event__attr_swap(event_t *self)
407{
408 size_t size;
409
410 self->attr.attr.type = bswap_32(self->attr.attr.type);
411 self->attr.attr.size = bswap_32(self->attr.attr.size);
412 self->attr.attr.config = bswap_64(self->attr.attr.config);
413 self->attr.attr.sample_period = bswap_64(self->attr.attr.sample_period);
414 self->attr.attr.sample_type = bswap_64(self->attr.attr.sample_type);
415 self->attr.attr.read_format = bswap_64(self->attr.attr.read_format);
416 self->attr.attr.wakeup_events = bswap_32(self->attr.attr.wakeup_events);
417 self->attr.attr.bp_type = bswap_32(self->attr.attr.bp_type);
418 self->attr.attr.bp_addr = bswap_64(self->attr.attr.bp_addr);
419 self->attr.attr.bp_len = bswap_64(self->attr.attr.bp_len);
420
421 size = self->header.size;
422 size -= (void *)&self->attr.id - (void *)self;
423 mem_bswap_64(self->attr.id, size);
424}
425
426static void event__event_type_swap(event_t *self)
427{
428 self->event_type.event_type.event_id =
429 bswap_64(self->event_type.event_type.event_id);
430}
431
432static void event__tracing_data_swap(event_t *self)
433{
434 self->tracing_data.size = bswap_32(self->tracing_data.size);
435}
436
437typedef void (*event__swap_op)(event_t *self);
438
439static event__swap_op event__swap_ops[] = {
440 [PERF_RECORD_MMAP] = event__mmap_swap,
441 [PERF_RECORD_COMM] = event__comm_swap,
442 [PERF_RECORD_FORK] = event__task_swap,
443 [PERF_RECORD_EXIT] = event__task_swap,
444 [PERF_RECORD_LOST] = event__all64_swap,
445 [PERF_RECORD_READ] = event__read_swap,
446 [PERF_RECORD_SAMPLE] = event__all64_swap,
447 [PERF_RECORD_HEADER_ATTR] = event__attr_swap,
448 [PERF_RECORD_HEADER_EVENT_TYPE] = event__event_type_swap,
449 [PERF_RECORD_HEADER_TRACING_DATA] = event__tracing_data_swap,
450 [PERF_RECORD_HEADER_BUILD_ID] = NULL,
451 [PERF_RECORD_HEADER_MAX] = NULL,
452};
453
454struct sample_queue {
455 u64 timestamp;
456 u64 file_offset;
457 event_t *event;
458 struct list_head list;
459};
460
461static void perf_session_free_sample_buffers(struct perf_session *session)
462{
463 struct ordered_samples *os = &session->ordered_samples;
464
465 while (!list_empty(&os->to_free)) {
466 struct sample_queue *sq;
467
468 sq = list_entry(os->to_free.next, struct sample_queue, list);
469 list_del(&sq->list);
470 free(sq);
471 }
472}
473
474static int perf_session_deliver_event(struct perf_session *session,
475 event_t *event,
476 struct sample_data *sample,
477 struct perf_event_ops *ops,
478 u64 file_offset);
479
480static void flush_sample_queue(struct perf_session *s,
481 struct perf_event_ops *ops)
482{
483 struct ordered_samples *os = &s->ordered_samples;
484 struct list_head *head = &os->samples;
485 struct sample_queue *tmp, *iter;
486 struct sample_data sample;
487 u64 limit = os->next_flush;
488 u64 last_ts = os->last_sample ? os->last_sample->timestamp : 0ULL;
489
490 if (!ops->ordered_samples || !limit)
491 return;
492
493 list_for_each_entry_safe(iter, tmp, head, list) {
494 if (iter->timestamp > limit)
495 break;
496
497 event__parse_sample(iter->event, s, &sample);
498 perf_session_deliver_event(s, iter->event, &sample, ops,
499 iter->file_offset);
500
501 os->last_flush = iter->timestamp;
502 list_del(&iter->list);
503 list_add(&iter->list, &os->sample_cache);
504 }
505
506 if (list_empty(head)) {
507 os->last_sample = NULL;
508 } else if (last_ts <= limit) {
509 os->last_sample =
510 list_entry(head->prev, struct sample_queue, list);
511 }
512}
513
514/*
515 * When perf record finishes a pass on every buffers, it records this pseudo
516 * event.
517 * We record the max timestamp t found in the pass n.
518 * Assuming these timestamps are monotonic across cpus, we know that if
519 * a buffer still has events with timestamps below t, they will be all
520 * available and then read in the pass n + 1.
521 * Hence when we start to read the pass n + 2, we can safely flush every
522 * events with timestamps below t.
523 *
524 * ============ PASS n =================
525 * CPU 0 | CPU 1
526 * |
527 * cnt1 timestamps | cnt2 timestamps
528 * 1 | 2
529 * 2 | 3
530 * - | 4 <--- max recorded
531 *
532 * ============ PASS n + 1 ==============
533 * CPU 0 | CPU 1
534 * |
535 * cnt1 timestamps | cnt2 timestamps
536 * 3 | 5
537 * 4 | 6
538 * 5 | 7 <---- max recorded
539 *
540 * Flush every events below timestamp 4
541 *
542 * ============ PASS n + 2 ==============
543 * CPU 0 | CPU 1
544 * |
545 * cnt1 timestamps | cnt2 timestamps
546 * 6 | 8
547 * 7 | 9
548 * - | 10
549 *
550 * Flush every events below timestamp 7
551 * etc...
552 */
553static int process_finished_round(event_t *event __used,
554 struct perf_session *session,
555 struct perf_event_ops *ops)
556{
557 flush_sample_queue(session, ops);
558 session->ordered_samples.next_flush = session->ordered_samples.max_timestamp;
559
560 return 0;
561}
562
563/* The queue is ordered by time */
564static void __queue_event(struct sample_queue *new, struct perf_session *s)
565{
566 struct ordered_samples *os = &s->ordered_samples;
567 struct sample_queue *sample = os->last_sample;
568 u64 timestamp = new->timestamp;
569 struct list_head *p;
570
571 os->last_sample = new;
572
573 if (!sample) {
574 list_add(&new->list, &os->samples);
575 os->max_timestamp = timestamp;
576 return;
577 }
578
579 /*
580 * last_sample might point to some random place in the list as it's
581 * the last queued event. We expect that the new event is close to
582 * this.
583 */
584 if (sample->timestamp <= timestamp) {
585 while (sample->timestamp <= timestamp) {
586 p = sample->list.next;
587 if (p == &os->samples) {
588 list_add_tail(&new->list, &os->samples);
589 os->max_timestamp = timestamp;
590 return;
591 }
592 sample = list_entry(p, struct sample_queue, list);
593 }
594 list_add_tail(&new->list, &sample->list);
595 } else {
596 while (sample->timestamp > timestamp) {
597 p = sample->list.prev;
598 if (p == &os->samples) {
599 list_add(&new->list, &os->samples);
600 return;
601 }
602 sample = list_entry(p, struct sample_queue, list);
603 }
604 list_add(&new->list, &sample->list);
605 }
606}
607
608#define MAX_SAMPLE_BUFFER (64 * 1024 / sizeof(struct sample_queue))
609
610static int perf_session_queue_event(struct perf_session *s, event_t *event,
611 struct sample_data *data, u64 file_offset)
612{
613 struct ordered_samples *os = &s->ordered_samples;
614 struct list_head *sc = &os->sample_cache;
615 u64 timestamp = data->time;
616 struct sample_queue *new;
617
618 if (!timestamp || timestamp == ~0ULL)
619 return -ETIME;
620
621 if (timestamp < s->ordered_samples.last_flush) {
622 printf("Warning: Timestamp below last timeslice flush\n");
623 return -EINVAL;
624 }
625
626 if (!list_empty(sc)) {
627 new = list_entry(sc->next, struct sample_queue, list);
628 list_del(&new->list);
629 } else if (os->sample_buffer) {
630 new = os->sample_buffer + os->sample_buffer_idx;
631 if (++os->sample_buffer_idx == MAX_SAMPLE_BUFFER)
632 os->sample_buffer = NULL;
633 } else {
634 os->sample_buffer = malloc(MAX_SAMPLE_BUFFER * sizeof(*new));
635 if (!os->sample_buffer)
636 return -ENOMEM;
637 list_add(&os->sample_buffer->list, &os->to_free);
638 os->sample_buffer_idx = 2;
639 new = os->sample_buffer + 1;
640 }
641
642 new->timestamp = timestamp;
643 new->file_offset = file_offset;
644 new->event = event;
645
646 __queue_event(new, s);
647
648 return 0;
649}
650
651static void callchain__printf(struct sample_data *sample)
652{
653 unsigned int i;
654
655 printf("... chain: nr:%Lu\n", sample->callchain->nr);
656
657 for (i = 0; i < sample->callchain->nr; i++)
658 printf("..... %2d: %016Lx\n", i, sample->callchain->ips[i]);
659}
660
661static void perf_session__print_tstamp(struct perf_session *session,
662 event_t *event,
663 struct sample_data *sample)
664{
665 if (event->header.type != PERF_RECORD_SAMPLE &&
666 !session->sample_id_all) {
667 fputs("-1 -1 ", stdout);
668 return;
669 }
670
671 if ((session->sample_type & PERF_SAMPLE_CPU))
672 printf("%u ", sample->cpu);
673
674 if (session->sample_type & PERF_SAMPLE_TIME)
675 printf("%Lu ", sample->time);
676}
677
678static void dump_event(struct perf_session *session, event_t *event,
679 u64 file_offset, struct sample_data *sample)
680{
681 if (!dump_trace)
682 return;
683
684 printf("\n%#Lx [%#x]: event: %d\n", file_offset, event->header.size,
685 event->header.type);
686
687 trace_event(event);
688
689 if (sample)
690 perf_session__print_tstamp(session, event, sample);
691
692 printf("%#Lx [%#x]: PERF_RECORD_%s", file_offset, event->header.size,
693 event__get_event_name(event->header.type));
694}
695
696static void dump_sample(struct perf_session *session, event_t *event,
697 struct sample_data *sample)
698{
699 if (!dump_trace)
700 return;
701
702 printf("(IP, %d): %d/%d: %#Lx period: %Ld\n", event->header.misc,
703 sample->pid, sample->tid, sample->ip, sample->period);
704
705 if (session->sample_type & PERF_SAMPLE_CALLCHAIN)
706 callchain__printf(sample);
707}
708
709static int perf_session_deliver_event(struct perf_session *session,
710 event_t *event,
711 struct sample_data *sample,
712 struct perf_event_ops *ops,
713 u64 file_offset)
714{
715 dump_event(session, event, file_offset, sample);
716
717 switch (event->header.type) {
718 case PERF_RECORD_SAMPLE:
719 dump_sample(session, event, sample);
720 return ops->sample(event, sample, session);
721 case PERF_RECORD_MMAP:
722 return ops->mmap(event, sample, session);
723 case PERF_RECORD_COMM:
724 return ops->comm(event, sample, session);
725 case PERF_RECORD_FORK:
726 return ops->fork(event, sample, session);
727 case PERF_RECORD_EXIT:
728 return ops->exit(event, sample, session);
729 case PERF_RECORD_LOST:
730 return ops->lost(event, sample, session);
731 case PERF_RECORD_READ:
732 return ops->read(event, sample, session);
733 case PERF_RECORD_THROTTLE:
734 return ops->throttle(event, sample, session);
735 case PERF_RECORD_UNTHROTTLE:
736 return ops->unthrottle(event, sample, session);
737 default:
738 ++session->hists.stats.nr_unknown_events;
739 return -1;
740 }
741}
742
743static int perf_session__preprocess_sample(struct perf_session *session,
744 event_t *event, struct sample_data *sample)
745{
746 if (event->header.type != PERF_RECORD_SAMPLE ||
747 !(session->sample_type & PERF_SAMPLE_CALLCHAIN))
748 return 0;
749
750 if (!ip_callchain__valid(sample->callchain, event)) {
751 pr_debug("call-chain problem with event, skipping it.\n");
752 ++session->hists.stats.nr_invalid_chains;
753 session->hists.stats.total_invalid_chains += sample->period;
754 return -EINVAL;
755 }
756 return 0;
757}
758
759static int perf_session__process_user_event(struct perf_session *session, event_t *event,
760 struct perf_event_ops *ops, u64 file_offset)
761{
762 dump_event(session, event, file_offset, NULL);
763
764 /* These events are processed right away */
765 switch (event->header.type) {
766 case PERF_RECORD_HEADER_ATTR:
767 return ops->attr(event, session);
768 case PERF_RECORD_HEADER_EVENT_TYPE:
769 return ops->event_type(event, session);
770 case PERF_RECORD_HEADER_TRACING_DATA:
771 /* setup for reading amidst mmap */
772 lseek(session->fd, file_offset, SEEK_SET);
773 return ops->tracing_data(event, session);
774 case PERF_RECORD_HEADER_BUILD_ID:
775 return ops->build_id(event, session);
776 case PERF_RECORD_FINISHED_ROUND:
777 return ops->finished_round(event, session, ops);
778 default:
779 return -EINVAL;
780 }
781}
782
783static int perf_session__process_event(struct perf_session *session,
784 event_t *event,
785 struct perf_event_ops *ops,
786 u64 file_offset)
787{
788 struct sample_data sample;
789 int ret;
790
791 if (session->header.needs_swap && event__swap_ops[event->header.type])
792 event__swap_ops[event->header.type](event);
793
794 if (event->header.type >= PERF_RECORD_HEADER_MAX)
795 return -EINVAL;
796
797 hists__inc_nr_events(&session->hists, event->header.type);
798
799 if (event->header.type >= PERF_RECORD_USER_TYPE_START)
800 return perf_session__process_user_event(session, event, ops, file_offset);
801
802 /*
803 * For all kernel events we get the sample data
804 */
805 event__parse_sample(event, session, &sample);
806
807 /* Preprocess sample records - precheck callchains */
808 if (perf_session__preprocess_sample(session, event, &sample))
809 return 0;
810
811 if (ops->ordered_samples) {
812 ret = perf_session_queue_event(session, event, &sample,
813 file_offset);
814 if (ret != -ETIME)
815 return ret;
816 }
817
818 return perf_session_deliver_event(session, event, &sample, ops,
819 file_offset);
820}
821
822void perf_event_header__bswap(struct perf_event_header *self)
823{
824 self->type = bswap_32(self->type);
825 self->misc = bswap_16(self->misc);
826 self->size = bswap_16(self->size);
827}
828
829static struct thread *perf_session__register_idle_thread(struct perf_session *self)
830{
831 struct thread *thread = perf_session__findnew(self, 0);
832
833 if (thread == NULL || thread__set_comm(thread, "swapper")) {
834 pr_err("problem inserting idle task.\n");
835 thread = NULL;
836 }
837
838 return thread;
839}
840
841static void perf_session__warn_about_errors(const struct perf_session *session,
842 const struct perf_event_ops *ops)
843{
844 if (ops->lost == event__process_lost &&
845 session->hists.stats.total_lost != 0) {
846 ui__warning("Processed %Lu events and LOST %Lu!\n\n"
847 "Check IO/CPU overload!\n\n",
848 session->hists.stats.total_period,
849 session->hists.stats.total_lost);
850 }
851
852 if (session->hists.stats.nr_unknown_events != 0) {
853 ui__warning("Found %u unknown events!\n\n"
854 "Is this an older tool processing a perf.data "
855 "file generated by a more recent tool?\n\n"
856 "If that is not the case, consider "
857 "reporting to linux-kernel@vger.kernel.org.\n\n",
858 session->hists.stats.nr_unknown_events);
859 }
860
861 if (session->hists.stats.nr_invalid_chains != 0) {
862 ui__warning("Found invalid callchains!\n\n"
863 "%u out of %u events were discarded for this reason.\n\n"
864 "Consider reporting to linux-kernel@vger.kernel.org.\n\n",
865 session->hists.stats.nr_invalid_chains,
866 session->hists.stats.nr_events[PERF_RECORD_SAMPLE]);
867 }
868}
869
870#define session_done() (*(volatile int *)(&session_done))
871volatile int session_done;
872
873static int __perf_session__process_pipe_events(struct perf_session *self,
874 struct perf_event_ops *ops)
875{
876 event_t event;
877 uint32_t size;
878 int skip = 0;
879 u64 head;
880 int err;
881 void *p;
882
883 perf_event_ops__fill_defaults(ops);
884
885 head = 0;
886more:
887 err = readn(self->fd, &event, sizeof(struct perf_event_header));
888 if (err <= 0) {
889 if (err == 0)
890 goto done;
891
892 pr_err("failed to read event header\n");
893 goto out_err;
894 }
895
896 if (self->header.needs_swap)
897 perf_event_header__bswap(&event.header);
898
899 size = event.header.size;
900 if (size == 0)
901 size = 8;
902
903 p = &event;
904 p += sizeof(struct perf_event_header);
905
906 if (size - sizeof(struct perf_event_header)) {
907 err = readn(self->fd, p, size - sizeof(struct perf_event_header));
908 if (err <= 0) {
909 if (err == 0) {
910 pr_err("unexpected end of event stream\n");
911 goto done;
912 }
913
914 pr_err("failed to read event data\n");
915 goto out_err;
916 }
917 }
918
919 if (size == 0 ||
920 (skip = perf_session__process_event(self, &event, ops, head)) < 0) {
921 dump_printf("%#Lx [%#x]: skipping unknown header type: %d\n",
922 head, event.header.size, event.header.type);
923 /*
924 * assume we lost track of the stream, check alignment, and
925 * increment a single u64 in the hope to catch on again 'soon'.
926 */
927 if (unlikely(head & 7))
928 head &= ~7ULL;
929
930 size = 8;
931 }
932
933 head += size;
934
935 if (skip > 0)
936 head += skip;
937
938 if (!session_done())
939 goto more;
940done:
941 err = 0;
942out_err:
943 perf_session__warn_about_errors(self, ops);
944 perf_session_free_sample_buffers(self);
945 return err;
946}
947
948int __perf_session__process_events(struct perf_session *session,
949 u64 data_offset, u64 data_size,
950 u64 file_size, struct perf_event_ops *ops)
951{
952 u64 head, page_offset, file_offset, file_pos, progress_next;
953 int err, mmap_prot, mmap_flags, map_idx = 0;
954 struct ui_progress *progress;
955 size_t page_size, mmap_size;
956 char *buf, *mmaps[8];
957 event_t *event;
958 uint32_t size;
959
960 perf_event_ops__fill_defaults(ops);
961
962 page_size = sysconf(_SC_PAGESIZE);
963
964 page_offset = page_size * (data_offset / page_size);
965 file_offset = page_offset;
966 head = data_offset - page_offset;
967
968 if (data_offset + data_size < file_size)
969 file_size = data_offset + data_size;
970
971 progress_next = file_size / 16;
972 progress = ui_progress__new("Processing events...", file_size);
973 if (progress == NULL)
974 return -1;
975
976 mmap_size = session->mmap_window;
977 if (mmap_size > file_size)
978 mmap_size = file_size;
979
980 memset(mmaps, 0, sizeof(mmaps));
981
982 mmap_prot = PROT_READ;
983 mmap_flags = MAP_SHARED;
984
985 if (session->header.needs_swap) {
986 mmap_prot |= PROT_WRITE;
987 mmap_flags = MAP_PRIVATE;
988 }
989remap:
990 buf = mmap(NULL, mmap_size, mmap_prot, mmap_flags, session->fd,
991 file_offset);
992 if (buf == MAP_FAILED) {
993 pr_err("failed to mmap file\n");
994 err = -errno;
995 goto out_err;
996 }
997 mmaps[map_idx] = buf;
998 map_idx = (map_idx + 1) & (ARRAY_SIZE(mmaps) - 1);
999 file_pos = file_offset + head;
1000
1001more:
1002 event = (event_t *)(buf + head);
1003
1004 if (session->header.needs_swap)
1005 perf_event_header__bswap(&event->header);
1006 size = event->header.size;
1007 if (size == 0)
1008 size = 8;
1009
1010 if (head + event->header.size > mmap_size) {
1011 if (mmaps[map_idx]) {
1012 munmap(mmaps[map_idx], mmap_size);
1013 mmaps[map_idx] = NULL;
1014 }
1015
1016 page_offset = page_size * (head / page_size);
1017 file_offset += page_offset;
1018 head -= page_offset;
1019 goto remap;
1020 }
1021
1022 size = event->header.size;
1023
1024 if (size == 0 ||
1025 perf_session__process_event(session, event, ops, file_pos) < 0) {
1026 dump_printf("%#Lx [%#x]: skipping unknown header type: %d\n",
1027 file_offset + head, event->header.size,
1028 event->header.type);
1029 /*
1030 * assume we lost track of the stream, check alignment, and
1031 * increment a single u64 in the hope to catch on again 'soon'.
1032 */
1033 if (unlikely(head & 7))
1034 head &= ~7ULL;
1035
1036 size = 8;
1037 }
1038
1039 head += size;
1040 file_pos += size;
1041
1042 if (file_pos >= progress_next) {
1043 progress_next += file_size / 16;
1044 ui_progress__update(progress, file_pos);
1045 }
1046
1047 if (file_pos < file_size)
1048 goto more;
1049
1050 err = 0;
1051 /* do the final flush for ordered samples */
1052 session->ordered_samples.next_flush = ULLONG_MAX;
1053 flush_sample_queue(session, ops);
1054out_err:
1055 ui_progress__delete(progress);
1056 perf_session__warn_about_errors(session, ops);
1057 perf_session_free_sample_buffers(session);
1058 return err;
1059}
1060
1061int perf_session__process_events(struct perf_session *self,
1062 struct perf_event_ops *ops)
1063{
1064 int err;
1065
1066 if (perf_session__register_idle_thread(self) == NULL)
1067 return -ENOMEM;
1068
1069 if (!self->fd_pipe)
1070 err = __perf_session__process_events(self,
1071 self->header.data_offset,
1072 self->header.data_size,
1073 self->size, ops);
1074 else
1075 err = __perf_session__process_pipe_events(self, ops);
1076
1077 return err;
1078}
1079
1080bool perf_session__has_traces(struct perf_session *self, const char *msg)
1081{
1082 if (!(self->sample_type & PERF_SAMPLE_RAW)) {
1083 pr_err("No trace sample to read. Did you call 'perf %s'?\n", msg);
1084 return false;
1085 }
1086
1087 return true;
1088}
1089
1090int perf_session__set_kallsyms_ref_reloc_sym(struct map **maps,
1091 const char *symbol_name,
1092 u64 addr)
1093{
1094 char *bracket;
1095 enum map_type i;
1096 struct ref_reloc_sym *ref;
1097
1098 ref = zalloc(sizeof(struct ref_reloc_sym));
1099 if (ref == NULL)
1100 return -ENOMEM;
1101
1102 ref->name = strdup(symbol_name);
1103 if (ref->name == NULL) {
1104 free(ref);
1105 return -ENOMEM;
1106 }
1107
1108 bracket = strchr(ref->name, ']');
1109 if (bracket)
1110 *bracket = '\0';
1111
1112 ref->addr = addr;
1113
1114 for (i = 0; i < MAP__NR_TYPES; ++i) {
1115 struct kmap *kmap = map__kmap(maps[i]);
1116 kmap->ref_reloc_sym = ref;
1117 }
1118
1119 return 0;
1120}
1121
1122size_t perf_session__fprintf_dsos(struct perf_session *self, FILE *fp)
1123{
1124 return __dsos__fprintf(&self->host_machine.kernel_dsos, fp) +
1125 __dsos__fprintf(&self->host_machine.user_dsos, fp) +
1126 machines__fprintf_dsos(&self->machines, fp);
1127}
1128
1129size_t perf_session__fprintf_dsos_buildid(struct perf_session *self, FILE *fp,
1130 bool with_hits)
1131{
1132 size_t ret = machine__fprintf_dsos_buildid(&self->host_machine, fp, with_hits);
1133 return ret + machines__fprintf_dsos_buildid(&self->machines, fp, with_hits);
1134}
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
new file mode 100644
index 00000000000..decd83f274f
--- /dev/null
+++ b/tools/perf/util/session.h
@@ -0,0 +1,157 @@
1#ifndef __PERF_SESSION_H
2#define __PERF_SESSION_H
3
4#include "hist.h"
5#include "event.h"
6#include "header.h"
7#include "symbol.h"
8#include "thread.h"
9#include <linux/rbtree.h>
10#include "../../../include/linux/perf_event.h"
11
12struct sample_queue;
13struct ip_callchain;
14struct thread;
15
16struct ordered_samples {
17 u64 last_flush;
18 u64 next_flush;
19 u64 max_timestamp;
20 struct list_head samples;
21 struct list_head sample_cache;
22 struct list_head to_free;
23 struct sample_queue *sample_buffer;
24 struct sample_queue *last_sample;
25 int sample_buffer_idx;
26};
27
28struct perf_session {
29 struct perf_header header;
30 unsigned long size;
31 unsigned long mmap_window;
32 struct rb_root threads;
33 struct list_head dead_threads;
34 struct thread *last_match;
35 struct machine host_machine;
36 struct rb_root machines;
37 struct rb_root hists_tree;
38 /*
39 * FIXME: should point to the first entry in hists_tree and
40 * be a hists instance. Right now its only 'report'
41 * that is using ->hists_tree while all the rest use
42 * ->hists.
43 */
44 struct hists hists;
45 u64 sample_type;
46 int fd;
47 bool fd_pipe;
48 bool repipe;
49 bool sample_id_all;
50 u16 id_hdr_size;
51 int cwdlen;
52 char *cwd;
53 struct ordered_samples ordered_samples;
54 char filename[0];
55};
56
57struct perf_event_ops;
58
59typedef int (*event_op)(event_t *self, struct sample_data *sample,
60 struct perf_session *session);
61typedef int (*event_synth_op)(event_t *self, struct perf_session *session);
62typedef int (*event_op2)(event_t *self, struct perf_session *session,
63 struct perf_event_ops *ops);
64
65struct perf_event_ops {
66 event_op sample,
67 mmap,
68 comm,
69 fork,
70 exit,
71 lost,
72 read,
73 throttle,
74 unthrottle;
75 event_synth_op attr,
76 event_type,
77 tracing_data,
78 build_id;
79 event_op2 finished_round;
80 bool ordered_samples;
81 bool ordering_requires_timestamps;
82};
83
84struct perf_session *perf_session__new(const char *filename, int mode,
85 bool force, bool repipe,
86 struct perf_event_ops *ops);
87void perf_session__delete(struct perf_session *self);
88
89void perf_event_header__bswap(struct perf_event_header *self);
90
91int __perf_session__process_events(struct perf_session *self,
92 u64 data_offset, u64 data_size, u64 size,
93 struct perf_event_ops *ops);
94int perf_session__process_events(struct perf_session *self,
95 struct perf_event_ops *event_ops);
96
97struct map_symbol *perf_session__resolve_callchain(struct perf_session *self,
98 struct thread *thread,
99 struct ip_callchain *chain,
100 struct symbol **parent);
101
102bool perf_session__has_traces(struct perf_session *self, const char *msg);
103
104int perf_session__set_kallsyms_ref_reloc_sym(struct map **maps,
105 const char *symbol_name,
106 u64 addr);
107
108void mem_bswap_64(void *src, int byte_size);
109
110int perf_session__create_kernel_maps(struct perf_session *self);
111
112void perf_session__update_sample_type(struct perf_session *self);
113void perf_session__set_sample_id_all(struct perf_session *session, bool value);
114void perf_session__set_sample_type(struct perf_session *session, u64 type);
115void perf_session__remove_thread(struct perf_session *self, struct thread *th);
116
117static inline
118struct machine *perf_session__find_host_machine(struct perf_session *self)
119{
120 return &self->host_machine;
121}
122
123static inline
124struct machine *perf_session__find_machine(struct perf_session *self, pid_t pid)
125{
126 if (pid == HOST_KERNEL_ID)
127 return &self->host_machine;
128 return machines__find(&self->machines, pid);
129}
130
131static inline
132struct machine *perf_session__findnew_machine(struct perf_session *self, pid_t pid)
133{
134 if (pid == HOST_KERNEL_ID)
135 return &self->host_machine;
136 return machines__findnew(&self->machines, pid);
137}
138
139static inline
140void perf_session__process_machines(struct perf_session *self,
141 machine__process_t process)
142{
143 process(&self->host_machine, self);
144 return machines__process(&self->machines, process, self);
145}
146
147size_t perf_session__fprintf_dsos(struct perf_session *self, FILE *fp);
148
149size_t perf_session__fprintf_dsos_buildid(struct perf_session *self,
150 FILE *fp, bool with_hits);
151
152static inline
153size_t perf_session__fprintf_nr_events(struct perf_session *self, FILE *fp)
154{
155 return hists__fprintf_nr_events(&self->hists, fp);
156}
157#endif /* __PERF_SESSION_H */
diff --git a/tools/perf/util/sigchain.c b/tools/perf/util/sigchain.c
index 1118b99e57d..ba785e9b184 100644
--- a/tools/perf/util/sigchain.c
+++ b/tools/perf/util/sigchain.c
@@ -16,7 +16,7 @@ static void check_signum(int sig)
16 die("BUG: signal out of range: %d", sig); 16 die("BUG: signal out of range: %d", sig);
17} 17}
18 18
19int sigchain_push(int sig, sigchain_fun f) 19static int sigchain_push(int sig, sigchain_fun f)
20{ 20{
21 struct sigchain_signal *s = signals + sig; 21 struct sigchain_signal *s = signals + sig;
22 check_signum(sig); 22 check_signum(sig);
diff --git a/tools/perf/util/sigchain.h b/tools/perf/util/sigchain.h
index 618083bce0c..959d64eb555 100644
--- a/tools/perf/util/sigchain.h
+++ b/tools/perf/util/sigchain.h
@@ -1,11 +1,10 @@
1#ifndef SIGCHAIN_H 1#ifndef __PERF_SIGCHAIN_H
2#define SIGCHAIN_H 2#define __PERF_SIGCHAIN_H
3 3
4typedef void (*sigchain_fun)(int); 4typedef void (*sigchain_fun)(int);
5 5
6int sigchain_push(int sig, sigchain_fun f);
7int sigchain_pop(int sig); 6int sigchain_pop(int sig);
8 7
9void sigchain_push_common(sigchain_fun f); 8void sigchain_push_common(sigchain_fun f);
10 9
11#endif /* SIGCHAIN_H */ 10#endif /* __PERF_SIGCHAIN_H */
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c
new file mode 100644
index 00000000000..f44fa541d56
--- /dev/null
+++ b/tools/perf/util/sort.c
@@ -0,0 +1,345 @@
1#include "sort.h"
2#include "hist.h"
3
4regex_t parent_regex;
5const char default_parent_pattern[] = "^sys_|^do_page_fault";
6const char *parent_pattern = default_parent_pattern;
7const char default_sort_order[] = "comm,dso,symbol";
8const char *sort_order = default_sort_order;
9int sort__need_collapse = 0;
10int sort__has_parent = 0;
11
12enum sort_type sort__first_dimension;
13
14char * field_sep;
15
16LIST_HEAD(hist_entry__sort_list);
17
18static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf,
19 size_t size, unsigned int width);
20static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf,
21 size_t size, unsigned int width);
22static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf,
23 size_t size, unsigned int width);
24static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf,
25 size_t size, unsigned int width);
26static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf,
27 size_t size, unsigned int width);
28static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf,
29 size_t size, unsigned int width);
30
31struct sort_entry sort_thread = {
32 .se_header = "Command: Pid",
33 .se_cmp = sort__thread_cmp,
34 .se_snprintf = hist_entry__thread_snprintf,
35 .se_width_idx = HISTC_THREAD,
36};
37
38struct sort_entry sort_comm = {
39 .se_header = "Command",
40 .se_cmp = sort__comm_cmp,
41 .se_collapse = sort__comm_collapse,
42 .se_snprintf = hist_entry__comm_snprintf,
43 .se_width_idx = HISTC_COMM,
44};
45
46struct sort_entry sort_dso = {
47 .se_header = "Shared Object",
48 .se_cmp = sort__dso_cmp,
49 .se_snprintf = hist_entry__dso_snprintf,
50 .se_width_idx = HISTC_DSO,
51};
52
53struct sort_entry sort_sym = {
54 .se_header = "Symbol",
55 .se_cmp = sort__sym_cmp,
56 .se_snprintf = hist_entry__sym_snprintf,
57 .se_width_idx = HISTC_SYMBOL,
58};
59
60struct sort_entry sort_parent = {
61 .se_header = "Parent symbol",
62 .se_cmp = sort__parent_cmp,
63 .se_snprintf = hist_entry__parent_snprintf,
64 .se_width_idx = HISTC_PARENT,
65};
66
67struct sort_entry sort_cpu = {
68 .se_header = "CPU",
69 .se_cmp = sort__cpu_cmp,
70 .se_snprintf = hist_entry__cpu_snprintf,
71 .se_width_idx = HISTC_CPU,
72};
73
74struct sort_dimension {
75 const char *name;
76 struct sort_entry *entry;
77 int taken;
78};
79
80static struct sort_dimension sort_dimensions[] = {
81 { .name = "pid", .entry = &sort_thread, },
82 { .name = "comm", .entry = &sort_comm, },
83 { .name = "dso", .entry = &sort_dso, },
84 { .name = "symbol", .entry = &sort_sym, },
85 { .name = "parent", .entry = &sort_parent, },
86 { .name = "cpu", .entry = &sort_cpu, },
87};
88
89int64_t cmp_null(void *l, void *r)
90{
91 if (!l && !r)
92 return 0;
93 else if (!l)
94 return -1;
95 else
96 return 1;
97}
98
99/* --sort pid */
100
101int64_t
102sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)
103{
104 return right->thread->pid - left->thread->pid;
105}
106
107static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...)
108{
109 int n;
110 va_list ap;
111
112 va_start(ap, fmt);
113 n = vsnprintf(bf, size, fmt, ap);
114 if (field_sep && n > 0) {
115 char *sep = bf;
116
117 while (1) {
118 sep = strchr(sep, *field_sep);
119 if (sep == NULL)
120 break;
121 *sep = '.';
122 }
123 }
124 va_end(ap);
125 return n;
126}
127
128static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf,
129 size_t size, unsigned int width)
130{
131 return repsep_snprintf(bf, size, "%*s:%5d", width,
132 self->thread->comm ?: "", self->thread->pid);
133}
134
135static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf,
136 size_t size, unsigned int width)
137{
138 return repsep_snprintf(bf, size, "%*s", width, self->thread->comm);
139}
140
141/* --sort dso */
142
143int64_t
144sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
145{
146 struct dso *dso_l = left->ms.map ? left->ms.map->dso : NULL;
147 struct dso *dso_r = right->ms.map ? right->ms.map->dso : NULL;
148 const char *dso_name_l, *dso_name_r;
149
150 if (!dso_l || !dso_r)
151 return cmp_null(dso_l, dso_r);
152
153 if (verbose) {
154 dso_name_l = dso_l->long_name;
155 dso_name_r = dso_r->long_name;
156 } else {
157 dso_name_l = dso_l->short_name;
158 dso_name_r = dso_r->short_name;
159 }
160
161 return strcmp(dso_name_l, dso_name_r);
162}
163
164static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf,
165 size_t size, unsigned int width)
166{
167 if (self->ms.map && self->ms.map->dso) {
168 const char *dso_name = !verbose ? self->ms.map->dso->short_name :
169 self->ms.map->dso->long_name;
170 return repsep_snprintf(bf, size, "%-*s", width, dso_name);
171 }
172
173 return repsep_snprintf(bf, size, "%-*s", width, "[unknown]");
174}
175
176/* --sort symbol */
177
178int64_t
179sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
180{
181 u64 ip_l, ip_r;
182
183 if (left->ms.sym == right->ms.sym)
184 return 0;
185
186 ip_l = left->ms.sym ? left->ms.sym->start : left->ip;
187 ip_r = right->ms.sym ? right->ms.sym->start : right->ip;
188
189 return (int64_t)(ip_r - ip_l);
190}
191
192static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf,
193 size_t size, unsigned int width __used)
194{
195 size_t ret = 0;
196
197 if (verbose) {
198 char o = self->ms.map ? dso__symtab_origin(self->ms.map->dso) : '!';
199 ret += repsep_snprintf(bf, size, "%-#*llx %c ",
200 BITS_PER_LONG / 4, self->ip, o);
201 }
202
203 ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", self->level);
204 if (self->ms.sym)
205 ret += repsep_snprintf(bf + ret, size - ret, "%s",
206 self->ms.sym->name);
207 else
208 ret += repsep_snprintf(bf + ret, size - ret, "%-#*llx",
209 BITS_PER_LONG / 4, self->ip);
210
211 return ret;
212}
213
214/* --sort comm */
215
216int64_t
217sort__comm_cmp(struct hist_entry *left, struct hist_entry *right)
218{
219 return right->thread->pid - left->thread->pid;
220}
221
222int64_t
223sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)
224{
225 char *comm_l = left->thread->comm;
226 char *comm_r = right->thread->comm;
227
228 if (!comm_l || !comm_r)
229 return cmp_null(comm_l, comm_r);
230
231 return strcmp(comm_l, comm_r);
232}
233
234/* --sort parent */
235
236int64_t
237sort__parent_cmp(struct hist_entry *left, struct hist_entry *right)
238{
239 struct symbol *sym_l = left->parent;
240 struct symbol *sym_r = right->parent;
241
242 if (!sym_l || !sym_r)
243 return cmp_null(sym_l, sym_r);
244
245 return strcmp(sym_l->name, sym_r->name);
246}
247
248static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf,
249 size_t size, unsigned int width)
250{
251 return repsep_snprintf(bf, size, "%-*s", width,
252 self->parent ? self->parent->name : "[other]");
253}
254
255/* --sort cpu */
256
257int64_t
258sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right)
259{
260 return right->cpu - left->cpu;
261}
262
263static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf,
264 size_t size, unsigned int width)
265{
266 return repsep_snprintf(bf, size, "%-*d", width, self->cpu);
267}
268
269int sort_dimension__add(const char *tok)
270{
271 unsigned int i;
272
273 for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) {
274 struct sort_dimension *sd = &sort_dimensions[i];
275
276 if (sd->taken)
277 continue;
278
279 if (strncasecmp(tok, sd->name, strlen(tok)))
280 continue;
281
282 if (sd->entry->se_collapse)
283 sort__need_collapse = 1;
284
285 if (sd->entry == &sort_parent) {
286 int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED);
287 if (ret) {
288 char err[BUFSIZ];
289
290 regerror(ret, &parent_regex, err, sizeof(err));
291 pr_err("Invalid regex: %s\n%s", parent_pattern, err);
292 return -EINVAL;
293 }
294 sort__has_parent = 1;
295 }
296
297 if (list_empty(&hist_entry__sort_list)) {
298 if (!strcmp(sd->name, "pid"))
299 sort__first_dimension = SORT_PID;
300 else if (!strcmp(sd->name, "comm"))
301 sort__first_dimension = SORT_COMM;
302 else if (!strcmp(sd->name, "dso"))
303 sort__first_dimension = SORT_DSO;
304 else if (!strcmp(sd->name, "symbol"))
305 sort__first_dimension = SORT_SYM;
306 else if (!strcmp(sd->name, "parent"))
307 sort__first_dimension = SORT_PARENT;
308 else if (!strcmp(sd->name, "cpu"))
309 sort__first_dimension = SORT_CPU;
310 }
311
312 list_add_tail(&sd->entry->list, &hist_entry__sort_list);
313 sd->taken = 1;
314
315 return 0;
316 }
317
318 return -ESRCH;
319}
320
321void setup_sorting(const char * const usagestr[], const struct option *opts)
322{
323 char *tmp, *tok, *str = strdup(sort_order);
324
325 for (tok = strtok_r(str, ", ", &tmp);
326 tok; tok = strtok_r(NULL, ", ", &tmp)) {
327 if (sort_dimension__add(tok) < 0) {
328 error("Unknown --sort key: `%s'", tok);
329 usage_with_options(usagestr, opts);
330 }
331 }
332
333 free(str);
334}
335
336void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list,
337 const char *list_name, FILE *fp)
338{
339 if (list && strlist__nr_entries(list) == 1) {
340 if (fp != NULL)
341 fprintf(fp, "# %s: %s\n", list_name,
342 strlist__entry(list, 0)->s);
343 self->elide = true;
344 }
345}
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h
new file mode 100644
index 00000000000..0b91053a7d1
--- /dev/null
+++ b/tools/perf/util/sort.h
@@ -0,0 +1,124 @@
1#ifndef __PERF_SORT_H
2#define __PERF_SORT_H
3#include "../builtin.h"
4
5#include "util.h"
6
7#include "color.h"
8#include <linux/list.h>
9#include "cache.h"
10#include <linux/rbtree.h>
11#include "symbol.h"
12#include "string.h"
13#include "callchain.h"
14#include "strlist.h"
15#include "values.h"
16
17#include "../perf.h"
18#include "debug.h"
19#include "header.h"
20
21#include "parse-options.h"
22#include "parse-events.h"
23
24#include "thread.h"
25#include "sort.h"
26
27extern regex_t parent_regex;
28extern const char *sort_order;
29extern const char default_parent_pattern[];
30extern const char *parent_pattern;
31extern const char default_sort_order[];
32extern int sort__need_collapse;
33extern int sort__has_parent;
34extern char *field_sep;
35extern struct sort_entry sort_comm;
36extern struct sort_entry sort_dso;
37extern struct sort_entry sort_sym;
38extern struct sort_entry sort_parent;
39extern enum sort_type sort__first_dimension;
40
41/**
42 * struct hist_entry - histogram entry
43 *
44 * @row_offset - offset from the first callchain expanded to appear on screen
45 * @nr_rows - rows expanded in callchain, recalculated on folding/unfolding
46 */
47struct hist_entry {
48 struct rb_node rb_node;
49 u64 period;
50 u64 period_sys;
51 u64 period_us;
52 u64 period_guest_sys;
53 u64 period_guest_us;
54 struct map_symbol ms;
55 struct thread *thread;
56 u64 ip;
57 s32 cpu;
58 u32 nr_events;
59
60 /* XXX These two should move to some tree widget lib */
61 u16 row_offset;
62 u16 nr_rows;
63
64 bool init_have_children;
65 char level;
66 u8 filtered;
67 struct symbol *parent;
68 union {
69 unsigned long position;
70 struct hist_entry *pair;
71 struct rb_root sorted_chain;
72 };
73 struct callchain_root callchain[0];
74};
75
76enum sort_type {
77 SORT_PID,
78 SORT_COMM,
79 SORT_DSO,
80 SORT_SYM,
81 SORT_PARENT,
82 SORT_CPU,
83};
84
85/*
86 * configurable sorting bits
87 */
88
89struct sort_entry {
90 struct list_head list;
91
92 const char *se_header;
93
94 int64_t (*se_cmp)(struct hist_entry *, struct hist_entry *);
95 int64_t (*se_collapse)(struct hist_entry *, struct hist_entry *);
96 int (*se_snprintf)(struct hist_entry *self, char *bf, size_t size,
97 unsigned int width);
98 u8 se_width_idx;
99 bool elide;
100};
101
102extern struct sort_entry sort_thread;
103extern struct list_head hist_entry__sort_list;
104
105void setup_sorting(const char * const usagestr[], const struct option *opts);
106
107extern size_t sort__thread_print(FILE *, struct hist_entry *, unsigned int);
108extern size_t sort__comm_print(FILE *, struct hist_entry *, unsigned int);
109extern size_t sort__dso_print(FILE *, struct hist_entry *, unsigned int);
110extern size_t sort__sym_print(FILE *, struct hist_entry *, unsigned int __used);
111extern int64_t cmp_null(void *, void *);
112extern int64_t sort__thread_cmp(struct hist_entry *, struct hist_entry *);
113extern int64_t sort__comm_cmp(struct hist_entry *, struct hist_entry *);
114extern int64_t sort__comm_collapse(struct hist_entry *, struct hist_entry *);
115extern int64_t sort__dso_cmp(struct hist_entry *, struct hist_entry *);
116extern int64_t sort__sym_cmp(struct hist_entry *, struct hist_entry *);
117extern int64_t sort__parent_cmp(struct hist_entry *, struct hist_entry *);
118int64_t sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right);
119extern size_t sort__parent_print(FILE *, struct hist_entry *, unsigned int);
120extern int sort_dimension__add(const char *);
121void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list,
122 const char *list_name, FILE *fp);
123
124#endif /* __PERF_SORT_H */
diff --git a/tools/perf/util/strbuf.c b/tools/perf/util/strbuf.c
index eaba0930680..92e068517c1 100644
--- a/tools/perf/util/strbuf.c
+++ b/tools/perf/util/strbuf.c
@@ -16,7 +16,7 @@ int prefixcmp(const char *str, const char *prefix)
16 */ 16 */
17char strbuf_slopbuf[1]; 17char strbuf_slopbuf[1];
18 18
19void strbuf_init(struct strbuf *sb, size_t hint) 19void strbuf_init(struct strbuf *sb, ssize_t hint)
20{ 20{
21 sb->alloc = sb->len = 0; 21 sb->alloc = sb->len = 0;
22 sb->buf = strbuf_slopbuf; 22 sb->buf = strbuf_slopbuf;
@@ -41,16 +41,6 @@ char *strbuf_detach(struct strbuf *sb, size_t *sz)
41 return res; 41 return res;
42} 42}
43 43
44void strbuf_attach(struct strbuf *sb, void *buf, size_t len, size_t alloc)
45{
46 strbuf_release(sb);
47 sb->buf = buf;
48 sb->len = len;
49 sb->alloc = alloc;
50 strbuf_grow(sb, 0);
51 sb->buf[sb->len] = '\0';
52}
53
54void strbuf_grow(struct strbuf *sb, size_t extra) 44void strbuf_grow(struct strbuf *sb, size_t extra)
55{ 45{
56 if (sb->len + extra + 1 <= sb->len) 46 if (sb->len + extra + 1 <= sb->len)
@@ -60,93 +50,7 @@ void strbuf_grow(struct strbuf *sb, size_t extra)
60 ALLOC_GROW(sb->buf, sb->len + extra + 1, sb->alloc); 50 ALLOC_GROW(sb->buf, sb->len + extra + 1, sb->alloc);
61} 51}
62 52
63void strbuf_trim(struct strbuf *sb) 53static void strbuf_splice(struct strbuf *sb, size_t pos, size_t len,
64{
65 char *b = sb->buf;
66 while (sb->len > 0 && isspace((unsigned char)sb->buf[sb->len - 1]))
67 sb->len--;
68 while (sb->len > 0 && isspace(*b)) {
69 b++;
70 sb->len--;
71 }
72 memmove(sb->buf, b, sb->len);
73 sb->buf[sb->len] = '\0';
74}
75void strbuf_rtrim(struct strbuf *sb)
76{
77 while (sb->len > 0 && isspace((unsigned char)sb->buf[sb->len - 1]))
78 sb->len--;
79 sb->buf[sb->len] = '\0';
80}
81
82void strbuf_ltrim(struct strbuf *sb)
83{
84 char *b = sb->buf;
85 while (sb->len > 0 && isspace(*b)) {
86 b++;
87 sb->len--;
88 }
89 memmove(sb->buf, b, sb->len);
90 sb->buf[sb->len] = '\0';
91}
92
93void strbuf_tolower(struct strbuf *sb)
94{
95 int i;
96 for (i = 0; i < sb->len; i++)
97 sb->buf[i] = tolower(sb->buf[i]);
98}
99
100struct strbuf **strbuf_split(const struct strbuf *sb, int delim)
101{
102 int alloc = 2, pos = 0;
103 char *n, *p;
104 struct strbuf **ret;
105 struct strbuf *t;
106
107 ret = calloc(alloc, sizeof(struct strbuf *));
108 p = n = sb->buf;
109 while (n < sb->buf + sb->len) {
110 int len;
111 n = memchr(n, delim, sb->len - (n - sb->buf));
112 if (pos + 1 >= alloc) {
113 alloc = alloc * 2;
114 ret = realloc(ret, sizeof(struct strbuf *) * alloc);
115 }
116 if (!n)
117 n = sb->buf + sb->len - 1;
118 len = n - p + 1;
119 t = malloc(sizeof(struct strbuf));
120 strbuf_init(t, len);
121 strbuf_add(t, p, len);
122 ret[pos] = t;
123 ret[++pos] = NULL;
124 p = ++n;
125 }
126 return ret;
127}
128
129void strbuf_list_free(struct strbuf **sbs)
130{
131 struct strbuf **s = sbs;
132
133 while (*s) {
134 strbuf_release(*s);
135 free(*s++);
136 }
137 free(sbs);
138}
139
140int strbuf_cmp(const struct strbuf *a, const struct strbuf *b)
141{
142 int len = a->len < b->len ? a->len: b->len;
143 int cmp = memcmp(a->buf, b->buf, len);
144 if (cmp)
145 return cmp;
146 return a->len < b->len ? -1: a->len != b->len;
147}
148
149void strbuf_splice(struct strbuf *sb, size_t pos, size_t len,
150 const void *data, size_t dlen) 54 const void *data, size_t dlen)
151{ 55{
152 if (pos + len < pos) 56 if (pos + len < pos)
@@ -165,11 +69,6 @@ void strbuf_splice(struct strbuf *sb, size_t pos, size_t len,
165 strbuf_setlen(sb, sb->len + dlen - len); 69 strbuf_setlen(sb, sb->len + dlen - len);
166} 70}
167 71
168void strbuf_insert(struct strbuf *sb, size_t pos, const void *data, size_t len)
169{
170 strbuf_splice(sb, pos, 0, data, len);
171}
172
173void strbuf_remove(struct strbuf *sb, size_t pos, size_t len) 72void strbuf_remove(struct strbuf *sb, size_t pos, size_t len)
174{ 73{
175 strbuf_splice(sb, pos, len, NULL, 0); 74 strbuf_splice(sb, pos, len, NULL, 0);
@@ -182,13 +81,6 @@ void strbuf_add(struct strbuf *sb, const void *data, size_t len)
182 strbuf_setlen(sb, sb->len + len); 81 strbuf_setlen(sb, sb->len + len);
183} 82}
184 83
185void strbuf_adddup(struct strbuf *sb, size_t pos, size_t len)
186{
187 strbuf_grow(sb, len);
188 memcpy(sb->buf + sb->len, sb->buf + pos, len);
189 strbuf_setlen(sb, sb->len + len);
190}
191
192void strbuf_addf(struct strbuf *sb, const char *fmt, ...) 84void strbuf_addf(struct strbuf *sb, const char *fmt, ...)
193{ 85{
194 int len; 86 int len;
@@ -213,58 +105,7 @@ void strbuf_addf(struct strbuf *sb, const char *fmt, ...)
213 strbuf_setlen(sb, sb->len + len); 105 strbuf_setlen(sb, sb->len + len);
214} 106}
215 107
216void strbuf_expand(struct strbuf *sb, const char *format, expand_fn_t fn, 108ssize_t strbuf_read(struct strbuf *sb, int fd, ssize_t hint)
217 void *context)
218{
219 for (;;) {
220 const char *percent;
221 size_t consumed;
222
223 percent = strchrnul(format, '%');
224 strbuf_add(sb, format, percent - format);
225 if (!*percent)
226 break;
227 format = percent + 1;
228
229 consumed = fn(sb, format, context);
230 if (consumed)
231 format += consumed;
232 else
233 strbuf_addch(sb, '%');
234 }
235}
236
237size_t strbuf_expand_dict_cb(struct strbuf *sb, const char *placeholder,
238 void *context)
239{
240 struct strbuf_expand_dict_entry *e = context;
241 size_t len;
242
243 for (; e->placeholder && (len = strlen(e->placeholder)); e++) {
244 if (!strncmp(placeholder, e->placeholder, len)) {
245 if (e->value)
246 strbuf_addstr(sb, e->value);
247 return len;
248 }
249 }
250 return 0;
251}
252
253size_t strbuf_fread(struct strbuf *sb, size_t size, FILE *f)
254{
255 size_t res;
256 size_t oldalloc = sb->alloc;
257
258 strbuf_grow(sb, size);
259 res = fread(sb->buf + sb->len, 1, size, f);
260 if (res > 0)
261 strbuf_setlen(sb, sb->len + res);
262 else if (res < 0 && oldalloc == 0)
263 strbuf_release(sb);
264 return res;
265}
266
267ssize_t strbuf_read(struct strbuf *sb, int fd, size_t hint)
268{ 109{
269 size_t oldlen = sb->len; 110 size_t oldlen = sb->len;
270 size_t oldalloc = sb->alloc; 111 size_t oldalloc = sb->alloc;
@@ -290,70 +131,3 @@ ssize_t strbuf_read(struct strbuf *sb, int fd, size_t hint)
290 sb->buf[sb->len] = '\0'; 131 sb->buf[sb->len] = '\0';
291 return sb->len - oldlen; 132 return sb->len - oldlen;
292} 133}
293
294#define STRBUF_MAXLINK (2*PATH_MAX)
295
296int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint)
297{
298 size_t oldalloc = sb->alloc;
299
300 if (hint < 32)
301 hint = 32;
302
303 while (hint < STRBUF_MAXLINK) {
304 int len;
305
306 strbuf_grow(sb, hint);
307 len = readlink(path, sb->buf, hint);
308 if (len < 0) {
309 if (errno != ERANGE)
310 break;
311 } else if (len < hint) {
312 strbuf_setlen(sb, len);
313 return 0;
314 }
315
316 /* .. the buffer was too small - try again */
317 hint *= 2;
318 }
319 if (oldalloc == 0)
320 strbuf_release(sb);
321 return -1;
322}
323
324int strbuf_getline(struct strbuf *sb, FILE *fp, int term)
325{
326 int ch;
327
328 strbuf_grow(sb, 0);
329 if (feof(fp))
330 return EOF;
331
332 strbuf_reset(sb);
333 while ((ch = fgetc(fp)) != EOF) {
334 if (ch == term)
335 break;
336 strbuf_grow(sb, 1);
337 sb->buf[sb->len++] = ch;
338 }
339 if (ch == EOF && sb->len == 0)
340 return EOF;
341
342 sb->buf[sb->len] = '\0';
343 return 0;
344}
345
346int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint)
347{
348 int fd, len;
349
350 fd = open(path, O_RDONLY);
351 if (fd < 0)
352 return -1;
353 len = strbuf_read(sb, fd, hint);
354 close(fd);
355 if (len < 0)
356 return -1;
357
358 return len;
359}
diff --git a/tools/perf/util/strbuf.h b/tools/perf/util/strbuf.h
index 9ee908a3ec5..436ac319f6c 100644
--- a/tools/perf/util/strbuf.h
+++ b/tools/perf/util/strbuf.h
@@ -1,5 +1,5 @@
1#ifndef STRBUF_H 1#ifndef __PERF_STRBUF_H
2#define STRBUF_H 2#define __PERF_STRBUF_H
3 3
4/* 4/*
5 * Strbuf's can be use in many ways: as a byte array, or to store arbitrary 5 * Strbuf's can be use in many ways: as a byte array, or to store arbitrary
@@ -50,18 +50,12 @@ struct strbuf {
50#define STRBUF_INIT { 0, 0, strbuf_slopbuf } 50#define STRBUF_INIT { 0, 0, strbuf_slopbuf }
51 51
52/*----- strbuf life cycle -----*/ 52/*----- strbuf life cycle -----*/
53extern void strbuf_init(struct strbuf *, size_t); 53extern void strbuf_init(struct strbuf *buf, ssize_t hint);
54extern void strbuf_release(struct strbuf *); 54extern void strbuf_release(struct strbuf *);
55extern char *strbuf_detach(struct strbuf *, size_t *); 55extern char *strbuf_detach(struct strbuf *, size_t *);
56extern void strbuf_attach(struct strbuf *, void *, size_t, size_t);
57static inline void strbuf_swap(struct strbuf *a, struct strbuf *b) {
58 struct strbuf tmp = *a;
59 *a = *b;
60 *b = tmp;
61}
62 56
63/*----- strbuf size related -----*/ 57/*----- strbuf size related -----*/
64static inline size_t strbuf_avail(const struct strbuf *sb) { 58static inline ssize_t strbuf_avail(const struct strbuf *sb) {
65 return sb->alloc ? sb->alloc - sb->len - 1 : 0; 59 return sb->alloc ? sb->alloc - sb->len - 1 : 0;
66} 60}
67 61
@@ -74,17 +68,6 @@ static inline void strbuf_setlen(struct strbuf *sb, size_t len) {
74 sb->len = len; 68 sb->len = len;
75 sb->buf[len] = '\0'; 69 sb->buf[len] = '\0';
76} 70}
77#define strbuf_reset(sb) strbuf_setlen(sb, 0)
78
79/*----- content related -----*/
80extern void strbuf_trim(struct strbuf *);
81extern void strbuf_rtrim(struct strbuf *);
82extern void strbuf_ltrim(struct strbuf *);
83extern int strbuf_cmp(const struct strbuf *, const struct strbuf *);
84extern void strbuf_tolower(struct strbuf *);
85
86extern struct strbuf **strbuf_split(const struct strbuf *, int delim);
87extern void strbuf_list_free(struct strbuf **);
88 71
89/*----- add data in your buffer -----*/ 72/*----- add data in your buffer -----*/
90static inline void strbuf_addch(struct strbuf *sb, int c) { 73static inline void strbuf_addch(struct strbuf *sb, int c) {
@@ -93,45 +76,17 @@ static inline void strbuf_addch(struct strbuf *sb, int c) {
93 sb->buf[sb->len] = '\0'; 76 sb->buf[sb->len] = '\0';
94} 77}
95 78
96extern void strbuf_insert(struct strbuf *, size_t pos, const void *, size_t);
97extern void strbuf_remove(struct strbuf *, size_t pos, size_t len); 79extern void strbuf_remove(struct strbuf *, size_t pos, size_t len);
98 80
99/* splice pos..pos+len with given data */
100extern void strbuf_splice(struct strbuf *, size_t pos, size_t len,
101 const void *, size_t);
102
103extern void strbuf_add(struct strbuf *, const void *, size_t); 81extern void strbuf_add(struct strbuf *, const void *, size_t);
104static inline void strbuf_addstr(struct strbuf *sb, const char *s) { 82static inline void strbuf_addstr(struct strbuf *sb, const char *s) {
105 strbuf_add(sb, s, strlen(s)); 83 strbuf_add(sb, s, strlen(s));
106} 84}
107static inline void strbuf_addbuf(struct strbuf *sb, const struct strbuf *sb2) {
108 strbuf_add(sb, sb2->buf, sb2->len);
109}
110extern void strbuf_adddup(struct strbuf *sb, size_t pos, size_t len);
111
112typedef size_t (*expand_fn_t) (struct strbuf *sb, const char *placeholder, void *context);
113extern void strbuf_expand(struct strbuf *sb, const char *format, expand_fn_t fn, void *context);
114struct strbuf_expand_dict_entry {
115 const char *placeholder;
116 const char *value;
117};
118extern size_t strbuf_expand_dict_cb(struct strbuf *sb, const char *placeholder, void *context);
119 85
120__attribute__((format(printf,2,3))) 86__attribute__((format(printf,2,3)))
121extern void strbuf_addf(struct strbuf *sb, const char *fmt, ...); 87extern void strbuf_addf(struct strbuf *sb, const char *fmt, ...);
122 88
123extern size_t strbuf_fread(struct strbuf *, size_t, FILE *);
124/* XXX: if read fails, any partial read is undone */ 89/* XXX: if read fails, any partial read is undone */
125extern ssize_t strbuf_read(struct strbuf *, int fd, size_t hint); 90extern ssize_t strbuf_read(struct strbuf *, int fd, ssize_t hint);
126extern int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint);
127extern int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint);
128
129extern int strbuf_getline(struct strbuf *, FILE *, int);
130
131extern void stripspace(struct strbuf *buf, int skip_comments);
132extern int launch_editor(const char *path, struct strbuf *buffer, const char *const *env);
133
134extern int strbuf_branchname(struct strbuf *sb, const char *name);
135extern int strbuf_check_branch_ref(struct strbuf *sb, const char *name);
136 91
137#endif /* STRBUF_H */ 92#endif /* __PERF_STRBUF_H */
diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c
index c93eca9a7be..8fc0bd3a3a4 100644
--- a/tools/perf/util/string.c
+++ b/tools/perf/util/string.c
@@ -1,34 +1,296 @@
1#include "util.h"
1#include "string.h" 2#include "string.h"
2 3
3static int hex(char ch) 4#define K 1024LL
5/*
6 * perf_atoll()
7 * Parse (\d+)(b|B|kb|KB|mb|MB|gb|GB|tb|TB) (e.g. "256MB")
8 * and return its numeric value
9 */
10s64 perf_atoll(const char *str)
4{ 11{
5 if ((ch >= '0') && (ch <= '9')) 12 unsigned int i;
6 return ch - '0'; 13 s64 length = -1, unit = 1;
7 if ((ch >= 'a') && (ch <= 'f')) 14
8 return ch - 'a' + 10; 15 if (!isdigit(str[0]))
9 if ((ch >= 'A') && (ch <= 'F')) 16 goto out_err;
10 return ch - 'A' + 10; 17
11 return -1; 18 for (i = 1; i < strlen(str); i++) {
19 switch (str[i]) {
20 case 'B':
21 case 'b':
22 break;
23 case 'K':
24 if (str[i + 1] != 'B')
25 goto out_err;
26 else
27 goto kilo;
28 case 'k':
29 if (str[i + 1] != 'b')
30 goto out_err;
31kilo:
32 unit = K;
33 break;
34 case 'M':
35 if (str[i + 1] != 'B')
36 goto out_err;
37 else
38 goto mega;
39 case 'm':
40 if (str[i + 1] != 'b')
41 goto out_err;
42mega:
43 unit = K * K;
44 break;
45 case 'G':
46 if (str[i + 1] != 'B')
47 goto out_err;
48 else
49 goto giga;
50 case 'g':
51 if (str[i + 1] != 'b')
52 goto out_err;
53giga:
54 unit = K * K * K;
55 break;
56 case 'T':
57 if (str[i + 1] != 'B')
58 goto out_err;
59 else
60 goto tera;
61 case 't':
62 if (str[i + 1] != 'b')
63 goto out_err;
64tera:
65 unit = K * K * K * K;
66 break;
67 case '\0': /* only specified figures */
68 unit = 1;
69 break;
70 default:
71 if (!isdigit(str[i]))
72 goto out_err;
73 break;
74 }
75 }
76
77 length = atoll(str) * unit;
78 goto out;
79
80out_err:
81 length = -1;
82out:
83 return length;
12} 84}
13 85
14/* 86/*
15 * While we find nice hex chars, build a long_val. 87 * Helper function for splitting a string into an argv-like array.
16 * Return number of chars processed. 88 * originaly copied from lib/argv_split.c
17 */ 89 */
18int hex2u64(const char *ptr, u64 *long_val) 90static const char *skip_sep(const char *cp)
19{ 91{
20 const char *p = ptr; 92 while (*cp && isspace(*cp))
21 *long_val = 0; 93 cp++;
22 94
23 while (*p) { 95 return cp;
24 const int hex_val = hex(*p); 96}
25 97
26 if (hex_val < 0) 98static const char *skip_arg(const char *cp)
27 break; 99{
100 while (*cp && !isspace(*cp))
101 cp++;
102
103 return cp;
104}
105
106static int count_argc(const char *str)
107{
108 int count = 0;
109
110 while (*str) {
111 str = skip_sep(str);
112 if (*str) {
113 count++;
114 str = skip_arg(str);
115 }
116 }
117
118 return count;
119}
120
121/**
122 * argv_free - free an argv
123 * @argv - the argument vector to be freed
124 *
125 * Frees an argv and the strings it points to.
126 */
127void argv_free(char **argv)
128{
129 char **p;
130 for (p = argv; *p; p++)
131 free(*p);
132
133 free(argv);
134}
135
136/**
137 * argv_split - split a string at whitespace, returning an argv
138 * @str: the string to be split
139 * @argcp: returned argument count
140 *
141 * Returns an array of pointers to strings which are split out from
142 * @str. This is performed by strictly splitting on white-space; no
143 * quote processing is performed. Multiple whitespace characters are
144 * considered to be a single argument separator. The returned array
145 * is always NULL-terminated. Returns NULL on memory allocation
146 * failure.
147 */
148char **argv_split(const char *str, int *argcp)
149{
150 int argc = count_argc(str);
151 char **argv = zalloc(sizeof(*argv) * (argc+1));
152 char **argvp;
153
154 if (argv == NULL)
155 goto out;
156
157 if (argcp)
158 *argcp = argc;
159
160 argvp = argv;
161
162 while (*str) {
163 str = skip_sep(str);
164
165 if (*str) {
166 const char *p = str;
167 char *t;
168
169 str = skip_arg(str);
28 170
29 *long_val = (*long_val << 4) | hex_val; 171 t = strndup(p, str-p);
30 p++; 172 if (t == NULL)
173 goto fail;
174 *argvp++ = t;
175 }
31 } 176 }
177 *argvp = NULL;
32 178
33 return p - ptr; 179out:
180 return argv;
181
182fail:
183 argv_free(argv);
184 return NULL;
185}
186
187/* Character class matching */
188static bool __match_charclass(const char *pat, char c, const char **npat)
189{
190 bool complement = false, ret = true;
191
192 if (*pat == '!') {
193 complement = true;
194 pat++;
195 }
196 if (*pat++ == c) /* First character is special */
197 goto end;
198
199 while (*pat && *pat != ']') { /* Matching */
200 if (*pat == '-' && *(pat + 1) != ']') { /* Range */
201 if (*(pat - 1) <= c && c <= *(pat + 1))
202 goto end;
203 if (*(pat - 1) > *(pat + 1))
204 goto error;
205 pat += 2;
206 } else if (*pat++ == c)
207 goto end;
208 }
209 if (!*pat)
210 goto error;
211 ret = false;
212
213end:
214 while (*pat && *pat != ']') /* Searching closing */
215 pat++;
216 if (!*pat)
217 goto error;
218 *npat = pat + 1;
219 return complement ? !ret : ret;
220
221error:
222 return false;
223}
224
225/* Glob/lazy pattern matching */
226static bool __match_glob(const char *str, const char *pat, bool ignore_space)
227{
228 while (*str && *pat && *pat != '*') {
229 if (ignore_space) {
230 /* Ignore spaces for lazy matching */
231 if (isspace(*str)) {
232 str++;
233 continue;
234 }
235 if (isspace(*pat)) {
236 pat++;
237 continue;
238 }
239 }
240 if (*pat == '?') { /* Matches any single character */
241 str++;
242 pat++;
243 continue;
244 } else if (*pat == '[') /* Character classes/Ranges */
245 if (__match_charclass(pat + 1, *str, &pat)) {
246 str++;
247 continue;
248 } else
249 return false;
250 else if (*pat == '\\') /* Escaped char match as normal char */
251 pat++;
252 if (*str++ != *pat++)
253 return false;
254 }
255 /* Check wild card */
256 if (*pat == '*') {
257 while (*pat == '*')
258 pat++;
259 if (!*pat) /* Tail wild card matches all */
260 return true;
261 while (*str)
262 if (__match_glob(str++, pat, ignore_space))
263 return true;
264 }
265 return !*str && !*pat;
266}
267
268/**
269 * strglobmatch - glob expression pattern matching
270 * @str: the target string to match
271 * @pat: the pattern string to match
272 *
273 * This returns true if the @str matches @pat. @pat can includes wildcards
274 * ('*','?') and character classes ([CHARS], complementation and ranges are
275 * also supported). Also, this supports escape character ('\') to use special
276 * characters as normal character.
277 *
278 * Note: if @pat syntax is broken, this always returns false.
279 */
280bool strglobmatch(const char *str, const char *pat)
281{
282 return __match_glob(str, pat, false);
283}
284
285/**
286 * strlazymatch - matching pattern strings lazily with glob pattern
287 * @str: the target string to match
288 * @pat: the pattern string to match
289 *
290 * This is similar to strglobmatch, except this ignores spaces in
291 * the target string.
292 */
293bool strlazymatch(const char *str, const char *pat)
294{
295 return __match_glob(str, pat, true);
34} 296}
diff --git a/tools/perf/util/string.h b/tools/perf/util/string.h
deleted file mode 100644
index 37b03255b42..00000000000
--- a/tools/perf/util/string.h
+++ /dev/null
@@ -1,8 +0,0 @@
1#ifndef _PERF_STRING_H_
2#define _PERF_STRING_H_
3
4#include "../types.h"
5
6int hex2u64(const char *ptr, u64 *val);
7
8#endif
diff --git a/tools/perf/util/strlist.c b/tools/perf/util/strlist.c
new file mode 100644
index 00000000000..6783a204355
--- /dev/null
+++ b/tools/perf/util/strlist.c
@@ -0,0 +1,200 @@
1/*
2 * (c) 2009 Arnaldo Carvalho de Melo <acme@redhat.com>
3 *
4 * Licensed under the GPLv2.
5 */
6
7#include "strlist.h"
8#include <errno.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12
13static struct str_node *str_node__new(const char *s, bool dupstr)
14{
15 struct str_node *self = malloc(sizeof(*self));
16
17 if (self != NULL) {
18 if (dupstr) {
19 s = strdup(s);
20 if (s == NULL)
21 goto out_delete;
22 }
23 self->s = s;
24 }
25
26 return self;
27
28out_delete:
29 free(self);
30 return NULL;
31}
32
33static void str_node__delete(struct str_node *self, bool dupstr)
34{
35 if (dupstr)
36 free((void *)self->s);
37 free(self);
38}
39
40int strlist__add(struct strlist *self, const char *new_entry)
41{
42 struct rb_node **p = &self->entries.rb_node;
43 struct rb_node *parent = NULL;
44 struct str_node *sn;
45
46 while (*p != NULL) {
47 int rc;
48
49 parent = *p;
50 sn = rb_entry(parent, struct str_node, rb_node);
51 rc = strcmp(sn->s, new_entry);
52
53 if (rc > 0)
54 p = &(*p)->rb_left;
55 else if (rc < 0)
56 p = &(*p)->rb_right;
57 else
58 return -EEXIST;
59 }
60
61 sn = str_node__new(new_entry, self->dupstr);
62 if (sn == NULL)
63 return -ENOMEM;
64
65 rb_link_node(&sn->rb_node, parent, p);
66 rb_insert_color(&sn->rb_node, &self->entries);
67 ++self->nr_entries;
68
69 return 0;
70}
71
72int strlist__load(struct strlist *self, const char *filename)
73{
74 char entry[1024];
75 int err;
76 FILE *fp = fopen(filename, "r");
77
78 if (fp == NULL)
79 return errno;
80
81 while (fgets(entry, sizeof(entry), fp) != NULL) {
82 const size_t len = strlen(entry);
83
84 if (len == 0)
85 continue;
86 entry[len - 1] = '\0';
87
88 err = strlist__add(self, entry);
89 if (err != 0)
90 goto out;
91 }
92
93 err = 0;
94out:
95 fclose(fp);
96 return err;
97}
98
99void strlist__remove(struct strlist *self, struct str_node *sn)
100{
101 rb_erase(&sn->rb_node, &self->entries);
102 str_node__delete(sn, self->dupstr);
103}
104
105struct str_node *strlist__find(struct strlist *self, const char *entry)
106{
107 struct rb_node **p = &self->entries.rb_node;
108 struct rb_node *parent = NULL;
109
110 while (*p != NULL) {
111 struct str_node *sn;
112 int rc;
113
114 parent = *p;
115 sn = rb_entry(parent, struct str_node, rb_node);
116 rc = strcmp(sn->s, entry);
117
118 if (rc > 0)
119 p = &(*p)->rb_left;
120 else if (rc < 0)
121 p = &(*p)->rb_right;
122 else
123 return sn;
124 }
125
126 return NULL;
127}
128
129static int strlist__parse_list_entry(struct strlist *self, const char *s)
130{
131 if (strncmp(s, "file://", 7) == 0)
132 return strlist__load(self, s + 7);
133
134 return strlist__add(self, s);
135}
136
137int strlist__parse_list(struct strlist *self, const char *s)
138{
139 char *sep;
140 int err;
141
142 while ((sep = strchr(s, ',')) != NULL) {
143 *sep = '\0';
144 err = strlist__parse_list_entry(self, s);
145 *sep = ',';
146 if (err != 0)
147 return err;
148 s = sep + 1;
149 }
150
151 return *s ? strlist__parse_list_entry(self, s) : 0;
152}
153
154struct strlist *strlist__new(bool dupstr, const char *slist)
155{
156 struct strlist *self = malloc(sizeof(*self));
157
158 if (self != NULL) {
159 self->entries = RB_ROOT;
160 self->dupstr = dupstr;
161 self->nr_entries = 0;
162 if (slist && strlist__parse_list(self, slist) != 0)
163 goto out_error;
164 }
165
166 return self;
167out_error:
168 free(self);
169 return NULL;
170}
171
172void strlist__delete(struct strlist *self)
173{
174 if (self != NULL) {
175 struct str_node *pos;
176 struct rb_node *next = rb_first(&self->entries);
177
178 while (next) {
179 pos = rb_entry(next, struct str_node, rb_node);
180 next = rb_next(&pos->rb_node);
181 strlist__remove(self, pos);
182 }
183 self->entries = RB_ROOT;
184 free(self);
185 }
186}
187
188struct str_node *strlist__entry(const struct strlist *self, unsigned int idx)
189{
190 struct rb_node *nd;
191
192 for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) {
193 struct str_node *pos = rb_entry(nd, struct str_node, rb_node);
194
195 if (!idx--)
196 return pos;
197 }
198
199 return NULL;
200}
diff --git a/tools/perf/util/strlist.h b/tools/perf/util/strlist.h
new file mode 100644
index 00000000000..3ba839007d2
--- /dev/null
+++ b/tools/perf/util/strlist.h
@@ -0,0 +1,78 @@
1#ifndef __PERF_STRLIST_H
2#define __PERF_STRLIST_H
3
4#include <linux/rbtree.h>
5#include <stdbool.h>
6
7struct str_node {
8 struct rb_node rb_node;
9 const char *s;
10};
11
12struct strlist {
13 struct rb_root entries;
14 unsigned int nr_entries;
15 bool dupstr;
16};
17
18struct strlist *strlist__new(bool dupstr, const char *slist);
19void strlist__delete(struct strlist *self);
20
21void strlist__remove(struct strlist *self, struct str_node *sn);
22int strlist__load(struct strlist *self, const char *filename);
23int strlist__add(struct strlist *self, const char *str);
24
25struct str_node *strlist__entry(const struct strlist *self, unsigned int idx);
26struct str_node *strlist__find(struct strlist *self, const char *entry);
27
28static inline bool strlist__has_entry(struct strlist *self, const char *entry)
29{
30 return strlist__find(self, entry) != NULL;
31}
32
33static inline bool strlist__empty(const struct strlist *self)
34{
35 return self->nr_entries == 0;
36}
37
38static inline unsigned int strlist__nr_entries(const struct strlist *self)
39{
40 return self->nr_entries;
41}
42
43/* For strlist iteration */
44static inline struct str_node *strlist__first(struct strlist *self)
45{
46 struct rb_node *rn = rb_first(&self->entries);
47 return rn ? rb_entry(rn, struct str_node, rb_node) : NULL;
48}
49static inline struct str_node *strlist__next(struct str_node *sn)
50{
51 struct rb_node *rn;
52 if (!sn)
53 return NULL;
54 rn = rb_next(&sn->rb_node);
55 return rn ? rb_entry(rn, struct str_node, rb_node) : NULL;
56}
57
58/**
59 * strlist_for_each - iterate over a strlist
60 * @pos: the &struct str_node to use as a loop cursor.
61 * @self: the &struct strlist for loop.
62 */
63#define strlist__for_each(pos, self) \
64 for (pos = strlist__first(self); pos; pos = strlist__next(pos))
65
66/**
67 * strlist_for_each_safe - iterate over a strlist safe against removal of
68 * str_node
69 * @pos: the &struct str_node to use as a loop cursor.
70 * @n: another &struct str_node to use as temporary storage.
71 * @self: the &struct strlist for loop.
72 */
73#define strlist__for_each_safe(pos, n, self) \
74 for (pos = strlist__first(self), n = strlist__next(pos); pos;\
75 pos = n, n = strlist__next(n))
76
77int strlist__parse_list(struct strlist *self, const char *s);
78#endif /* __PERF_STRLIST_H */
diff --git a/tools/perf/util/svghelper.c b/tools/perf/util/svghelper.c
new file mode 100644
index 00000000000..b3637db025a
--- /dev/null
+++ b/tools/perf/util/svghelper.c
@@ -0,0 +1,500 @@
1/*
2 * svghelper.c - helper functions for outputting svg
3 *
4 * (C) Copyright 2009 Intel Corporation
5 *
6 * Authors:
7 * Arjan van de Ven <arjan@linux.intel.com>
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; version 2
12 * of the License.
13 */
14
15#include <stdio.h>
16#include <stdlib.h>
17#include <unistd.h>
18#include <string.h>
19
20#include "svghelper.h"
21
22static u64 first_time, last_time;
23static u64 turbo_frequency, max_freq;
24
25
26#define SLOT_MULT 30.0
27#define SLOT_HEIGHT 25.0
28
29int svg_page_width = 1000;
30
31#define MIN_TEXT_SIZE 0.01
32
33static u64 total_height;
34static FILE *svgfile;
35
36static double cpu2slot(int cpu)
37{
38 return 2 * cpu + 1;
39}
40
41static double cpu2y(int cpu)
42{
43 return cpu2slot(cpu) * SLOT_MULT;
44}
45
46static double time2pixels(u64 time)
47{
48 double X;
49
50 X = 1.0 * svg_page_width * (time - first_time) / (last_time - first_time);
51 return X;
52}
53
54/*
55 * Round text sizes so that the svg viewer only needs a discrete
56 * number of renderings of the font
57 */
58static double round_text_size(double size)
59{
60 int loop = 100;
61 double target = 10.0;
62
63 if (size >= 10.0)
64 return size;
65 while (loop--) {
66 if (size >= target)
67 return target;
68 target = target / 2.0;
69 }
70 return size;
71}
72
73void open_svg(const char *filename, int cpus, int rows, u64 start, u64 end)
74{
75 int new_width;
76
77 svgfile = fopen(filename, "w");
78 if (!svgfile) {
79 fprintf(stderr, "Cannot open %s for output\n", filename);
80 return;
81 }
82 first_time = start;
83 first_time = first_time / 100000000 * 100000000;
84 last_time = end;
85
86 /*
87 * if the recording is short, we default to a width of 1000, but
88 * for longer recordings we want at least 200 units of width per second
89 */
90 new_width = (last_time - first_time) / 5000000;
91
92 if (new_width > svg_page_width)
93 svg_page_width = new_width;
94
95 total_height = (1 + rows + cpu2slot(cpus)) * SLOT_MULT;
96 fprintf(svgfile, "<?xml version=\"1.0\" standalone=\"no\"?> \n");
97 fprintf(svgfile, "<svg width=\"%i\" height=\"%llu\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\">\n", svg_page_width, total_height);
98
99 fprintf(svgfile, "<defs>\n <style type=\"text/css\">\n <![CDATA[\n");
100
101 fprintf(svgfile, " rect { stroke-width: 1; }\n");
102 fprintf(svgfile, " rect.process { fill:rgb(180,180,180); fill-opacity:0.9; stroke-width:1; stroke:rgb( 0, 0, 0); } \n");
103 fprintf(svgfile, " rect.process2 { fill:rgb(180,180,180); fill-opacity:0.9; stroke-width:0; stroke:rgb( 0, 0, 0); } \n");
104 fprintf(svgfile, " rect.sample { fill:rgb( 0, 0,255); fill-opacity:0.8; stroke-width:0; stroke:rgb( 0, 0, 0); } \n");
105 fprintf(svgfile, " rect.blocked { fill:rgb(255, 0, 0); fill-opacity:0.5; stroke-width:0; stroke:rgb( 0, 0, 0); } \n");
106 fprintf(svgfile, " rect.waiting { fill:rgb(224,214, 0); fill-opacity:0.8; stroke-width:0; stroke:rgb( 0, 0, 0); } \n");
107 fprintf(svgfile, " rect.WAITING { fill:rgb(255,214, 48); fill-opacity:0.6; stroke-width:0; stroke:rgb( 0, 0, 0); } \n");
108 fprintf(svgfile, " rect.cpu { fill:rgb(192,192,192); fill-opacity:0.2; stroke-width:0.5; stroke:rgb(128,128,128); } \n");
109 fprintf(svgfile, " rect.pstate { fill:rgb(128,128,128); fill-opacity:0.8; stroke-width:0; } \n");
110 fprintf(svgfile, " rect.c1 { fill:rgb(255,214,214); fill-opacity:0.5; stroke-width:0; } \n");
111 fprintf(svgfile, " rect.c2 { fill:rgb(255,172,172); fill-opacity:0.5; stroke-width:0; } \n");
112 fprintf(svgfile, " rect.c3 { fill:rgb(255,130,130); fill-opacity:0.5; stroke-width:0; } \n");
113 fprintf(svgfile, " rect.c4 { fill:rgb(255, 88, 88); fill-opacity:0.5; stroke-width:0; } \n");
114 fprintf(svgfile, " rect.c5 { fill:rgb(255, 44, 44); fill-opacity:0.5; stroke-width:0; } \n");
115 fprintf(svgfile, " rect.c6 { fill:rgb(255, 0, 0); fill-opacity:0.5; stroke-width:0; } \n");
116 fprintf(svgfile, " line.pstate { stroke:rgb(255,255, 0); stroke-opacity:0.8; stroke-width:2; } \n");
117
118 fprintf(svgfile, " ]]>\n </style>\n</defs>\n");
119}
120
121void svg_box(int Yslot, u64 start, u64 end, const char *type)
122{
123 if (!svgfile)
124 return;
125
126 fprintf(svgfile, "<rect x=\"%4.8f\" width=\"%4.8f\" y=\"%4.1f\" height=\"%4.1f\" class=\"%s\"/>\n",
127 time2pixels(start), time2pixels(end)-time2pixels(start), Yslot * SLOT_MULT, SLOT_HEIGHT, type);
128}
129
130void svg_sample(int Yslot, int cpu, u64 start, u64 end)
131{
132 double text_size;
133 if (!svgfile)
134 return;
135
136 fprintf(svgfile, "<rect x=\"%4.8f\" width=\"%4.8f\" y=\"%4.1f\" height=\"%4.1f\" class=\"sample\"/>\n",
137 time2pixels(start), time2pixels(end)-time2pixels(start), Yslot * SLOT_MULT, SLOT_HEIGHT);
138
139 text_size = (time2pixels(end)-time2pixels(start));
140 if (cpu > 9)
141 text_size = text_size/2;
142 if (text_size > 1.25)
143 text_size = 1.25;
144 text_size = round_text_size(text_size);
145
146 if (text_size > MIN_TEXT_SIZE)
147 fprintf(svgfile, "<text x=\"%1.8f\" y=\"%1.8f\" font-size=\"%1.8fpt\">%i</text>\n",
148 time2pixels(start), Yslot * SLOT_MULT + SLOT_HEIGHT - 1, text_size, cpu + 1);
149
150}
151
152static char *time_to_string(u64 duration)
153{
154 static char text[80];
155
156 text[0] = 0;
157
158 if (duration < 1000) /* less than 1 usec */
159 return text;
160
161 if (duration < 1000 * 1000) { /* less than 1 msec */
162 sprintf(text, "%4.1f us", duration / 1000.0);
163 return text;
164 }
165 sprintf(text, "%4.1f ms", duration / 1000.0 / 1000);
166
167 return text;
168}
169
170void svg_waiting(int Yslot, u64 start, u64 end)
171{
172 char *text;
173 const char *style;
174 double font_size;
175
176 if (!svgfile)
177 return;
178
179 style = "waiting";
180
181 if (end-start > 10 * 1000000) /* 10 msec */
182 style = "WAITING";
183
184 text = time_to_string(end-start);
185
186 font_size = 1.0 * (time2pixels(end)-time2pixels(start));
187
188 if (font_size > 3)
189 font_size = 3;
190
191 font_size = round_text_size(font_size);
192
193 fprintf(svgfile, "<g transform=\"translate(%4.8f,%4.8f)\">\n", time2pixels(start), Yslot * SLOT_MULT);
194 fprintf(svgfile, "<rect x=\"0\" width=\"%4.8f\" y=\"0\" height=\"%4.1f\" class=\"%s\"/>\n",
195 time2pixels(end)-time2pixels(start), SLOT_HEIGHT, style);
196 if (font_size > MIN_TEXT_SIZE)
197 fprintf(svgfile, "<text transform=\"rotate(90)\" font-size=\"%1.8fpt\"> %s</text>\n",
198 font_size, text);
199 fprintf(svgfile, "</g>\n");
200}
201
202static char *cpu_model(void)
203{
204 static char cpu_m[255];
205 char buf[256];
206 FILE *file;
207
208 cpu_m[0] = 0;
209 /* CPU type */
210 file = fopen("/proc/cpuinfo", "r");
211 if (file) {
212 while (fgets(buf, 255, file)) {
213 if (strstr(buf, "model name")) {
214 strncpy(cpu_m, &buf[13], 255);
215 break;
216 }
217 }
218 fclose(file);
219 }
220
221 /* CPU type */
222 file = fopen("/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies", "r");
223 if (file) {
224 while (fgets(buf, 255, file)) {
225 unsigned int freq;
226 freq = strtoull(buf, NULL, 10);
227 if (freq > max_freq)
228 max_freq = freq;
229 }
230 fclose(file);
231 }
232 return cpu_m;
233}
234
235void svg_cpu_box(int cpu, u64 __max_freq, u64 __turbo_freq)
236{
237 char cpu_string[80];
238 if (!svgfile)
239 return;
240
241 max_freq = __max_freq;
242 turbo_frequency = __turbo_freq;
243
244 fprintf(svgfile, "<rect x=\"%4.8f\" width=\"%4.8f\" y=\"%4.1f\" height=\"%4.1f\" class=\"cpu\"/>\n",
245 time2pixels(first_time),
246 time2pixels(last_time)-time2pixels(first_time),
247 cpu2y(cpu), SLOT_MULT+SLOT_HEIGHT);
248
249 sprintf(cpu_string, "CPU %i", (int)cpu+1);
250 fprintf(svgfile, "<text x=\"%4.8f\" y=\"%4.8f\">%s</text>\n",
251 10+time2pixels(first_time), cpu2y(cpu) + SLOT_HEIGHT/2, cpu_string);
252
253 fprintf(svgfile, "<text transform=\"translate(%4.8f,%4.8f)\" font-size=\"1.25pt\">%s</text>\n",
254 10+time2pixels(first_time), cpu2y(cpu) + SLOT_MULT + SLOT_HEIGHT - 4, cpu_model());
255}
256
257void svg_process(int cpu, u64 start, u64 end, const char *type, const char *name)
258{
259 double width;
260
261 if (!svgfile)
262 return;
263
264
265 fprintf(svgfile, "<g transform=\"translate(%4.8f,%4.8f)\">\n", time2pixels(start), cpu2y(cpu));
266 fprintf(svgfile, "<rect x=\"0\" width=\"%4.8f\" y=\"0\" height=\"%4.1f\" class=\"%s\"/>\n",
267 time2pixels(end)-time2pixels(start), SLOT_MULT+SLOT_HEIGHT, type);
268 width = time2pixels(end)-time2pixels(start);
269 if (width > 6)
270 width = 6;
271
272 width = round_text_size(width);
273
274 if (width > MIN_TEXT_SIZE)
275 fprintf(svgfile, "<text transform=\"rotate(90)\" font-size=\"%3.8fpt\">%s</text>\n",
276 width, name);
277
278 fprintf(svgfile, "</g>\n");
279}
280
281void svg_cstate(int cpu, u64 start, u64 end, int type)
282{
283 double width;
284 char style[128];
285
286 if (!svgfile)
287 return;
288
289
290 if (type > 6)
291 type = 6;
292 sprintf(style, "c%i", type);
293
294 fprintf(svgfile, "<rect class=\"%s\" x=\"%4.8f\" width=\"%4.8f\" y=\"%4.1f\" height=\"%4.1f\"/>\n",
295 style,
296 time2pixels(start), time2pixels(end)-time2pixels(start),
297 cpu2y(cpu), SLOT_MULT+SLOT_HEIGHT);
298
299 width = (time2pixels(end)-time2pixels(start))/2.0;
300 if (width > 6)
301 width = 6;
302
303 width = round_text_size(width);
304
305 if (width > MIN_TEXT_SIZE)
306 fprintf(svgfile, "<text x=\"%4.8f\" y=\"%4.8f\" font-size=\"%3.8fpt\">C%i</text>\n",
307 time2pixels(start), cpu2y(cpu)+width, width, type);
308}
309
310static char *HzToHuman(unsigned long hz)
311{
312 static char buffer[1024];
313 unsigned long long Hz;
314
315 memset(buffer, 0, 1024);
316
317 Hz = hz;
318
319 /* default: just put the Number in */
320 sprintf(buffer, "%9lli", Hz);
321
322 if (Hz > 1000)
323 sprintf(buffer, " %6lli Mhz", (Hz+500)/1000);
324
325 if (Hz > 1500000)
326 sprintf(buffer, " %6.2f Ghz", (Hz+5000.0)/1000000);
327
328 if (Hz == turbo_frequency)
329 sprintf(buffer, "Turbo");
330
331 return buffer;
332}
333
334void svg_pstate(int cpu, u64 start, u64 end, u64 freq)
335{
336 double height = 0;
337
338 if (!svgfile)
339 return;
340
341 if (max_freq)
342 height = freq * 1.0 / max_freq * (SLOT_HEIGHT + SLOT_MULT);
343 height = 1 + cpu2y(cpu) + SLOT_MULT + SLOT_HEIGHT - height;
344 fprintf(svgfile, "<line x1=\"%4.8f\" x2=\"%4.8f\" y1=\"%4.1f\" y2=\"%4.1f\" class=\"pstate\"/>\n",
345 time2pixels(start), time2pixels(end), height, height);
346 fprintf(svgfile, "<text x=\"%4.8f\" y=\"%4.8f\" font-size=\"0.25pt\">%s</text>\n",
347 time2pixels(start), height+0.9, HzToHuman(freq));
348
349}
350
351
352void svg_partial_wakeline(u64 start, int row1, char *desc1, int row2, char *desc2)
353{
354 double height;
355
356 if (!svgfile)
357 return;
358
359
360 if (row1 < row2) {
361 if (row1) {
362 fprintf(svgfile, "<line x1=\"%4.8f\" y1=\"%4.2f\" x2=\"%4.8f\" y2=\"%4.2f\" style=\"stroke:rgb(32,255,32);stroke-width:0.009\"/>\n",
363 time2pixels(start), row1 * SLOT_MULT + SLOT_HEIGHT, time2pixels(start), row1 * SLOT_MULT + SLOT_HEIGHT + SLOT_MULT/32);
364 if (desc2)
365 fprintf(svgfile, "<g transform=\"translate(%4.8f,%4.8f)\"><text transform=\"rotate(90)\" font-size=\"0.02pt\">%s &gt;</text></g>\n",
366 time2pixels(start), row1 * SLOT_MULT + SLOT_HEIGHT + SLOT_HEIGHT/48, desc2);
367 }
368 if (row2) {
369 fprintf(svgfile, "<line x1=\"%4.8f\" y1=\"%4.2f\" x2=\"%4.8f\" y2=\"%4.2f\" style=\"stroke:rgb(32,255,32);stroke-width:0.009\"/>\n",
370 time2pixels(start), row2 * SLOT_MULT - SLOT_MULT/32, time2pixels(start), row2 * SLOT_MULT);
371 if (desc1)
372 fprintf(svgfile, "<g transform=\"translate(%4.8f,%4.8f)\"><text transform=\"rotate(90)\" font-size=\"0.02pt\">%s &gt;</text></g>\n",
373 time2pixels(start), row2 * SLOT_MULT - SLOT_MULT/32, desc1);
374 }
375 } else {
376 if (row2) {
377 fprintf(svgfile, "<line x1=\"%4.8f\" y1=\"%4.2f\" x2=\"%4.8f\" y2=\"%4.2f\" style=\"stroke:rgb(32,255,32);stroke-width:0.009\"/>\n",
378 time2pixels(start), row2 * SLOT_MULT + SLOT_HEIGHT, time2pixels(start), row2 * SLOT_MULT + SLOT_HEIGHT + SLOT_MULT/32);
379 if (desc1)
380 fprintf(svgfile, "<g transform=\"translate(%4.8f,%4.8f)\"><text transform=\"rotate(90)\" font-size=\"0.02pt\">%s &lt;</text></g>\n",
381 time2pixels(start), row2 * SLOT_MULT + SLOT_HEIGHT + SLOT_MULT/48, desc1);
382 }
383 if (row1) {
384 fprintf(svgfile, "<line x1=\"%4.8f\" y1=\"%4.2f\" x2=\"%4.8f\" y2=\"%4.2f\" style=\"stroke:rgb(32,255,32);stroke-width:0.009\"/>\n",
385 time2pixels(start), row1 * SLOT_MULT - SLOT_MULT/32, time2pixels(start), row1 * SLOT_MULT);
386 if (desc2)
387 fprintf(svgfile, "<g transform=\"translate(%4.8f,%4.8f)\"><text transform=\"rotate(90)\" font-size=\"0.02pt\">%s &lt;</text></g>\n",
388 time2pixels(start), row1 * SLOT_MULT - SLOT_HEIGHT/32, desc2);
389 }
390 }
391 height = row1 * SLOT_MULT;
392 if (row2 > row1)
393 height += SLOT_HEIGHT;
394 if (row1)
395 fprintf(svgfile, "<circle cx=\"%4.8f\" cy=\"%4.2f\" r = \"0.01\" style=\"fill:rgb(32,255,32)\"/>\n",
396 time2pixels(start), height);
397}
398
399void svg_wakeline(u64 start, int row1, int row2)
400{
401 double height;
402
403 if (!svgfile)
404 return;
405
406
407 if (row1 < row2)
408 fprintf(svgfile, "<line x1=\"%4.8f\" y1=\"%4.2f\" x2=\"%4.8f\" y2=\"%4.2f\" style=\"stroke:rgb(32,255,32);stroke-width:0.009\"/>\n",
409 time2pixels(start), row1 * SLOT_MULT + SLOT_HEIGHT, time2pixels(start), row2 * SLOT_MULT);
410 else
411 fprintf(svgfile, "<line x1=\"%4.8f\" y1=\"%4.2f\" x2=\"%4.8f\" y2=\"%4.2f\" style=\"stroke:rgb(32,255,32);stroke-width:0.009\"/>\n",
412 time2pixels(start), row2 * SLOT_MULT + SLOT_HEIGHT, time2pixels(start), row1 * SLOT_MULT);
413
414 height = row1 * SLOT_MULT;
415 if (row2 > row1)
416 height += SLOT_HEIGHT;
417 fprintf(svgfile, "<circle cx=\"%4.8f\" cy=\"%4.2f\" r = \"0.01\" style=\"fill:rgb(32,255,32)\"/>\n",
418 time2pixels(start), height);
419}
420
421void svg_interrupt(u64 start, int row)
422{
423 if (!svgfile)
424 return;
425
426 fprintf(svgfile, "<circle cx=\"%4.8f\" cy=\"%4.2f\" r = \"0.01\" style=\"fill:rgb(255,128,128)\"/>\n",
427 time2pixels(start), row * SLOT_MULT);
428 fprintf(svgfile, "<circle cx=\"%4.8f\" cy=\"%4.2f\" r = \"0.01\" style=\"fill:rgb(255,128,128)\"/>\n",
429 time2pixels(start), row * SLOT_MULT + SLOT_HEIGHT);
430}
431
432void svg_text(int Yslot, u64 start, const char *text)
433{
434 if (!svgfile)
435 return;
436
437 fprintf(svgfile, "<text x=\"%4.8f\" y=\"%4.8f\">%s</text>\n",
438 time2pixels(start), Yslot * SLOT_MULT+SLOT_HEIGHT/2, text);
439}
440
441static void svg_legenda_box(int X, const char *text, const char *style)
442{
443 double boxsize;
444 boxsize = SLOT_HEIGHT / 2;
445
446 fprintf(svgfile, "<rect x=\"%i\" width=\"%4.8f\" y=\"0\" height=\"%4.1f\" class=\"%s\"/>\n",
447 X, boxsize, boxsize, style);
448 fprintf(svgfile, "<text transform=\"translate(%4.8f, %4.8f)\" font-size=\"%4.8fpt\">%s</text>\n",
449 X + boxsize + 5, boxsize, 0.8 * boxsize, text);
450}
451
452void svg_legenda(void)
453{
454 if (!svgfile)
455 return;
456
457 svg_legenda_box(0, "Running", "sample");
458 svg_legenda_box(100, "Idle","rect.c1");
459 svg_legenda_box(200, "Deeper Idle", "rect.c3");
460 svg_legenda_box(350, "Deepest Idle", "rect.c6");
461 svg_legenda_box(550, "Sleeping", "process2");
462 svg_legenda_box(650, "Waiting for cpu", "waiting");
463 svg_legenda_box(800, "Blocked on IO", "blocked");
464}
465
466void svg_time_grid(void)
467{
468 u64 i;
469
470 if (!svgfile)
471 return;
472
473 i = first_time;
474 while (i < last_time) {
475 int color = 220;
476 double thickness = 0.075;
477 if ((i % 100000000) == 0) {
478 thickness = 0.5;
479 color = 192;
480 }
481 if ((i % 1000000000) == 0) {
482 thickness = 2.0;
483 color = 128;
484 }
485
486 fprintf(svgfile, "<line x1=\"%4.8f\" y1=\"%4.2f\" x2=\"%4.8f\" y2=\"%llu\" style=\"stroke:rgb(%i,%i,%i);stroke-width:%1.3f\"/>\n",
487 time2pixels(i), SLOT_MULT/2, time2pixels(i), total_height, color, color, color, thickness);
488
489 i += 10000000;
490 }
491}
492
493void svg_close(void)
494{
495 if (svgfile) {
496 fprintf(svgfile, "</svg>\n");
497 fclose(svgfile);
498 svgfile = NULL;
499 }
500}
diff --git a/tools/perf/util/svghelper.h b/tools/perf/util/svghelper.h
new file mode 100644
index 00000000000..e0781989cc3
--- /dev/null
+++ b/tools/perf/util/svghelper.h
@@ -0,0 +1,28 @@
1#ifndef __PERF_SVGHELPER_H
2#define __PERF_SVGHELPER_H
3
4#include "types.h"
5
6extern void open_svg(const char *filename, int cpus, int rows, u64 start, u64 end);
7extern void svg_box(int Yslot, u64 start, u64 end, const char *type);
8extern void svg_sample(int Yslot, int cpu, u64 start, u64 end);
9extern void svg_waiting(int Yslot, u64 start, u64 end);
10extern void svg_cpu_box(int cpu, u64 max_frequency, u64 turbo_frequency);
11
12
13extern void svg_process(int cpu, u64 start, u64 end, const char *type, const char *name);
14extern void svg_cstate(int cpu, u64 start, u64 end, int type);
15extern void svg_pstate(int cpu, u64 start, u64 end, u64 freq);
16
17
18extern void svg_time_grid(void);
19extern void svg_legenda(void);
20extern void svg_wakeline(u64 start, int row1, int row2);
21extern void svg_partial_wakeline(u64 start, int row1, char *desc1, int row2, char *desc2);
22extern void svg_interrupt(u64 start, int row);
23extern void svg_text(int Yslot, u64 start, const char *text);
24extern void svg_close(void);
25
26extern int svg_page_width;
27
28#endif /* __PERF_SVGHELPER_H */
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 86e14375e74..15ccfba8cdf 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -1,93 +1,257 @@
1#include "util.h" 1#define _GNU_SOURCE
2#include "../perf.h" 2#include <ctype.h>
3#include "string.h" 3#include <dirent.h>
4#include <errno.h>
5#include <libgen.h>
6#include <stdlib.h>
7#include <stdio.h>
8#include <string.h>
9#include <sys/types.h>
10#include <sys/stat.h>
11#include <sys/param.h>
12#include <fcntl.h>
13#include <unistd.h>
14#include "build-id.h"
15#include "debug.h"
4#include "symbol.h" 16#include "symbol.h"
17#include "strlist.h"
5 18
6#include <libelf.h> 19#include <libelf.h>
7#include <gelf.h> 20#include <gelf.h>
8#include <elf.h> 21#include <elf.h>
22#include <limits.h>
23#include <sys/utsname.h>
24
25#ifndef KSYM_NAME_LEN
26#define KSYM_NAME_LEN 128
27#endif
28
29#ifndef NT_GNU_BUILD_ID
30#define NT_GNU_BUILD_ID 3
31#endif
32
33static bool dso__build_id_equal(const struct dso *self, u8 *build_id);
34static int elf_read_build_id(Elf *elf, void *bf, size_t size);
35static void dsos__add(struct list_head *head, struct dso *dso);
36static struct map *map__new2(u64 start, struct dso *dso, enum map_type type);
37static int dso__load_kernel_sym(struct dso *self, struct map *map,
38 symbol_filter_t filter);
39static int dso__load_guest_kernel_sym(struct dso *self, struct map *map,
40 symbol_filter_t filter);
41static int vmlinux_path__nr_entries;
42static char **vmlinux_path;
43
44struct symbol_conf symbol_conf = {
45 .exclude_other = true,
46 .use_modules = true,
47 .try_vmlinux_path = true,
48 .symfs = "",
49};
50
51int dso__name_len(const struct dso *self)
52{
53 if (verbose)
54 return self->long_name_len;
9 55
10const char *sym_hist_filter; 56 return self->short_name_len;
57}
11 58
12static struct symbol *symbol__new(u64 start, u64 len, 59bool dso__loaded(const struct dso *self, enum map_type type)
13 const char *name, unsigned int priv_size,
14 u64 obj_start, int verbose)
15{ 60{
16 size_t namelen = strlen(name) + 1; 61 return self->loaded & (1 << type);
17 struct symbol *self = calloc(1, priv_size + sizeof(*self) + namelen); 62}
18 63
19 if (!self) 64bool dso__sorted_by_name(const struct dso *self, enum map_type type)
20 return NULL; 65{
66 return self->sorted_by_name & (1 << type);
67}
68
69static void dso__set_sorted_by_name(struct dso *self, enum map_type type)
70{
71 self->sorted_by_name |= (1 << type);
72}
73
74bool symbol_type__is_a(char symbol_type, enum map_type map_type)
75{
76 switch (map_type) {
77 case MAP__FUNCTION:
78 return symbol_type == 'T' || symbol_type == 'W';
79 case MAP__VARIABLE:
80 return symbol_type == 'D' || symbol_type == 'd';
81 default:
82 return false;
83 }
84}
21 85
22 if (verbose >= 2) 86static void symbols__fixup_end(struct rb_root *self)
23 printf("new symbol: %016Lx [%08lx]: %s, hist: %p, obj_start: %p\n", 87{
24 (u64)start, (unsigned long)len, name, self->hist, (void *)(unsigned long)obj_start); 88 struct rb_node *nd, *prevnd = rb_first(self);
89 struct symbol *curr, *prev;
25 90
26 self->obj_start= obj_start; 91 if (prevnd == NULL)
27 self->hist = NULL; 92 return;
28 self->hist_sum = 0;
29 93
30 if (sym_hist_filter && !strcmp(name, sym_hist_filter)) 94 curr = rb_entry(prevnd, struct symbol, rb_node);
31 self->hist = calloc(sizeof(u64), len);
32 95
33 if (priv_size) { 96 for (nd = rb_next(prevnd); nd; nd = rb_next(nd)) {
34 memset(self, 0, priv_size); 97 prev = curr;
35 self = ((void *)self) + priv_size; 98 curr = rb_entry(nd, struct symbol, rb_node);
99
100 if (prev->end == prev->start && prev->end != curr->start)
101 prev->end = curr->start - 1;
36 } 102 }
37 self->start = start; 103
38 self->end = start + len - 1; 104 /* Last entry */
105 if (curr->end == curr->start)
106 curr->end = roundup(curr->start, 4096);
107}
108
109static void __map_groups__fixup_end(struct map_groups *self, enum map_type type)
110{
111 struct map *prev, *curr;
112 struct rb_node *nd, *prevnd = rb_first(&self->maps[type]);
113
114 if (prevnd == NULL)
115 return;
116
117 curr = rb_entry(prevnd, struct map, rb_node);
118
119 for (nd = rb_next(prevnd); nd; nd = rb_next(nd)) {
120 prev = curr;
121 curr = rb_entry(nd, struct map, rb_node);
122 prev->end = curr->start - 1;
123 }
124
125 /*
126 * We still haven't the actual symbols, so guess the
127 * last map final address.
128 */
129 curr->end = ~0ULL;
130}
131
132static void map_groups__fixup_end(struct map_groups *self)
133{
134 int i;
135 for (i = 0; i < MAP__NR_TYPES; ++i)
136 __map_groups__fixup_end(self, i);
137}
138
139static struct symbol *symbol__new(u64 start, u64 len, u8 binding,
140 const char *name)
141{
142 size_t namelen = strlen(name) + 1;
143 struct symbol *self = calloc(1, (symbol_conf.priv_size +
144 sizeof(*self) + namelen));
145 if (self == NULL)
146 return NULL;
147
148 if (symbol_conf.priv_size)
149 self = ((void *)self) + symbol_conf.priv_size;
150
151 self->start = start;
152 self->end = len ? start + len - 1 : start;
153 self->binding = binding;
154 self->namelen = namelen - 1;
155
156 pr_debug4("%s: %s %#Lx-%#Lx\n", __func__, name, start, self->end);
157
39 memcpy(self->name, name, namelen); 158 memcpy(self->name, name, namelen);
40 159
41 return self; 160 return self;
42} 161}
43 162
44static void symbol__delete(struct symbol *self, unsigned int priv_size) 163void symbol__delete(struct symbol *self)
45{ 164{
46 free(((void *)self) - priv_size); 165 free(((void *)self) - symbol_conf.priv_size);
47} 166}
48 167
49static size_t symbol__fprintf(struct symbol *self, FILE *fp) 168static size_t symbol__fprintf(struct symbol *self, FILE *fp)
50{ 169{
51 return fprintf(fp, " %llx-%llx %s\n", 170 return fprintf(fp, " %llx-%llx %c %s\n",
52 self->start, self->end, self->name); 171 self->start, self->end,
172 self->binding == STB_GLOBAL ? 'g' :
173 self->binding == STB_LOCAL ? 'l' : 'w',
174 self->name);
53} 175}
54 176
55struct dso *dso__new(const char *name, unsigned int sym_priv_size) 177void dso__set_long_name(struct dso *self, char *name)
56{ 178{
57 struct dso *self = malloc(sizeof(*self) + strlen(name) + 1); 179 if (name == NULL)
180 return;
181 self->long_name = name;
182 self->long_name_len = strlen(name);
183}
184
185static void dso__set_short_name(struct dso *self, const char *name)
186{
187 if (name == NULL)
188 return;
189 self->short_name = name;
190 self->short_name_len = strlen(name);
191}
192
193static void dso__set_basename(struct dso *self)
194{
195 dso__set_short_name(self, basename(self->long_name));
196}
197
198struct dso *dso__new(const char *name)
199{
200 struct dso *self = calloc(1, sizeof(*self) + strlen(name) + 1);
58 201
59 if (self != NULL) { 202 if (self != NULL) {
203 int i;
60 strcpy(self->name, name); 204 strcpy(self->name, name);
61 self->syms = RB_ROOT; 205 dso__set_long_name(self, self->name);
62 self->sym_priv_size = sym_priv_size; 206 dso__set_short_name(self, self->name);
63 self->find_symbol = dso__find_symbol; 207 for (i = 0; i < MAP__NR_TYPES; ++i)
208 self->symbols[i] = self->symbol_names[i] = RB_ROOT;
209 self->slen_calculated = 0;
210 self->origin = DSO__ORIG_NOT_FOUND;
211 self->loaded = 0;
212 self->sorted_by_name = 0;
213 self->has_build_id = 0;
214 self->kernel = DSO_TYPE_USER;
215 INIT_LIST_HEAD(&self->node);
64 } 216 }
65 217
66 return self; 218 return self;
67} 219}
68 220
69static void dso__delete_symbols(struct dso *self) 221static void symbols__delete(struct rb_root *self)
70{ 222{
71 struct symbol *pos; 223 struct symbol *pos;
72 struct rb_node *next = rb_first(&self->syms); 224 struct rb_node *next = rb_first(self);
73 225
74 while (next) { 226 while (next) {
75 pos = rb_entry(next, struct symbol, rb_node); 227 pos = rb_entry(next, struct symbol, rb_node);
76 next = rb_next(&pos->rb_node); 228 next = rb_next(&pos->rb_node);
77 rb_erase(&pos->rb_node, &self->syms); 229 rb_erase(&pos->rb_node, self);
78 symbol__delete(pos, self->sym_priv_size); 230 symbol__delete(pos);
79 } 231 }
80} 232}
81 233
82void dso__delete(struct dso *self) 234void dso__delete(struct dso *self)
83{ 235{
84 dso__delete_symbols(self); 236 int i;
237 for (i = 0; i < MAP__NR_TYPES; ++i)
238 symbols__delete(&self->symbols[i]);
239 if (self->sname_alloc)
240 free((char *)self->short_name);
241 if (self->lname_alloc)
242 free(self->long_name);
85 free(self); 243 free(self);
86} 244}
87 245
88static void dso__insert_symbol(struct dso *self, struct symbol *sym) 246void dso__set_build_id(struct dso *self, void *build_id)
89{ 247{
90 struct rb_node **p = &self->syms.rb_node; 248 memcpy(self->build_id, build_id, sizeof(self->build_id));
249 self->has_build_id = 1;
250}
251
252static void symbols__insert(struct rb_root *self, struct symbol *sym)
253{
254 struct rb_node **p = &self->rb_node;
91 struct rb_node *parent = NULL; 255 struct rb_node *parent = NULL;
92 const u64 ip = sym->start; 256 const u64 ip = sym->start;
93 struct symbol *s; 257 struct symbol *s;
@@ -101,17 +265,17 @@ static void dso__insert_symbol(struct dso *self, struct symbol *sym)
101 p = &(*p)->rb_right; 265 p = &(*p)->rb_right;
102 } 266 }
103 rb_link_node(&sym->rb_node, parent, p); 267 rb_link_node(&sym->rb_node, parent, p);
104 rb_insert_color(&sym->rb_node, &self->syms); 268 rb_insert_color(&sym->rb_node, self);
105} 269}
106 270
107struct symbol *dso__find_symbol(struct dso *self, u64 ip) 271static struct symbol *symbols__find(struct rb_root *self, u64 ip)
108{ 272{
109 struct rb_node *n; 273 struct rb_node *n;
110 274
111 if (self == NULL) 275 if (self == NULL)
112 return NULL; 276 return NULL;
113 277
114 n = self->syms.rb_node; 278 n = self->rb_node;
115 279
116 while (n) { 280 while (n) {
117 struct symbol *s = rb_entry(n, struct symbol, rb_node); 281 struct symbol *s = rb_entry(n, struct symbol, rb_node);
@@ -127,12 +291,136 @@ struct symbol *dso__find_symbol(struct dso *self, u64 ip)
127 return NULL; 291 return NULL;
128} 292}
129 293
130size_t dso__fprintf(struct dso *self, FILE *fp) 294struct symbol_name_rb_node {
295 struct rb_node rb_node;
296 struct symbol sym;
297};
298
299static void symbols__insert_by_name(struct rb_root *self, struct symbol *sym)
131{ 300{
132 size_t ret = fprintf(fp, "dso: %s\n", self->name); 301 struct rb_node **p = &self->rb_node;
302 struct rb_node *parent = NULL;
303 struct symbol_name_rb_node *symn, *s;
304
305 symn = container_of(sym, struct symbol_name_rb_node, sym);
133 306
307 while (*p != NULL) {
308 parent = *p;
309 s = rb_entry(parent, struct symbol_name_rb_node, rb_node);
310 if (strcmp(sym->name, s->sym.name) < 0)
311 p = &(*p)->rb_left;
312 else
313 p = &(*p)->rb_right;
314 }
315 rb_link_node(&symn->rb_node, parent, p);
316 rb_insert_color(&symn->rb_node, self);
317}
318
319static void symbols__sort_by_name(struct rb_root *self, struct rb_root *source)
320{
134 struct rb_node *nd; 321 struct rb_node *nd;
135 for (nd = rb_first(&self->syms); nd; nd = rb_next(nd)) { 322
323 for (nd = rb_first(source); nd; nd = rb_next(nd)) {
324 struct symbol *pos = rb_entry(nd, struct symbol, rb_node);
325 symbols__insert_by_name(self, pos);
326 }
327}
328
329static struct symbol *symbols__find_by_name(struct rb_root *self, const char *name)
330{
331 struct rb_node *n;
332
333 if (self == NULL)
334 return NULL;
335
336 n = self->rb_node;
337
338 while (n) {
339 struct symbol_name_rb_node *s;
340 int cmp;
341
342 s = rb_entry(n, struct symbol_name_rb_node, rb_node);
343 cmp = strcmp(name, s->sym.name);
344
345 if (cmp < 0)
346 n = n->rb_left;
347 else if (cmp > 0)
348 n = n->rb_right;
349 else
350 return &s->sym;
351 }
352
353 return NULL;
354}
355
356struct symbol *dso__find_symbol(struct dso *self,
357 enum map_type type, u64 addr)
358{
359 return symbols__find(&self->symbols[type], addr);
360}
361
362struct symbol *dso__find_symbol_by_name(struct dso *self, enum map_type type,
363 const char *name)
364{
365 return symbols__find_by_name(&self->symbol_names[type], name);
366}
367
368void dso__sort_by_name(struct dso *self, enum map_type type)
369{
370 dso__set_sorted_by_name(self, type);
371 return symbols__sort_by_name(&self->symbol_names[type],
372 &self->symbols[type]);
373}
374
375int build_id__sprintf(const u8 *self, int len, char *bf)
376{
377 char *bid = bf;
378 const u8 *raw = self;
379 int i;
380
381 for (i = 0; i < len; ++i) {
382 sprintf(bid, "%02x", *raw);
383 ++raw;
384 bid += 2;
385 }
386
387 return raw - self;
388}
389
390size_t dso__fprintf_buildid(struct dso *self, FILE *fp)
391{
392 char sbuild_id[BUILD_ID_SIZE * 2 + 1];
393
394 build_id__sprintf(self->build_id, sizeof(self->build_id), sbuild_id);
395 return fprintf(fp, "%s", sbuild_id);
396}
397
398size_t dso__fprintf_symbols_by_name(struct dso *self, enum map_type type, FILE *fp)
399{
400 size_t ret = 0;
401 struct rb_node *nd;
402 struct symbol_name_rb_node *pos;
403
404 for (nd = rb_first(&self->symbol_names[type]); nd; nd = rb_next(nd)) {
405 pos = rb_entry(nd, struct symbol_name_rb_node, rb_node);
406 fprintf(fp, "%s\n", pos->sym.name);
407 }
408
409 return ret;
410}
411
412size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp)
413{
414 struct rb_node *nd;
415 size_t ret = fprintf(fp, "dso: %s (", self->short_name);
416
417 if (self->short_name != self->long_name)
418 ret += fprintf(fp, "%s, ", self->long_name);
419 ret += fprintf(fp, "%s, %sloaded, ", map_type__name[type],
420 self->loaded ? "" : "NOT ");
421 ret += dso__fprintf_buildid(self, fp);
422 ret += fprintf(fp, ")\n");
423 for (nd = rb_first(&self->symbols[type]); nd; nd = rb_next(nd)) {
136 struct symbol *pos = rb_entry(nd, struct symbol, rb_node); 424 struct symbol *pos = rb_entry(nd, struct symbol, rb_node);
137 ret += symbol__fprintf(pos, fp); 425 ret += symbol__fprintf(pos, fp);
138 } 426 }
@@ -140,29 +428,37 @@ size_t dso__fprintf(struct dso *self, FILE *fp)
140 return ret; 428 return ret;
141} 429}
142 430
143static int dso__load_kallsyms(struct dso *self, symbol_filter_t filter, int verbose) 431int kallsyms__parse(const char *filename, void *arg,
432 int (*process_symbol)(void *arg, const char *name,
433 char type, u64 start, u64 end))
144{ 434{
145 struct rb_node *nd, *prevnd;
146 char *line = NULL; 435 char *line = NULL;
147 size_t n; 436 size_t n;
148 FILE *file = fopen("/proc/kallsyms", "r"); 437 int err = -1;
438 u64 prev_start = 0;
439 char prev_symbol_type = 0;
440 char *prev_symbol_name;
441 FILE *file = fopen(filename, "r");
149 442
150 if (file == NULL) 443 if (file == NULL)
151 goto out_failure; 444 goto out_failure;
152 445
446 prev_symbol_name = malloc(KSYM_NAME_LEN);
447 if (prev_symbol_name == NULL)
448 goto out_close;
449
450 err = 0;
451
153 while (!feof(file)) { 452 while (!feof(file)) {
154 u64 start; 453 u64 start;
155 struct symbol *sym;
156 int line_len, len; 454 int line_len, len;
157 char symbol_type; 455 char symbol_type;
456 char *symbol_name;
158 457
159 line_len = getline(&line, &n, file); 458 line_len = getline(&line, &n, file);
160 if (line_len < 0) 459 if (line_len < 0 || !line)
161 break; 460 break;
162 461
163 if (!line)
164 goto out_failure;
165
166 line[--line_len] = '\0'; /* \n */ 462 line[--line_len] = '\0'; /* \n */
167 463
168 len = hex2u64(line, &start); 464 len = hex2u64(line, &start);
@@ -172,62 +468,234 @@ static int dso__load_kallsyms(struct dso *self, symbol_filter_t filter, int verb
172 continue; 468 continue;
173 469
174 symbol_type = toupper(line[len]); 470 symbol_type = toupper(line[len]);
175 /* 471 len += 2;
176 * We're interested only in code ('T'ext) 472 symbol_name = line + len;
177 */ 473 len = line_len - len;
178 if (symbol_type != 'T' && symbol_type != 'W')
179 continue;
180 /*
181 * Well fix up the end later, when we have all sorted.
182 */
183 sym = symbol__new(start, 0xdead, line + len + 2,
184 self->sym_priv_size, 0, verbose);
185 474
186 if (sym == NULL) 475 if (len >= KSYM_NAME_LEN) {
187 goto out_delete_line; 476 err = -1;
477 break;
478 }
188 479
189 if (filter && filter(self, sym)) 480 if (prev_symbol_type) {
190 symbol__delete(sym, self->sym_priv_size); 481 u64 end = start;
191 else 482 if (end != prev_start)
192 dso__insert_symbol(self, sym); 483 --end;
484 err = process_symbol(arg, prev_symbol_name,
485 prev_symbol_type, prev_start, end);
486 if (err)
487 break;
488 }
489
490 memcpy(prev_symbol_name, symbol_name, len + 1);
491 prev_symbol_type = symbol_type;
492 prev_start = start;
193 } 493 }
194 494
495 free(prev_symbol_name);
496 free(line);
497out_close:
498 fclose(file);
499 return err;
500
501out_failure:
502 return -1;
503}
504
505struct process_kallsyms_args {
506 struct map *map;
507 struct dso *dso;
508};
509
510static u8 kallsyms2elf_type(char type)
511{
512 if (type == 'W')
513 return STB_WEAK;
514
515 return isupper(type) ? STB_GLOBAL : STB_LOCAL;
516}
517
518static int map__process_kallsym_symbol(void *arg, const char *name,
519 char type, u64 start, u64 end)
520{
521 struct symbol *sym;
522 struct process_kallsyms_args *a = arg;
523 struct rb_root *root = &a->dso->symbols[a->map->type];
524
525 if (!symbol_type__is_a(type, a->map->type))
526 return 0;
527
528 sym = symbol__new(start, end - start + 1,
529 kallsyms2elf_type(type), name);
530 if (sym == NULL)
531 return -ENOMEM;
195 /* 532 /*
196 * Now that we have all sorted out, just set the ->end of all 533 * We will pass the symbols to the filter later, in
197 * symbols 534 * map__split_kallsyms, when we have split the maps per module
198 */ 535 */
199 prevnd = rb_first(&self->syms); 536 symbols__insert(root, sym);
200 537
201 if (prevnd == NULL) 538 return 0;
202 goto out_delete_line; 539}
203 540
204 for (nd = rb_next(prevnd); nd; nd = rb_next(nd)) { 541/*
205 struct symbol *prev = rb_entry(prevnd, struct symbol, rb_node), 542 * Loads the function entries in /proc/kallsyms into kernel_map->dso,
206 *curr = rb_entry(nd, struct symbol, rb_node); 543 * so that we can in the next step set the symbol ->end address and then
544 * call kernel_maps__split_kallsyms.
545 */
546static int dso__load_all_kallsyms(struct dso *self, const char *filename,
547 struct map *map)
548{
549 struct process_kallsyms_args args = { .map = map, .dso = self, };
550 return kallsyms__parse(filename, &args, map__process_kallsym_symbol);
551}
207 552
208 prev->end = curr->start - 1; 553/*
209 prevnd = nd; 554 * Split the symbols into maps, making sure there are no overlaps, i.e. the
555 * kernel range is broken in several maps, named [kernel].N, as we don't have
556 * the original ELF section names vmlinux have.
557 */
558static int dso__split_kallsyms(struct dso *self, struct map *map,
559 symbol_filter_t filter)
560{
561 struct map_groups *kmaps = map__kmap(map)->kmaps;
562 struct machine *machine = kmaps->machine;
563 struct map *curr_map = map;
564 struct symbol *pos;
565 int count = 0, moved = 0;
566 struct rb_root *root = &self->symbols[map->type];
567 struct rb_node *next = rb_first(root);
568 int kernel_range = 0;
569
570 while (next) {
571 char *module;
572
573 pos = rb_entry(next, struct symbol, rb_node);
574 next = rb_next(&pos->rb_node);
575
576 module = strchr(pos->name, '\t');
577 if (module) {
578 if (!symbol_conf.use_modules)
579 goto discard_symbol;
580
581 *module++ = '\0';
582
583 if (strcmp(curr_map->dso->short_name, module)) {
584 if (curr_map != map &&
585 self->kernel == DSO_TYPE_GUEST_KERNEL &&
586 machine__is_default_guest(machine)) {
587 /*
588 * We assume all symbols of a module are
589 * continuous in * kallsyms, so curr_map
590 * points to a module and all its
591 * symbols are in its kmap. Mark it as
592 * loaded.
593 */
594 dso__set_loaded(curr_map->dso,
595 curr_map->type);
596 }
597
598 curr_map = map_groups__find_by_name(kmaps,
599 map->type, module);
600 if (curr_map == NULL) {
601 pr_debug("%s/proc/{kallsyms,modules} "
602 "inconsistency while looking "
603 "for \"%s\" module!\n",
604 machine->root_dir, module);
605 curr_map = map;
606 goto discard_symbol;
607 }
608
609 if (curr_map->dso->loaded &&
610 !machine__is_default_guest(machine))
611 goto discard_symbol;
612 }
613 /*
614 * So that we look just like we get from .ko files,
615 * i.e. not prelinked, relative to map->start.
616 */
617 pos->start = curr_map->map_ip(curr_map, pos->start);
618 pos->end = curr_map->map_ip(curr_map, pos->end);
619 } else if (curr_map != map) {
620 char dso_name[PATH_MAX];
621 struct dso *dso;
622
623 if (count == 0) {
624 curr_map = map;
625 goto filter_symbol;
626 }
627
628 if (self->kernel == DSO_TYPE_GUEST_KERNEL)
629 snprintf(dso_name, sizeof(dso_name),
630 "[guest.kernel].%d",
631 kernel_range++);
632 else
633 snprintf(dso_name, sizeof(dso_name),
634 "[kernel].%d",
635 kernel_range++);
636
637 dso = dso__new(dso_name);
638 if (dso == NULL)
639 return -1;
640
641 dso->kernel = self->kernel;
642
643 curr_map = map__new2(pos->start, dso, map->type);
644 if (curr_map == NULL) {
645 dso__delete(dso);
646 return -1;
647 }
648
649 curr_map->map_ip = curr_map->unmap_ip = identity__map_ip;
650 map_groups__insert(kmaps, curr_map);
651 ++kernel_range;
652 }
653filter_symbol:
654 if (filter && filter(curr_map, pos)) {
655discard_symbol: rb_erase(&pos->rb_node, root);
656 symbol__delete(pos);
657 } else {
658 if (curr_map != map) {
659 rb_erase(&pos->rb_node, root);
660 symbols__insert(&curr_map->dso->symbols[curr_map->type], pos);
661 ++moved;
662 } else
663 ++count;
664 }
210 } 665 }
211 666
212 free(line); 667 if (curr_map != map &&
213 fclose(file); 668 self->kernel == DSO_TYPE_GUEST_KERNEL &&
669 machine__is_default_guest(kmaps->machine)) {
670 dso__set_loaded(curr_map->dso, curr_map->type);
671 }
214 672
215 return 0; 673 return count + moved;
674}
216 675
217out_delete_line: 676int dso__load_kallsyms(struct dso *self, const char *filename,
218 free(line); 677 struct map *map, symbol_filter_t filter)
219out_failure: 678{
220 return -1; 679 if (dso__load_all_kallsyms(self, filename, map) < 0)
680 return -1;
681
682 if (self->kernel == DSO_TYPE_GUEST_KERNEL)
683 self->origin = DSO__ORIG_GUEST_KERNEL;
684 else
685 self->origin = DSO__ORIG_KERNEL;
686
687 return dso__split_kallsyms(self, map, filter);
221} 688}
222 689
223static int dso__load_perf_map(struct dso *self, symbol_filter_t filter, int verbose) 690static int dso__load_perf_map(struct dso *self, struct map *map,
691 symbol_filter_t filter)
224{ 692{
225 char *line = NULL; 693 char *line = NULL;
226 size_t n; 694 size_t n;
227 FILE *file; 695 FILE *file;
228 int nr_syms = 0; 696 int nr_syms = 0;
229 697
230 file = fopen(self->name, "r"); 698 file = fopen(self->long_name, "r");
231 if (file == NULL) 699 if (file == NULL)
232 goto out_failure; 700 goto out_failure;
233 701
@@ -257,16 +725,15 @@ static int dso__load_perf_map(struct dso *self, symbol_filter_t filter, int verb
257 if (len + 2 >= line_len) 725 if (len + 2 >= line_len)
258 continue; 726 continue;
259 727
260 sym = symbol__new(start, size, line + len, 728 sym = symbol__new(start, size, STB_GLOBAL, line + len);
261 self->sym_priv_size, start, verbose);
262 729
263 if (sym == NULL) 730 if (sym == NULL)
264 goto out_delete_line; 731 goto out_delete_line;
265 732
266 if (filter && filter(self, sym)) 733 if (filter && filter(map, sym))
267 symbol__delete(sym, self->sym_priv_size); 734 symbol__delete(sym);
268 else { 735 else {
269 dso__insert_symbol(self, sym); 736 symbols__insert(&self->symbols[map->type], sym);
270 nr_syms++; 737 nr_syms++;
271 } 738 }
272 } 739 }
@@ -286,13 +753,13 @@ out_failure:
286 * elf_symtab__for_each_symbol - iterate thru all the symbols 753 * elf_symtab__for_each_symbol - iterate thru all the symbols
287 * 754 *
288 * @self: struct elf_symtab instance to iterate 755 * @self: struct elf_symtab instance to iterate
289 * @index: uint32_t index 756 * @idx: uint32_t idx
290 * @sym: GElf_Sym iterator 757 * @sym: GElf_Sym iterator
291 */ 758 */
292#define elf_symtab__for_each_symbol(syms, nr_syms, index, sym) \ 759#define elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) \
293 for (index = 0, gelf_getsym(syms, index, &sym);\ 760 for (idx = 0, gelf_getsym(syms, idx, &sym);\
294 index < nr_syms; \ 761 idx < nr_syms; \
295 index++, gelf_getsym(syms, index, &sym)) 762 idx++, gelf_getsym(syms, idx, &sym))
296 763
297static inline uint8_t elf_sym__type(const GElf_Sym *sym) 764static inline uint8_t elf_sym__type(const GElf_Sym *sym)
298{ 765{
@@ -303,8 +770,40 @@ static inline int elf_sym__is_function(const GElf_Sym *sym)
303{ 770{
304 return elf_sym__type(sym) == STT_FUNC && 771 return elf_sym__type(sym) == STT_FUNC &&
305 sym->st_name != 0 && 772 sym->st_name != 0 &&
306 sym->st_shndx != SHN_UNDEF && 773 sym->st_shndx != SHN_UNDEF;
307 sym->st_size != 0; 774}
775
776static inline bool elf_sym__is_object(const GElf_Sym *sym)
777{
778 return elf_sym__type(sym) == STT_OBJECT &&
779 sym->st_name != 0 &&
780 sym->st_shndx != SHN_UNDEF;
781}
782
783static inline int elf_sym__is_label(const GElf_Sym *sym)
784{
785 return elf_sym__type(sym) == STT_NOTYPE &&
786 sym->st_name != 0 &&
787 sym->st_shndx != SHN_UNDEF &&
788 sym->st_shndx != SHN_ABS;
789}
790
791static inline const char *elf_sec__name(const GElf_Shdr *shdr,
792 const Elf_Data *secstrs)
793{
794 return secstrs->d_buf + shdr->sh_name;
795}
796
797static inline int elf_sec__is_text(const GElf_Shdr *shdr,
798 const Elf_Data *secstrs)
799{
800 return strstr(elf_sec__name(shdr, secstrs), "text") != NULL;
801}
802
803static inline bool elf_sec__is_data(const GElf_Shdr *shdr,
804 const Elf_Data *secstrs)
805{
806 return strstr(elf_sec__name(shdr, secstrs), "data") != NULL;
308} 807}
309 808
310static inline const char *elf_sym__name(const GElf_Sym *sym, 809static inline const char *elf_sym__name(const GElf_Sym *sym,
@@ -315,7 +814,7 @@ static inline const char *elf_sym__name(const GElf_Sym *sym,
315 814
316static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, 815static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep,
317 GElf_Shdr *shp, const char *name, 816 GElf_Shdr *shp, const char *name,
318 size_t *index) 817 size_t *idx)
319{ 818{
320 Elf_Scn *sec = NULL; 819 Elf_Scn *sec = NULL;
321 size_t cnt = 1; 820 size_t cnt = 1;
@@ -326,8 +825,8 @@ static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep,
326 gelf_getshdr(sec, shp); 825 gelf_getshdr(sec, shp);
327 str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name); 826 str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name);
328 if (!strcmp(name, str)) { 827 if (!strcmp(name, str)) {
329 if (index) 828 if (idx)
330 *index = cnt; 829 *idx = cnt;
331 break; 830 break;
332 } 831 }
333 ++cnt; 832 ++cnt;
@@ -346,56 +845,85 @@ static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep,
346 idx < nr_entries; \ 845 idx < nr_entries; \
347 ++idx, pos = gelf_getrela(reldata, idx, &pos_mem)) 846 ++idx, pos = gelf_getrela(reldata, idx, &pos_mem))
348 847
349static int dso__synthesize_plt_symbols(struct dso *self, Elf *elf, 848/*
350 GElf_Ehdr *ehdr, Elf_Scn *scn_dynsym, 849 * We need to check if we have a .dynsym, so that we can handle the
351 GElf_Shdr *shdr_dynsym, 850 * .plt, synthesizing its symbols, that aren't on the symtabs (be it
352 size_t dynsym_idx, int verbose) 851 * .dynsym or .symtab).
852 * And always look at the original dso, not at debuginfo packages, that
853 * have the PLT data stripped out (shdr_rel_plt.sh_type == SHT_NOBITS).
854 */
855static int dso__synthesize_plt_symbols(struct dso *self, struct map *map,
856 symbol_filter_t filter)
353{ 857{
354 uint32_t nr_rel_entries, idx; 858 uint32_t nr_rel_entries, idx;
355 GElf_Sym sym; 859 GElf_Sym sym;
356 u64 plt_offset; 860 u64 plt_offset;
357 GElf_Shdr shdr_plt; 861 GElf_Shdr shdr_plt;
358 struct symbol *f; 862 struct symbol *f;
359 GElf_Shdr shdr_rel_plt; 863 GElf_Shdr shdr_rel_plt, shdr_dynsym;
360 Elf_Data *reldata, *syms, *symstrs; 864 Elf_Data *reldata, *syms, *symstrs;
361 Elf_Scn *scn_plt_rel, *scn_symstrs; 865 Elf_Scn *scn_plt_rel, *scn_symstrs, *scn_dynsym;
866 size_t dynsym_idx;
867 GElf_Ehdr ehdr;
362 char sympltname[1024]; 868 char sympltname[1024];
363 int nr = 0, symidx; 869 Elf *elf;
870 int nr = 0, symidx, fd, err = 0;
871 char name[PATH_MAX];
872
873 snprintf(name, sizeof(name), "%s%s",
874 symbol_conf.symfs, self->long_name);
875 fd = open(name, O_RDONLY);
876 if (fd < 0)
877 goto out;
878
879 elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
880 if (elf == NULL)
881 goto out_close;
882
883 if (gelf_getehdr(elf, &ehdr) == NULL)
884 goto out_elf_end;
364 885
365 scn_plt_rel = elf_section_by_name(elf, ehdr, &shdr_rel_plt, 886 scn_dynsym = elf_section_by_name(elf, &ehdr, &shdr_dynsym,
887 ".dynsym", &dynsym_idx);
888 if (scn_dynsym == NULL)
889 goto out_elf_end;
890
891 scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt,
366 ".rela.plt", NULL); 892 ".rela.plt", NULL);
367 if (scn_plt_rel == NULL) { 893 if (scn_plt_rel == NULL) {
368 scn_plt_rel = elf_section_by_name(elf, ehdr, &shdr_rel_plt, 894 scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt,
369 ".rel.plt", NULL); 895 ".rel.plt", NULL);
370 if (scn_plt_rel == NULL) 896 if (scn_plt_rel == NULL)
371 return 0; 897 goto out_elf_end;
372 } 898 }
373 899
900 err = -1;
901
374 if (shdr_rel_plt.sh_link != dynsym_idx) 902 if (shdr_rel_plt.sh_link != dynsym_idx)
375 return 0; 903 goto out_elf_end;
376 904
377 if (elf_section_by_name(elf, ehdr, &shdr_plt, ".plt", NULL) == NULL) 905 if (elf_section_by_name(elf, &ehdr, &shdr_plt, ".plt", NULL) == NULL)
378 return 0; 906 goto out_elf_end;
379 907
380 /* 908 /*
381 * Fetch the relocation section to find the indexes to the GOT 909 * Fetch the relocation section to find the idxes to the GOT
382 * and the symbols in the .dynsym they refer to. 910 * and the symbols in the .dynsym they refer to.
383 */ 911 */
384 reldata = elf_getdata(scn_plt_rel, NULL); 912 reldata = elf_getdata(scn_plt_rel, NULL);
385 if (reldata == NULL) 913 if (reldata == NULL)
386 return -1; 914 goto out_elf_end;
387 915
388 syms = elf_getdata(scn_dynsym, NULL); 916 syms = elf_getdata(scn_dynsym, NULL);
389 if (syms == NULL) 917 if (syms == NULL)
390 return -1; 918 goto out_elf_end;
391 919
392 scn_symstrs = elf_getscn(elf, shdr_dynsym->sh_link); 920 scn_symstrs = elf_getscn(elf, shdr_dynsym.sh_link);
393 if (scn_symstrs == NULL) 921 if (scn_symstrs == NULL)
394 return -1; 922 goto out_elf_end;
395 923
396 symstrs = elf_getdata(scn_symstrs, NULL); 924 symstrs = elf_getdata(scn_symstrs, NULL);
397 if (symstrs == NULL) 925 if (symstrs == NULL)
398 return -1; 926 goto out_elf_end;
399 927
400 nr_rel_entries = shdr_rel_plt.sh_size / shdr_rel_plt.sh_entsize; 928 nr_rel_entries = shdr_rel_plt.sh_size / shdr_rel_plt.sh_entsize;
401 plt_offset = shdr_plt.sh_offset; 929 plt_offset = shdr_plt.sh_offset;
@@ -412,12 +940,16 @@ static int dso__synthesize_plt_symbols(struct dso *self, Elf *elf,
412 "%s@plt", elf_sym__name(&sym, symstrs)); 940 "%s@plt", elf_sym__name(&sym, symstrs));
413 941
414 f = symbol__new(plt_offset, shdr_plt.sh_entsize, 942 f = symbol__new(plt_offset, shdr_plt.sh_entsize,
415 sympltname, self->sym_priv_size, 0, verbose); 943 STB_GLOBAL, sympltname);
416 if (!f) 944 if (!f)
417 return -1; 945 goto out_elf_end;
418 946
419 dso__insert_symbol(self, f); 947 if (filter && filter(map, f))
420 ++nr; 948 symbol__delete(f);
949 else {
950 symbols__insert(&self->symbols[map->type], f);
951 ++nr;
952 }
421 } 953 }
422 } else if (shdr_rel_plt.sh_type == SHT_REL) { 954 } else if (shdr_rel_plt.sh_type == SHT_REL) {
423 GElf_Rel pos_mem, *pos; 955 GElf_Rel pos_mem, *pos;
@@ -430,81 +962,133 @@ static int dso__synthesize_plt_symbols(struct dso *self, Elf *elf,
430 "%s@plt", elf_sym__name(&sym, symstrs)); 962 "%s@plt", elf_sym__name(&sym, symstrs));
431 963
432 f = symbol__new(plt_offset, shdr_plt.sh_entsize, 964 f = symbol__new(plt_offset, shdr_plt.sh_entsize,
433 sympltname, self->sym_priv_size, 0, verbose); 965 STB_GLOBAL, sympltname);
434 if (!f) 966 if (!f)
435 return -1; 967 goto out_elf_end;
436 968
437 dso__insert_symbol(self, f); 969 if (filter && filter(map, f))
438 ++nr; 970 symbol__delete(f);
971 else {
972 symbols__insert(&self->symbols[map->type], f);
973 ++nr;
974 }
439 } 975 }
440 } else {
441 /*
442 * TODO: There are still one more shdr_rel_plt.sh_type
443 * I have to investigate, but probably should be ignored.
444 */
445 } 976 }
446 977
447 return nr; 978 err = 0;
979out_elf_end:
980 elf_end(elf);
981out_close:
982 close(fd);
983
984 if (err == 0)
985 return nr;
986out:
987 pr_debug("%s: problems reading %s PLT info.\n",
988 __func__, self->long_name);
989 return 0;
448} 990}
449 991
450static int dso__load_sym(struct dso *self, int fd, const char *name, 992static bool elf_sym__is_a(GElf_Sym *self, enum map_type type)
451 symbol_filter_t filter, int verbose)
452{ 993{
453 Elf_Data *symstrs; 994 switch (type) {
995 case MAP__FUNCTION:
996 return elf_sym__is_function(self);
997 case MAP__VARIABLE:
998 return elf_sym__is_object(self);
999 default:
1000 return false;
1001 }
1002}
1003
1004static bool elf_sec__is_a(GElf_Shdr *self, Elf_Data *secstrs, enum map_type type)
1005{
1006 switch (type) {
1007 case MAP__FUNCTION:
1008 return elf_sec__is_text(self, secstrs);
1009 case MAP__VARIABLE:
1010 return elf_sec__is_data(self, secstrs);
1011 default:
1012 return false;
1013 }
1014}
1015
1016static size_t elf_addr_to_index(Elf *elf, GElf_Addr addr)
1017{
1018 Elf_Scn *sec = NULL;
1019 GElf_Shdr shdr;
1020 size_t cnt = 1;
1021
1022 while ((sec = elf_nextscn(elf, sec)) != NULL) {
1023 gelf_getshdr(sec, &shdr);
1024
1025 if ((addr >= shdr.sh_addr) &&
1026 (addr < (shdr.sh_addr + shdr.sh_size)))
1027 return cnt;
1028
1029 ++cnt;
1030 }
1031
1032 return -1;
1033}
1034
1035static int dso__load_sym(struct dso *self, struct map *map, const char *name,
1036 int fd, symbol_filter_t filter, int kmodule,
1037 int want_symtab)
1038{
1039 struct kmap *kmap = self->kernel ? map__kmap(map) : NULL;
1040 struct map *curr_map = map;
1041 struct dso *curr_dso = self;
1042 Elf_Data *symstrs, *secstrs;
454 uint32_t nr_syms; 1043 uint32_t nr_syms;
455 int err = -1; 1044 int err = -1;
456 uint32_t index; 1045 uint32_t idx;
457 GElf_Ehdr ehdr; 1046 GElf_Ehdr ehdr;
458 GElf_Shdr shdr; 1047 GElf_Shdr shdr, opdshdr;
459 Elf_Data *syms; 1048 Elf_Data *syms, *opddata = NULL;
460 GElf_Sym sym; 1049 GElf_Sym sym;
461 Elf_Scn *sec, *sec_dynsym; 1050 Elf_Scn *sec, *sec_strndx, *opdsec;
462 Elf *elf; 1051 Elf *elf;
463 size_t dynsym_idx;
464 int nr = 0; 1052 int nr = 0;
1053 size_t opdidx = 0;
465 1054
466 elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); 1055 elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
467 if (elf == NULL) { 1056 if (elf == NULL) {
468 if (verbose) 1057 pr_debug("%s: cannot read %s ELF file.\n", __func__, name);
469 fprintf(stderr, "%s: cannot read %s ELF file.\n",
470 __func__, name);
471 goto out_close; 1058 goto out_close;
472 } 1059 }
473 1060
474 if (gelf_getehdr(elf, &ehdr) == NULL) { 1061 if (gelf_getehdr(elf, &ehdr) == NULL) {
475 if (verbose) 1062 pr_debug("%s: cannot get elf header.\n", __func__);
476 fprintf(stderr, "%s: cannot get elf header.\n", __func__);
477 goto out_elf_end; 1063 goto out_elf_end;
478 } 1064 }
479 1065
480 /* 1066 /* Always reject images with a mismatched build-id: */
481 * We need to check if we have a .dynsym, so that we can handle the 1067 if (self->has_build_id) {
482 * .plt, synthesizing its symbols, that aren't on the symtabs (be it 1068 u8 build_id[BUILD_ID_SIZE];
483 * .dynsym or .symtab) 1069
484 */ 1070 if (elf_read_build_id(elf, build_id,
485 sec_dynsym = elf_section_by_name(elf, &ehdr, &shdr, 1071 BUILD_ID_SIZE) != BUILD_ID_SIZE)
486 ".dynsym", &dynsym_idx); 1072 goto out_elf_end;
487 if (sec_dynsym != NULL) { 1073
488 nr = dso__synthesize_plt_symbols(self, elf, &ehdr, 1074 if (!dso__build_id_equal(self, build_id))
489 sec_dynsym, &shdr,
490 dynsym_idx, verbose);
491 if (nr < 0)
492 goto out_elf_end; 1075 goto out_elf_end;
493 } 1076 }
494 1077
495 /*
496 * But if we have a full .symtab (that is a superset of .dynsym) we
497 * should add the symbols not in the .dynsyn
498 */
499 sec = elf_section_by_name(elf, &ehdr, &shdr, ".symtab", NULL); 1078 sec = elf_section_by_name(elf, &ehdr, &shdr, ".symtab", NULL);
500 if (sec == NULL) { 1079 if (sec == NULL) {
501 if (sec_dynsym == NULL) 1080 if (want_symtab)
502 goto out_elf_end; 1081 goto out_elf_end;
503 1082
504 sec = sec_dynsym; 1083 sec = elf_section_by_name(elf, &ehdr, &shdr, ".dynsym", NULL);
505 gelf_getshdr(sec, &shdr); 1084 if (sec == NULL)
1085 goto out_elf_end;
506 } 1086 }
507 1087
1088 opdsec = elf_section_by_name(elf, &ehdr, &opdshdr, ".opd", &opdidx);
1089 if (opdsec)
1090 opddata = elf_rawdata(opdsec, NULL);
1091
508 syms = elf_getdata(sec, NULL); 1092 syms = elf_getdata(sec, NULL);
509 if (syms == NULL) 1093 if (syms == NULL)
510 goto out_elf_end; 1094 goto out_elf_end;
@@ -517,40 +1101,155 @@ static int dso__load_sym(struct dso *self, int fd, const char *name,
517 if (symstrs == NULL) 1101 if (symstrs == NULL)
518 goto out_elf_end; 1102 goto out_elf_end;
519 1103
1104 sec_strndx = elf_getscn(elf, ehdr.e_shstrndx);
1105 if (sec_strndx == NULL)
1106 goto out_elf_end;
1107
1108 secstrs = elf_getdata(sec_strndx, NULL);
1109 if (secstrs == NULL)
1110 goto out_elf_end;
1111
520 nr_syms = shdr.sh_size / shdr.sh_entsize; 1112 nr_syms = shdr.sh_size / shdr.sh_entsize;
521 1113
522 memset(&sym, 0, sizeof(sym)); 1114 memset(&sym, 0, sizeof(sym));
523 1115 if (self->kernel == DSO_TYPE_USER) {
524 elf_symtab__for_each_symbol(syms, nr_syms, index, sym) { 1116 self->adjust_symbols = (ehdr.e_type == ET_EXEC ||
1117 elf_section_by_name(elf, &ehdr, &shdr,
1118 ".gnu.prelink_undo",
1119 NULL) != NULL);
1120 } else self->adjust_symbols = 0;
1121
1122 elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) {
525 struct symbol *f; 1123 struct symbol *f;
526 u64 obj_start; 1124 const char *elf_name = elf_sym__name(&sym, symstrs);
1125 char *demangled = NULL;
1126 int is_label = elf_sym__is_label(&sym);
1127 const char *section_name;
527 1128
528 if (!elf_sym__is_function(&sym)) 1129 if (kmap && kmap->ref_reloc_sym && kmap->ref_reloc_sym->name &&
1130 strcmp(elf_name, kmap->ref_reloc_sym->name) == 0)
1131 kmap->ref_reloc_sym->unrelocated_addr = sym.st_value;
1132
1133 if (!is_label && !elf_sym__is_a(&sym, map->type))
529 continue; 1134 continue;
530 1135
1136 /* Reject ARM ELF "mapping symbols": these aren't unique and
1137 * don't identify functions, so will confuse the profile
1138 * output: */
1139 if (ehdr.e_machine == EM_ARM) {
1140 if (!strcmp(elf_name, "$a") ||
1141 !strcmp(elf_name, "$d") ||
1142 !strcmp(elf_name, "$t"))
1143 continue;
1144 }
1145
1146 if (opdsec && sym.st_shndx == opdidx) {
1147 u32 offset = sym.st_value - opdshdr.sh_addr;
1148 u64 *opd = opddata->d_buf + offset;
1149 sym.st_value = *opd;
1150 sym.st_shndx = elf_addr_to_index(elf, sym.st_value);
1151 }
1152
531 sec = elf_getscn(elf, sym.st_shndx); 1153 sec = elf_getscn(elf, sym.st_shndx);
532 if (!sec) 1154 if (!sec)
533 goto out_elf_end; 1155 goto out_elf_end;
534 1156
535 gelf_getshdr(sec, &shdr); 1157 gelf_getshdr(sec, &shdr);
536 obj_start = sym.st_value;
537 1158
538 sym.st_value -= shdr.sh_addr - shdr.sh_offset; 1159 if (is_label && !elf_sec__is_a(&shdr, secstrs, map->type))
1160 continue;
1161
1162 section_name = elf_sec__name(&shdr, secstrs);
1163
1164 if (self->kernel != DSO_TYPE_USER || kmodule) {
1165 char dso_name[PATH_MAX];
1166
1167 if (strcmp(section_name,
1168 (curr_dso->short_name +
1169 self->short_name_len)) == 0)
1170 goto new_symbol;
1171
1172 if (strcmp(section_name, ".text") == 0) {
1173 curr_map = map;
1174 curr_dso = self;
1175 goto new_symbol;
1176 }
1177
1178 snprintf(dso_name, sizeof(dso_name),
1179 "%s%s", self->short_name, section_name);
1180
1181 curr_map = map_groups__find_by_name(kmap->kmaps, map->type, dso_name);
1182 if (curr_map == NULL) {
1183 u64 start = sym.st_value;
1184
1185 if (kmodule)
1186 start += map->start + shdr.sh_offset;
1187
1188 curr_dso = dso__new(dso_name);
1189 if (curr_dso == NULL)
1190 goto out_elf_end;
1191 curr_dso->kernel = self->kernel;
1192 curr_map = map__new2(start, curr_dso,
1193 map->type);
1194 if (curr_map == NULL) {
1195 dso__delete(curr_dso);
1196 goto out_elf_end;
1197 }
1198 curr_map->map_ip = identity__map_ip;
1199 curr_map->unmap_ip = identity__map_ip;
1200 curr_dso->origin = self->origin;
1201 map_groups__insert(kmap->kmaps, curr_map);
1202 dsos__add(&self->node, curr_dso);
1203 dso__set_loaded(curr_dso, map->type);
1204 } else
1205 curr_dso = curr_map->dso;
1206
1207 goto new_symbol;
1208 }
539 1209
1210 if (curr_dso->adjust_symbols) {
1211 pr_debug4("%s: adjusting symbol: st_value: %#Lx "
1212 "sh_addr: %#Lx sh_offset: %#Lx\n", __func__,
1213 (u64)sym.st_value, (u64)shdr.sh_addr,
1214 (u64)shdr.sh_offset);
1215 sym.st_value -= shdr.sh_addr - shdr.sh_offset;
1216 }
1217 /*
1218 * We need to figure out if the object was created from C++ sources
1219 * DWARF DW_compile_unit has this, but we don't always have access
1220 * to it...
1221 */
1222 demangled = bfd_demangle(NULL, elf_name, DMGL_PARAMS | DMGL_ANSI);
1223 if (demangled != NULL)
1224 elf_name = demangled;
1225new_symbol:
540 f = symbol__new(sym.st_value, sym.st_size, 1226 f = symbol__new(sym.st_value, sym.st_size,
541 elf_sym__name(&sym, symstrs), 1227 GELF_ST_BIND(sym.st_info), elf_name);
542 self->sym_priv_size, obj_start, verbose); 1228 free(demangled);
543 if (!f) 1229 if (!f)
544 goto out_elf_end; 1230 goto out_elf_end;
545 1231
546 if (filter && filter(self, f)) 1232 if (filter && filter(curr_map, f))
547 symbol__delete(f, self->sym_priv_size); 1233 symbol__delete(f);
548 else { 1234 else {
549 dso__insert_symbol(self, f); 1235 symbols__insert(&curr_dso->symbols[curr_map->type], f);
550 nr++; 1236 nr++;
551 } 1237 }
552 } 1238 }
553 1239
1240 /*
1241 * For misannotated, zeroed, ASM function sizes.
1242 */
1243 if (nr > 0) {
1244 symbols__fixup_end(&self->symbols[map->type]);
1245 if (kmap) {
1246 /*
1247 * We need to fixup this here too because we create new
1248 * maps here, for things like vsyscall sections.
1249 */
1250 __map_groups__fixup_end(kmap->kmaps, map->type);
1251 }
1252 }
554 err = nr; 1253 err = nr;
555out_elf_end: 1254out_elf_end:
556 elf_end(elf); 1255 elf_end(elf);
@@ -558,84 +1257,1339 @@ out_close:
558 return err; 1257 return err;
559} 1258}
560 1259
561int dso__load(struct dso *self, symbol_filter_t filter, int verbose) 1260static bool dso__build_id_equal(const struct dso *self, u8 *build_id)
1261{
1262 return memcmp(self->build_id, build_id, sizeof(self->build_id)) == 0;
1263}
1264
1265bool __dsos__read_build_ids(struct list_head *head, bool with_hits)
1266{
1267 bool have_build_id = false;
1268 struct dso *pos;
1269
1270 list_for_each_entry(pos, head, node) {
1271 if (with_hits && !pos->hit)
1272 continue;
1273 if (pos->has_build_id) {
1274 have_build_id = true;
1275 continue;
1276 }
1277 if (filename__read_build_id(pos->long_name, pos->build_id,
1278 sizeof(pos->build_id)) > 0) {
1279 have_build_id = true;
1280 pos->has_build_id = true;
1281 }
1282 }
1283
1284 return have_build_id;
1285}
1286
1287/*
1288 * Align offset to 4 bytes as needed for note name and descriptor data.
1289 */
1290#define NOTE_ALIGN(n) (((n) + 3) & -4U)
1291
1292static int elf_read_build_id(Elf *elf, void *bf, size_t size)
1293{
1294 int err = -1;
1295 GElf_Ehdr ehdr;
1296 GElf_Shdr shdr;
1297 Elf_Data *data;
1298 Elf_Scn *sec;
1299 Elf_Kind ek;
1300 void *ptr;
1301
1302 if (size < BUILD_ID_SIZE)
1303 goto out;
1304
1305 ek = elf_kind(elf);
1306 if (ek != ELF_K_ELF)
1307 goto out;
1308
1309 if (gelf_getehdr(elf, &ehdr) == NULL) {
1310 pr_err("%s: cannot get elf header.\n", __func__);
1311 goto out;
1312 }
1313
1314 sec = elf_section_by_name(elf, &ehdr, &shdr,
1315 ".note.gnu.build-id", NULL);
1316 if (sec == NULL) {
1317 sec = elf_section_by_name(elf, &ehdr, &shdr,
1318 ".notes", NULL);
1319 if (sec == NULL)
1320 goto out;
1321 }
1322
1323 data = elf_getdata(sec, NULL);
1324 if (data == NULL)
1325 goto out;
1326
1327 ptr = data->d_buf;
1328 while (ptr < (data->d_buf + data->d_size)) {
1329 GElf_Nhdr *nhdr = ptr;
1330 int namesz = NOTE_ALIGN(nhdr->n_namesz),
1331 descsz = NOTE_ALIGN(nhdr->n_descsz);
1332 const char *name;
1333
1334 ptr += sizeof(*nhdr);
1335 name = ptr;
1336 ptr += namesz;
1337 if (nhdr->n_type == NT_GNU_BUILD_ID &&
1338 nhdr->n_namesz == sizeof("GNU")) {
1339 if (memcmp(name, "GNU", sizeof("GNU")) == 0) {
1340 memcpy(bf, ptr, BUILD_ID_SIZE);
1341 err = BUILD_ID_SIZE;
1342 break;
1343 }
1344 }
1345 ptr += descsz;
1346 }
1347
1348out:
1349 return err;
1350}
1351
1352int filename__read_build_id(const char *filename, void *bf, size_t size)
1353{
1354 int fd, err = -1;
1355 Elf *elf;
1356
1357 if (size < BUILD_ID_SIZE)
1358 goto out;
1359
1360 fd = open(filename, O_RDONLY);
1361 if (fd < 0)
1362 goto out;
1363
1364 elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
1365 if (elf == NULL) {
1366 pr_debug2("%s: cannot read %s ELF file.\n", __func__, filename);
1367 goto out_close;
1368 }
1369
1370 err = elf_read_build_id(elf, bf, size);
1371
1372 elf_end(elf);
1373out_close:
1374 close(fd);
1375out:
1376 return err;
1377}
1378
1379int sysfs__read_build_id(const char *filename, void *build_id, size_t size)
1380{
1381 int fd, err = -1;
1382
1383 if (size < BUILD_ID_SIZE)
1384 goto out;
1385
1386 fd = open(filename, O_RDONLY);
1387 if (fd < 0)
1388 goto out;
1389
1390 while (1) {
1391 char bf[BUFSIZ];
1392 GElf_Nhdr nhdr;
1393 int namesz, descsz;
1394
1395 if (read(fd, &nhdr, sizeof(nhdr)) != sizeof(nhdr))
1396 break;
1397
1398 namesz = NOTE_ALIGN(nhdr.n_namesz);
1399 descsz = NOTE_ALIGN(nhdr.n_descsz);
1400 if (nhdr.n_type == NT_GNU_BUILD_ID &&
1401 nhdr.n_namesz == sizeof("GNU")) {
1402 if (read(fd, bf, namesz) != namesz)
1403 break;
1404 if (memcmp(bf, "GNU", sizeof("GNU")) == 0) {
1405 if (read(fd, build_id,
1406 BUILD_ID_SIZE) == BUILD_ID_SIZE) {
1407 err = 0;
1408 break;
1409 }
1410 } else if (read(fd, bf, descsz) != descsz)
1411 break;
1412 } else {
1413 int n = namesz + descsz;
1414 if (read(fd, bf, n) != n)
1415 break;
1416 }
1417 }
1418 close(fd);
1419out:
1420 return err;
1421}
1422
1423char dso__symtab_origin(const struct dso *self)
562{ 1424{
563 int size = strlen(self->name) + sizeof("/usr/lib/debug%s.debug"); 1425 static const char origin[] = {
564 char *name = malloc(size); 1426 [DSO__ORIG_KERNEL] = 'k',
565 int variant = 0; 1427 [DSO__ORIG_JAVA_JIT] = 'j',
1428 [DSO__ORIG_BUILD_ID_CACHE] = 'B',
1429 [DSO__ORIG_FEDORA] = 'f',
1430 [DSO__ORIG_UBUNTU] = 'u',
1431 [DSO__ORIG_BUILDID] = 'b',
1432 [DSO__ORIG_DSO] = 'd',
1433 [DSO__ORIG_KMODULE] = 'K',
1434 [DSO__ORIG_GUEST_KERNEL] = 'g',
1435 [DSO__ORIG_GUEST_KMODULE] = 'G',
1436 };
1437
1438 if (self == NULL || self->origin == DSO__ORIG_NOT_FOUND)
1439 return '!';
1440 return origin[self->origin];
1441}
1442
1443int dso__load(struct dso *self, struct map *map, symbol_filter_t filter)
1444{
1445 int size = PATH_MAX;
1446 char *name;
566 int ret = -1; 1447 int ret = -1;
567 int fd; 1448 int fd;
1449 struct machine *machine;
1450 const char *root_dir;
1451 int want_symtab;
1452
1453 dso__set_loaded(self, map->type);
568 1454
1455 if (self->kernel == DSO_TYPE_KERNEL)
1456 return dso__load_kernel_sym(self, map, filter);
1457 else if (self->kernel == DSO_TYPE_GUEST_KERNEL)
1458 return dso__load_guest_kernel_sym(self, map, filter);
1459
1460 if (map->groups && map->groups->machine)
1461 machine = map->groups->machine;
1462 else
1463 machine = NULL;
1464
1465 name = malloc(size);
569 if (!name) 1466 if (!name)
570 return -1; 1467 return -1;
571 1468
572 if (strncmp(self->name, "/tmp/perf-", 10) == 0) 1469 self->adjust_symbols = 0;
573 return dso__load_perf_map(self, filter, verbose);
574 1470
575more: 1471 if (strncmp(self->name, "/tmp/perf-", 10) == 0) {
576 do { 1472 ret = dso__load_perf_map(self, map, filter);
577 switch (variant) { 1473 self->origin = ret > 0 ? DSO__ORIG_JAVA_JIT :
578 case 0: /* Fedora */ 1474 DSO__ORIG_NOT_FOUND;
579 snprintf(name, size, "/usr/lib/debug%s.debug", self->name); 1475 return ret;
1476 }
1477
1478 /* Iterate over candidate debug images.
1479 * On the first pass, only load images if they have a full symtab.
1480 * Failing that, do a second pass where we accept .dynsym also
1481 */
1482 for (self->origin = DSO__ORIG_BUILD_ID_CACHE, want_symtab = 1;
1483 self->origin != DSO__ORIG_NOT_FOUND;
1484 self->origin++) {
1485 switch (self->origin) {
1486 case DSO__ORIG_BUILD_ID_CACHE:
1487 /* skip the locally configured cache if a symfs is given */
1488 if (symbol_conf.symfs[0] ||
1489 (dso__build_id_filename(self, name, size) == NULL)) {
1490 continue;
1491 }
1492 break;
1493 case DSO__ORIG_FEDORA:
1494 snprintf(name, size, "%s/usr/lib/debug%s.debug",
1495 symbol_conf.symfs, self->long_name);
580 break; 1496 break;
581 case 1: /* Ubuntu */ 1497 case DSO__ORIG_UBUNTU:
582 snprintf(name, size, "/usr/lib/debug%s", self->name); 1498 snprintf(name, size, "%s/usr/lib/debug%s",
1499 symbol_conf.symfs, self->long_name);
583 break; 1500 break;
584 case 2: /* Sane people */ 1501 case DSO__ORIG_BUILDID: {
585 snprintf(name, size, "%s", self->name); 1502 char build_id_hex[BUILD_ID_SIZE * 2 + 1];
1503
1504 if (!self->has_build_id)
1505 continue;
1506
1507 build_id__sprintf(self->build_id,
1508 sizeof(self->build_id),
1509 build_id_hex);
1510 snprintf(name, size,
1511 "%s/usr/lib/debug/.build-id/%.2s/%s.debug",
1512 symbol_conf.symfs, build_id_hex, build_id_hex + 2);
1513 }
1514 break;
1515 case DSO__ORIG_DSO:
1516 snprintf(name, size, "%s%s",
1517 symbol_conf.symfs, self->long_name);
1518 break;
1519 case DSO__ORIG_GUEST_KMODULE:
1520 if (map->groups && map->groups->machine)
1521 root_dir = map->groups->machine->root_dir;
1522 else
1523 root_dir = "";
1524 snprintf(name, size, "%s%s%s", symbol_conf.symfs,
1525 root_dir, self->long_name);
1526 break;
1527
1528 case DSO__ORIG_KMODULE:
1529 snprintf(name, size, "%s%s", symbol_conf.symfs,
1530 self->long_name);
586 break; 1531 break;
587 1532
588 default: 1533 default:
589 goto out; 1534 /*
1535 * If we wanted a full symtab but no image had one,
1536 * relax our requirements and repeat the search.
1537 */
1538 if (want_symtab) {
1539 want_symtab = 0;
1540 self->origin = DSO__ORIG_BUILD_ID_CACHE;
1541 } else
1542 continue;
590 } 1543 }
591 variant++;
592 1544
1545 /* Name is now the name of the next image to try */
593 fd = open(name, O_RDONLY); 1546 fd = open(name, O_RDONLY);
594 } while (fd < 0); 1547 if (fd < 0)
1548 continue;
595 1549
596 ret = dso__load_sym(self, fd, name, filter, verbose); 1550 ret = dso__load_sym(self, map, name, fd, filter, 0,
597 close(fd); 1551 want_symtab);
1552 close(fd);
1553
1554 /*
1555 * Some people seem to have debuginfo files _WITHOUT_ debug
1556 * info!?!?
1557 */
1558 if (!ret)
1559 continue;
1560
1561 if (ret > 0) {
1562 int nr_plt = dso__synthesize_plt_symbols(self, map, filter);
1563 if (nr_plt > 0)
1564 ret += nr_plt;
1565 break;
1566 }
1567 }
598 1568
1569 free(name);
1570 if (ret < 0 && strstr(self->name, " (deleted)") != NULL)
1571 return 0;
1572 return ret;
1573}
1574
1575struct map *map_groups__find_by_name(struct map_groups *self,
1576 enum map_type type, const char *name)
1577{
1578 struct rb_node *nd;
1579
1580 for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) {
1581 struct map *map = rb_entry(nd, struct map, rb_node);
1582
1583 if (map->dso && strcmp(map->dso->short_name, name) == 0)
1584 return map;
1585 }
1586
1587 return NULL;
1588}
1589
1590static int dso__kernel_module_get_build_id(struct dso *self,
1591 const char *root_dir)
1592{
1593 char filename[PATH_MAX];
599 /* 1594 /*
600 * Some people seem to have debuginfo files _WITHOUT_ debug info!?!? 1595 * kernel module short names are of the form "[module]" and
1596 * we need just "module" here.
601 */ 1597 */
602 if (!ret) 1598 const char *name = self->short_name + 1;
603 goto more; 1599
1600 snprintf(filename, sizeof(filename),
1601 "%s/sys/module/%.*s/notes/.note.gnu.build-id",
1602 root_dir, (int)strlen(name) - 1, name);
1603
1604 if (sysfs__read_build_id(filename, self->build_id,
1605 sizeof(self->build_id)) == 0)
1606 self->has_build_id = true;
1607
1608 return 0;
1609}
1610
1611static int map_groups__set_modules_path_dir(struct map_groups *self,
1612 const char *dir_name)
1613{
1614 struct dirent *dent;
1615 DIR *dir = opendir(dir_name);
1616 int ret = 0;
1617
1618 if (!dir) {
1619 pr_debug("%s: cannot open %s dir\n", __func__, dir_name);
1620 return -1;
1621 }
1622
1623 while ((dent = readdir(dir)) != NULL) {
1624 char path[PATH_MAX];
1625 struct stat st;
1626
1627 /*sshfs might return bad dent->d_type, so we have to stat*/
1628 sprintf(path, "%s/%s", dir_name, dent->d_name);
1629 if (stat(path, &st))
1630 continue;
1631
1632 if (S_ISDIR(st.st_mode)) {
1633 if (!strcmp(dent->d_name, ".") ||
1634 !strcmp(dent->d_name, ".."))
1635 continue;
1636
1637 snprintf(path, sizeof(path), "%s/%s",
1638 dir_name, dent->d_name);
1639 ret = map_groups__set_modules_path_dir(self, path);
1640 if (ret < 0)
1641 goto out;
1642 } else {
1643 char *dot = strrchr(dent->d_name, '.'),
1644 dso_name[PATH_MAX];
1645 struct map *map;
1646 char *long_name;
1647
1648 if (dot == NULL || strcmp(dot, ".ko"))
1649 continue;
1650 snprintf(dso_name, sizeof(dso_name), "[%.*s]",
1651 (int)(dot - dent->d_name), dent->d_name);
1652
1653 strxfrchar(dso_name, '-', '_');
1654 map = map_groups__find_by_name(self, MAP__FUNCTION, dso_name);
1655 if (map == NULL)
1656 continue;
1657
1658 snprintf(path, sizeof(path), "%s/%s",
1659 dir_name, dent->d_name);
1660
1661 long_name = strdup(path);
1662 if (long_name == NULL) {
1663 ret = -1;
1664 goto out;
1665 }
1666 dso__set_long_name(map->dso, long_name);
1667 map->dso->lname_alloc = 1;
1668 dso__kernel_module_get_build_id(map->dso, "");
1669 }
1670 }
604 1671
605out: 1672out:
606 free(name); 1673 closedir(dir);
607 return ret; 1674 return ret;
608} 1675}
609 1676
610static int dso__load_vmlinux(struct dso *self, const char *vmlinux, 1677static char *get_kernel_version(const char *root_dir)
611 symbol_filter_t filter, int verbose)
612{ 1678{
613 int err, fd = open(vmlinux, O_RDONLY); 1679 char version[PATH_MAX];
1680 FILE *file;
1681 char *name, *tmp;
1682 const char *prefix = "Linux version ";
1683
1684 sprintf(version, "%s/proc/version", root_dir);
1685 file = fopen(version, "r");
1686 if (!file)
1687 return NULL;
1688
1689 version[0] = '\0';
1690 tmp = fgets(version, sizeof(version), file);
1691 fclose(file);
1692
1693 name = strstr(version, prefix);
1694 if (!name)
1695 return NULL;
1696 name += strlen(prefix);
1697 tmp = strchr(name, ' ');
1698 if (tmp)
1699 *tmp = '\0';
1700
1701 return strdup(name);
1702}
1703
1704static int machine__set_modules_path(struct machine *self)
1705{
1706 char *version;
1707 char modules_path[PATH_MAX];
1708
1709 version = get_kernel_version(self->root_dir);
1710 if (!version)
1711 return -1;
1712
1713 snprintf(modules_path, sizeof(modules_path), "%s/lib/modules/%s/kernel",
1714 self->root_dir, version);
1715 free(version);
1716
1717 return map_groups__set_modules_path_dir(&self->kmaps, modules_path);
1718}
1719
1720/*
1721 * Constructor variant for modules (where we know from /proc/modules where
1722 * they are loaded) and for vmlinux, where only after we load all the
1723 * symbols we'll know where it starts and ends.
1724 */
1725static struct map *map__new2(u64 start, struct dso *dso, enum map_type type)
1726{
1727 struct map *self = calloc(1, (sizeof(*self) +
1728 (dso->kernel ? sizeof(struct kmap) : 0)));
1729 if (self != NULL) {
1730 /*
1731 * ->end will be filled after we load all the symbols
1732 */
1733 map__init(self, type, start, 0, 0, dso);
1734 }
1735
1736 return self;
1737}
1738
1739struct map *machine__new_module(struct machine *self, u64 start,
1740 const char *filename)
1741{
1742 struct map *map;
1743 struct dso *dso = __dsos__findnew(&self->kernel_dsos, filename);
1744
1745 if (dso == NULL)
1746 return NULL;
1747
1748 map = map__new2(start, dso, MAP__FUNCTION);
1749 if (map == NULL)
1750 return NULL;
1751
1752 if (machine__is_host(self))
1753 dso->origin = DSO__ORIG_KMODULE;
1754 else
1755 dso->origin = DSO__ORIG_GUEST_KMODULE;
1756 map_groups__insert(&self->kmaps, map);
1757 return map;
1758}
1759
1760static int machine__create_modules(struct machine *self)
1761{
1762 char *line = NULL;
1763 size_t n;
1764 FILE *file;
1765 struct map *map;
1766 const char *modules;
1767 char path[PATH_MAX];
1768
1769 if (machine__is_default_guest(self))
1770 modules = symbol_conf.default_guest_modules;
1771 else {
1772 sprintf(path, "%s/proc/modules", self->root_dir);
1773 modules = path;
1774 }
1775
1776 file = fopen(modules, "r");
1777 if (file == NULL)
1778 return -1;
1779
1780 while (!feof(file)) {
1781 char name[PATH_MAX];
1782 u64 start;
1783 char *sep;
1784 int line_len;
1785
1786 line_len = getline(&line, &n, file);
1787 if (line_len < 0)
1788 break;
1789
1790 if (!line)
1791 goto out_failure;
1792
1793 line[--line_len] = '\0'; /* \n */
1794
1795 sep = strrchr(line, 'x');
1796 if (sep == NULL)
1797 continue;
1798
1799 hex2u64(sep + 1, &start);
1800
1801 sep = strchr(line, ' ');
1802 if (sep == NULL)
1803 continue;
1804
1805 *sep = '\0';
1806
1807 snprintf(name, sizeof(name), "[%s]", line);
1808 map = machine__new_module(self, start, name);
1809 if (map == NULL)
1810 goto out_delete_line;
1811 dso__kernel_module_get_build_id(map->dso, self->root_dir);
1812 }
1813
1814 free(line);
1815 fclose(file);
1816
1817 return machine__set_modules_path(self);
614 1818
1819out_delete_line:
1820 free(line);
1821out_failure:
1822 return -1;
1823}
1824
1825int dso__load_vmlinux(struct dso *self, struct map *map,
1826 const char *vmlinux, symbol_filter_t filter)
1827{
1828 int err = -1, fd;
1829 char symfs_vmlinux[PATH_MAX];
1830
1831 snprintf(symfs_vmlinux, sizeof(symfs_vmlinux), "%s/%s",
1832 symbol_conf.symfs, vmlinux);
1833 fd = open(symfs_vmlinux, O_RDONLY);
615 if (fd < 0) 1834 if (fd < 0)
616 return -1; 1835 return -1;
617 1836
618 err = dso__load_sym(self, fd, vmlinux, filter, verbose); 1837 dso__set_loaded(self, map->type);
1838 err = dso__load_sym(self, map, symfs_vmlinux, fd, filter, 0, 0);
619 close(fd); 1839 close(fd);
620 1840
1841 if (err > 0)
1842 pr_debug("Using %s for symbols\n", symfs_vmlinux);
1843
621 return err; 1844 return err;
622} 1845}
623 1846
624int dso__load_kernel(struct dso *self, const char *vmlinux, 1847int dso__load_vmlinux_path(struct dso *self, struct map *map,
625 symbol_filter_t filter, int verbose) 1848 symbol_filter_t filter)
626{ 1849{
627 int err = -1; 1850 int i, err = 0;
1851 char *filename;
1852
1853 pr_debug("Looking at the vmlinux_path (%d entries long)\n",
1854 vmlinux_path__nr_entries + 1);
1855
1856 filename = dso__build_id_filename(self, NULL, 0);
1857 if (filename != NULL) {
1858 err = dso__load_vmlinux(self, map, filename, filter);
1859 if (err > 0) {
1860 dso__set_long_name(self, filename);
1861 goto out;
1862 }
1863 free(filename);
1864 }
1865
1866 for (i = 0; i < vmlinux_path__nr_entries; ++i) {
1867 err = dso__load_vmlinux(self, map, vmlinux_path[i], filter);
1868 if (err > 0) {
1869 dso__set_long_name(self, strdup(vmlinux_path[i]));
1870 break;
1871 }
1872 }
1873out:
1874 return err;
1875}
1876
1877static int dso__load_kernel_sym(struct dso *self, struct map *map,
1878 symbol_filter_t filter)
1879{
1880 int err;
1881 const char *kallsyms_filename = NULL;
1882 char *kallsyms_allocated_filename = NULL;
1883 /*
1884 * Step 1: if the user specified a kallsyms or vmlinux filename, use
1885 * it and only it, reporting errors to the user if it cannot be used.
1886 *
1887 * For instance, try to analyse an ARM perf.data file _without_ a
1888 * build-id, or if the user specifies the wrong path to the right
1889 * vmlinux file, obviously we can't fallback to another vmlinux (a
1890 * x86_86 one, on the machine where analysis is being performed, say),
1891 * or worse, /proc/kallsyms.
1892 *
1893 * If the specified file _has_ a build-id and there is a build-id
1894 * section in the perf.data file, we will still do the expected
1895 * validation in dso__load_vmlinux and will bail out if they don't
1896 * match.
1897 */
1898 if (symbol_conf.kallsyms_name != NULL) {
1899 kallsyms_filename = symbol_conf.kallsyms_name;
1900 goto do_kallsyms;
1901 }
1902
1903 if (symbol_conf.vmlinux_name != NULL) {
1904 err = dso__load_vmlinux(self, map,
1905 symbol_conf.vmlinux_name, filter);
1906 if (err > 0) {
1907 dso__set_long_name(self,
1908 strdup(symbol_conf.vmlinux_name));
1909 goto out_fixup;
1910 }
1911 return err;
1912 }
1913
1914 if (vmlinux_path != NULL) {
1915 err = dso__load_vmlinux_path(self, map, filter);
1916 if (err > 0)
1917 goto out_fixup;
1918 }
1919
1920 /* do not try local files if a symfs was given */
1921 if (symbol_conf.symfs[0] != 0)
1922 return -1;
1923
1924 /*
1925 * Say the kernel DSO was created when processing the build-id header table,
1926 * we have a build-id, so check if it is the same as the running kernel,
1927 * using it if it is.
1928 */
1929 if (self->has_build_id) {
1930 u8 kallsyms_build_id[BUILD_ID_SIZE];
1931 char sbuild_id[BUILD_ID_SIZE * 2 + 1];
1932
1933 if (sysfs__read_build_id("/sys/kernel/notes", kallsyms_build_id,
1934 sizeof(kallsyms_build_id)) == 0) {
1935 if (dso__build_id_equal(self, kallsyms_build_id)) {
1936 kallsyms_filename = "/proc/kallsyms";
1937 goto do_kallsyms;
1938 }
1939 }
1940 /*
1941 * Now look if we have it on the build-id cache in
1942 * $HOME/.debug/[kernel.kallsyms].
1943 */
1944 build_id__sprintf(self->build_id, sizeof(self->build_id),
1945 sbuild_id);
1946
1947 if (asprintf(&kallsyms_allocated_filename,
1948 "%s/.debug/[kernel.kallsyms]/%s",
1949 getenv("HOME"), sbuild_id) == -1) {
1950 pr_err("Not enough memory for kallsyms file lookup\n");
1951 return -1;
1952 }
1953
1954 kallsyms_filename = kallsyms_allocated_filename;
1955
1956 if (access(kallsyms_filename, F_OK)) {
1957 pr_err("No kallsyms or vmlinux with build-id %s "
1958 "was found\n", sbuild_id);
1959 free(kallsyms_allocated_filename);
1960 return -1;
1961 }
1962 } else {
1963 /*
1964 * Last resort, if we don't have a build-id and couldn't find
1965 * any vmlinux file, try the running kernel kallsyms table.
1966 */
1967 kallsyms_filename = "/proc/kallsyms";
1968 }
1969
1970do_kallsyms:
1971 err = dso__load_kallsyms(self, kallsyms_filename, map, filter);
1972 if (err > 0)
1973 pr_debug("Using %s for symbols\n", kallsyms_filename);
1974 free(kallsyms_allocated_filename);
1975
1976 if (err > 0) {
1977out_fixup:
1978 if (kallsyms_filename != NULL)
1979 dso__set_long_name(self, strdup("[kernel.kallsyms]"));
1980 map__fixup_start(map);
1981 map__fixup_end(map);
1982 }
628 1983
629 if (vmlinux) 1984 return err;
630 err = dso__load_vmlinux(self, vmlinux, filter, verbose); 1985}
1986
1987static int dso__load_guest_kernel_sym(struct dso *self, struct map *map,
1988 symbol_filter_t filter)
1989{
1990 int err;
1991 const char *kallsyms_filename = NULL;
1992 struct machine *machine;
1993 char path[PATH_MAX];
1994
1995 if (!map->groups) {
1996 pr_debug("Guest kernel map hasn't the point to groups\n");
1997 return -1;
1998 }
1999 machine = map->groups->machine;
631 2000
632 if (err) 2001 if (machine__is_default_guest(machine)) {
633 err = dso__load_kallsyms(self, filter, verbose); 2002 /*
2003 * if the user specified a vmlinux filename, use it and only
2004 * it, reporting errors to the user if it cannot be used.
2005 * Or use file guest_kallsyms inputted by user on commandline
2006 */
2007 if (symbol_conf.default_guest_vmlinux_name != NULL) {
2008 err = dso__load_vmlinux(self, map,
2009 symbol_conf.default_guest_vmlinux_name, filter);
2010 goto out_try_fixup;
2011 }
2012
2013 kallsyms_filename = symbol_conf.default_guest_kallsyms;
2014 if (!kallsyms_filename)
2015 return -1;
2016 } else {
2017 sprintf(path, "%s/proc/kallsyms", machine->root_dir);
2018 kallsyms_filename = path;
2019 }
2020
2021 err = dso__load_kallsyms(self, kallsyms_filename, map, filter);
2022 if (err > 0)
2023 pr_debug("Using %s for symbols\n", kallsyms_filename);
2024
2025out_try_fixup:
2026 if (err > 0) {
2027 if (kallsyms_filename != NULL) {
2028 machine__mmap_name(machine, path, sizeof(path));
2029 dso__set_long_name(self, strdup(path));
2030 }
2031 map__fixup_start(map);
2032 map__fixup_end(map);
2033 }
634 2034
635 return err; 2035 return err;
636} 2036}
637 2037
638void symbol__init(void) 2038static void dsos__add(struct list_head *head, struct dso *dso)
2039{
2040 list_add_tail(&dso->node, head);
2041}
2042
2043static struct dso *dsos__find(struct list_head *head, const char *name)
2044{
2045 struct dso *pos;
2046
2047 list_for_each_entry(pos, head, node)
2048 if (strcmp(pos->long_name, name) == 0)
2049 return pos;
2050 return NULL;
2051}
2052
2053struct dso *__dsos__findnew(struct list_head *head, const char *name)
639{ 2054{
2055 struct dso *dso = dsos__find(head, name);
2056
2057 if (!dso) {
2058 dso = dso__new(name);
2059 if (dso != NULL) {
2060 dsos__add(head, dso);
2061 dso__set_basename(dso);
2062 }
2063 }
2064
2065 return dso;
2066}
2067
2068size_t __dsos__fprintf(struct list_head *head, FILE *fp)
2069{
2070 struct dso *pos;
2071 size_t ret = 0;
2072
2073 list_for_each_entry(pos, head, node) {
2074 int i;
2075 for (i = 0; i < MAP__NR_TYPES; ++i)
2076 ret += dso__fprintf(pos, i, fp);
2077 }
2078
2079 return ret;
2080}
2081
2082size_t machines__fprintf_dsos(struct rb_root *self, FILE *fp)
2083{
2084 struct rb_node *nd;
2085 size_t ret = 0;
2086
2087 for (nd = rb_first(self); nd; nd = rb_next(nd)) {
2088 struct machine *pos = rb_entry(nd, struct machine, rb_node);
2089 ret += __dsos__fprintf(&pos->kernel_dsos, fp);
2090 ret += __dsos__fprintf(&pos->user_dsos, fp);
2091 }
2092
2093 return ret;
2094}
2095
2096static size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp,
2097 bool with_hits)
2098{
2099 struct dso *pos;
2100 size_t ret = 0;
2101
2102 list_for_each_entry(pos, head, node) {
2103 if (with_hits && !pos->hit)
2104 continue;
2105 ret += dso__fprintf_buildid(pos, fp);
2106 ret += fprintf(fp, " %s\n", pos->long_name);
2107 }
2108 return ret;
2109}
2110
2111size_t machine__fprintf_dsos_buildid(struct machine *self, FILE *fp, bool with_hits)
2112{
2113 return __dsos__fprintf_buildid(&self->kernel_dsos, fp, with_hits) +
2114 __dsos__fprintf_buildid(&self->user_dsos, fp, with_hits);
2115}
2116
2117size_t machines__fprintf_dsos_buildid(struct rb_root *self, FILE *fp, bool with_hits)
2118{
2119 struct rb_node *nd;
2120 size_t ret = 0;
2121
2122 for (nd = rb_first(self); nd; nd = rb_next(nd)) {
2123 struct machine *pos = rb_entry(nd, struct machine, rb_node);
2124 ret += machine__fprintf_dsos_buildid(pos, fp, with_hits);
2125 }
2126 return ret;
2127}
2128
2129struct dso *dso__new_kernel(const char *name)
2130{
2131 struct dso *self = dso__new(name ?: "[kernel.kallsyms]");
2132
2133 if (self != NULL) {
2134 dso__set_short_name(self, "[kernel]");
2135 self->kernel = DSO_TYPE_KERNEL;
2136 }
2137
2138 return self;
2139}
2140
2141static struct dso *dso__new_guest_kernel(struct machine *machine,
2142 const char *name)
2143{
2144 char bf[PATH_MAX];
2145 struct dso *self = dso__new(name ?: machine__mmap_name(machine, bf, sizeof(bf)));
2146
2147 if (self != NULL) {
2148 dso__set_short_name(self, "[guest.kernel]");
2149 self->kernel = DSO_TYPE_GUEST_KERNEL;
2150 }
2151
2152 return self;
2153}
2154
2155void dso__read_running_kernel_build_id(struct dso *self, struct machine *machine)
2156{
2157 char path[PATH_MAX];
2158
2159 if (machine__is_default_guest(machine))
2160 return;
2161 sprintf(path, "%s/sys/kernel/notes", machine->root_dir);
2162 if (sysfs__read_build_id(path, self->build_id,
2163 sizeof(self->build_id)) == 0)
2164 self->has_build_id = true;
2165}
2166
2167static struct dso *machine__create_kernel(struct machine *self)
2168{
2169 const char *vmlinux_name = NULL;
2170 struct dso *kernel;
2171
2172 if (machine__is_host(self)) {
2173 vmlinux_name = symbol_conf.vmlinux_name;
2174 kernel = dso__new_kernel(vmlinux_name);
2175 } else {
2176 if (machine__is_default_guest(self))
2177 vmlinux_name = symbol_conf.default_guest_vmlinux_name;
2178 kernel = dso__new_guest_kernel(self, vmlinux_name);
2179 }
2180
2181 if (kernel != NULL) {
2182 dso__read_running_kernel_build_id(kernel, self);
2183 dsos__add(&self->kernel_dsos, kernel);
2184 }
2185 return kernel;
2186}
2187
2188struct process_args {
2189 u64 start;
2190};
2191
2192static int symbol__in_kernel(void *arg, const char *name,
2193 char type __used, u64 start, u64 end __used)
2194{
2195 struct process_args *args = arg;
2196
2197 if (strchr(name, '['))
2198 return 0;
2199
2200 args->start = start;
2201 return 1;
2202}
2203
2204/* Figure out the start address of kernel map from /proc/kallsyms */
2205static u64 machine__get_kernel_start_addr(struct machine *machine)
2206{
2207 const char *filename;
2208 char path[PATH_MAX];
2209 struct process_args args;
2210
2211 if (machine__is_host(machine)) {
2212 filename = "/proc/kallsyms";
2213 } else {
2214 if (machine__is_default_guest(machine))
2215 filename = (char *)symbol_conf.default_guest_kallsyms;
2216 else {
2217 sprintf(path, "%s/proc/kallsyms", machine->root_dir);
2218 filename = path;
2219 }
2220 }
2221
2222 if (kallsyms__parse(filename, &args, symbol__in_kernel) <= 0)
2223 return 0;
2224
2225 return args.start;
2226}
2227
2228int __machine__create_kernel_maps(struct machine *self, struct dso *kernel)
2229{
2230 enum map_type type;
2231 u64 start = machine__get_kernel_start_addr(self);
2232
2233 for (type = 0; type < MAP__NR_TYPES; ++type) {
2234 struct kmap *kmap;
2235
2236 self->vmlinux_maps[type] = map__new2(start, kernel, type);
2237 if (self->vmlinux_maps[type] == NULL)
2238 return -1;
2239
2240 self->vmlinux_maps[type]->map_ip =
2241 self->vmlinux_maps[type]->unmap_ip = identity__map_ip;
2242
2243 kmap = map__kmap(self->vmlinux_maps[type]);
2244 kmap->kmaps = &self->kmaps;
2245 map_groups__insert(&self->kmaps, self->vmlinux_maps[type]);
2246 }
2247
2248 return 0;
2249}
2250
2251void machine__destroy_kernel_maps(struct machine *self)
2252{
2253 enum map_type type;
2254
2255 for (type = 0; type < MAP__NR_TYPES; ++type) {
2256 struct kmap *kmap;
2257
2258 if (self->vmlinux_maps[type] == NULL)
2259 continue;
2260
2261 kmap = map__kmap(self->vmlinux_maps[type]);
2262 map_groups__remove(&self->kmaps, self->vmlinux_maps[type]);
2263 if (kmap->ref_reloc_sym) {
2264 /*
2265 * ref_reloc_sym is shared among all maps, so free just
2266 * on one of them.
2267 */
2268 if (type == MAP__FUNCTION) {
2269 free((char *)kmap->ref_reloc_sym->name);
2270 kmap->ref_reloc_sym->name = NULL;
2271 free(kmap->ref_reloc_sym);
2272 }
2273 kmap->ref_reloc_sym = NULL;
2274 }
2275
2276 map__delete(self->vmlinux_maps[type]);
2277 self->vmlinux_maps[type] = NULL;
2278 }
2279}
2280
2281int machine__create_kernel_maps(struct machine *self)
2282{
2283 struct dso *kernel = machine__create_kernel(self);
2284
2285 if (kernel == NULL ||
2286 __machine__create_kernel_maps(self, kernel) < 0)
2287 return -1;
2288
2289 if (symbol_conf.use_modules && machine__create_modules(self) < 0)
2290 pr_debug("Problems creating module maps, continuing anyway...\n");
2291 /*
2292 * Now that we have all the maps created, just set the ->end of them:
2293 */
2294 map_groups__fixup_end(&self->kmaps);
2295 return 0;
2296}
2297
2298static void vmlinux_path__exit(void)
2299{
2300 while (--vmlinux_path__nr_entries >= 0) {
2301 free(vmlinux_path[vmlinux_path__nr_entries]);
2302 vmlinux_path[vmlinux_path__nr_entries] = NULL;
2303 }
2304
2305 free(vmlinux_path);
2306 vmlinux_path = NULL;
2307}
2308
2309static int vmlinux_path__init(void)
2310{
2311 struct utsname uts;
2312 char bf[PATH_MAX];
2313
2314 vmlinux_path = malloc(sizeof(char *) * 5);
2315 if (vmlinux_path == NULL)
2316 return -1;
2317
2318 vmlinux_path[vmlinux_path__nr_entries] = strdup("vmlinux");
2319 if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
2320 goto out_fail;
2321 ++vmlinux_path__nr_entries;
2322 vmlinux_path[vmlinux_path__nr_entries] = strdup("/boot/vmlinux");
2323 if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
2324 goto out_fail;
2325 ++vmlinux_path__nr_entries;
2326
2327 /* only try running kernel version if no symfs was given */
2328 if (symbol_conf.symfs[0] != 0)
2329 return 0;
2330
2331 if (uname(&uts) < 0)
2332 return -1;
2333
2334 snprintf(bf, sizeof(bf), "/boot/vmlinux-%s", uts.release);
2335 vmlinux_path[vmlinux_path__nr_entries] = strdup(bf);
2336 if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
2337 goto out_fail;
2338 ++vmlinux_path__nr_entries;
2339 snprintf(bf, sizeof(bf), "/lib/modules/%s/build/vmlinux", uts.release);
2340 vmlinux_path[vmlinux_path__nr_entries] = strdup(bf);
2341 if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
2342 goto out_fail;
2343 ++vmlinux_path__nr_entries;
2344 snprintf(bf, sizeof(bf), "/usr/lib/debug/lib/modules/%s/vmlinux",
2345 uts.release);
2346 vmlinux_path[vmlinux_path__nr_entries] = strdup(bf);
2347 if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
2348 goto out_fail;
2349 ++vmlinux_path__nr_entries;
2350
2351 return 0;
2352
2353out_fail:
2354 vmlinux_path__exit();
2355 return -1;
2356}
2357
2358size_t machine__fprintf_vmlinux_path(struct machine *self, FILE *fp)
2359{
2360 int i;
2361 size_t printed = 0;
2362 struct dso *kdso = self->vmlinux_maps[MAP__FUNCTION]->dso;
2363
2364 if (kdso->has_build_id) {
2365 char filename[PATH_MAX];
2366 if (dso__build_id_filename(kdso, filename, sizeof(filename)))
2367 printed += fprintf(fp, "[0] %s\n", filename);
2368 }
2369
2370 for (i = 0; i < vmlinux_path__nr_entries; ++i)
2371 printed += fprintf(fp, "[%d] %s\n",
2372 i + kdso->has_build_id, vmlinux_path[i]);
2373
2374 return printed;
2375}
2376
2377static int setup_list(struct strlist **list, const char *list_str,
2378 const char *list_name)
2379{
2380 if (list_str == NULL)
2381 return 0;
2382
2383 *list = strlist__new(true, list_str);
2384 if (!*list) {
2385 pr_err("problems parsing %s list\n", list_name);
2386 return -1;
2387 }
2388 return 0;
2389}
2390
2391int symbol__init(void)
2392{
2393 const char *symfs;
2394
2395 if (symbol_conf.initialized)
2396 return 0;
2397
640 elf_version(EV_CURRENT); 2398 elf_version(EV_CURRENT);
2399 if (symbol_conf.sort_by_name)
2400 symbol_conf.priv_size += (sizeof(struct symbol_name_rb_node) -
2401 sizeof(struct symbol));
2402
2403 if (symbol_conf.try_vmlinux_path && vmlinux_path__init() < 0)
2404 return -1;
2405
2406 if (symbol_conf.field_sep && *symbol_conf.field_sep == '.') {
2407 pr_err("'.' is the only non valid --field-separator argument\n");
2408 return -1;
2409 }
2410
2411 if (setup_list(&symbol_conf.dso_list,
2412 symbol_conf.dso_list_str, "dso") < 0)
2413 return -1;
2414
2415 if (setup_list(&symbol_conf.comm_list,
2416 symbol_conf.comm_list_str, "comm") < 0)
2417 goto out_free_dso_list;
2418
2419 if (setup_list(&symbol_conf.sym_list,
2420 symbol_conf.sym_list_str, "symbol") < 0)
2421 goto out_free_comm_list;
2422
2423 /*
2424 * A path to symbols of "/" is identical to ""
2425 * reset here for simplicity.
2426 */
2427 symfs = realpath(symbol_conf.symfs, NULL);
2428 if (symfs == NULL)
2429 symfs = symbol_conf.symfs;
2430 if (strcmp(symfs, "/") == 0)
2431 symbol_conf.symfs = "";
2432 if (symfs != symbol_conf.symfs)
2433 free((void *)symfs);
2434
2435 symbol_conf.initialized = true;
2436 return 0;
2437
2438out_free_dso_list:
2439 strlist__delete(symbol_conf.dso_list);
2440out_free_comm_list:
2441 strlist__delete(symbol_conf.comm_list);
2442 return -1;
2443}
2444
2445void symbol__exit(void)
2446{
2447 if (!symbol_conf.initialized)
2448 return;
2449 strlist__delete(symbol_conf.sym_list);
2450 strlist__delete(symbol_conf.dso_list);
2451 strlist__delete(symbol_conf.comm_list);
2452 vmlinux_path__exit();
2453 symbol_conf.sym_list = symbol_conf.dso_list = symbol_conf.comm_list = NULL;
2454 symbol_conf.initialized = false;
2455}
2456
2457int machines__create_kernel_maps(struct rb_root *self, pid_t pid)
2458{
2459 struct machine *machine = machines__findnew(self, pid);
2460
2461 if (machine == NULL)
2462 return -1;
2463
2464 return machine__create_kernel_maps(machine);
2465}
2466
2467static int hex(char ch)
2468{
2469 if ((ch >= '0') && (ch <= '9'))
2470 return ch - '0';
2471 if ((ch >= 'a') && (ch <= 'f'))
2472 return ch - 'a' + 10;
2473 if ((ch >= 'A') && (ch <= 'F'))
2474 return ch - 'A' + 10;
2475 return -1;
2476}
2477
2478/*
2479 * While we find nice hex chars, build a long_val.
2480 * Return number of chars processed.
2481 */
2482int hex2u64(const char *ptr, u64 *long_val)
2483{
2484 const char *p = ptr;
2485 *long_val = 0;
2486
2487 while (*p) {
2488 const int hex_val = hex(*p);
2489
2490 if (hex_val < 0)
2491 break;
2492
2493 *long_val = (*long_val << 4) | hex_val;
2494 p++;
2495 }
2496
2497 return p - ptr;
2498}
2499
2500char *strxfrchar(char *s, char from, char to)
2501{
2502 char *p = s;
2503
2504 while ((p = strchr(p, from)) != NULL)
2505 *p++ = to;
2506
2507 return s;
2508}
2509
2510int machines__create_guest_kernel_maps(struct rb_root *self)
2511{
2512 int ret = 0;
2513 struct dirent **namelist = NULL;
2514 int i, items = 0;
2515 char path[PATH_MAX];
2516 pid_t pid;
2517
2518 if (symbol_conf.default_guest_vmlinux_name ||
2519 symbol_conf.default_guest_modules ||
2520 symbol_conf.default_guest_kallsyms) {
2521 machines__create_kernel_maps(self, DEFAULT_GUEST_KERNEL_ID);
2522 }
2523
2524 if (symbol_conf.guestmount) {
2525 items = scandir(symbol_conf.guestmount, &namelist, NULL, NULL);
2526 if (items <= 0)
2527 return -ENOENT;
2528 for (i = 0; i < items; i++) {
2529 if (!isdigit(namelist[i]->d_name[0])) {
2530 /* Filter out . and .. */
2531 continue;
2532 }
2533 pid = atoi(namelist[i]->d_name);
2534 sprintf(path, "%s/%s/proc/kallsyms",
2535 symbol_conf.guestmount,
2536 namelist[i]->d_name);
2537 ret = access(path, R_OK);
2538 if (ret) {
2539 pr_debug("Can't access file %s\n", path);
2540 goto failure;
2541 }
2542 machines__create_kernel_maps(self, pid);
2543 }
2544failure:
2545 free(namelist);
2546 }
2547
2548 return ret;
2549}
2550
2551void machines__destroy_guest_kernel_maps(struct rb_root *self)
2552{
2553 struct rb_node *next = rb_first(self);
2554
2555 while (next) {
2556 struct machine *pos = rb_entry(next, struct machine, rb_node);
2557
2558 next = rb_next(&pos->rb_node);
2559 rb_erase(&pos->rb_node, self);
2560 machine__delete(pos);
2561 }
2562}
2563
2564int machine__load_kallsyms(struct machine *self, const char *filename,
2565 enum map_type type, symbol_filter_t filter)
2566{
2567 struct map *map = self->vmlinux_maps[type];
2568 int ret = dso__load_kallsyms(map->dso, filename, map, filter);
2569
2570 if (ret > 0) {
2571 dso__set_loaded(map->dso, type);
2572 /*
2573 * Since /proc/kallsyms will have multiple sessions for the
2574 * kernel, with modules between them, fixup the end of all
2575 * sections.
2576 */
2577 __map_groups__fixup_end(&self->kmaps, type);
2578 }
2579
2580 return ret;
2581}
2582
2583int machine__load_vmlinux_path(struct machine *self, enum map_type type,
2584 symbol_filter_t filter)
2585{
2586 struct map *map = self->vmlinux_maps[type];
2587 int ret = dso__load_vmlinux_path(map->dso, map, filter);
2588
2589 if (ret > 0) {
2590 dso__set_loaded(map->dso, type);
2591 map__reloc_vmlinux(map);
2592 }
2593
2594 return ret;
641} 2595}
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index ea332e56e45..670cd1c88f5 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -1,49 +1,236 @@
1#ifndef _PERF_SYMBOL_ 1#ifndef __PERF_SYMBOL
2#define _PERF_SYMBOL_ 1 2#define __PERF_SYMBOL 1
3 3
4#include <linux/types.h> 4#include <linux/types.h>
5#include "../types.h" 5#include <stdbool.h>
6#include "list.h" 6#include <stdint.h>
7#include "rbtree.h" 7#include "map.h"
8#include <linux/list.h>
9#include <linux/rbtree.h>
10#include <stdio.h>
11
12#ifdef HAVE_CPLUS_DEMANGLE
13extern char *cplus_demangle(const char *, int);
14
15static inline char *bfd_demangle(void __used *v, const char *c, int i)
16{
17 return cplus_demangle(c, i);
18}
19#else
20#ifdef NO_DEMANGLE
21static inline char *bfd_demangle(void __used *v, const char __used *c,
22 int __used i)
23{
24 return NULL;
25}
26#else
27#include <bfd.h>
28#endif
29#endif
30
31int hex2u64(const char *ptr, u64 *val);
32char *strxfrchar(char *s, char from, char to);
33
34/*
35 * libelf 0.8.x and earlier do not support ELF_C_READ_MMAP;
36 * for newer versions we can use mmap to reduce memory usage:
37 */
38#ifdef LIBELF_NO_MMAP
39# define PERF_ELF_C_READ_MMAP ELF_C_READ
40#else
41# define PERF_ELF_C_READ_MMAP ELF_C_READ_MMAP
42#endif
43
44#ifndef DMGL_PARAMS
45#define DMGL_PARAMS (1 << 0) /* Include function args */
46#define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */
47#endif
48
49#define BUILD_ID_SIZE 20
8 50
9struct symbol { 51struct symbol {
10 struct rb_node rb_node; 52 struct rb_node rb_node;
11 u64 start; 53 u64 start;
12 u64 end; 54 u64 end;
13 u64 obj_start; 55 u16 namelen;
14 u64 hist_sum; 56 u8 binding;
15 u64 *hist;
16 void *priv;
17 char name[0]; 57 char name[0];
18}; 58};
19 59
60void symbol__delete(struct symbol *self);
61
62struct strlist;
63
64struct symbol_conf {
65 unsigned short priv_size;
66 bool try_vmlinux_path,
67 use_modules,
68 sort_by_name,
69 show_nr_samples,
70 use_callchain,
71 exclude_other,
72 show_cpu_utilization,
73 initialized;
74 const char *vmlinux_name,
75 *kallsyms_name,
76 *source_prefix,
77 *field_sep;
78 const char *default_guest_vmlinux_name,
79 *default_guest_kallsyms,
80 *default_guest_modules;
81 const char *guestmount;
82 const char *dso_list_str,
83 *comm_list_str,
84 *sym_list_str,
85 *col_width_list_str;
86 struct strlist *dso_list,
87 *comm_list,
88 *sym_list;
89 const char *symfs;
90};
91
92extern struct symbol_conf symbol_conf;
93
94static inline void *symbol__priv(struct symbol *self)
95{
96 return ((void *)self) - symbol_conf.priv_size;
97}
98
99struct ref_reloc_sym {
100 const char *name;
101 u64 addr;
102 u64 unrelocated_addr;
103};
104
105struct map_symbol {
106 struct map *map;
107 struct symbol *sym;
108 bool unfolded;
109 bool has_children;
110};
111
112struct addr_location {
113 struct thread *thread;
114 struct map *map;
115 struct symbol *sym;
116 u64 addr;
117 char level;
118 bool filtered;
119 u8 cpumode;
120 s32 cpu;
121};
122
123enum dso_kernel_type {
124 DSO_TYPE_USER = 0,
125 DSO_TYPE_KERNEL,
126 DSO_TYPE_GUEST_KERNEL
127};
128
20struct dso { 129struct dso {
21 struct list_head node; 130 struct list_head node;
22 struct rb_root syms; 131 struct rb_root symbols[MAP__NR_TYPES];
23 unsigned int sym_priv_size; 132 struct rb_root symbol_names[MAP__NR_TYPES];
24 struct symbol *(*find_symbol)(struct dso *, u64 ip); 133 enum dso_kernel_type kernel;
134 u8 adjust_symbols:1;
135 u8 slen_calculated:1;
136 u8 has_build_id:1;
137 u8 hit:1;
138 u8 annotate_warned:1;
139 u8 sname_alloc:1;
140 u8 lname_alloc:1;
141 unsigned char origin;
142 u8 sorted_by_name;
143 u8 loaded;
144 u8 build_id[BUILD_ID_SIZE];
145 const char *short_name;
146 char *long_name;
147 u16 long_name_len;
148 u16 short_name_len;
25 char name[0]; 149 char name[0];
26}; 150};
27 151
28const char *sym_hist_filter; 152struct dso *dso__new(const char *name);
153struct dso *dso__new_kernel(const char *name);
154void dso__delete(struct dso *self);
29 155
30typedef int (*symbol_filter_t)(struct dso *self, struct symbol *sym); 156int dso__name_len(const struct dso *self);
31 157
32struct dso *dso__new(const char *name, unsigned int sym_priv_size); 158bool dso__loaded(const struct dso *self, enum map_type type);
33void dso__delete(struct dso *self); 159bool dso__sorted_by_name(const struct dso *self, enum map_type type);
34 160
35static inline void *dso__sym_priv(struct dso *self, struct symbol *sym) 161static inline void dso__set_loaded(struct dso *self, enum map_type type)
36{ 162{
37 return ((void *)sym) - self->sym_priv_size; 163 self->loaded |= (1 << type);
38} 164}
39 165
40struct symbol *dso__find_symbol(struct dso *self, u64 ip); 166void dso__sort_by_name(struct dso *self, enum map_type type);
167
168struct dso *__dsos__findnew(struct list_head *head, const char *name);
169
170int dso__load(struct dso *self, struct map *map, symbol_filter_t filter);
171int dso__load_vmlinux(struct dso *self, struct map *map,
172 const char *vmlinux, symbol_filter_t filter);
173int dso__load_vmlinux_path(struct dso *self, struct map *map,
174 symbol_filter_t filter);
175int dso__load_kallsyms(struct dso *self, const char *filename, struct map *map,
176 symbol_filter_t filter);
177int machine__load_kallsyms(struct machine *self, const char *filename,
178 enum map_type type, symbol_filter_t filter);
179int machine__load_vmlinux_path(struct machine *self, enum map_type type,
180 symbol_filter_t filter);
181
182size_t __dsos__fprintf(struct list_head *head, FILE *fp);
183
184size_t machine__fprintf_dsos_buildid(struct machine *self, FILE *fp, bool with_hits);
185size_t machines__fprintf_dsos(struct rb_root *self, FILE *fp);
186size_t machines__fprintf_dsos_buildid(struct rb_root *self, FILE *fp, bool with_hits);
187
188size_t dso__fprintf_buildid(struct dso *self, FILE *fp);
189size_t dso__fprintf_symbols_by_name(struct dso *self, enum map_type type, FILE *fp);
190size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp);
191
192enum dso_origin {
193 DSO__ORIG_KERNEL = 0,
194 DSO__ORIG_GUEST_KERNEL,
195 DSO__ORIG_JAVA_JIT,
196 DSO__ORIG_BUILD_ID_CACHE,
197 DSO__ORIG_FEDORA,
198 DSO__ORIG_UBUNTU,
199 DSO__ORIG_BUILDID,
200 DSO__ORIG_DSO,
201 DSO__ORIG_GUEST_KMODULE,
202 DSO__ORIG_KMODULE,
203 DSO__ORIG_NOT_FOUND,
204};
205
206char dso__symtab_origin(const struct dso *self);
207void dso__set_long_name(struct dso *self, char *name);
208void dso__set_build_id(struct dso *self, void *build_id);
209void dso__read_running_kernel_build_id(struct dso *self, struct machine *machine);
210struct symbol *dso__find_symbol(struct dso *self, enum map_type type, u64 addr);
211struct symbol *dso__find_symbol_by_name(struct dso *self, enum map_type type,
212 const char *name);
213
214int filename__read_build_id(const char *filename, void *bf, size_t size);
215int sysfs__read_build_id(const char *filename, void *bf, size_t size);
216bool __dsos__read_build_ids(struct list_head *head, bool with_hits);
217int build_id__sprintf(const u8 *self, int len, char *bf);
218int kallsyms__parse(const char *filename, void *arg,
219 int (*process_symbol)(void *arg, const char *name,
220 char type, u64 start, u64 end));
221
222void machine__destroy_kernel_maps(struct machine *self);
223int __machine__create_kernel_maps(struct machine *self, struct dso *kernel);
224int machine__create_kernel_maps(struct machine *self);
225
226int machines__create_kernel_maps(struct rb_root *self, pid_t pid);
227int machines__create_guest_kernel_maps(struct rb_root *self);
228void machines__destroy_guest_kernel_maps(struct rb_root *self);
41 229
42int dso__load_kernel(struct dso *self, const char *vmlinux, 230int symbol__init(void);
43 symbol_filter_t filter, int verbose); 231void symbol__exit(void);
44int dso__load(struct dso *self, symbol_filter_t filter, int verbose); 232bool symbol_type__is_a(char symbol_type, enum map_type map_type);
45 233
46size_t dso__fprintf(struct dso *self, FILE *fp); 234size_t machine__fprintf_vmlinux_path(struct machine *self, FILE *fp);
47 235
48void symbol__init(void); 236#endif /* __PERF_SYMBOL */
49#endif /* _PERF_SYMBOL_ */
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
new file mode 100644
index 00000000000..00f4eade2e3
--- /dev/null
+++ b/tools/perf/util/thread.c
@@ -0,0 +1,195 @@
1#include "../perf.h"
2#include <stdlib.h>
3#include <stdio.h>
4#include <string.h>
5#include "session.h"
6#include "thread.h"
7#include "util.h"
8#include "debug.h"
9
10/* Skip "." and ".." directories */
11static int filter(const struct dirent *dir)
12{
13 if (dir->d_name[0] == '.')
14 return 0;
15 else
16 return 1;
17}
18
19struct thread_map *thread_map__new_by_pid(pid_t pid)
20{
21 struct thread_map *threads;
22 char name[256];
23 int items;
24 struct dirent **namelist = NULL;
25 int i;
26
27 sprintf(name, "/proc/%d/task", pid);
28 items = scandir(name, &namelist, filter, NULL);
29 if (items <= 0)
30 return NULL;
31
32 threads = malloc(sizeof(*threads) + sizeof(pid_t) * items);
33 if (threads != NULL) {
34 for (i = 0; i < items; i++)
35 threads->map[i] = atoi(namelist[i]->d_name);
36 threads->nr = items;
37 }
38
39 for (i=0; i<items; i++)
40 free(namelist[i]);
41 free(namelist);
42
43 return threads;
44}
45
46struct thread_map *thread_map__new_by_tid(pid_t tid)
47{
48 struct thread_map *threads = malloc(sizeof(*threads) + sizeof(pid_t));
49
50 if (threads != NULL) {
51 threads->map[0] = tid;
52 threads->nr = 1;
53 }
54
55 return threads;
56}
57
58struct thread_map *thread_map__new(pid_t pid, pid_t tid)
59{
60 if (pid != -1)
61 return thread_map__new_by_pid(pid);
62 return thread_map__new_by_tid(tid);
63}
64
65static struct thread *thread__new(pid_t pid)
66{
67 struct thread *self = zalloc(sizeof(*self));
68
69 if (self != NULL) {
70 map_groups__init(&self->mg);
71 self->pid = pid;
72 self->comm = malloc(32);
73 if (self->comm)
74 snprintf(self->comm, 32, ":%d", self->pid);
75 }
76
77 return self;
78}
79
80void thread__delete(struct thread *self)
81{
82 map_groups__exit(&self->mg);
83 free(self->comm);
84 free(self);
85}
86
87int thread__set_comm(struct thread *self, const char *comm)
88{
89 int err;
90
91 if (self->comm)
92 free(self->comm);
93 self->comm = strdup(comm);
94 err = self->comm == NULL ? -ENOMEM : 0;
95 if (!err) {
96 self->comm_set = true;
97 map_groups__flush(&self->mg);
98 }
99 return err;
100}
101
102int thread__comm_len(struct thread *self)
103{
104 if (!self->comm_len) {
105 if (!self->comm)
106 return 0;
107 self->comm_len = strlen(self->comm);
108 }
109
110 return self->comm_len;
111}
112
113static size_t thread__fprintf(struct thread *self, FILE *fp)
114{
115 return fprintf(fp, "Thread %d %s\n", self->pid, self->comm) +
116 map_groups__fprintf(&self->mg, verbose, fp);
117}
118
119struct thread *perf_session__findnew(struct perf_session *self, pid_t pid)
120{
121 struct rb_node **p = &self->threads.rb_node;
122 struct rb_node *parent = NULL;
123 struct thread *th;
124
125 /*
126 * Font-end cache - PID lookups come in blocks,
127 * so most of the time we dont have to look up
128 * the full rbtree:
129 */
130 if (self->last_match && self->last_match->pid == pid)
131 return self->last_match;
132
133 while (*p != NULL) {
134 parent = *p;
135 th = rb_entry(parent, struct thread, rb_node);
136
137 if (th->pid == pid) {
138 self->last_match = th;
139 return th;
140 }
141
142 if (pid < th->pid)
143 p = &(*p)->rb_left;
144 else
145 p = &(*p)->rb_right;
146 }
147
148 th = thread__new(pid);
149 if (th != NULL) {
150 rb_link_node(&th->rb_node, parent, p);
151 rb_insert_color(&th->rb_node, &self->threads);
152 self->last_match = th;
153 }
154
155 return th;
156}
157
158void thread__insert_map(struct thread *self, struct map *map)
159{
160 map_groups__fixup_overlappings(&self->mg, map, verbose, stderr);
161 map_groups__insert(&self->mg, map);
162}
163
164int thread__fork(struct thread *self, struct thread *parent)
165{
166 int i;
167
168 if (parent->comm_set) {
169 if (self->comm)
170 free(self->comm);
171 self->comm = strdup(parent->comm);
172 if (!self->comm)
173 return -ENOMEM;
174 self->comm_set = true;
175 }
176
177 for (i = 0; i < MAP__NR_TYPES; ++i)
178 if (map_groups__clone(&self->mg, &parent->mg, i) < 0)
179 return -ENOMEM;
180 return 0;
181}
182
183size_t perf_session__fprintf(struct perf_session *self, FILE *fp)
184{
185 size_t ret = 0;
186 struct rb_node *nd;
187
188 for (nd = rb_first(&self->threads); nd; nd = rb_next(nd)) {
189 struct thread *pos = rb_entry(nd, struct thread, rb_node);
190
191 ret += thread__fprintf(pos, fp);
192 }
193
194 return ret;
195}
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h
new file mode 100644
index 00000000000..d7574101054
--- /dev/null
+++ b/tools/perf/util/thread.h
@@ -0,0 +1,62 @@
1#ifndef __PERF_THREAD_H
2#define __PERF_THREAD_H
3
4#include <linux/rbtree.h>
5#include <unistd.h>
6#include "symbol.h"
7
8struct thread {
9 union {
10 struct rb_node rb_node;
11 struct list_head node;
12 };
13 struct map_groups mg;
14 pid_t pid;
15 char shortname[3];
16 bool comm_set;
17 char *comm;
18 int comm_len;
19};
20
21struct thread_map {
22 int nr;
23 int map[];
24};
25
26struct perf_session;
27
28void thread__delete(struct thread *self);
29
30struct thread_map *thread_map__new_by_pid(pid_t pid);
31struct thread_map *thread_map__new_by_tid(pid_t tid);
32struct thread_map *thread_map__new(pid_t pid, pid_t tid);
33
34static inline void thread_map__delete(struct thread_map *threads)
35{
36 free(threads);
37}
38
39int thread__set_comm(struct thread *self, const char *comm);
40int thread__comm_len(struct thread *self);
41struct thread *perf_session__findnew(struct perf_session *self, pid_t pid);
42void thread__insert_map(struct thread *self, struct map *map);
43int thread__fork(struct thread *self, struct thread *parent);
44size_t perf_session__fprintf(struct perf_session *self, FILE *fp);
45
46static inline struct map *thread__find_map(struct thread *self,
47 enum map_type type, u64 addr)
48{
49 return self ? map_groups__find(&self->mg, type, addr) : NULL;
50}
51
52void thread__find_addr_map(struct thread *self,
53 struct perf_session *session, u8 cpumode,
54 enum map_type type, pid_t pid, u64 addr,
55 struct addr_location *al);
56
57void thread__find_addr_location(struct thread *self,
58 struct perf_session *session, u8 cpumode,
59 enum map_type type, pid_t pid, u64 addr,
60 struct addr_location *al,
61 symbol_filter_t filter);
62#endif /* __PERF_THREAD_H */
diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c
new file mode 100644
index 00000000000..35729f4c40c
--- /dev/null
+++ b/tools/perf/util/trace-event-info.c
@@ -0,0 +1,565 @@
1/*
2 * Copyright (C) 2008,2009, Steven Rostedt <srostedt@redhat.com>
3 *
4 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License (not later!)
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
20 */
21#define _GNU_SOURCE
22#include <dirent.h>
23#include <mntent.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27#include <stdarg.h>
28#include <sys/types.h>
29#include <sys/stat.h>
30#include <sys/wait.h>
31#include <pthread.h>
32#include <fcntl.h>
33#include <unistd.h>
34#include <ctype.h>
35#include <errno.h>
36#include <stdbool.h>
37#include <linux/list.h>
38#include <linux/kernel.h>
39
40#include "../perf.h"
41#include "trace-event.h"
42#include "debugfs.h"
43#include "evsel.h"
44
45#define VERSION "0.5"
46
47#define _STR(x) #x
48#define STR(x) _STR(x)
49#define MAX_PATH 256
50
51#define TRACE_CTRL "tracing_on"
52#define TRACE "trace"
53#define AVAILABLE "available_tracers"
54#define CURRENT "current_tracer"
55#define ITER_CTRL "trace_options"
56#define MAX_LATENCY "tracing_max_latency"
57
58unsigned int page_size;
59
60static const char *output_file = "trace.info";
61static int output_fd;
62
63struct event_list {
64 struct event_list *next;
65 const char *event;
66};
67
68struct events {
69 struct events *sibling;
70 struct events *children;
71 struct events *next;
72 char *name;
73};
74
75
76
77static void die(const char *fmt, ...)
78{
79 va_list ap;
80 int ret = errno;
81
82 if (errno)
83 perror("trace-cmd");
84 else
85 ret = -1;
86
87 va_start(ap, fmt);
88 fprintf(stderr, " ");
89 vfprintf(stderr, fmt, ap);
90 va_end(ap);
91
92 fprintf(stderr, "\n");
93 exit(ret);
94}
95
96void *malloc_or_die(unsigned int size)
97{
98 void *data;
99
100 data = malloc(size);
101 if (!data)
102 die("malloc");
103 return data;
104}
105
106static const char *find_debugfs(void)
107{
108 const char *path = debugfs_mount(NULL);
109
110 if (!path)
111 die("Your kernel not support debugfs filesystem");
112
113 return path;
114}
115
116/*
117 * Finds the path to the debugfs/tracing
118 * Allocates the string and stores it.
119 */
120static const char *find_tracing_dir(void)
121{
122 static char *tracing;
123 static int tracing_found;
124 const char *debugfs;
125
126 if (tracing_found)
127 return tracing;
128
129 debugfs = find_debugfs();
130
131 tracing = malloc_or_die(strlen(debugfs) + 9);
132
133 sprintf(tracing, "%s/tracing", debugfs);
134
135 tracing_found = 1;
136 return tracing;
137}
138
139static char *get_tracing_file(const char *name)
140{
141 const char *tracing;
142 char *file;
143
144 tracing = find_tracing_dir();
145 if (!tracing)
146 return NULL;
147
148 file = malloc_or_die(strlen(tracing) + strlen(name) + 2);
149
150 sprintf(file, "%s/%s", tracing, name);
151 return file;
152}
153
154static void put_tracing_file(char *file)
155{
156 free(file);
157}
158
159static ssize_t calc_data_size;
160
161static ssize_t write_or_die(const void *buf, size_t len)
162{
163 int ret;
164
165 if (calc_data_size) {
166 calc_data_size += len;
167 return len;
168 }
169
170 ret = write(output_fd, buf, len);
171 if (ret < 0)
172 die("writing to '%s'", output_file);
173
174 return ret;
175}
176
177int bigendian(void)
178{
179 unsigned char str[] = { 0x1, 0x2, 0x3, 0x4, 0x0, 0x0, 0x0, 0x0};
180 unsigned int *ptr;
181
182 ptr = (unsigned int *)(void *)str;
183 return *ptr == 0x01020304;
184}
185
186static unsigned long long copy_file_fd(int fd)
187{
188 unsigned long long size = 0;
189 char buf[BUFSIZ];
190 int r;
191
192 do {
193 r = read(fd, buf, BUFSIZ);
194 if (r > 0) {
195 size += r;
196 write_or_die(buf, r);
197 }
198 } while (r > 0);
199
200 return size;
201}
202
203static unsigned long long copy_file(const char *file)
204{
205 unsigned long long size = 0;
206 int fd;
207
208 fd = open(file, O_RDONLY);
209 if (fd < 0)
210 die("Can't read '%s'", file);
211 size = copy_file_fd(fd);
212 close(fd);
213
214 return size;
215}
216
217static unsigned long get_size_fd(int fd)
218{
219 unsigned long long size = 0;
220 char buf[BUFSIZ];
221 int r;
222
223 do {
224 r = read(fd, buf, BUFSIZ);
225 if (r > 0)
226 size += r;
227 } while (r > 0);
228
229 lseek(fd, 0, SEEK_SET);
230
231 return size;
232}
233
234static unsigned long get_size(const char *file)
235{
236 unsigned long long size = 0;
237 int fd;
238
239 fd = open(file, O_RDONLY);
240 if (fd < 0)
241 die("Can't read '%s'", file);
242 size = get_size_fd(fd);
243 close(fd);
244
245 return size;
246}
247
248static void read_header_files(void)
249{
250 unsigned long long size, check_size;
251 char *path;
252 int fd;
253
254 path = get_tracing_file("events/header_page");
255 fd = open(path, O_RDONLY);
256 if (fd < 0)
257 die("can't read '%s'", path);
258
259 /* unfortunately, you can not stat debugfs files for size */
260 size = get_size_fd(fd);
261
262 write_or_die("header_page", 12);
263 write_or_die(&size, 8);
264 check_size = copy_file_fd(fd);
265 close(fd);
266
267 if (size != check_size)
268 die("wrong size for '%s' size=%lld read=%lld",
269 path, size, check_size);
270 put_tracing_file(path);
271
272 path = get_tracing_file("events/header_event");
273 fd = open(path, O_RDONLY);
274 if (fd < 0)
275 die("can't read '%s'", path);
276
277 size = get_size_fd(fd);
278
279 write_or_die("header_event", 13);
280 write_or_die(&size, 8);
281 check_size = copy_file_fd(fd);
282 if (size != check_size)
283 die("wrong size for '%s'", path);
284 put_tracing_file(path);
285 close(fd);
286}
287
288static bool name_in_tp_list(char *sys, struct tracepoint_path *tps)
289{
290 while (tps) {
291 if (!strcmp(sys, tps->name))
292 return true;
293 tps = tps->next;
294 }
295
296 return false;
297}
298
299static void copy_event_system(const char *sys, struct tracepoint_path *tps)
300{
301 unsigned long long size, check_size;
302 struct dirent *dent;
303 struct stat st;
304 char *format;
305 DIR *dir;
306 int count = 0;
307 int ret;
308
309 dir = opendir(sys);
310 if (!dir)
311 die("can't read directory '%s'", sys);
312
313 while ((dent = readdir(dir))) {
314 if (dent->d_type != DT_DIR ||
315 strcmp(dent->d_name, ".") == 0 ||
316 strcmp(dent->d_name, "..") == 0 ||
317 !name_in_tp_list(dent->d_name, tps))
318 continue;
319 format = malloc_or_die(strlen(sys) + strlen(dent->d_name) + 10);
320 sprintf(format, "%s/%s/format", sys, dent->d_name);
321 ret = stat(format, &st);
322 free(format);
323 if (ret < 0)
324 continue;
325 count++;
326 }
327
328 write_or_die(&count, 4);
329
330 rewinddir(dir);
331 while ((dent = readdir(dir))) {
332 if (dent->d_type != DT_DIR ||
333 strcmp(dent->d_name, ".") == 0 ||
334 strcmp(dent->d_name, "..") == 0 ||
335 !name_in_tp_list(dent->d_name, tps))
336 continue;
337 format = malloc_or_die(strlen(sys) + strlen(dent->d_name) + 10);
338 sprintf(format, "%s/%s/format", sys, dent->d_name);
339 ret = stat(format, &st);
340
341 if (ret >= 0) {
342 /* unfortunately, you can not stat debugfs files for size */
343 size = get_size(format);
344 write_or_die(&size, 8);
345 check_size = copy_file(format);
346 if (size != check_size)
347 die("error in size of file '%s'", format);
348 }
349
350 free(format);
351 }
352 closedir(dir);
353}
354
355static void read_ftrace_files(struct tracepoint_path *tps)
356{
357 char *path;
358
359 path = get_tracing_file("events/ftrace");
360
361 copy_event_system(path, tps);
362
363 put_tracing_file(path);
364}
365
366static bool system_in_tp_list(char *sys, struct tracepoint_path *tps)
367{
368 while (tps) {
369 if (!strcmp(sys, tps->system))
370 return true;
371 tps = tps->next;
372 }
373
374 return false;
375}
376
377static void read_event_files(struct tracepoint_path *tps)
378{
379 struct dirent *dent;
380 struct stat st;
381 char *path;
382 char *sys;
383 DIR *dir;
384 int count = 0;
385 int ret;
386
387 path = get_tracing_file("events");
388
389 dir = opendir(path);
390 if (!dir)
391 die("can't read directory '%s'", path);
392
393 while ((dent = readdir(dir))) {
394 if (dent->d_type != DT_DIR ||
395 strcmp(dent->d_name, ".") == 0 ||
396 strcmp(dent->d_name, "..") == 0 ||
397 strcmp(dent->d_name, "ftrace") == 0 ||
398 !system_in_tp_list(dent->d_name, tps))
399 continue;
400 count++;
401 }
402
403 write_or_die(&count, 4);
404
405 rewinddir(dir);
406 while ((dent = readdir(dir))) {
407 if (dent->d_type != DT_DIR ||
408 strcmp(dent->d_name, ".") == 0 ||
409 strcmp(dent->d_name, "..") == 0 ||
410 strcmp(dent->d_name, "ftrace") == 0 ||
411 !system_in_tp_list(dent->d_name, tps))
412 continue;
413 sys = malloc_or_die(strlen(path) + strlen(dent->d_name) + 2);
414 sprintf(sys, "%s/%s", path, dent->d_name);
415 ret = stat(sys, &st);
416 if (ret >= 0) {
417 write_or_die(dent->d_name, strlen(dent->d_name) + 1);
418 copy_event_system(sys, tps);
419 }
420 free(sys);
421 }
422
423 closedir(dir);
424 put_tracing_file(path);
425}
426
427static void read_proc_kallsyms(void)
428{
429 unsigned int size, check_size;
430 const char *path = "/proc/kallsyms";
431 struct stat st;
432 int ret;
433
434 ret = stat(path, &st);
435 if (ret < 0) {
436 /* not found */
437 size = 0;
438 write_or_die(&size, 4);
439 return;
440 }
441 size = get_size(path);
442 write_or_die(&size, 4);
443 check_size = copy_file(path);
444 if (size != check_size)
445 die("error in size of file '%s'", path);
446
447}
448
449static void read_ftrace_printk(void)
450{
451 unsigned int size, check_size;
452 char *path;
453 struct stat st;
454 int ret;
455
456 path = get_tracing_file("printk_formats");
457 ret = stat(path, &st);
458 if (ret < 0) {
459 /* not found */
460 size = 0;
461 write_or_die(&size, 4);
462 goto out;
463 }
464 size = get_size(path);
465 write_or_die(&size, 4);
466 check_size = copy_file(path);
467 if (size != check_size)
468 die("error in size of file '%s'", path);
469out:
470 put_tracing_file(path);
471}
472
473static struct tracepoint_path *
474get_tracepoints_path(struct list_head *pattrs)
475{
476 struct tracepoint_path path, *ppath = &path;
477 struct perf_evsel *pos;
478 int nr_tracepoints = 0;
479
480 list_for_each_entry(pos, pattrs, node) {
481 if (pos->attr.type != PERF_TYPE_TRACEPOINT)
482 continue;
483 ++nr_tracepoints;
484 ppath->next = tracepoint_id_to_path(pos->attr.config);
485 if (!ppath->next)
486 die("%s\n", "No memory to alloc tracepoints list");
487 ppath = ppath->next;
488 }
489
490 return nr_tracepoints > 0 ? path.next : NULL;
491}
492
493bool have_tracepoints(struct list_head *pattrs)
494{
495 struct perf_evsel *pos;
496
497 list_for_each_entry(pos, pattrs, node)
498 if (pos->attr.type == PERF_TYPE_TRACEPOINT)
499 return true;
500
501 return false;
502}
503
504int read_tracing_data(int fd, struct list_head *pattrs)
505{
506 char buf[BUFSIZ];
507 struct tracepoint_path *tps = get_tracepoints_path(pattrs);
508
509 /*
510 * What? No tracepoints? No sense writing anything here, bail out.
511 */
512 if (tps == NULL)
513 return -1;
514
515 output_fd = fd;
516
517 buf[0] = 23;
518 buf[1] = 8;
519 buf[2] = 68;
520 memcpy(buf + 3, "tracing", 7);
521
522 write_or_die(buf, 10);
523
524 write_or_die(VERSION, strlen(VERSION) + 1);
525
526 /* save endian */
527 if (bigendian())
528 buf[0] = 1;
529 else
530 buf[0] = 0;
531
532 write_or_die(buf, 1);
533
534 /* save size of long */
535 buf[0] = sizeof(long);
536 write_or_die(buf, 1);
537
538 /* save page_size */
539 page_size = sysconf(_SC_PAGESIZE);
540 write_or_die(&page_size, 4);
541
542 read_header_files();
543 read_ftrace_files(tps);
544 read_event_files(tps);
545 read_proc_kallsyms();
546 read_ftrace_printk();
547
548 return 0;
549}
550
551ssize_t read_tracing_data_size(int fd, struct list_head *pattrs)
552{
553 ssize_t size;
554 int err = 0;
555
556 calc_data_size = 1;
557 err = read_tracing_data(fd, pattrs);
558 size = calc_data_size - 1;
559 calc_data_size = 0;
560
561 if (err < 0)
562 return err;
563
564 return size;
565}
diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c
new file mode 100644
index 00000000000..73a02223c62
--- /dev/null
+++ b/tools/perf/util/trace-event-parse.c
@@ -0,0 +1,3233 @@
1/*
2 * Copyright (C) 2009, Steven Rostedt <srostedt@redhat.com>
3 *
4 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License (not later!)
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
20 *
21 * The parts for function graph printing was taken and modified from the
22 * Linux Kernel that were written by Frederic Weisbecker.
23 */
24#define _GNU_SOURCE
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28#include <ctype.h>
29#include <errno.h>
30
31#undef _GNU_SOURCE
32#include "../perf.h"
33#include "util.h"
34#include "trace-event.h"
35
36int header_page_ts_offset;
37int header_page_ts_size;
38int header_page_size_offset;
39int header_page_size_size;
40int header_page_overwrite_offset;
41int header_page_overwrite_size;
42int header_page_data_offset;
43int header_page_data_size;
44
45bool latency_format;
46
47static char *input_buf;
48static unsigned long long input_buf_ptr;
49static unsigned long long input_buf_siz;
50
51static int cpus;
52static int long_size;
53static int is_flag_field;
54static int is_symbolic_field;
55
56static struct format_field *
57find_any_field(struct event *event, const char *name);
58
59static void init_input_buf(char *buf, unsigned long long size)
60{
61 input_buf = buf;
62 input_buf_siz = size;
63 input_buf_ptr = 0;
64}
65
66struct cmdline {
67 char *comm;
68 int pid;
69};
70
71static struct cmdline *cmdlines;
72static int cmdline_count;
73
74static int cmdline_cmp(const void *a, const void *b)
75{
76 const struct cmdline *ca = a;
77 const struct cmdline *cb = b;
78
79 if (ca->pid < cb->pid)
80 return -1;
81 if (ca->pid > cb->pid)
82 return 1;
83
84 return 0;
85}
86
87void parse_cmdlines(char *file, int size __unused)
88{
89 struct cmdline_list {
90 struct cmdline_list *next;
91 char *comm;
92 int pid;
93 } *list = NULL, *item;
94 char *line;
95 char *next = NULL;
96 int i;
97
98 line = strtok_r(file, "\n", &next);
99 while (line) {
100 item = malloc_or_die(sizeof(*item));
101 sscanf(line, "%d %as", &item->pid,
102 (float *)(void *)&item->comm); /* workaround gcc warning */
103 item->next = list;
104 list = item;
105 line = strtok_r(NULL, "\n", &next);
106 cmdline_count++;
107 }
108
109 cmdlines = malloc_or_die(sizeof(*cmdlines) * cmdline_count);
110
111 i = 0;
112 while (list) {
113 cmdlines[i].pid = list->pid;
114 cmdlines[i].comm = list->comm;
115 i++;
116 item = list;
117 list = list->next;
118 free(item);
119 }
120
121 qsort(cmdlines, cmdline_count, sizeof(*cmdlines), cmdline_cmp);
122}
123
124static struct func_map {
125 unsigned long long addr;
126 char *func;
127 char *mod;
128} *func_list;
129static unsigned int func_count;
130
131static int func_cmp(const void *a, const void *b)
132{
133 const struct func_map *fa = a;
134 const struct func_map *fb = b;
135
136 if (fa->addr < fb->addr)
137 return -1;
138 if (fa->addr > fb->addr)
139 return 1;
140
141 return 0;
142}
143
144void parse_proc_kallsyms(char *file, unsigned int size __unused)
145{
146 struct func_list {
147 struct func_list *next;
148 unsigned long long addr;
149 char *func;
150 char *mod;
151 } *list = NULL, *item;
152 char *line;
153 char *next = NULL;
154 char *addr_str;
155 char ch;
156 int ret;
157 int i;
158
159 line = strtok_r(file, "\n", &next);
160 while (line) {
161 item = malloc_or_die(sizeof(*item));
162 item->mod = NULL;
163 ret = sscanf(line, "%as %c %as\t[%as",
164 (float *)(void *)&addr_str, /* workaround gcc warning */
165 &ch,
166 (float *)(void *)&item->func,
167 (float *)(void *)&item->mod);
168 item->addr = strtoull(addr_str, NULL, 16);
169 free(addr_str);
170
171 /* truncate the extra ']' */
172 if (item->mod)
173 item->mod[strlen(item->mod) - 1] = 0;
174
175
176 item->next = list;
177 list = item;
178 line = strtok_r(NULL, "\n", &next);
179 func_count++;
180 }
181
182 func_list = malloc_or_die(sizeof(*func_list) * (func_count + 1));
183
184 i = 0;
185 while (list) {
186 func_list[i].func = list->func;
187 func_list[i].addr = list->addr;
188 func_list[i].mod = list->mod;
189 i++;
190 item = list;
191 list = list->next;
192 free(item);
193 }
194
195 qsort(func_list, func_count, sizeof(*func_list), func_cmp);
196
197 /*
198 * Add a special record at the end.
199 */
200 func_list[func_count].func = NULL;
201 func_list[func_count].addr = 0;
202 func_list[func_count].mod = NULL;
203}
204
205/*
206 * We are searching for a record in between, not an exact
207 * match.
208 */
209static int func_bcmp(const void *a, const void *b)
210{
211 const struct func_map *fa = a;
212 const struct func_map *fb = b;
213
214 if ((fa->addr == fb->addr) ||
215
216 (fa->addr > fb->addr &&
217 fa->addr < (fb+1)->addr))
218 return 0;
219
220 if (fa->addr < fb->addr)
221 return -1;
222
223 return 1;
224}
225
226static struct func_map *find_func(unsigned long long addr)
227{
228 struct func_map *func;
229 struct func_map key;
230
231 key.addr = addr;
232
233 func = bsearch(&key, func_list, func_count, sizeof(*func_list),
234 func_bcmp);
235
236 return func;
237}
238
239void print_funcs(void)
240{
241 int i;
242
243 for (i = 0; i < (int)func_count; i++) {
244 printf("%016llx %s",
245 func_list[i].addr,
246 func_list[i].func);
247 if (func_list[i].mod)
248 printf(" [%s]\n", func_list[i].mod);
249 else
250 printf("\n");
251 }
252}
253
254static struct printk_map {
255 unsigned long long addr;
256 char *printk;
257} *printk_list;
258static unsigned int printk_count;
259
260static int printk_cmp(const void *a, const void *b)
261{
262 const struct func_map *fa = a;
263 const struct func_map *fb = b;
264
265 if (fa->addr < fb->addr)
266 return -1;
267 if (fa->addr > fb->addr)
268 return 1;
269
270 return 0;
271}
272
273static struct printk_map *find_printk(unsigned long long addr)
274{
275 struct printk_map *printk;
276 struct printk_map key;
277
278 key.addr = addr;
279
280 printk = bsearch(&key, printk_list, printk_count, sizeof(*printk_list),
281 printk_cmp);
282
283 return printk;
284}
285
286void parse_ftrace_printk(char *file, unsigned int size __unused)
287{
288 struct printk_list {
289 struct printk_list *next;
290 unsigned long long addr;
291 char *printk;
292 } *list = NULL, *item;
293 char *line;
294 char *next = NULL;
295 char *addr_str;
296 int i;
297
298 line = strtok_r(file, "\n", &next);
299 while (line) {
300 addr_str = strsep(&line, ":");
301 if (!line) {
302 warning("error parsing print strings");
303 break;
304 }
305 item = malloc_or_die(sizeof(*item));
306 item->addr = strtoull(addr_str, NULL, 16);
307 /* fmt still has a space, skip it */
308 item->printk = strdup(line+1);
309 item->next = list;
310 list = item;
311 line = strtok_r(NULL, "\n", &next);
312 printk_count++;
313 }
314
315 printk_list = malloc_or_die(sizeof(*printk_list) * printk_count + 1);
316
317 i = 0;
318 while (list) {
319 printk_list[i].printk = list->printk;
320 printk_list[i].addr = list->addr;
321 i++;
322 item = list;
323 list = list->next;
324 free(item);
325 }
326
327 qsort(printk_list, printk_count, sizeof(*printk_list), printk_cmp);
328}
329
330void print_printk(void)
331{
332 int i;
333
334 for (i = 0; i < (int)printk_count; i++) {
335 printf("%016llx %s\n",
336 printk_list[i].addr,
337 printk_list[i].printk);
338 }
339}
340
341static struct event *alloc_event(void)
342{
343 struct event *event;
344
345 event = malloc_or_die(sizeof(*event));
346 memset(event, 0, sizeof(*event));
347
348 return event;
349}
350
351enum event_type {
352 EVENT_ERROR,
353 EVENT_NONE,
354 EVENT_SPACE,
355 EVENT_NEWLINE,
356 EVENT_OP,
357 EVENT_DELIM,
358 EVENT_ITEM,
359 EVENT_DQUOTE,
360 EVENT_SQUOTE,
361};
362
363static struct event *event_list;
364
365static void add_event(struct event *event)
366{
367 event->next = event_list;
368 event_list = event;
369}
370
371static int event_item_type(enum event_type type)
372{
373 switch (type) {
374 case EVENT_ITEM ... EVENT_SQUOTE:
375 return 1;
376 case EVENT_ERROR ... EVENT_DELIM:
377 default:
378 return 0;
379 }
380}
381
382static void free_arg(struct print_arg *arg)
383{
384 if (!arg)
385 return;
386
387 switch (arg->type) {
388 case PRINT_ATOM:
389 if (arg->atom.atom)
390 free(arg->atom.atom);
391 break;
392 case PRINT_NULL:
393 case PRINT_FIELD ... PRINT_OP:
394 default:
395 /* todo */
396 break;
397 }
398
399 free(arg);
400}
401
402static enum event_type get_type(int ch)
403{
404 if (ch == '\n')
405 return EVENT_NEWLINE;
406 if (isspace(ch))
407 return EVENT_SPACE;
408 if (isalnum(ch) || ch == '_')
409 return EVENT_ITEM;
410 if (ch == '\'')
411 return EVENT_SQUOTE;
412 if (ch == '"')
413 return EVENT_DQUOTE;
414 if (!isprint(ch))
415 return EVENT_NONE;
416 if (ch == '(' || ch == ')' || ch == ',')
417 return EVENT_DELIM;
418
419 return EVENT_OP;
420}
421
422static int __read_char(void)
423{
424 if (input_buf_ptr >= input_buf_siz)
425 return -1;
426
427 return input_buf[input_buf_ptr++];
428}
429
430static int __peek_char(void)
431{
432 if (input_buf_ptr >= input_buf_siz)
433 return -1;
434
435 return input_buf[input_buf_ptr];
436}
437
438static enum event_type __read_token(char **tok)
439{
440 char buf[BUFSIZ];
441 int ch, last_ch, quote_ch, next_ch;
442 int i = 0;
443 int tok_size = 0;
444 enum event_type type;
445
446 *tok = NULL;
447
448
449 ch = __read_char();
450 if (ch < 0)
451 return EVENT_NONE;
452
453 type = get_type(ch);
454 if (type == EVENT_NONE)
455 return type;
456
457 buf[i++] = ch;
458
459 switch (type) {
460 case EVENT_NEWLINE:
461 case EVENT_DELIM:
462 *tok = malloc_or_die(2);
463 (*tok)[0] = ch;
464 (*tok)[1] = 0;
465 return type;
466
467 case EVENT_OP:
468 switch (ch) {
469 case '-':
470 next_ch = __peek_char();
471 if (next_ch == '>') {
472 buf[i++] = __read_char();
473 break;
474 }
475 /* fall through */
476 case '+':
477 case '|':
478 case '&':
479 case '>':
480 case '<':
481 last_ch = ch;
482 ch = __peek_char();
483 if (ch != last_ch)
484 goto test_equal;
485 buf[i++] = __read_char();
486 switch (last_ch) {
487 case '>':
488 case '<':
489 goto test_equal;
490 default:
491 break;
492 }
493 break;
494 case '!':
495 case '=':
496 goto test_equal;
497 default: /* what should we do instead? */
498 break;
499 }
500 buf[i] = 0;
501 *tok = strdup(buf);
502 return type;
503
504 test_equal:
505 ch = __peek_char();
506 if (ch == '=')
507 buf[i++] = __read_char();
508 break;
509
510 case EVENT_DQUOTE:
511 case EVENT_SQUOTE:
512 /* don't keep quotes */
513 i--;
514 quote_ch = ch;
515 last_ch = 0;
516 do {
517 if (i == (BUFSIZ - 1)) {
518 buf[i] = 0;
519 if (*tok) {
520 *tok = realloc(*tok, tok_size + BUFSIZ);
521 if (!*tok)
522 return EVENT_NONE;
523 strcat(*tok, buf);
524 } else
525 *tok = strdup(buf);
526
527 if (!*tok)
528 return EVENT_NONE;
529 tok_size += BUFSIZ;
530 i = 0;
531 }
532 last_ch = ch;
533 ch = __read_char();
534 buf[i++] = ch;
535 /* the '\' '\' will cancel itself */
536 if (ch == '\\' && last_ch == '\\')
537 last_ch = 0;
538 } while (ch != quote_ch || last_ch == '\\');
539 /* remove the last quote */
540 i--;
541 goto out;
542
543 case EVENT_ERROR ... EVENT_SPACE:
544 case EVENT_ITEM:
545 default:
546 break;
547 }
548
549 while (get_type(__peek_char()) == type) {
550 if (i == (BUFSIZ - 1)) {
551 buf[i] = 0;
552 if (*tok) {
553 *tok = realloc(*tok, tok_size + BUFSIZ);
554 if (!*tok)
555 return EVENT_NONE;
556 strcat(*tok, buf);
557 } else
558 *tok = strdup(buf);
559
560 if (!*tok)
561 return EVENT_NONE;
562 tok_size += BUFSIZ;
563 i = 0;
564 }
565 ch = __read_char();
566 buf[i++] = ch;
567 }
568
569 out:
570 buf[i] = 0;
571 if (*tok) {
572 *tok = realloc(*tok, tok_size + i);
573 if (!*tok)
574 return EVENT_NONE;
575 strcat(*tok, buf);
576 } else
577 *tok = strdup(buf);
578 if (!*tok)
579 return EVENT_NONE;
580
581 return type;
582}
583
584static void free_token(char *tok)
585{
586 if (tok)
587 free(tok);
588}
589
590static enum event_type read_token(char **tok)
591{
592 enum event_type type;
593
594 for (;;) {
595 type = __read_token(tok);
596 if (type != EVENT_SPACE)
597 return type;
598
599 free_token(*tok);
600 }
601
602 /* not reached */
603 return EVENT_NONE;
604}
605
606/* no newline */
607static enum event_type read_token_item(char **tok)
608{
609 enum event_type type;
610
611 for (;;) {
612 type = __read_token(tok);
613 if (type != EVENT_SPACE && type != EVENT_NEWLINE)
614 return type;
615
616 free_token(*tok);
617 }
618
619 /* not reached */
620 return EVENT_NONE;
621}
622
623static int test_type(enum event_type type, enum event_type expect)
624{
625 if (type != expect) {
626 warning("Error: expected type %d but read %d",
627 expect, type);
628 return -1;
629 }
630 return 0;
631}
632
633static int __test_type_token(enum event_type type, char *token,
634 enum event_type expect, const char *expect_tok,
635 bool warn)
636{
637 if (type != expect) {
638 if (warn)
639 warning("Error: expected type %d but read %d",
640 expect, type);
641 return -1;
642 }
643
644 if (strcmp(token, expect_tok) != 0) {
645 if (warn)
646 warning("Error: expected '%s' but read '%s'",
647 expect_tok, token);
648 return -1;
649 }
650 return 0;
651}
652
653static int test_type_token(enum event_type type, char *token,
654 enum event_type expect, const char *expect_tok)
655{
656 return __test_type_token(type, token, expect, expect_tok, true);
657}
658
659static int __read_expect_type(enum event_type expect, char **tok, int newline_ok)
660{
661 enum event_type type;
662
663 if (newline_ok)
664 type = read_token(tok);
665 else
666 type = read_token_item(tok);
667 return test_type(type, expect);
668}
669
670static int read_expect_type(enum event_type expect, char **tok)
671{
672 return __read_expect_type(expect, tok, 1);
673}
674
675static int __read_expected(enum event_type expect, const char *str,
676 int newline_ok, bool warn)
677{
678 enum event_type type;
679 char *token;
680 int ret;
681
682 if (newline_ok)
683 type = read_token(&token);
684 else
685 type = read_token_item(&token);
686
687 ret = __test_type_token(type, token, expect, str, warn);
688
689 free_token(token);
690
691 return ret;
692}
693
694static int read_expected(enum event_type expect, const char *str)
695{
696 return __read_expected(expect, str, 1, true);
697}
698
699static int read_expected_item(enum event_type expect, const char *str)
700{
701 return __read_expected(expect, str, 0, true);
702}
703
704static char *event_read_name(void)
705{
706 char *token;
707
708 if (read_expected(EVENT_ITEM, "name") < 0)
709 return NULL;
710
711 if (read_expected(EVENT_OP, ":") < 0)
712 return NULL;
713
714 if (read_expect_type(EVENT_ITEM, &token) < 0)
715 goto fail;
716
717 return token;
718
719 fail:
720 free_token(token);
721 return NULL;
722}
723
724static int event_read_id(void)
725{
726 char *token;
727 int id;
728
729 if (read_expected_item(EVENT_ITEM, "ID") < 0)
730 return -1;
731
732 if (read_expected(EVENT_OP, ":") < 0)
733 return -1;
734
735 if (read_expect_type(EVENT_ITEM, &token) < 0)
736 goto fail;
737
738 id = strtoul(token, NULL, 0);
739 free_token(token);
740 return id;
741
742 fail:
743 free_token(token);
744 return -1;
745}
746
747static int field_is_string(struct format_field *field)
748{
749 if ((field->flags & FIELD_IS_ARRAY) &&
750 (!strstr(field->type, "char") || !strstr(field->type, "u8") ||
751 !strstr(field->type, "s8")))
752 return 1;
753
754 return 0;
755}
756
757static int field_is_dynamic(struct format_field *field)
758{
759 if (!strncmp(field->type, "__data_loc", 10))
760 return 1;
761
762 return 0;
763}
764
765static int event_read_fields(struct event *event, struct format_field **fields)
766{
767 struct format_field *field = NULL;
768 enum event_type type;
769 char *token;
770 char *last_token;
771 int count = 0;
772
773 do {
774 type = read_token(&token);
775 if (type == EVENT_NEWLINE) {
776 free_token(token);
777 return count;
778 }
779
780 count++;
781
782 if (test_type_token(type, token, EVENT_ITEM, "field"))
783 goto fail;
784 free_token(token);
785
786 type = read_token(&token);
787 /*
788 * The ftrace fields may still use the "special" name.
789 * Just ignore it.
790 */
791 if (event->flags & EVENT_FL_ISFTRACE &&
792 type == EVENT_ITEM && strcmp(token, "special") == 0) {
793 free_token(token);
794 type = read_token(&token);
795 }
796
797 if (test_type_token(type, token, EVENT_OP, ":") < 0)
798 return -1;
799
800 if (read_expect_type(EVENT_ITEM, &token) < 0)
801 goto fail;
802
803 last_token = token;
804
805 field = malloc_or_die(sizeof(*field));
806 memset(field, 0, sizeof(*field));
807
808 /* read the rest of the type */
809 for (;;) {
810 type = read_token(&token);
811 if (type == EVENT_ITEM ||
812 (type == EVENT_OP && strcmp(token, "*") == 0) ||
813 /*
814 * Some of the ftrace fields are broken and have
815 * an illegal "." in them.
816 */
817 (event->flags & EVENT_FL_ISFTRACE &&
818 type == EVENT_OP && strcmp(token, ".") == 0)) {
819
820 if (strcmp(token, "*") == 0)
821 field->flags |= FIELD_IS_POINTER;
822
823 if (field->type) {
824 field->type = realloc(field->type,
825 strlen(field->type) +
826 strlen(last_token) + 2);
827 strcat(field->type, " ");
828 strcat(field->type, last_token);
829 } else
830 field->type = last_token;
831 last_token = token;
832 continue;
833 }
834
835 break;
836 }
837
838 if (!field->type) {
839 die("no type found");
840 goto fail;
841 }
842 field->name = last_token;
843
844 if (test_type(type, EVENT_OP))
845 goto fail;
846
847 if (strcmp(token, "[") == 0) {
848 enum event_type last_type = type;
849 char *brackets = token;
850 int len;
851
852 field->flags |= FIELD_IS_ARRAY;
853
854 type = read_token(&token);
855 while (strcmp(token, "]") != 0) {
856 if (last_type == EVENT_ITEM &&
857 type == EVENT_ITEM)
858 len = 2;
859 else
860 len = 1;
861 last_type = type;
862
863 brackets = realloc(brackets,
864 strlen(brackets) +
865 strlen(token) + len);
866 if (len == 2)
867 strcat(brackets, " ");
868 strcat(brackets, token);
869 free_token(token);
870 type = read_token(&token);
871 if (type == EVENT_NONE) {
872 die("failed to find token");
873 goto fail;
874 }
875 }
876
877 free_token(token);
878
879 brackets = realloc(brackets, strlen(brackets) + 2);
880 strcat(brackets, "]");
881
882 /* add brackets to type */
883
884 type = read_token(&token);
885 /*
886 * If the next token is not an OP, then it is of
887 * the format: type [] item;
888 */
889 if (type == EVENT_ITEM) {
890 field->type = realloc(field->type,
891 strlen(field->type) +
892 strlen(field->name) +
893 strlen(brackets) + 2);
894 strcat(field->type, " ");
895 strcat(field->type, field->name);
896 free_token(field->name);
897 strcat(field->type, brackets);
898 field->name = token;
899 type = read_token(&token);
900 } else {
901 field->type = realloc(field->type,
902 strlen(field->type) +
903 strlen(brackets) + 1);
904 strcat(field->type, brackets);
905 }
906 free(brackets);
907 }
908
909 if (field_is_string(field)) {
910 field->flags |= FIELD_IS_STRING;
911 if (field_is_dynamic(field))
912 field->flags |= FIELD_IS_DYNAMIC;
913 }
914
915 if (test_type_token(type, token, EVENT_OP, ";"))
916 goto fail;
917 free_token(token);
918
919 if (read_expected(EVENT_ITEM, "offset") < 0)
920 goto fail_expect;
921
922 if (read_expected(EVENT_OP, ":") < 0)
923 goto fail_expect;
924
925 if (read_expect_type(EVENT_ITEM, &token))
926 goto fail;
927 field->offset = strtoul(token, NULL, 0);
928 free_token(token);
929
930 if (read_expected(EVENT_OP, ";") < 0)
931 goto fail_expect;
932
933 if (read_expected(EVENT_ITEM, "size") < 0)
934 goto fail_expect;
935
936 if (read_expected(EVENT_OP, ":") < 0)
937 goto fail_expect;
938
939 if (read_expect_type(EVENT_ITEM, &token))
940 goto fail;
941 field->size = strtoul(token, NULL, 0);
942 free_token(token);
943
944 if (read_expected(EVENT_OP, ";") < 0)
945 goto fail_expect;
946
947 type = read_token(&token);
948 if (type != EVENT_NEWLINE) {
949 /* newer versions of the kernel have a "signed" type */
950 if (test_type_token(type, token, EVENT_ITEM, "signed"))
951 goto fail;
952
953 free_token(token);
954
955 if (read_expected(EVENT_OP, ":") < 0)
956 goto fail_expect;
957
958 if (read_expect_type(EVENT_ITEM, &token))
959 goto fail;
960
961 if (strtoul(token, NULL, 0))
962 field->flags |= FIELD_IS_SIGNED;
963
964 free_token(token);
965 if (read_expected(EVENT_OP, ";") < 0)
966 goto fail_expect;
967
968 if (read_expect_type(EVENT_NEWLINE, &token))
969 goto fail;
970 }
971
972 free_token(token);
973
974 *fields = field;
975 fields = &field->next;
976
977 } while (1);
978
979 return 0;
980
981fail:
982 free_token(token);
983fail_expect:
984 if (field)
985 free(field);
986 return -1;
987}
988
989static int event_read_format(struct event *event)
990{
991 char *token;
992 int ret;
993
994 if (read_expected_item(EVENT_ITEM, "format") < 0)
995 return -1;
996
997 if (read_expected(EVENT_OP, ":") < 0)
998 return -1;
999
1000 if (read_expect_type(EVENT_NEWLINE, &token))
1001 goto fail;
1002 free_token(token);
1003
1004 ret = event_read_fields(event, &event->format.common_fields);
1005 if (ret < 0)
1006 return ret;
1007 event->format.nr_common = ret;
1008
1009 ret = event_read_fields(event, &event->format.fields);
1010 if (ret < 0)
1011 return ret;
1012 event->format.nr_fields = ret;
1013
1014 return 0;
1015
1016 fail:
1017 free_token(token);
1018 return -1;
1019}
1020
1021enum event_type
1022process_arg_token(struct event *event, struct print_arg *arg,
1023 char **tok, enum event_type type);
1024
1025static enum event_type
1026process_arg(struct event *event, struct print_arg *arg, char **tok)
1027{
1028 enum event_type type;
1029 char *token;
1030
1031 type = read_token(&token);
1032 *tok = token;
1033
1034 return process_arg_token(event, arg, tok, type);
1035}
1036
1037static enum event_type
1038process_cond(struct event *event, struct print_arg *top, char **tok)
1039{
1040 struct print_arg *arg, *left, *right;
1041 enum event_type type;
1042 char *token = NULL;
1043
1044 arg = malloc_or_die(sizeof(*arg));
1045 memset(arg, 0, sizeof(*arg));
1046
1047 left = malloc_or_die(sizeof(*left));
1048
1049 right = malloc_or_die(sizeof(*right));
1050
1051 arg->type = PRINT_OP;
1052 arg->op.left = left;
1053 arg->op.right = right;
1054
1055 *tok = NULL;
1056 type = process_arg(event, left, &token);
1057 if (test_type_token(type, token, EVENT_OP, ":"))
1058 goto out_free;
1059
1060 arg->op.op = token;
1061
1062 type = process_arg(event, right, &token);
1063
1064 top->op.right = arg;
1065
1066 *tok = token;
1067 return type;
1068
1069out_free:
1070 free_token(*tok);
1071 free(right);
1072 free(left);
1073 free_arg(arg);
1074 return EVENT_ERROR;
1075}
1076
1077static enum event_type
1078process_array(struct event *event, struct print_arg *top, char **tok)
1079{
1080 struct print_arg *arg;
1081 enum event_type type;
1082 char *token = NULL;
1083
1084 arg = malloc_or_die(sizeof(*arg));
1085 memset(arg, 0, sizeof(*arg));
1086
1087 *tok = NULL;
1088 type = process_arg(event, arg, &token);
1089 if (test_type_token(type, token, EVENT_OP, "]"))
1090 goto out_free;
1091
1092 top->op.right = arg;
1093
1094 free_token(token);
1095 type = read_token_item(&token);
1096 *tok = token;
1097
1098 return type;
1099
1100out_free:
1101 free_token(*tok);
1102 free_arg(arg);
1103 return EVENT_ERROR;
1104}
1105
1106static int get_op_prio(char *op)
1107{
1108 if (!op[1]) {
1109 switch (op[0]) {
1110 case '*':
1111 case '/':
1112 case '%':
1113 return 6;
1114 case '+':
1115 case '-':
1116 return 7;
1117 /* '>>' and '<<' are 8 */
1118 case '<':
1119 case '>':
1120 return 9;
1121 /* '==' and '!=' are 10 */
1122 case '&':
1123 return 11;
1124 case '^':
1125 return 12;
1126 case '|':
1127 return 13;
1128 case '?':
1129 return 16;
1130 default:
1131 die("unknown op '%c'", op[0]);
1132 return -1;
1133 }
1134 } else {
1135 if (strcmp(op, "++") == 0 ||
1136 strcmp(op, "--") == 0) {
1137 return 3;
1138 } else if (strcmp(op, ">>") == 0 ||
1139 strcmp(op, "<<") == 0) {
1140 return 8;
1141 } else if (strcmp(op, ">=") == 0 ||
1142 strcmp(op, "<=") == 0) {
1143 return 9;
1144 } else if (strcmp(op, "==") == 0 ||
1145 strcmp(op, "!=") == 0) {
1146 return 10;
1147 } else if (strcmp(op, "&&") == 0) {
1148 return 14;
1149 } else if (strcmp(op, "||") == 0) {
1150 return 15;
1151 } else {
1152 die("unknown op '%s'", op);
1153 return -1;
1154 }
1155 }
1156}
1157
1158static void set_op_prio(struct print_arg *arg)
1159{
1160
1161 /* single ops are the greatest */
1162 if (!arg->op.left || arg->op.left->type == PRINT_NULL) {
1163 arg->op.prio = 0;
1164 return;
1165 }
1166
1167 arg->op.prio = get_op_prio(arg->op.op);
1168}
1169
1170static enum event_type
1171process_op(struct event *event, struct print_arg *arg, char **tok)
1172{
1173 struct print_arg *left, *right = NULL;
1174 enum event_type type;
1175 char *token;
1176
1177 /* the op is passed in via tok */
1178 token = *tok;
1179
1180 if (arg->type == PRINT_OP && !arg->op.left) {
1181 /* handle single op */
1182 if (token[1]) {
1183 die("bad op token %s", token);
1184 return EVENT_ERROR;
1185 }
1186 switch (token[0]) {
1187 case '!':
1188 case '+':
1189 case '-':
1190 break;
1191 default:
1192 die("bad op token %s", token);
1193 return EVENT_ERROR;
1194 }
1195
1196 /* make an empty left */
1197 left = malloc_or_die(sizeof(*left));
1198 left->type = PRINT_NULL;
1199 arg->op.left = left;
1200
1201 right = malloc_or_die(sizeof(*right));
1202 arg->op.right = right;
1203
1204 type = process_arg(event, right, tok);
1205
1206 } else if (strcmp(token, "?") == 0) {
1207
1208 left = malloc_or_die(sizeof(*left));
1209 /* copy the top arg to the left */
1210 *left = *arg;
1211
1212 arg->type = PRINT_OP;
1213 arg->op.op = token;
1214 arg->op.left = left;
1215 arg->op.prio = 0;
1216
1217 type = process_cond(event, arg, tok);
1218
1219 } else if (strcmp(token, ">>") == 0 ||
1220 strcmp(token, "<<") == 0 ||
1221 strcmp(token, "&") == 0 ||
1222 strcmp(token, "|") == 0 ||
1223 strcmp(token, "&&") == 0 ||
1224 strcmp(token, "||") == 0 ||
1225 strcmp(token, "-") == 0 ||
1226 strcmp(token, "+") == 0 ||
1227 strcmp(token, "*") == 0 ||
1228 strcmp(token, "^") == 0 ||
1229 strcmp(token, "/") == 0 ||
1230 strcmp(token, "<") == 0 ||
1231 strcmp(token, ">") == 0 ||
1232 strcmp(token, "==") == 0 ||
1233 strcmp(token, "!=") == 0) {
1234
1235 left = malloc_or_die(sizeof(*left));
1236
1237 /* copy the top arg to the left */
1238 *left = *arg;
1239
1240 arg->type = PRINT_OP;
1241 arg->op.op = token;
1242 arg->op.left = left;
1243
1244 set_op_prio(arg);
1245
1246 right = malloc_or_die(sizeof(*right));
1247
1248 type = read_token_item(&token);
1249 *tok = token;
1250
1251 /* could just be a type pointer */
1252 if ((strcmp(arg->op.op, "*") == 0) &&
1253 type == EVENT_DELIM && (strcmp(token, ")") == 0)) {
1254 if (left->type != PRINT_ATOM)
1255 die("bad pointer type");
1256 left->atom.atom = realloc(left->atom.atom,
1257 sizeof(left->atom.atom) + 3);
1258 strcat(left->atom.atom, " *");
1259 *arg = *left;
1260 free(arg);
1261
1262 return type;
1263 }
1264
1265 type = process_arg_token(event, right, tok, type);
1266
1267 arg->op.right = right;
1268
1269 } else if (strcmp(token, "[") == 0) {
1270
1271 left = malloc_or_die(sizeof(*left));
1272 *left = *arg;
1273
1274 arg->type = PRINT_OP;
1275 arg->op.op = token;
1276 arg->op.left = left;
1277
1278 arg->op.prio = 0;
1279 type = process_array(event, arg, tok);
1280
1281 } else {
1282 warning("unknown op '%s'", token);
1283 event->flags |= EVENT_FL_FAILED;
1284 /* the arg is now the left side */
1285 return EVENT_NONE;
1286 }
1287
1288 if (type == EVENT_OP) {
1289 int prio;
1290
1291 /* higher prios need to be closer to the root */
1292 prio = get_op_prio(*tok);
1293
1294 if (prio > arg->op.prio)
1295 return process_op(event, arg, tok);
1296
1297 return process_op(event, right, tok);
1298 }
1299
1300 return type;
1301}
1302
1303static enum event_type
1304process_entry(struct event *event __unused, struct print_arg *arg,
1305 char **tok)
1306{
1307 enum event_type type;
1308 char *field;
1309 char *token;
1310
1311 if (read_expected(EVENT_OP, "->") < 0)
1312 return EVENT_ERROR;
1313
1314 if (read_expect_type(EVENT_ITEM, &token) < 0)
1315 goto fail;
1316 field = token;
1317
1318 arg->type = PRINT_FIELD;
1319 arg->field.name = field;
1320
1321 if (is_flag_field) {
1322 arg->field.field = find_any_field(event, arg->field.name);
1323 arg->field.field->flags |= FIELD_IS_FLAG;
1324 is_flag_field = 0;
1325 } else if (is_symbolic_field) {
1326 arg->field.field = find_any_field(event, arg->field.name);
1327 arg->field.field->flags |= FIELD_IS_SYMBOLIC;
1328 is_symbolic_field = 0;
1329 }
1330
1331 type = read_token(&token);
1332 *tok = token;
1333
1334 return type;
1335
1336fail:
1337 free_token(token);
1338 return EVENT_ERROR;
1339}
1340
1341static char *arg_eval (struct print_arg *arg);
1342
1343static long long arg_num_eval(struct print_arg *arg)
1344{
1345 long long left, right;
1346 long long val = 0;
1347
1348 switch (arg->type) {
1349 case PRINT_ATOM:
1350 val = strtoll(arg->atom.atom, NULL, 0);
1351 break;
1352 case PRINT_TYPE:
1353 val = arg_num_eval(arg->typecast.item);
1354 break;
1355 case PRINT_OP:
1356 switch (arg->op.op[0]) {
1357 case '|':
1358 left = arg_num_eval(arg->op.left);
1359 right = arg_num_eval(arg->op.right);
1360 if (arg->op.op[1])
1361 val = left || right;
1362 else
1363 val = left | right;
1364 break;
1365 case '&':
1366 left = arg_num_eval(arg->op.left);
1367 right = arg_num_eval(arg->op.right);
1368 if (arg->op.op[1])
1369 val = left && right;
1370 else
1371 val = left & right;
1372 break;
1373 case '<':
1374 left = arg_num_eval(arg->op.left);
1375 right = arg_num_eval(arg->op.right);
1376 switch (arg->op.op[1]) {
1377 case 0:
1378 val = left < right;
1379 break;
1380 case '<':
1381 val = left << right;
1382 break;
1383 case '=':
1384 val = left <= right;
1385 break;
1386 default:
1387 die("unknown op '%s'", arg->op.op);
1388 }
1389 break;
1390 case '>':
1391 left = arg_num_eval(arg->op.left);
1392 right = arg_num_eval(arg->op.right);
1393 switch (arg->op.op[1]) {
1394 case 0:
1395 val = left > right;
1396 break;
1397 case '>':
1398 val = left >> right;
1399 break;
1400 case '=':
1401 val = left >= right;
1402 break;
1403 default:
1404 die("unknown op '%s'", arg->op.op);
1405 }
1406 break;
1407 case '=':
1408 left = arg_num_eval(arg->op.left);
1409 right = arg_num_eval(arg->op.right);
1410
1411 if (arg->op.op[1] != '=')
1412 die("unknown op '%s'", arg->op.op);
1413
1414 val = left == right;
1415 break;
1416 case '!':
1417 left = arg_num_eval(arg->op.left);
1418 right = arg_num_eval(arg->op.right);
1419
1420 switch (arg->op.op[1]) {
1421 case '=':
1422 val = left != right;
1423 break;
1424 default:
1425 die("unknown op '%s'", arg->op.op);
1426 }
1427 break;
1428 default:
1429 die("unknown op '%s'", arg->op.op);
1430 }
1431 break;
1432
1433 case PRINT_NULL:
1434 case PRINT_FIELD ... PRINT_SYMBOL:
1435 case PRINT_STRING:
1436 default:
1437 die("invalid eval type %d", arg->type);
1438
1439 }
1440 return val;
1441}
1442
1443static char *arg_eval (struct print_arg *arg)
1444{
1445 long long val;
1446 static char buf[20];
1447
1448 switch (arg->type) {
1449 case PRINT_ATOM:
1450 return arg->atom.atom;
1451 case PRINT_TYPE:
1452 return arg_eval(arg->typecast.item);
1453 case PRINT_OP:
1454 val = arg_num_eval(arg);
1455 sprintf(buf, "%lld", val);
1456 return buf;
1457
1458 case PRINT_NULL:
1459 case PRINT_FIELD ... PRINT_SYMBOL:
1460 case PRINT_STRING:
1461 default:
1462 die("invalid eval type %d", arg->type);
1463 break;
1464 }
1465
1466 return NULL;
1467}
1468
1469static enum event_type
1470process_fields(struct event *event, struct print_flag_sym **list, char **tok)
1471{
1472 enum event_type type;
1473 struct print_arg *arg = NULL;
1474 struct print_flag_sym *field;
1475 char *token = NULL;
1476 char *value;
1477
1478 do {
1479 free_token(token);
1480 type = read_token_item(&token);
1481 if (test_type_token(type, token, EVENT_OP, "{"))
1482 break;
1483
1484 arg = malloc_or_die(sizeof(*arg));
1485
1486 free_token(token);
1487 type = process_arg(event, arg, &token);
1488 if (test_type_token(type, token, EVENT_DELIM, ","))
1489 goto out_free;
1490
1491 field = malloc_or_die(sizeof(*field));
1492 memset(field, 0, sizeof(*field));
1493
1494 value = arg_eval(arg);
1495 field->value = strdup(value);
1496
1497 free_token(token);
1498 type = process_arg(event, arg, &token);
1499 if (test_type_token(type, token, EVENT_OP, "}"))
1500 goto out_free;
1501
1502 value = arg_eval(arg);
1503 field->str = strdup(value);
1504 free_arg(arg);
1505 arg = NULL;
1506
1507 *list = field;
1508 list = &field->next;
1509
1510 free_token(token);
1511 type = read_token_item(&token);
1512 } while (type == EVENT_DELIM && strcmp(token, ",") == 0);
1513
1514 *tok = token;
1515 return type;
1516
1517out_free:
1518 free_arg(arg);
1519 free_token(token);
1520
1521 return EVENT_ERROR;
1522}
1523
1524static enum event_type
1525process_flags(struct event *event, struct print_arg *arg, char **tok)
1526{
1527 struct print_arg *field;
1528 enum event_type type;
1529 char *token;
1530
1531 memset(arg, 0, sizeof(*arg));
1532 arg->type = PRINT_FLAGS;
1533
1534 if (read_expected_item(EVENT_DELIM, "(") < 0)
1535 return EVENT_ERROR;
1536
1537 field = malloc_or_die(sizeof(*field));
1538
1539 type = process_arg(event, field, &token);
1540 if (test_type_token(type, token, EVENT_DELIM, ","))
1541 goto out_free;
1542
1543 arg->flags.field = field;
1544
1545 type = read_token_item(&token);
1546 if (event_item_type(type)) {
1547 arg->flags.delim = token;
1548 type = read_token_item(&token);
1549 }
1550
1551 if (test_type_token(type, token, EVENT_DELIM, ","))
1552 goto out_free;
1553
1554 type = process_fields(event, &arg->flags.flags, &token);
1555 if (test_type_token(type, token, EVENT_DELIM, ")"))
1556 goto out_free;
1557
1558 free_token(token);
1559 type = read_token_item(tok);
1560 return type;
1561
1562out_free:
1563 free_token(token);
1564 return EVENT_ERROR;
1565}
1566
1567static enum event_type
1568process_symbols(struct event *event, struct print_arg *arg, char **tok)
1569{
1570 struct print_arg *field;
1571 enum event_type type;
1572 char *token;
1573
1574 memset(arg, 0, sizeof(*arg));
1575 arg->type = PRINT_SYMBOL;
1576
1577 if (read_expected_item(EVENT_DELIM, "(") < 0)
1578 return EVENT_ERROR;
1579
1580 field = malloc_or_die(sizeof(*field));
1581
1582 type = process_arg(event, field, &token);
1583 if (test_type_token(type, token, EVENT_DELIM, ","))
1584 goto out_free;
1585
1586 arg->symbol.field = field;
1587
1588 type = process_fields(event, &arg->symbol.symbols, &token);
1589 if (test_type_token(type, token, EVENT_DELIM, ")"))
1590 goto out_free;
1591
1592 free_token(token);
1593 type = read_token_item(tok);
1594 return type;
1595
1596out_free:
1597 free_token(token);
1598 return EVENT_ERROR;
1599}
1600
1601static enum event_type
1602process_paren(struct event *event, struct print_arg *arg, char **tok)
1603{
1604 struct print_arg *item_arg;
1605 enum event_type type;
1606 char *token;
1607
1608 type = process_arg(event, arg, &token);
1609
1610 if (type == EVENT_ERROR)
1611 return EVENT_ERROR;
1612
1613 if (type == EVENT_OP)
1614 type = process_op(event, arg, &token);
1615
1616 if (type == EVENT_ERROR)
1617 return EVENT_ERROR;
1618
1619 if (test_type_token(type, token, EVENT_DELIM, ")")) {
1620 free_token(token);
1621 return EVENT_ERROR;
1622 }
1623
1624 free_token(token);
1625 type = read_token_item(&token);
1626
1627 /*
1628 * If the next token is an item or another open paren, then
1629 * this was a typecast.
1630 */
1631 if (event_item_type(type) ||
1632 (type == EVENT_DELIM && strcmp(token, "(") == 0)) {
1633
1634 /* make this a typecast and contine */
1635
1636 /* prevous must be an atom */
1637 if (arg->type != PRINT_ATOM)
1638 die("previous needed to be PRINT_ATOM");
1639
1640 item_arg = malloc_or_die(sizeof(*item_arg));
1641
1642 arg->type = PRINT_TYPE;
1643 arg->typecast.type = arg->atom.atom;
1644 arg->typecast.item = item_arg;
1645 type = process_arg_token(event, item_arg, &token, type);
1646
1647 }
1648
1649 *tok = token;
1650 return type;
1651}
1652
1653
1654static enum event_type
1655process_str(struct event *event __unused, struct print_arg *arg, char **tok)
1656{
1657 enum event_type type;
1658 char *token;
1659
1660 if (read_expected(EVENT_DELIM, "(") < 0)
1661 return EVENT_ERROR;
1662
1663 if (read_expect_type(EVENT_ITEM, &token) < 0)
1664 goto fail;
1665
1666 arg->type = PRINT_STRING;
1667 arg->string.string = token;
1668 arg->string.offset = -1;
1669
1670 if (read_expected(EVENT_DELIM, ")") < 0)
1671 return EVENT_ERROR;
1672
1673 type = read_token(&token);
1674 *tok = token;
1675
1676 return type;
1677fail:
1678 free_token(token);
1679 return EVENT_ERROR;
1680}
1681
1682enum event_type
1683process_arg_token(struct event *event, struct print_arg *arg,
1684 char **tok, enum event_type type)
1685{
1686 char *token;
1687 char *atom;
1688
1689 token = *tok;
1690
1691 switch (type) {
1692 case EVENT_ITEM:
1693 if (strcmp(token, "REC") == 0) {
1694 free_token(token);
1695 type = process_entry(event, arg, &token);
1696 } else if (strcmp(token, "__print_flags") == 0) {
1697 free_token(token);
1698 is_flag_field = 1;
1699 type = process_flags(event, arg, &token);
1700 } else if (strcmp(token, "__print_symbolic") == 0) {
1701 free_token(token);
1702 is_symbolic_field = 1;
1703 type = process_symbols(event, arg, &token);
1704 } else if (strcmp(token, "__get_str") == 0) {
1705 free_token(token);
1706 type = process_str(event, arg, &token);
1707 } else {
1708 atom = token;
1709 /* test the next token */
1710 type = read_token_item(&token);
1711
1712 /* atoms can be more than one token long */
1713 while (type == EVENT_ITEM) {
1714 atom = realloc(atom, strlen(atom) + strlen(token) + 2);
1715 strcat(atom, " ");
1716 strcat(atom, token);
1717 free_token(token);
1718 type = read_token_item(&token);
1719 }
1720
1721 /* todo, test for function */
1722
1723 arg->type = PRINT_ATOM;
1724 arg->atom.atom = atom;
1725 }
1726 break;
1727 case EVENT_DQUOTE:
1728 case EVENT_SQUOTE:
1729 arg->type = PRINT_ATOM;
1730 arg->atom.atom = token;
1731 type = read_token_item(&token);
1732 break;
1733 case EVENT_DELIM:
1734 if (strcmp(token, "(") == 0) {
1735 free_token(token);
1736 type = process_paren(event, arg, &token);
1737 break;
1738 }
1739 case EVENT_OP:
1740 /* handle single ops */
1741 arg->type = PRINT_OP;
1742 arg->op.op = token;
1743 arg->op.left = NULL;
1744 type = process_op(event, arg, &token);
1745
1746 break;
1747
1748 case EVENT_ERROR ... EVENT_NEWLINE:
1749 default:
1750 die("unexpected type %d", type);
1751 }
1752 *tok = token;
1753
1754 return type;
1755}
1756
1757static int event_read_print_args(struct event *event, struct print_arg **list)
1758{
1759 enum event_type type = EVENT_ERROR;
1760 struct print_arg *arg;
1761 char *token;
1762 int args = 0;
1763
1764 do {
1765 if (type == EVENT_NEWLINE) {
1766 free_token(token);
1767 type = read_token_item(&token);
1768 continue;
1769 }
1770
1771 arg = malloc_or_die(sizeof(*arg));
1772 memset(arg, 0, sizeof(*arg));
1773
1774 type = process_arg(event, arg, &token);
1775
1776 if (type == EVENT_ERROR) {
1777 free_arg(arg);
1778 return -1;
1779 }
1780
1781 *list = arg;
1782 args++;
1783
1784 if (type == EVENT_OP) {
1785 type = process_op(event, arg, &token);
1786 list = &arg->next;
1787 continue;
1788 }
1789
1790 if (type == EVENT_DELIM && strcmp(token, ",") == 0) {
1791 free_token(token);
1792 *list = arg;
1793 list = &arg->next;
1794 continue;
1795 }
1796 break;
1797 } while (type != EVENT_NONE);
1798
1799 if (type != EVENT_NONE)
1800 free_token(token);
1801
1802 return args;
1803}
1804
1805static int event_read_print(struct event *event)
1806{
1807 enum event_type type;
1808 char *token;
1809 int ret;
1810
1811 if (read_expected_item(EVENT_ITEM, "print") < 0)
1812 return -1;
1813
1814 if (read_expected(EVENT_ITEM, "fmt") < 0)
1815 return -1;
1816
1817 if (read_expected(EVENT_OP, ":") < 0)
1818 return -1;
1819
1820 if (read_expect_type(EVENT_DQUOTE, &token) < 0)
1821 goto fail;
1822
1823 concat:
1824 event->print_fmt.format = token;
1825 event->print_fmt.args = NULL;
1826
1827 /* ok to have no arg */
1828 type = read_token_item(&token);
1829
1830 if (type == EVENT_NONE)
1831 return 0;
1832
1833 /* Handle concatination of print lines */
1834 if (type == EVENT_DQUOTE) {
1835 char *cat;
1836
1837 cat = malloc_or_die(strlen(event->print_fmt.format) +
1838 strlen(token) + 1);
1839 strcpy(cat, event->print_fmt.format);
1840 strcat(cat, token);
1841 free_token(token);
1842 free_token(event->print_fmt.format);
1843 event->print_fmt.format = NULL;
1844 token = cat;
1845 goto concat;
1846 }
1847
1848 if (test_type_token(type, token, EVENT_DELIM, ","))
1849 goto fail;
1850
1851 free_token(token);
1852
1853 ret = event_read_print_args(event, &event->print_fmt.args);
1854 if (ret < 0)
1855 return -1;
1856
1857 return ret;
1858
1859 fail:
1860 free_token(token);
1861 return -1;
1862}
1863
1864static struct format_field *
1865find_common_field(struct event *event, const char *name)
1866{
1867 struct format_field *format;
1868
1869 for (format = event->format.common_fields;
1870 format; format = format->next) {
1871 if (strcmp(format->name, name) == 0)
1872 break;
1873 }
1874
1875 return format;
1876}
1877
1878static struct format_field *
1879find_field(struct event *event, const char *name)
1880{
1881 struct format_field *format;
1882
1883 for (format = event->format.fields;
1884 format; format = format->next) {
1885 if (strcmp(format->name, name) == 0)
1886 break;
1887 }
1888
1889 return format;
1890}
1891
1892static struct format_field *
1893find_any_field(struct event *event, const char *name)
1894{
1895 struct format_field *format;
1896
1897 format = find_common_field(event, name);
1898 if (format)
1899 return format;
1900 return find_field(event, name);
1901}
1902
1903unsigned long long read_size(void *ptr, int size)
1904{
1905 switch (size) {
1906 case 1:
1907 return *(unsigned char *)ptr;
1908 case 2:
1909 return data2host2(ptr);
1910 case 4:
1911 return data2host4(ptr);
1912 case 8:
1913 return data2host8(ptr);
1914 default:
1915 /* BUG! */
1916 return 0;
1917 }
1918}
1919
1920unsigned long long
1921raw_field_value(struct event *event, const char *name, void *data)
1922{
1923 struct format_field *field;
1924
1925 field = find_any_field(event, name);
1926 if (!field)
1927 return 0ULL;
1928
1929 return read_size(data + field->offset, field->size);
1930}
1931
1932void *raw_field_ptr(struct event *event, const char *name, void *data)
1933{
1934 struct format_field *field;
1935
1936 field = find_any_field(event, name);
1937 if (!field)
1938 return NULL;
1939
1940 if (field->flags & FIELD_IS_DYNAMIC) {
1941 int offset;
1942
1943 offset = *(int *)(data + field->offset);
1944 offset &= 0xffff;
1945
1946 return data + offset;
1947 }
1948
1949 return data + field->offset;
1950}
1951
1952static int get_common_info(const char *type, int *offset, int *size)
1953{
1954 struct event *event;
1955 struct format_field *field;
1956
1957 /*
1958 * All events should have the same common elements.
1959 * Pick any event to find where the type is;
1960 */
1961 if (!event_list)
1962 die("no event_list!");
1963
1964 event = event_list;
1965 field = find_common_field(event, type);
1966 if (!field)
1967 die("field '%s' not found", type);
1968
1969 *offset = field->offset;
1970 *size = field->size;
1971
1972 return 0;
1973}
1974
1975static int __parse_common(void *data, int *size, int *offset,
1976 const char *name)
1977{
1978 int ret;
1979
1980 if (!*size) {
1981 ret = get_common_info(name, offset, size);
1982 if (ret < 0)
1983 return ret;
1984 }
1985 return read_size(data + *offset, *size);
1986}
1987
1988int trace_parse_common_type(void *data)
1989{
1990 static int type_offset;
1991 static int type_size;
1992
1993 return __parse_common(data, &type_size, &type_offset,
1994 "common_type");
1995}
1996
1997int trace_parse_common_pid(void *data)
1998{
1999 static int pid_offset;
2000 static int pid_size;
2001
2002 return __parse_common(data, &pid_size, &pid_offset,
2003 "common_pid");
2004}
2005
2006int parse_common_pc(void *data)
2007{
2008 static int pc_offset;
2009 static int pc_size;
2010
2011 return __parse_common(data, &pc_size, &pc_offset,
2012 "common_preempt_count");
2013}
2014
2015int parse_common_flags(void *data)
2016{
2017 static int flags_offset;
2018 static int flags_size;
2019
2020 return __parse_common(data, &flags_size, &flags_offset,
2021 "common_flags");
2022}
2023
2024int parse_common_lock_depth(void *data)
2025{
2026 static int ld_offset;
2027 static int ld_size;
2028 int ret;
2029
2030 ret = __parse_common(data, &ld_size, &ld_offset,
2031 "common_lock_depth");
2032 if (ret < 0)
2033 return -1;
2034
2035 return ret;
2036}
2037
2038struct event *trace_find_event(int id)
2039{
2040 struct event *event;
2041
2042 for (event = event_list; event; event = event->next) {
2043 if (event->id == id)
2044 break;
2045 }
2046 return event;
2047}
2048
2049struct event *trace_find_next_event(struct event *event)
2050{
2051 if (!event)
2052 return event_list;
2053
2054 return event->next;
2055}
2056
2057static unsigned long long eval_num_arg(void *data, int size,
2058 struct event *event, struct print_arg *arg)
2059{
2060 unsigned long long val = 0;
2061 unsigned long long left, right;
2062 struct print_arg *larg;
2063
2064 switch (arg->type) {
2065 case PRINT_NULL:
2066 /* ?? */
2067 return 0;
2068 case PRINT_ATOM:
2069 return strtoull(arg->atom.atom, NULL, 0);
2070 case PRINT_FIELD:
2071 if (!arg->field.field) {
2072 arg->field.field = find_any_field(event, arg->field.name);
2073 if (!arg->field.field)
2074 die("field %s not found", arg->field.name);
2075 }
2076 /* must be a number */
2077 val = read_size(data + arg->field.field->offset,
2078 arg->field.field->size);
2079 break;
2080 case PRINT_FLAGS:
2081 case PRINT_SYMBOL:
2082 break;
2083 case PRINT_TYPE:
2084 return eval_num_arg(data, size, event, arg->typecast.item);
2085 case PRINT_STRING:
2086 return 0;
2087 break;
2088 case PRINT_OP:
2089 if (strcmp(arg->op.op, "[") == 0) {
2090 /*
2091 * Arrays are special, since we don't want
2092 * to read the arg as is.
2093 */
2094 if (arg->op.left->type != PRINT_FIELD)
2095 goto default_op; /* oops, all bets off */
2096 larg = arg->op.left;
2097 if (!larg->field.field) {
2098 larg->field.field =
2099 find_any_field(event, larg->field.name);
2100 if (!larg->field.field)
2101 die("field %s not found", larg->field.name);
2102 }
2103 right = eval_num_arg(data, size, event, arg->op.right);
2104 val = read_size(data + larg->field.field->offset +
2105 right * long_size, long_size);
2106 break;
2107 }
2108 default_op:
2109 left = eval_num_arg(data, size, event, arg->op.left);
2110 right = eval_num_arg(data, size, event, arg->op.right);
2111 switch (arg->op.op[0]) {
2112 case '|':
2113 if (arg->op.op[1])
2114 val = left || right;
2115 else
2116 val = left | right;
2117 break;
2118 case '&':
2119 if (arg->op.op[1])
2120 val = left && right;
2121 else
2122 val = left & right;
2123 break;
2124 case '<':
2125 switch (arg->op.op[1]) {
2126 case 0:
2127 val = left < right;
2128 break;
2129 case '<':
2130 val = left << right;
2131 break;
2132 case '=':
2133 val = left <= right;
2134 break;
2135 default:
2136 die("unknown op '%s'", arg->op.op);
2137 }
2138 break;
2139 case '>':
2140 switch (arg->op.op[1]) {
2141 case 0:
2142 val = left > right;
2143 break;
2144 case '>':
2145 val = left >> right;
2146 break;
2147 case '=':
2148 val = left >= right;
2149 break;
2150 default:
2151 die("unknown op '%s'", arg->op.op);
2152 }
2153 break;
2154 case '=':
2155 if (arg->op.op[1] != '=')
2156 die("unknown op '%s'", arg->op.op);
2157 val = left == right;
2158 break;
2159 case '-':
2160 val = left - right;
2161 break;
2162 case '+':
2163 val = left + right;
2164 break;
2165 default:
2166 die("unknown op '%s'", arg->op.op);
2167 }
2168 break;
2169 default: /* not sure what to do there */
2170 return 0;
2171 }
2172 return val;
2173}
2174
2175struct flag {
2176 const char *name;
2177 unsigned long long value;
2178};
2179
2180static const struct flag flags[] = {
2181 { "HI_SOFTIRQ", 0 },
2182 { "TIMER_SOFTIRQ", 1 },
2183 { "NET_TX_SOFTIRQ", 2 },
2184 { "NET_RX_SOFTIRQ", 3 },
2185 { "BLOCK_SOFTIRQ", 4 },
2186 { "BLOCK_IOPOLL_SOFTIRQ", 5 },
2187 { "TASKLET_SOFTIRQ", 6 },
2188 { "SCHED_SOFTIRQ", 7 },
2189 { "HRTIMER_SOFTIRQ", 8 },
2190 { "RCU_SOFTIRQ", 9 },
2191
2192 { "HRTIMER_NORESTART", 0 },
2193 { "HRTIMER_RESTART", 1 },
2194};
2195
2196unsigned long long eval_flag(const char *flag)
2197{
2198 int i;
2199
2200 /*
2201 * Some flags in the format files do not get converted.
2202 * If the flag is not numeric, see if it is something that
2203 * we already know about.
2204 */
2205 if (isdigit(flag[0]))
2206 return strtoull(flag, NULL, 0);
2207
2208 for (i = 0; i < (int)(sizeof(flags)/sizeof(flags[0])); i++)
2209 if (strcmp(flags[i].name, flag) == 0)
2210 return flags[i].value;
2211
2212 return 0;
2213}
2214
2215static void print_str_arg(void *data, int size,
2216 struct event *event, struct print_arg *arg)
2217{
2218 struct print_flag_sym *flag;
2219 unsigned long long val, fval;
2220 char *str;
2221 int print;
2222
2223 switch (arg->type) {
2224 case PRINT_NULL:
2225 /* ?? */
2226 return;
2227 case PRINT_ATOM:
2228 printf("%s", arg->atom.atom);
2229 return;
2230 case PRINT_FIELD:
2231 if (!arg->field.field) {
2232 arg->field.field = find_any_field(event, arg->field.name);
2233 if (!arg->field.field)
2234 die("field %s not found", arg->field.name);
2235 }
2236 str = malloc_or_die(arg->field.field->size + 1);
2237 memcpy(str, data + arg->field.field->offset,
2238 arg->field.field->size);
2239 str[arg->field.field->size] = 0;
2240 printf("%s", str);
2241 free(str);
2242 break;
2243 case PRINT_FLAGS:
2244 val = eval_num_arg(data, size, event, arg->flags.field);
2245 print = 0;
2246 for (flag = arg->flags.flags; flag; flag = flag->next) {
2247 fval = eval_flag(flag->value);
2248 if (!val && !fval) {
2249 printf("%s", flag->str);
2250 break;
2251 }
2252 if (fval && (val & fval) == fval) {
2253 if (print && arg->flags.delim)
2254 printf("%s", arg->flags.delim);
2255 printf("%s", flag->str);
2256 print = 1;
2257 val &= ~fval;
2258 }
2259 }
2260 break;
2261 case PRINT_SYMBOL:
2262 val = eval_num_arg(data, size, event, arg->symbol.field);
2263 for (flag = arg->symbol.symbols; flag; flag = flag->next) {
2264 fval = eval_flag(flag->value);
2265 if (val == fval) {
2266 printf("%s", flag->str);
2267 break;
2268 }
2269 }
2270 break;
2271
2272 case PRINT_TYPE:
2273 break;
2274 case PRINT_STRING: {
2275 int str_offset;
2276
2277 if (arg->string.offset == -1) {
2278 struct format_field *f;
2279
2280 f = find_any_field(event, arg->string.string);
2281 arg->string.offset = f->offset;
2282 }
2283 str_offset = *(int *)(data + arg->string.offset);
2284 str_offset &= 0xffff;
2285 printf("%s", ((char *)data) + str_offset);
2286 break;
2287 }
2288 case PRINT_OP:
2289 /*
2290 * The only op for string should be ? :
2291 */
2292 if (arg->op.op[0] != '?')
2293 return;
2294 val = eval_num_arg(data, size, event, arg->op.left);
2295 if (val)
2296 print_str_arg(data, size, event, arg->op.right->op.left);
2297 else
2298 print_str_arg(data, size, event, arg->op.right->op.right);
2299 break;
2300 default:
2301 /* well... */
2302 break;
2303 }
2304}
2305
2306static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struct event *event)
2307{
2308 static struct format_field *field, *ip_field;
2309 struct print_arg *args, *arg, **next;
2310 unsigned long long ip, val;
2311 char *ptr;
2312 void *bptr;
2313
2314 if (!field) {
2315 field = find_field(event, "buf");
2316 if (!field)
2317 die("can't find buffer field for binary printk");
2318 ip_field = find_field(event, "ip");
2319 if (!ip_field)
2320 die("can't find ip field for binary printk");
2321 }
2322
2323 ip = read_size(data + ip_field->offset, ip_field->size);
2324
2325 /*
2326 * The first arg is the IP pointer.
2327 */
2328 args = malloc_or_die(sizeof(*args));
2329 arg = args;
2330 arg->next = NULL;
2331 next = &arg->next;
2332
2333 arg->type = PRINT_ATOM;
2334 arg->atom.atom = malloc_or_die(32);
2335 sprintf(arg->atom.atom, "%lld", ip);
2336
2337 /* skip the first "%pf : " */
2338 for (ptr = fmt + 6, bptr = data + field->offset;
2339 bptr < data + size && *ptr; ptr++) {
2340 int ls = 0;
2341
2342 if (*ptr == '%') {
2343 process_again:
2344 ptr++;
2345 switch (*ptr) {
2346 case '%':
2347 break;
2348 case 'l':
2349 ls++;
2350 goto process_again;
2351 case 'L':
2352 ls = 2;
2353 goto process_again;
2354 case '0' ... '9':
2355 goto process_again;
2356 case 'p':
2357 ls = 1;
2358 /* fall through */
2359 case 'd':
2360 case 'u':
2361 case 'x':
2362 case 'i':
2363 /* the pointers are always 4 bytes aligned */
2364 bptr = (void *)(((unsigned long)bptr + 3) &
2365 ~3);
2366 switch (ls) {
2367 case 0:
2368 case 1:
2369 ls = long_size;
2370 break;
2371 case 2:
2372 ls = 8;
2373 default:
2374 break;
2375 }
2376 val = read_size(bptr, ls);
2377 bptr += ls;
2378 arg = malloc_or_die(sizeof(*arg));
2379 arg->next = NULL;
2380 arg->type = PRINT_ATOM;
2381 arg->atom.atom = malloc_or_die(32);
2382 sprintf(arg->atom.atom, "%lld", val);
2383 *next = arg;
2384 next = &arg->next;
2385 break;
2386 case 's':
2387 arg = malloc_or_die(sizeof(*arg));
2388 arg->next = NULL;
2389 arg->type = PRINT_STRING;
2390 arg->string.string = strdup(bptr);
2391 bptr += strlen(bptr) + 1;
2392 *next = arg;
2393 next = &arg->next;
2394 default:
2395 break;
2396 }
2397 }
2398 }
2399
2400 return args;
2401}
2402
2403static void free_args(struct print_arg *args)
2404{
2405 struct print_arg *next;
2406
2407 while (args) {
2408 next = args->next;
2409
2410 if (args->type == PRINT_ATOM)
2411 free(args->atom.atom);
2412 else
2413 free(args->string.string);
2414 free(args);
2415 args = next;
2416 }
2417}
2418
2419static char *get_bprint_format(void *data, int size __unused, struct event *event)
2420{
2421 unsigned long long addr;
2422 static struct format_field *field;
2423 struct printk_map *printk;
2424 char *format;
2425 char *p;
2426
2427 if (!field) {
2428 field = find_field(event, "fmt");
2429 if (!field)
2430 die("can't find format field for binary printk");
2431 printf("field->offset = %d size=%d\n", field->offset, field->size);
2432 }
2433
2434 addr = read_size(data + field->offset, field->size);
2435
2436 printk = find_printk(addr);
2437 if (!printk) {
2438 format = malloc_or_die(45);
2439 sprintf(format, "%%pf : (NO FORMAT FOUND at %llx)\n",
2440 addr);
2441 return format;
2442 }
2443
2444 p = printk->printk;
2445 /* Remove any quotes. */
2446 if (*p == '"')
2447 p++;
2448 format = malloc_or_die(strlen(p) + 10);
2449 sprintf(format, "%s : %s", "%pf", p);
2450 /* remove ending quotes and new line since we will add one too */
2451 p = format + strlen(format) - 1;
2452 if (*p == '"')
2453 *p = 0;
2454
2455 p -= 2;
2456 if (strcmp(p, "\\n") == 0)
2457 *p = 0;
2458
2459 return format;
2460}
2461
2462static void pretty_print(void *data, int size, struct event *event)
2463{
2464 struct print_fmt *print_fmt = &event->print_fmt;
2465 struct print_arg *arg = print_fmt->args;
2466 struct print_arg *args = NULL;
2467 const char *ptr = print_fmt->format;
2468 unsigned long long val;
2469 struct func_map *func;
2470 const char *saveptr;
2471 char *bprint_fmt = NULL;
2472 char format[32];
2473 int show_func;
2474 int len;
2475 int ls;
2476
2477 if (event->flags & EVENT_FL_ISFUNC)
2478 ptr = " %pF <-- %pF";
2479
2480 if (event->flags & EVENT_FL_ISBPRINT) {
2481 bprint_fmt = get_bprint_format(data, size, event);
2482 args = make_bprint_args(bprint_fmt, data, size, event);
2483 arg = args;
2484 ptr = bprint_fmt;
2485 }
2486
2487 for (; *ptr; ptr++) {
2488 ls = 0;
2489 if (*ptr == '\\') {
2490 ptr++;
2491 switch (*ptr) {
2492 case 'n':
2493 printf("\n");
2494 break;
2495 case 't':
2496 printf("\t");
2497 break;
2498 case 'r':
2499 printf("\r");
2500 break;
2501 case '\\':
2502 printf("\\");
2503 break;
2504 default:
2505 printf("%c", *ptr);
2506 break;
2507 }
2508
2509 } else if (*ptr == '%') {
2510 saveptr = ptr;
2511 show_func = 0;
2512 cont_process:
2513 ptr++;
2514 switch (*ptr) {
2515 case '%':
2516 printf("%%");
2517 break;
2518 case 'l':
2519 ls++;
2520 goto cont_process;
2521 case 'L':
2522 ls = 2;
2523 goto cont_process;
2524 case 'z':
2525 case 'Z':
2526 case '0' ... '9':
2527 goto cont_process;
2528 case 'p':
2529 if (long_size == 4)
2530 ls = 1;
2531 else
2532 ls = 2;
2533
2534 if (*(ptr+1) == 'F' ||
2535 *(ptr+1) == 'f') {
2536 ptr++;
2537 show_func = *ptr;
2538 }
2539
2540 /* fall through */
2541 case 'd':
2542 case 'i':
2543 case 'x':
2544 case 'X':
2545 case 'u':
2546 if (!arg)
2547 die("no argument match");
2548
2549 len = ((unsigned long)ptr + 1) -
2550 (unsigned long)saveptr;
2551
2552 /* should never happen */
2553 if (len > 32)
2554 die("bad format!");
2555
2556 memcpy(format, saveptr, len);
2557 format[len] = 0;
2558
2559 val = eval_num_arg(data, size, event, arg);
2560 arg = arg->next;
2561
2562 if (show_func) {
2563 func = find_func(val);
2564 if (func) {
2565 printf("%s", func->func);
2566 if (show_func == 'F')
2567 printf("+0x%llx",
2568 val - func->addr);
2569 break;
2570 }
2571 }
2572 switch (ls) {
2573 case 0:
2574 printf(format, (int)val);
2575 break;
2576 case 1:
2577 printf(format, (long)val);
2578 break;
2579 case 2:
2580 printf(format, (long long)val);
2581 break;
2582 default:
2583 die("bad count (%d)", ls);
2584 }
2585 break;
2586 case 's':
2587 if (!arg)
2588 die("no matching argument");
2589
2590 print_str_arg(data, size, event, arg);
2591 arg = arg->next;
2592 break;
2593 default:
2594 printf(">%c<", *ptr);
2595
2596 }
2597 } else
2598 printf("%c", *ptr);
2599 }
2600
2601 if (args) {
2602 free_args(args);
2603 free(bprint_fmt);
2604 }
2605}
2606
2607static inline int log10_cpu(int nb)
2608{
2609 if (nb / 100)
2610 return 3;
2611 if (nb / 10)
2612 return 2;
2613 return 1;
2614}
2615
2616static void print_lat_fmt(void *data, int size __unused)
2617{
2618 unsigned int lat_flags;
2619 unsigned int pc;
2620 int lock_depth;
2621 int hardirq;
2622 int softirq;
2623
2624 lat_flags = parse_common_flags(data);
2625 pc = parse_common_pc(data);
2626 lock_depth = parse_common_lock_depth(data);
2627
2628 hardirq = lat_flags & TRACE_FLAG_HARDIRQ;
2629 softirq = lat_flags & TRACE_FLAG_SOFTIRQ;
2630
2631 printf("%c%c%c",
2632 (lat_flags & TRACE_FLAG_IRQS_OFF) ? 'd' :
2633 (lat_flags & TRACE_FLAG_IRQS_NOSUPPORT) ?
2634 'X' : '.',
2635 (lat_flags & TRACE_FLAG_NEED_RESCHED) ?
2636 'N' : '.',
2637 (hardirq && softirq) ? 'H' :
2638 hardirq ? 'h' : softirq ? 's' : '.');
2639
2640 if (pc)
2641 printf("%x", pc);
2642 else
2643 printf(".");
2644
2645 if (lock_depth < 0)
2646 printf(".");
2647 else
2648 printf("%d", lock_depth);
2649}
2650
2651/* taken from Linux, written by Frederic Weisbecker */
2652static void print_graph_cpu(int cpu)
2653{
2654 int i;
2655 int log10_this = log10_cpu(cpu);
2656 int log10_all = log10_cpu(cpus);
2657
2658
2659 /*
2660 * Start with a space character - to make it stand out
2661 * to the right a bit when trace output is pasted into
2662 * email:
2663 */
2664 printf(" ");
2665
2666 /*
2667 * Tricky - we space the CPU field according to the max
2668 * number of online CPUs. On a 2-cpu system it would take
2669 * a maximum of 1 digit - on a 128 cpu system it would
2670 * take up to 3 digits:
2671 */
2672 for (i = 0; i < log10_all - log10_this; i++)
2673 printf(" ");
2674
2675 printf("%d) ", cpu);
2676}
2677
2678#define TRACE_GRAPH_PROCINFO_LENGTH 14
2679#define TRACE_GRAPH_INDENT 2
2680
2681static void print_graph_proc(int pid, const char *comm)
2682{
2683 /* sign + log10(MAX_INT) + '\0' */
2684 char pid_str[11];
2685 int spaces = 0;
2686 int len;
2687 int i;
2688
2689 sprintf(pid_str, "%d", pid);
2690
2691 /* 1 stands for the "-" character */
2692 len = strlen(comm) + strlen(pid_str) + 1;
2693
2694 if (len < TRACE_GRAPH_PROCINFO_LENGTH)
2695 spaces = TRACE_GRAPH_PROCINFO_LENGTH - len;
2696
2697 /* First spaces to align center */
2698 for (i = 0; i < spaces / 2; i++)
2699 printf(" ");
2700
2701 printf("%s-%s", comm, pid_str);
2702
2703 /* Last spaces to align center */
2704 for (i = 0; i < spaces - (spaces / 2); i++)
2705 printf(" ");
2706}
2707
2708static struct record *
2709get_return_for_leaf(int cpu, int cur_pid, unsigned long long cur_func,
2710 struct record *next)
2711{
2712 struct format_field *field;
2713 struct event *event;
2714 unsigned long val;
2715 int type;
2716 int pid;
2717
2718 type = trace_parse_common_type(next->data);
2719 event = trace_find_event(type);
2720 if (!event)
2721 return NULL;
2722
2723 if (!(event->flags & EVENT_FL_ISFUNCRET))
2724 return NULL;
2725
2726 pid = trace_parse_common_pid(next->data);
2727 field = find_field(event, "func");
2728 if (!field)
2729 die("function return does not have field func");
2730
2731 val = read_size(next->data + field->offset, field->size);
2732
2733 if (cur_pid != pid || cur_func != val)
2734 return NULL;
2735
2736 /* this is a leaf, now advance the iterator */
2737 return trace_read_data(cpu);
2738}
2739
2740/* Signal a overhead of time execution to the output */
2741static void print_graph_overhead(unsigned long long duration)
2742{
2743 /* Non nested entry or return */
2744 if (duration == ~0ULL)
2745 return (void)printf(" ");
2746
2747 /* Duration exceeded 100 msecs */
2748 if (duration > 100000ULL)
2749 return (void)printf("! ");
2750
2751 /* Duration exceeded 10 msecs */
2752 if (duration > 10000ULL)
2753 return (void)printf("+ ");
2754
2755 printf(" ");
2756}
2757
2758static void print_graph_duration(unsigned long long duration)
2759{
2760 unsigned long usecs = duration / 1000;
2761 unsigned long nsecs_rem = duration % 1000;
2762 /* log10(ULONG_MAX) + '\0' */
2763 char msecs_str[21];
2764 char nsecs_str[5];
2765 int len;
2766 int i;
2767
2768 sprintf(msecs_str, "%lu", usecs);
2769
2770 /* Print msecs */
2771 len = printf("%lu", usecs);
2772
2773 /* Print nsecs (we don't want to exceed 7 numbers) */
2774 if (len < 7) {
2775 snprintf(nsecs_str, 8 - len, "%03lu", nsecs_rem);
2776 len += printf(".%s", nsecs_str);
2777 }
2778
2779 printf(" us ");
2780
2781 /* Print remaining spaces to fit the row's width */
2782 for (i = len; i < 7; i++)
2783 printf(" ");
2784
2785 printf("| ");
2786}
2787
2788static void
2789print_graph_entry_leaf(struct event *event, void *data, struct record *ret_rec)
2790{
2791 unsigned long long rettime, calltime;
2792 unsigned long long duration, depth;
2793 unsigned long long val;
2794 struct format_field *field;
2795 struct func_map *func;
2796 struct event *ret_event;
2797 int type;
2798 int i;
2799
2800 type = trace_parse_common_type(ret_rec->data);
2801 ret_event = trace_find_event(type);
2802
2803 field = find_field(ret_event, "rettime");
2804 if (!field)
2805 die("can't find rettime in return graph");
2806 rettime = read_size(ret_rec->data + field->offset, field->size);
2807
2808 field = find_field(ret_event, "calltime");
2809 if (!field)
2810 die("can't find rettime in return graph");
2811 calltime = read_size(ret_rec->data + field->offset, field->size);
2812
2813 duration = rettime - calltime;
2814
2815 /* Overhead */
2816 print_graph_overhead(duration);
2817
2818 /* Duration */
2819 print_graph_duration(duration);
2820
2821 field = find_field(event, "depth");
2822 if (!field)
2823 die("can't find depth in entry graph");
2824 depth = read_size(data + field->offset, field->size);
2825
2826 /* Function */
2827 for (i = 0; i < (int)(depth * TRACE_GRAPH_INDENT); i++)
2828 printf(" ");
2829
2830 field = find_field(event, "func");
2831 if (!field)
2832 die("can't find func in entry graph");
2833 val = read_size(data + field->offset, field->size);
2834 func = find_func(val);
2835
2836 if (func)
2837 printf("%s();", func->func);
2838 else
2839 printf("%llx();", val);
2840}
2841
2842static void print_graph_nested(struct event *event, void *data)
2843{
2844 struct format_field *field;
2845 unsigned long long depth;
2846 unsigned long long val;
2847 struct func_map *func;
2848 int i;
2849
2850 /* No overhead */
2851 print_graph_overhead(-1);
2852
2853 /* No time */
2854 printf(" | ");
2855
2856 field = find_field(event, "depth");
2857 if (!field)
2858 die("can't find depth in entry graph");
2859 depth = read_size(data + field->offset, field->size);
2860
2861 /* Function */
2862 for (i = 0; i < (int)(depth * TRACE_GRAPH_INDENT); i++)
2863 printf(" ");
2864
2865 field = find_field(event, "func");
2866 if (!field)
2867 die("can't find func in entry graph");
2868 val = read_size(data + field->offset, field->size);
2869 func = find_func(val);
2870
2871 if (func)
2872 printf("%s() {", func->func);
2873 else
2874 printf("%llx() {", val);
2875}
2876
2877static void
2878pretty_print_func_ent(void *data, int size, struct event *event,
2879 int cpu, int pid, const char *comm,
2880 unsigned long secs, unsigned long usecs)
2881{
2882 struct format_field *field;
2883 struct record *rec;
2884 void *copy_data;
2885 unsigned long val;
2886
2887 printf("%5lu.%06lu | ", secs, usecs);
2888
2889 print_graph_cpu(cpu);
2890 print_graph_proc(pid, comm);
2891
2892 printf(" | ");
2893
2894 if (latency_format) {
2895 print_lat_fmt(data, size);
2896 printf(" | ");
2897 }
2898
2899 field = find_field(event, "func");
2900 if (!field)
2901 die("function entry does not have func field");
2902
2903 val = read_size(data + field->offset, field->size);
2904
2905 /*
2906 * peek_data may unmap the data pointer. Copy it first.
2907 */
2908 copy_data = malloc_or_die(size);
2909 memcpy(copy_data, data, size);
2910 data = copy_data;
2911
2912 rec = trace_peek_data(cpu);
2913 if (rec) {
2914 rec = get_return_for_leaf(cpu, pid, val, rec);
2915 if (rec) {
2916 print_graph_entry_leaf(event, data, rec);
2917 goto out_free;
2918 }
2919 }
2920 print_graph_nested(event, data);
2921out_free:
2922 free(data);
2923}
2924
2925static void
2926pretty_print_func_ret(void *data, int size __unused, struct event *event,
2927 int cpu, int pid, const char *comm,
2928 unsigned long secs, unsigned long usecs)
2929{
2930 unsigned long long rettime, calltime;
2931 unsigned long long duration, depth;
2932 struct format_field *field;
2933 int i;
2934
2935 printf("%5lu.%06lu | ", secs, usecs);
2936
2937 print_graph_cpu(cpu);
2938 print_graph_proc(pid, comm);
2939
2940 printf(" | ");
2941
2942 if (latency_format) {
2943 print_lat_fmt(data, size);
2944 printf(" | ");
2945 }
2946
2947 field = find_field(event, "rettime");
2948 if (!field)
2949 die("can't find rettime in return graph");
2950 rettime = read_size(data + field->offset, field->size);
2951
2952 field = find_field(event, "calltime");
2953 if (!field)
2954 die("can't find calltime in return graph");
2955 calltime = read_size(data + field->offset, field->size);
2956
2957 duration = rettime - calltime;
2958
2959 /* Overhead */
2960 print_graph_overhead(duration);
2961
2962 /* Duration */
2963 print_graph_duration(duration);
2964
2965 field = find_field(event, "depth");
2966 if (!field)
2967 die("can't find depth in entry graph");
2968 depth = read_size(data + field->offset, field->size);
2969
2970 /* Function */
2971 for (i = 0; i < (int)(depth * TRACE_GRAPH_INDENT); i++)
2972 printf(" ");
2973
2974 printf("}");
2975}
2976
2977static void
2978pretty_print_func_graph(void *data, int size, struct event *event,
2979 int cpu, int pid, const char *comm,
2980 unsigned long secs, unsigned long usecs)
2981{
2982 if (event->flags & EVENT_FL_ISFUNCENT)
2983 pretty_print_func_ent(data, size, event,
2984 cpu, pid, comm, secs, usecs);
2985 else if (event->flags & EVENT_FL_ISFUNCRET)
2986 pretty_print_func_ret(data, size, event,
2987 cpu, pid, comm, secs, usecs);
2988 printf("\n");
2989}
2990
2991void print_event(int cpu, void *data, int size, unsigned long long nsecs,
2992 char *comm)
2993{
2994 struct event *event;
2995 unsigned long secs;
2996 unsigned long usecs;
2997 int type;
2998 int pid;
2999
3000 secs = nsecs / NSECS_PER_SEC;
3001 nsecs -= secs * NSECS_PER_SEC;
3002 usecs = nsecs / NSECS_PER_USEC;
3003
3004 type = trace_parse_common_type(data);
3005
3006 event = trace_find_event(type);
3007 if (!event) {
3008 warning("ug! no event found for type %d", type);
3009 return;
3010 }
3011
3012 pid = trace_parse_common_pid(data);
3013
3014 if (event->flags & (EVENT_FL_ISFUNCENT | EVENT_FL_ISFUNCRET))
3015 return pretty_print_func_graph(data, size, event, cpu,
3016 pid, comm, secs, usecs);
3017
3018 if (latency_format) {
3019 printf("%8.8s-%-5d %3d",
3020 comm, pid, cpu);
3021 print_lat_fmt(data, size);
3022 } else
3023 printf("%16s-%-5d [%03d]", comm, pid, cpu);
3024
3025 printf(" %5lu.%06lu: %s: ", secs, usecs, event->name);
3026
3027 if (event->flags & EVENT_FL_FAILED) {
3028 printf("EVENT '%s' FAILED TO PARSE\n",
3029 event->name);
3030 return;
3031 }
3032
3033 pretty_print(data, size, event);
3034 printf("\n");
3035}
3036
3037static void print_fields(struct print_flag_sym *field)
3038{
3039 printf("{ %s, %s }", field->value, field->str);
3040 if (field->next) {
3041 printf(", ");
3042 print_fields(field->next);
3043 }
3044}
3045
3046static void print_args(struct print_arg *args)
3047{
3048 int print_paren = 1;
3049
3050 switch (args->type) {
3051 case PRINT_NULL:
3052 printf("null");
3053 break;
3054 case PRINT_ATOM:
3055 printf("%s", args->atom.atom);
3056 break;
3057 case PRINT_FIELD:
3058 printf("REC->%s", args->field.name);
3059 break;
3060 case PRINT_FLAGS:
3061 printf("__print_flags(");
3062 print_args(args->flags.field);
3063 printf(", %s, ", args->flags.delim);
3064 print_fields(args->flags.flags);
3065 printf(")");
3066 break;
3067 case PRINT_SYMBOL:
3068 printf("__print_symbolic(");
3069 print_args(args->symbol.field);
3070 printf(", ");
3071 print_fields(args->symbol.symbols);
3072 printf(")");
3073 break;
3074 case PRINT_STRING:
3075 printf("__get_str(%s)", args->string.string);
3076 break;
3077 case PRINT_TYPE:
3078 printf("(%s)", args->typecast.type);
3079 print_args(args->typecast.item);
3080 break;
3081 case PRINT_OP:
3082 if (strcmp(args->op.op, ":") == 0)
3083 print_paren = 0;
3084 if (print_paren)
3085 printf("(");
3086 print_args(args->op.left);
3087 printf(" %s ", args->op.op);
3088 print_args(args->op.right);
3089 if (print_paren)
3090 printf(")");
3091 break;
3092 default:
3093 /* we should warn... */
3094 return;
3095 }
3096 if (args->next) {
3097 printf("\n");
3098 print_args(args->next);
3099 }
3100}
3101
3102int parse_ftrace_file(char *buf, unsigned long size)
3103{
3104 struct format_field *field;
3105 struct print_arg *arg, **list;
3106 struct event *event;
3107 int ret;
3108
3109 init_input_buf(buf, size);
3110
3111 event = alloc_event();
3112 if (!event)
3113 return -ENOMEM;
3114
3115 event->flags |= EVENT_FL_ISFTRACE;
3116
3117 event->name = event_read_name();
3118 if (!event->name)
3119 die("failed to read ftrace event name");
3120
3121 if (strcmp(event->name, "function") == 0)
3122 event->flags |= EVENT_FL_ISFUNC;
3123
3124 else if (strcmp(event->name, "funcgraph_entry") == 0)
3125 event->flags |= EVENT_FL_ISFUNCENT;
3126
3127 else if (strcmp(event->name, "funcgraph_exit") == 0)
3128 event->flags |= EVENT_FL_ISFUNCRET;
3129
3130 else if (strcmp(event->name, "bprint") == 0)
3131 event->flags |= EVENT_FL_ISBPRINT;
3132
3133 event->id = event_read_id();
3134 if (event->id < 0)
3135 die("failed to read ftrace event id");
3136
3137 add_event(event);
3138
3139 ret = event_read_format(event);
3140 if (ret < 0)
3141 die("failed to read ftrace event format");
3142
3143 ret = event_read_print(event);
3144 if (ret < 0)
3145 die("failed to read ftrace event print fmt");
3146
3147 /* New ftrace handles args */
3148 if (ret > 0)
3149 return 0;
3150 /*
3151 * The arguments for ftrace files are parsed by the fields.
3152 * Set up the fields as their arguments.
3153 */
3154 list = &event->print_fmt.args;
3155 for (field = event->format.fields; field; field = field->next) {
3156 arg = malloc_or_die(sizeof(*arg));
3157 memset(arg, 0, sizeof(*arg));
3158 *list = arg;
3159 list = &arg->next;
3160 arg->type = PRINT_FIELD;
3161 arg->field.name = field->name;
3162 arg->field.field = field;
3163 }
3164 return 0;
3165}
3166
3167int parse_event_file(char *buf, unsigned long size, char *sys)
3168{
3169 struct event *event;
3170 int ret;
3171
3172 init_input_buf(buf, size);
3173
3174 event = alloc_event();
3175 if (!event)
3176 return -ENOMEM;
3177
3178 event->name = event_read_name();
3179 if (!event->name)
3180 die("failed to read event name");
3181
3182 event->id = event_read_id();
3183 if (event->id < 0)
3184 die("failed to read event id");
3185
3186 ret = event_read_format(event);
3187 if (ret < 0) {
3188 warning("failed to read event format for %s", event->name);
3189 goto event_failed;
3190 }
3191
3192 ret = event_read_print(event);
3193 if (ret < 0) {
3194 warning("failed to read event print fmt for %s", event->name);
3195 goto event_failed;
3196 }
3197
3198 event->system = strdup(sys);
3199
3200#define PRINT_ARGS 0
3201 if (PRINT_ARGS && event->print_fmt.args)
3202 print_args(event->print_fmt.args);
3203
3204 add_event(event);
3205 return 0;
3206
3207 event_failed:
3208 event->flags |= EVENT_FL_FAILED;
3209 /* still add it even if it failed */
3210 add_event(event);
3211 return -1;
3212}
3213
3214void parse_set_info(int nr_cpus, int long_sz)
3215{
3216 cpus = nr_cpus;
3217 long_size = long_sz;
3218}
3219
3220int common_pc(struct scripting_context *context)
3221{
3222 return parse_common_pc(context->event_data);
3223}
3224
3225int common_flags(struct scripting_context *context)
3226{
3227 return parse_common_flags(context->event_data);
3228}
3229
3230int common_lock_depth(struct scripting_context *context)
3231{
3232 return parse_common_lock_depth(context->event_data);
3233}
diff --git a/tools/perf/util/trace-event-read.c b/tools/perf/util/trace-event-read.c
new file mode 100644
index 00000000000..f55cc3a765a
--- /dev/null
+++ b/tools/perf/util/trace-event-read.c
@@ -0,0 +1,539 @@
1/*
2 * Copyright (C) 2009, Steven Rostedt <srostedt@redhat.com>
3 *
4 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License (not later!)
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
20 */
21#define _FILE_OFFSET_BITS 64
22
23#include <dirent.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27#include <getopt.h>
28#include <stdarg.h>
29#include <sys/types.h>
30#include <sys/stat.h>
31#include <sys/wait.h>
32#include <sys/mman.h>
33#include <pthread.h>
34#include <fcntl.h>
35#include <unistd.h>
36#include <ctype.h>
37#include <errno.h>
38
39#include "../perf.h"
40#include "util.h"
41#include "trace-event.h"
42
43static int input_fd;
44
45static int read_page;
46
47int file_bigendian;
48int host_bigendian;
49static int long_size;
50
51static unsigned long page_size;
52
53static ssize_t calc_data_size;
54static bool repipe;
55
56static int do_read(int fd, void *buf, int size)
57{
58 int rsize = size;
59
60 while (size) {
61 int ret = read(fd, buf, size);
62
63 if (ret <= 0)
64 return -1;
65
66 if (repipe) {
67 int retw = write(STDOUT_FILENO, buf, ret);
68
69 if (retw <= 0 || retw != ret)
70 die("repiping input file");
71 }
72
73 size -= ret;
74 buf += ret;
75 }
76
77 return rsize;
78}
79
80static int read_or_die(void *data, int size)
81{
82 int r;
83
84 r = do_read(input_fd, data, size);
85 if (r <= 0)
86 die("reading input file (size expected=%d received=%d)",
87 size, r);
88
89 if (calc_data_size)
90 calc_data_size += r;
91
92 return r;
93}
94
95/* If it fails, the next read will report it */
96static void skip(int size)
97{
98 char buf[BUFSIZ];
99 int r;
100
101 while (size) {
102 r = size > BUFSIZ ? BUFSIZ : size;
103 read_or_die(buf, r);
104 size -= r;
105 };
106}
107
108static unsigned int read4(void)
109{
110 unsigned int data;
111
112 read_or_die(&data, 4);
113 return __data2host4(data);
114}
115
116static unsigned long long read8(void)
117{
118 unsigned long long data;
119
120 read_or_die(&data, 8);
121 return __data2host8(data);
122}
123
124static char *read_string(void)
125{
126 char buf[BUFSIZ];
127 char *str = NULL;
128 int size = 0;
129 off_t r;
130 char c;
131
132 for (;;) {
133 r = read(input_fd, &c, 1);
134 if (r < 0)
135 die("reading input file");
136
137 if (!r)
138 die("no data");
139
140 if (repipe) {
141 int retw = write(STDOUT_FILENO, &c, 1);
142
143 if (retw <= 0 || retw != r)
144 die("repiping input file string");
145 }
146
147 buf[size++] = c;
148
149 if (!c)
150 break;
151 }
152
153 if (calc_data_size)
154 calc_data_size += size;
155
156 str = malloc_or_die(size);
157 memcpy(str, buf, size);
158
159 return str;
160}
161
162static void read_proc_kallsyms(void)
163{
164 unsigned int size;
165 char *buf;
166
167 size = read4();
168 if (!size)
169 return;
170
171 buf = malloc_or_die(size + 1);
172 read_or_die(buf, size);
173 buf[size] = '\0';
174
175 parse_proc_kallsyms(buf, size);
176
177 free(buf);
178}
179
180static void read_ftrace_printk(void)
181{
182 unsigned int size;
183 char *buf;
184
185 size = read4();
186 if (!size)
187 return;
188
189 buf = malloc_or_die(size);
190 read_or_die(buf, size);
191
192 parse_ftrace_printk(buf, size);
193
194 free(buf);
195}
196
197static void read_header_files(void)
198{
199 unsigned long long size;
200 char *header_event;
201 char buf[BUFSIZ];
202
203 read_or_die(buf, 12);
204
205 if (memcmp(buf, "header_page", 12) != 0)
206 die("did not read header page");
207
208 size = read8();
209 skip(size);
210
211 /*
212 * The size field in the page is of type long,
213 * use that instead, since it represents the kernel.
214 */
215 long_size = header_page_size_size;
216
217 read_or_die(buf, 13);
218 if (memcmp(buf, "header_event", 13) != 0)
219 die("did not read header event");
220
221 size = read8();
222 header_event = malloc_or_die(size);
223 read_or_die(header_event, size);
224 free(header_event);
225}
226
227static void read_ftrace_file(unsigned long long size)
228{
229 char *buf;
230
231 buf = malloc_or_die(size);
232 read_or_die(buf, size);
233 parse_ftrace_file(buf, size);
234 free(buf);
235}
236
237static void read_event_file(char *sys, unsigned long long size)
238{
239 char *buf;
240
241 buf = malloc_or_die(size);
242 read_or_die(buf, size);
243 parse_event_file(buf, size, sys);
244 free(buf);
245}
246
247static void read_ftrace_files(void)
248{
249 unsigned long long size;
250 int count;
251 int i;
252
253 count = read4();
254
255 for (i = 0; i < count; i++) {
256 size = read8();
257 read_ftrace_file(size);
258 }
259}
260
261static void read_event_files(void)
262{
263 unsigned long long size;
264 char *sys;
265 int systems;
266 int count;
267 int i,x;
268
269 systems = read4();
270
271 for (i = 0; i < systems; i++) {
272 sys = read_string();
273
274 count = read4();
275 for (x=0; x < count; x++) {
276 size = read8();
277 read_event_file(sys, size);
278 }
279 }
280}
281
282struct cpu_data {
283 unsigned long long offset;
284 unsigned long long size;
285 unsigned long long timestamp;
286 struct record *next;
287 char *page;
288 int cpu;
289 int index;
290 int page_size;
291};
292
293static struct cpu_data *cpu_data;
294
295static void update_cpu_data_index(int cpu)
296{
297 cpu_data[cpu].offset += page_size;
298 cpu_data[cpu].size -= page_size;
299 cpu_data[cpu].index = 0;
300}
301
302static void get_next_page(int cpu)
303{
304 off_t save_seek;
305 off_t ret;
306
307 if (!cpu_data[cpu].page)
308 return;
309
310 if (read_page) {
311 if (cpu_data[cpu].size <= page_size) {
312 free(cpu_data[cpu].page);
313 cpu_data[cpu].page = NULL;
314 return;
315 }
316
317 update_cpu_data_index(cpu);
318
319 /* other parts of the code may expect the pointer to not move */
320 save_seek = lseek(input_fd, 0, SEEK_CUR);
321
322 ret = lseek(input_fd, cpu_data[cpu].offset, SEEK_SET);
323 if (ret == (off_t)-1)
324 die("failed to lseek");
325 ret = read(input_fd, cpu_data[cpu].page, page_size);
326 if (ret < 0)
327 die("failed to read page");
328
329 /* reset the file pointer back */
330 lseek(input_fd, save_seek, SEEK_SET);
331
332 return;
333 }
334
335 munmap(cpu_data[cpu].page, page_size);
336 cpu_data[cpu].page = NULL;
337
338 if (cpu_data[cpu].size <= page_size)
339 return;
340
341 update_cpu_data_index(cpu);
342
343 cpu_data[cpu].page = mmap(NULL, page_size, PROT_READ, MAP_PRIVATE,
344 input_fd, cpu_data[cpu].offset);
345 if (cpu_data[cpu].page == MAP_FAILED)
346 die("failed to mmap cpu %d at offset 0x%llx",
347 cpu, cpu_data[cpu].offset);
348}
349
350static unsigned int type_len4host(unsigned int type_len_ts)
351{
352 if (file_bigendian)
353 return (type_len_ts >> 27) & ((1 << 5) - 1);
354 else
355 return type_len_ts & ((1 << 5) - 1);
356}
357
358static unsigned int ts4host(unsigned int type_len_ts)
359{
360 if (file_bigendian)
361 return type_len_ts & ((1 << 27) - 1);
362 else
363 return type_len_ts >> 5;
364}
365
366static int calc_index(void *ptr, int cpu)
367{
368 return (unsigned long)ptr - (unsigned long)cpu_data[cpu].page;
369}
370
371struct record *trace_peek_data(int cpu)
372{
373 struct record *data;
374 void *page = cpu_data[cpu].page;
375 int idx = cpu_data[cpu].index;
376 void *ptr = page + idx;
377 unsigned long long extend;
378 unsigned int type_len_ts;
379 unsigned int type_len;
380 unsigned int delta;
381 unsigned int length = 0;
382
383 if (cpu_data[cpu].next)
384 return cpu_data[cpu].next;
385
386 if (!page)
387 return NULL;
388
389 if (!idx) {
390 /* FIXME: handle header page */
391 if (header_page_ts_size != 8)
392 die("expected a long long type for timestamp");
393 cpu_data[cpu].timestamp = data2host8(ptr);
394 ptr += 8;
395 switch (header_page_size_size) {
396 case 4:
397 cpu_data[cpu].page_size = data2host4(ptr);
398 ptr += 4;
399 break;
400 case 8:
401 cpu_data[cpu].page_size = data2host8(ptr);
402 ptr += 8;
403 break;
404 default:
405 die("bad long size");
406 }
407 ptr = cpu_data[cpu].page + header_page_data_offset;
408 }
409
410read_again:
411 idx = calc_index(ptr, cpu);
412
413 if (idx >= cpu_data[cpu].page_size) {
414 get_next_page(cpu);
415 return trace_peek_data(cpu);
416 }
417
418 type_len_ts = data2host4(ptr);
419 ptr += 4;
420
421 type_len = type_len4host(type_len_ts);
422 delta = ts4host(type_len_ts);
423
424 switch (type_len) {
425 case RINGBUF_TYPE_PADDING:
426 if (!delta)
427 die("error, hit unexpected end of page");
428 length = data2host4(ptr);
429 ptr += 4;
430 length *= 4;
431 ptr += length;
432 goto read_again;
433
434 case RINGBUF_TYPE_TIME_EXTEND:
435 extend = data2host4(ptr);
436 ptr += 4;
437 extend <<= TS_SHIFT;
438 extend += delta;
439 cpu_data[cpu].timestamp += extend;
440 goto read_again;
441
442 case RINGBUF_TYPE_TIME_STAMP:
443 ptr += 12;
444 break;
445 case 0:
446 length = data2host4(ptr);
447 ptr += 4;
448 die("here! length=%d", length);
449 break;
450 default:
451 length = type_len * 4;
452 break;
453 }
454
455 cpu_data[cpu].timestamp += delta;
456
457 data = malloc_or_die(sizeof(*data));
458 memset(data, 0, sizeof(*data));
459
460 data->ts = cpu_data[cpu].timestamp;
461 data->size = length;
462 data->data = ptr;
463 ptr += length;
464
465 cpu_data[cpu].index = calc_index(ptr, cpu);
466 cpu_data[cpu].next = data;
467
468 return data;
469}
470
471struct record *trace_read_data(int cpu)
472{
473 struct record *data;
474
475 data = trace_peek_data(cpu);
476 cpu_data[cpu].next = NULL;
477
478 return data;
479}
480
481ssize_t trace_report(int fd, bool __repipe)
482{
483 char buf[BUFSIZ];
484 char test[] = { 23, 8, 68 };
485 char *version;
486 int show_version = 0;
487 int show_funcs = 0;
488 int show_printk = 0;
489 ssize_t size;
490
491 calc_data_size = 1;
492 repipe = __repipe;
493
494 input_fd = fd;
495
496 read_or_die(buf, 3);
497 if (memcmp(buf, test, 3) != 0)
498 die("no trace data in the file");
499
500 read_or_die(buf, 7);
501 if (memcmp(buf, "tracing", 7) != 0)
502 die("not a trace file (missing 'tracing' tag)");
503
504 version = read_string();
505 if (show_version)
506 printf("version = %s\n", version);
507 free(version);
508
509 read_or_die(buf, 1);
510 file_bigendian = buf[0];
511 host_bigendian = bigendian();
512
513 read_or_die(buf, 1);
514 long_size = buf[0];
515
516 page_size = read4();
517
518 read_header_files();
519
520 read_ftrace_files();
521 read_event_files();
522 read_proc_kallsyms();
523 read_ftrace_printk();
524
525 size = calc_data_size - 1;
526 calc_data_size = 0;
527 repipe = false;
528
529 if (show_funcs) {
530 print_funcs();
531 return size;
532 }
533 if (show_printk) {
534 print_printk();
535 return size;
536 }
537
538 return size;
539}
diff --git a/tools/perf/util/trace-event-scripting.c b/tools/perf/util/trace-event-scripting.c
new file mode 100644
index 00000000000..f7af2fca965
--- /dev/null
+++ b/tools/perf/util/trace-event-scripting.c
@@ -0,0 +1,167 @@
1/*
2 * trace-event-scripting. Scripting engine common and initialization code.
3 *
4 * Copyright (C) 2009-2010 Tom Zanussi <tzanussi@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20 */
21
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <ctype.h>
26#include <errno.h>
27
28#include "../perf.h"
29#include "util.h"
30#include "trace-event.h"
31
32struct scripting_context *scripting_context;
33
34static int stop_script_unsupported(void)
35{
36 return 0;
37}
38
39static void process_event_unsupported(int cpu __unused,
40 void *data __unused,
41 int size __unused,
42 unsigned long long nsecs __unused,
43 char *comm __unused)
44{
45}
46
47static void print_python_unsupported_msg(void)
48{
49 fprintf(stderr, "Python scripting not supported."
50 " Install libpython and rebuild perf to enable it.\n"
51 "For example:\n # apt-get install python-dev (ubuntu)"
52 "\n # yum install python-devel (Fedora)"
53 "\n etc.\n");
54}
55
56static int python_start_script_unsupported(const char *script __unused,
57 int argc __unused,
58 const char **argv __unused)
59{
60 print_python_unsupported_msg();
61
62 return -1;
63}
64
65static int python_generate_script_unsupported(const char *outfile __unused)
66{
67 print_python_unsupported_msg();
68
69 return -1;
70}
71
72struct scripting_ops python_scripting_unsupported_ops = {
73 .name = "Python",
74 .start_script = python_start_script_unsupported,
75 .stop_script = stop_script_unsupported,
76 .process_event = process_event_unsupported,
77 .generate_script = python_generate_script_unsupported,
78};
79
80static void register_python_scripting(struct scripting_ops *scripting_ops)
81{
82 int err;
83 err = script_spec_register("Python", scripting_ops);
84 if (err)
85 die("error registering Python script extension");
86
87 err = script_spec_register("py", scripting_ops);
88 if (err)
89 die("error registering py script extension");
90
91 scripting_context = malloc(sizeof(struct scripting_context));
92}
93
94#ifdef NO_LIBPYTHON
95void setup_python_scripting(void)
96{
97 register_python_scripting(&python_scripting_unsupported_ops);
98}
99#else
100extern struct scripting_ops python_scripting_ops;
101
102void setup_python_scripting(void)
103{
104 register_python_scripting(&python_scripting_ops);
105}
106#endif
107
108static void print_perl_unsupported_msg(void)
109{
110 fprintf(stderr, "Perl scripting not supported."
111 " Install libperl and rebuild perf to enable it.\n"
112 "For example:\n # apt-get install libperl-dev (ubuntu)"
113 "\n # yum install 'perl(ExtUtils::Embed)' (Fedora)"
114 "\n etc.\n");
115}
116
117static int perl_start_script_unsupported(const char *script __unused,
118 int argc __unused,
119 const char **argv __unused)
120{
121 print_perl_unsupported_msg();
122
123 return -1;
124}
125
126static int perl_generate_script_unsupported(const char *outfile __unused)
127{
128 print_perl_unsupported_msg();
129
130 return -1;
131}
132
133struct scripting_ops perl_scripting_unsupported_ops = {
134 .name = "Perl",
135 .start_script = perl_start_script_unsupported,
136 .stop_script = stop_script_unsupported,
137 .process_event = process_event_unsupported,
138 .generate_script = perl_generate_script_unsupported,
139};
140
141static void register_perl_scripting(struct scripting_ops *scripting_ops)
142{
143 int err;
144 err = script_spec_register("Perl", scripting_ops);
145 if (err)
146 die("error registering Perl script extension");
147
148 err = script_spec_register("pl", scripting_ops);
149 if (err)
150 die("error registering pl script extension");
151
152 scripting_context = malloc(sizeof(struct scripting_context));
153}
154
155#ifdef NO_LIBPERL
156void setup_perl_scripting(void)
157{
158 register_perl_scripting(&perl_scripting_unsupported_ops);
159}
160#else
161extern struct scripting_ops perl_scripting_ops;
162
163void setup_perl_scripting(void)
164{
165 register_perl_scripting(&perl_scripting_ops);
166}
167#endif
diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h
new file mode 100644
index 00000000000..b5f12ca24d9
--- /dev/null
+++ b/tools/perf/util/trace-event.h
@@ -0,0 +1,299 @@
1#ifndef __PERF_TRACE_EVENTS_H
2#define __PERF_TRACE_EVENTS_H
3
4#include <stdbool.h>
5#include "parse-events.h"
6
7#define __unused __attribute__((unused))
8
9
10#ifndef PAGE_MASK
11#define PAGE_MASK (page_size - 1)
12#endif
13
14enum {
15 RINGBUF_TYPE_PADDING = 29,
16 RINGBUF_TYPE_TIME_EXTEND = 30,
17 RINGBUF_TYPE_TIME_STAMP = 31,
18};
19
20#ifndef TS_SHIFT
21#define TS_SHIFT 27
22#endif
23
24#define NSECS_PER_SEC 1000000000ULL
25#define NSECS_PER_USEC 1000ULL
26
27enum format_flags {
28 FIELD_IS_ARRAY = 1,
29 FIELD_IS_POINTER = 2,
30 FIELD_IS_SIGNED = 4,
31 FIELD_IS_STRING = 8,
32 FIELD_IS_DYNAMIC = 16,
33 FIELD_IS_FLAG = 32,
34 FIELD_IS_SYMBOLIC = 64,
35};
36
37struct format_field {
38 struct format_field *next;
39 char *type;
40 char *name;
41 int offset;
42 int size;
43 unsigned long flags;
44};
45
46struct format {
47 int nr_common;
48 int nr_fields;
49 struct format_field *common_fields;
50 struct format_field *fields;
51};
52
53struct print_arg_atom {
54 char *atom;
55};
56
57struct print_arg_string {
58 char *string;
59 int offset;
60};
61
62struct print_arg_field {
63 char *name;
64 struct format_field *field;
65};
66
67struct print_flag_sym {
68 struct print_flag_sym *next;
69 char *value;
70 char *str;
71};
72
73struct print_arg_typecast {
74 char *type;
75 struct print_arg *item;
76};
77
78struct print_arg_flags {
79 struct print_arg *field;
80 char *delim;
81 struct print_flag_sym *flags;
82};
83
84struct print_arg_symbol {
85 struct print_arg *field;
86 struct print_flag_sym *symbols;
87};
88
89struct print_arg;
90
91struct print_arg_op {
92 char *op;
93 int prio;
94 struct print_arg *left;
95 struct print_arg *right;
96};
97
98struct print_arg_func {
99 char *name;
100 struct print_arg *args;
101};
102
103enum print_arg_type {
104 PRINT_NULL,
105 PRINT_ATOM,
106 PRINT_FIELD,
107 PRINT_FLAGS,
108 PRINT_SYMBOL,
109 PRINT_TYPE,
110 PRINT_STRING,
111 PRINT_OP,
112};
113
114struct print_arg {
115 struct print_arg *next;
116 enum print_arg_type type;
117 union {
118 struct print_arg_atom atom;
119 struct print_arg_field field;
120 struct print_arg_typecast typecast;
121 struct print_arg_flags flags;
122 struct print_arg_symbol symbol;
123 struct print_arg_func func;
124 struct print_arg_string string;
125 struct print_arg_op op;
126 };
127};
128
129struct print_fmt {
130 char *format;
131 struct print_arg *args;
132};
133
134struct event {
135 struct event *next;
136 char *name;
137 int id;
138 int flags;
139 struct format format;
140 struct print_fmt print_fmt;
141 char *system;
142};
143
144enum {
145 EVENT_FL_ISFTRACE = 0x01,
146 EVENT_FL_ISPRINT = 0x02,
147 EVENT_FL_ISBPRINT = 0x04,
148 EVENT_FL_ISFUNC = 0x08,
149 EVENT_FL_ISFUNCENT = 0x10,
150 EVENT_FL_ISFUNCRET = 0x20,
151
152 EVENT_FL_FAILED = 0x80000000
153};
154
155struct record {
156 unsigned long long ts;
157 int size;
158 void *data;
159};
160
161struct record *trace_peek_data(int cpu);
162struct record *trace_read_data(int cpu);
163
164void parse_set_info(int nr_cpus, int long_sz);
165
166ssize_t trace_report(int fd, bool repipe);
167
168void *malloc_or_die(unsigned int size);
169
170void parse_cmdlines(char *file, int size);
171void parse_proc_kallsyms(char *file, unsigned int size);
172void parse_ftrace_printk(char *file, unsigned int size);
173
174void print_funcs(void);
175void print_printk(void);
176
177int parse_ftrace_file(char *buf, unsigned long size);
178int parse_event_file(char *buf, unsigned long size, char *sys);
179void print_event(int cpu, void *data, int size, unsigned long long nsecs,
180 char *comm);
181
182extern int file_bigendian;
183extern int host_bigendian;
184
185int bigendian(void);
186
187static inline unsigned short __data2host2(unsigned short data)
188{
189 unsigned short swap;
190
191 if (host_bigendian == file_bigendian)
192 return data;
193
194 swap = ((data & 0xffULL) << 8) |
195 ((data & (0xffULL << 8)) >> 8);
196
197 return swap;
198}
199
200static inline unsigned int __data2host4(unsigned int data)
201{
202 unsigned int swap;
203
204 if (host_bigendian == file_bigendian)
205 return data;
206
207 swap = ((data & 0xffULL) << 24) |
208 ((data & (0xffULL << 8)) << 8) |
209 ((data & (0xffULL << 16)) >> 8) |
210 ((data & (0xffULL << 24)) >> 24);
211
212 return swap;
213}
214
215static inline unsigned long long __data2host8(unsigned long long data)
216{
217 unsigned long long swap;
218
219 if (host_bigendian == file_bigendian)
220 return data;
221
222 swap = ((data & 0xffULL) << 56) |
223 ((data & (0xffULL << 8)) << 40) |
224 ((data & (0xffULL << 16)) << 24) |
225 ((data & (0xffULL << 24)) << 8) |
226 ((data & (0xffULL << 32)) >> 8) |
227 ((data & (0xffULL << 40)) >> 24) |
228 ((data & (0xffULL << 48)) >> 40) |
229 ((data & (0xffULL << 56)) >> 56);
230
231 return swap;
232}
233
234#define data2host2(ptr) __data2host2(*(unsigned short *)ptr)
235#define data2host4(ptr) __data2host4(*(unsigned int *)ptr)
236#define data2host8(ptr) ({ \
237 unsigned long long __val; \
238 \
239 memcpy(&__val, (ptr), sizeof(unsigned long long)); \
240 __data2host8(__val); \
241})
242
243extern int header_page_ts_offset;
244extern int header_page_ts_size;
245extern int header_page_size_offset;
246extern int header_page_size_size;
247extern int header_page_data_offset;
248extern int header_page_data_size;
249
250extern bool latency_format;
251
252int trace_parse_common_type(void *data);
253int trace_parse_common_pid(void *data);
254int parse_common_pc(void *data);
255int parse_common_flags(void *data);
256int parse_common_lock_depth(void *data);
257struct event *trace_find_event(int id);
258struct event *trace_find_next_event(struct event *event);
259unsigned long long read_size(void *ptr, int size);
260unsigned long long
261raw_field_value(struct event *event, const char *name, void *data);
262void *raw_field_ptr(struct event *event, const char *name, void *data);
263unsigned long long eval_flag(const char *flag);
264
265int read_tracing_data(int fd, struct list_head *pattrs);
266ssize_t read_tracing_data_size(int fd, struct list_head *pattrs);
267
268/* taken from kernel/trace/trace.h */
269enum trace_flag_type {
270 TRACE_FLAG_IRQS_OFF = 0x01,
271 TRACE_FLAG_IRQS_NOSUPPORT = 0x02,
272 TRACE_FLAG_NEED_RESCHED = 0x04,
273 TRACE_FLAG_HARDIRQ = 0x08,
274 TRACE_FLAG_SOFTIRQ = 0x10,
275};
276
277struct scripting_ops {
278 const char *name;
279 int (*start_script) (const char *script, int argc, const char **argv);
280 int (*stop_script) (void);
281 void (*process_event) (int cpu, void *data, int size,
282 unsigned long long nsecs, char *comm);
283 int (*generate_script) (const char *outfile);
284};
285
286int script_spec_register(const char *spec, struct scripting_ops *ops);
287
288void setup_perl_scripting(void);
289void setup_python_scripting(void);
290
291struct scripting_context {
292 void *event_data;
293};
294
295int common_pc(struct scripting_context *context);
296int common_flags(struct scripting_context *context);
297int common_lock_depth(struct scripting_context *context);
298
299#endif /* __PERF_TRACE_EVENTS_H */
diff --git a/tools/perf/types.h b/tools/perf/util/types.h
index 5e75f900594..7d6b8331f89 100644
--- a/tools/perf/types.h
+++ b/tools/perf/util/types.h
@@ -1,5 +1,5 @@
1#ifndef _PERF_TYPES_H 1#ifndef __PERF_TYPES_H
2#define _PERF_TYPES_H 2#define __PERF_TYPES_H
3 3
4/* 4/*
5 * We define u64 as unsigned long long for every architecture 5 * We define u64 as unsigned long long for every architecture
@@ -14,4 +14,4 @@ typedef signed short s16;
14typedef unsigned char u8; 14typedef unsigned char u8;
15typedef signed char s8; 15typedef signed char s8;
16 16
17#endif /* _PERF_TYPES_H */ 17#endif /* __PERF_TYPES_H */
diff --git a/tools/perf/util/ui/browser.c b/tools/perf/util/ui/browser.c
new file mode 100644
index 00000000000..8bc010edca2
--- /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 00000000000..0dc7e4da36f
--- /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 00000000000..82b78f99251
--- /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 00000000000..ebda8c3fde9
--- /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 00000000000..e35437dfa5b
--- /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 00000000000..df8581a43e1
--- /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 00000000000..8d79daa4458
--- /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 00000000000..ab6028d0c40
--- /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 00000000000..5623da8e808
--- /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 00000000000..d7fc399d36b
--- /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 00000000000..a3820a0beb5
--- /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 00000000000..662085032eb
--- /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 00000000000..7b5a8926624
--- /dev/null
+++ b/tools/perf/util/ui/util.c
@@ -0,0 +1,127 @@
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 warning_str[] = "Warning!", ok[] = "Ok";
109
110bool ui__dialog_yesno(const char *msg)
111{
112 /* newtWinChoice should really be accepting const char pointers... */
113 return newtWinChoice(NULL, (char *)yes, (char *)no, (char *)msg) == 1;
114}
115
116void ui__warning(const char *format, ...)
117{
118 va_list args;
119
120 va_start(args, format);
121 if (use_browser > 0)
122 newtWinMessagev((char *)warning_str, (char *)ok,
123 (char *)format, args);
124 else
125 vfprintf(stderr, format, args);
126 va_end(args);
127}
diff --git a/tools/perf/util/ui/util.h b/tools/perf/util/ui/util.h
new file mode 100644
index 00000000000..afcbc1d9953
--- /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.c b/tools/perf/util/util.c
new file mode 100644
index 00000000000..5b3ea49aa63
--- /dev/null
+++ b/tools/perf/util/util.c
@@ -0,0 +1,133 @@
1#include "util.h"
2#include <sys/mman.h>
3
4int mkdir_p(char *path, mode_t mode)
5{
6 struct stat st;
7 int err;
8 char *d = path;
9
10 if (*d != '/')
11 return -1;
12
13 if (stat(path, &st) == 0)
14 return 0;
15
16 while (*++d == '/');
17
18 while ((d = strchr(d, '/'))) {
19 *d = '\0';
20 err = stat(path, &st) && mkdir(path, mode);
21 *d++ = '/';
22 if (err)
23 return -1;
24 while (*d == '/')
25 ++d;
26 }
27 return (stat(path, &st) && mkdir(path, mode)) ? -1 : 0;
28}
29
30static int slow_copyfile(const char *from, const char *to)
31{
32 int err = 0;
33 char *line = NULL;
34 size_t n;
35 FILE *from_fp = fopen(from, "r"), *to_fp;
36
37 if (from_fp == NULL)
38 goto out;
39
40 to_fp = fopen(to, "w");
41 if (to_fp == NULL)
42 goto out_fclose_from;
43
44 while (getline(&line, &n, from_fp) > 0)
45 if (fputs(line, to_fp) == EOF)
46 goto out_fclose_to;
47 err = 0;
48out_fclose_to:
49 fclose(to_fp);
50 free(line);
51out_fclose_from:
52 fclose(from_fp);
53out:
54 return err;
55}
56
57int copyfile(const char *from, const char *to)
58{
59 int fromfd, tofd;
60 struct stat st;
61 void *addr;
62 int err = -1;
63
64 if (stat(from, &st))
65 goto out;
66
67 if (st.st_size == 0) /* /proc? do it slowly... */
68 return slow_copyfile(from, to);
69
70 fromfd = open(from, O_RDONLY);
71 if (fromfd < 0)
72 goto out;
73
74 tofd = creat(to, 0755);
75 if (tofd < 0)
76 goto out_close_from;
77
78 addr = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fromfd, 0);
79 if (addr == MAP_FAILED)
80 goto out_close_to;
81
82 if (write(tofd, addr, st.st_size) == st.st_size)
83 err = 0;
84
85 munmap(addr, st.st_size);
86out_close_to:
87 close(tofd);
88 if (err)
89 unlink(to);
90out_close_from:
91 close(fromfd);
92out:
93 return err;
94}
95
96unsigned long convert_unit(unsigned long value, char *unit)
97{
98 *unit = ' ';
99
100 if (value > 1000) {
101 value /= 1000;
102 *unit = 'K';
103 }
104
105 if (value > 1000) {
106 value /= 1000;
107 *unit = 'M';
108 }
109
110 if (value > 1000) {
111 value /= 1000;
112 *unit = 'G';
113 }
114
115 return value;
116}
117
118int readn(int fd, void *buf, size_t n)
119{
120 void *buf_start = buf;
121
122 while (n) {
123 int ret = read(fd, buf, n);
124
125 if (ret <= 0)
126 return ret;
127
128 n -= ret;
129 buf += ret;
130 }
131
132 return buf - buf_start;
133}
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h
index b8cfed776d8..e833f26f3bf 100644
--- a/tools/perf/util/util.h
+++ b/tools/perf/util/util.h
@@ -39,18 +39,17 @@
39/* Approximation of the length of the decimal representation of this type. */ 39/* Approximation of the length of the decimal representation of this type. */
40#define decimal_length(x) ((int)(sizeof(x) * 2.56 + 0.5) + 1) 40#define decimal_length(x) ((int)(sizeof(x) * 2.56 + 0.5) + 1)
41 41
42#if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__USLC__) && !defined(_M_UNIX)
43#define _XOPEN_SOURCE 600 /* glibc2 and AIX 5.3L need 500, OpenBSD needs 600 for S_ISLNK() */
44#define _XOPEN_SOURCE_EXTENDED 1 /* AIX 5.3L needs this */
45#endif
46#define _ALL_SOURCE 1 42#define _ALL_SOURCE 1
47#define _GNU_SOURCE 1 43#define _GNU_SOURCE 1
48#define _BSD_SOURCE 1 44#define _BSD_SOURCE 1
45#define HAS_BOOL
49 46
50#include <unistd.h> 47#include <unistd.h>
51#include <stdio.h> 48#include <stdio.h>
52#include <sys/stat.h> 49#include <sys/stat.h>
50#include <sys/statfs.h>
53#include <fcntl.h> 51#include <fcntl.h>
52#include <stdbool.h>
54#include <stddef.h> 53#include <stddef.h>
55#include <stdlib.h> 54#include <stdlib.h>
56#include <stdarg.h> 55#include <stdarg.h>
@@ -67,7 +66,6 @@
67#include <assert.h> 66#include <assert.h>
68#include <regex.h> 67#include <regex.h>
69#include <utime.h> 68#include <utime.h>
70#ifndef __MINGW32__
71#include <sys/wait.h> 69#include <sys/wait.h>
72#include <sys/poll.h> 70#include <sys/poll.h>
73#include <sys/socket.h> 71#include <sys/socket.h>
@@ -81,25 +79,18 @@
81#include <netdb.h> 79#include <netdb.h>
82#include <pwd.h> 80#include <pwd.h>
83#include <inttypes.h> 81#include <inttypes.h>
84#if defined(__CYGWIN__) 82#include "../../../include/linux/magic.h"
85#undef _XOPEN_SOURCE 83#include "types.h"
86#include <grp.h> 84#include <sys/ttydefaults.h>
87#define _XOPEN_SOURCE 600
88#include "compat/cygwin.h"
89#else
90#undef _ALL_SOURCE /* AIX 5.3L defines a struct list with _ALL_SOURCE. */
91#include <grp.h>
92#define _ALL_SOURCE 1
93#endif
94#else /* __MINGW32__ */
95/* pull in Windows compatibility stuff */
96#include "compat/mingw.h"
97#endif /* __MINGW32__ */
98 85
99#ifndef NO_ICONV 86#ifndef NO_ICONV
100#include <iconv.h> 87#include <iconv.h>
101#endif 88#endif
102 89
90extern const char *graph_line;
91extern const char *graph_dotted_line;
92extern char buildid_dir[];
93
103/* On most systems <limits.h> would have given us this, but 94/* On most systems <limits.h> would have given us this, but
104 * not on some systems (e.g. GNU/Hurd). 95 * not on some systems (e.g. GNU/Hurd).
105 */ 96 */
@@ -150,10 +141,20 @@ extern void die(const char *err, ...) NORETURN __attribute__((format (printf, 1,
150extern int error(const char *err, ...) __attribute__((format (printf, 1, 2))); 141extern int error(const char *err, ...) __attribute__((format (printf, 1, 2)));
151extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2))); 142extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2)));
152 143
144#include "../../../include/linux/stringify.h"
145
146#define DIE_IF(cnd) \
147 do { if (cnd) \
148 die(" at (" __FILE__ ":" __stringify(__LINE__) "): " \
149 __stringify(cnd) "\n"); \
150 } while (0)
151
152
153extern void set_die_routine(void (*routine)(const char *err, va_list params) NORETURN); 153extern void set_die_routine(void (*routine)(const char *err, va_list params) NORETURN);
154 154
155extern int prefixcmp(const char *str, const char *prefix); 155extern int prefixcmp(const char *str, const char *prefix);
156extern time_t tm_to_time_t(const struct tm *tm); 156extern void set_buildid_dir(void);
157extern void disable_buildid_cache(void);
157 158
158static inline const char *skip_prefix(const char *str, const char *prefix) 159static inline const char *skip_prefix(const char *str, const char *prefix)
159{ 160{
@@ -161,119 +162,6 @@ static inline const char *skip_prefix(const char *str, const char *prefix)
161 return strncmp(str, prefix, len) ? NULL : str + len; 162 return strncmp(str, prefix, len) ? NULL : str + len;
162} 163}
163 164
164#if defined(NO_MMAP) || defined(USE_WIN32_MMAP)
165
166#ifndef PROT_READ
167#define PROT_READ 1
168#define PROT_WRITE 2
169#define MAP_PRIVATE 1
170#define MAP_FAILED ((void*)-1)
171#endif
172
173#define mmap git_mmap
174#define munmap git_munmap
175extern void *git_mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
176extern int git_munmap(void *start, size_t length);
177
178#else /* NO_MMAP || USE_WIN32_MMAP */
179
180#include <sys/mman.h>
181
182#endif /* NO_MMAP || USE_WIN32_MMAP */
183
184#ifdef NO_MMAP
185
186/* This value must be multiple of (pagesize * 2) */
187#define DEFAULT_PACKED_GIT_WINDOW_SIZE (1 * 1024 * 1024)
188
189#else /* NO_MMAP */
190
191/* This value must be multiple of (pagesize * 2) */
192#define DEFAULT_PACKED_GIT_WINDOW_SIZE \
193 (sizeof(void*) >= 8 \
194 ? 1 * 1024 * 1024 * 1024 \
195 : 32 * 1024 * 1024)
196
197#endif /* NO_MMAP */
198
199#ifdef NO_ST_BLOCKS_IN_STRUCT_STAT
200#define on_disk_bytes(st) ((st).st_size)
201#else
202#define on_disk_bytes(st) ((st).st_blocks * 512)
203#endif
204
205#define DEFAULT_PACKED_GIT_LIMIT \
206 ((1024L * 1024L) * (sizeof(void*) >= 8 ? 8192 : 256))
207
208#ifdef NO_PREAD
209#define pread git_pread
210extern ssize_t git_pread(int fd, void *buf, size_t count, off_t offset);
211#endif
212/*
213 * Forward decl that will remind us if its twin in cache.h changes.
214 * This function is used in compat/pread.c. But we can't include
215 * cache.h there.
216 */
217extern ssize_t read_in_full(int fd, void *buf, size_t count);
218
219#ifdef NO_SETENV
220#define setenv gitsetenv
221extern int gitsetenv(const char *, const char *, int);
222#endif
223
224#ifdef NO_MKDTEMP
225#define mkdtemp gitmkdtemp
226extern char *gitmkdtemp(char *);
227#endif
228
229#ifdef NO_UNSETENV
230#define unsetenv gitunsetenv
231extern void gitunsetenv(const char *);
232#endif
233
234#ifdef NO_STRCASESTR
235#define strcasestr gitstrcasestr
236extern char *gitstrcasestr(const char *haystack, const char *needle);
237#endif
238
239#ifdef NO_STRLCPY
240#define strlcpy gitstrlcpy
241extern size_t gitstrlcpy(char *, const char *, size_t);
242#endif
243
244#ifdef NO_STRTOUMAX
245#define strtoumax gitstrtoumax
246extern uintmax_t gitstrtoumax(const char *, char **, int);
247#endif
248
249#ifdef NO_HSTRERROR
250#define hstrerror githstrerror
251extern const char *githstrerror(int herror);
252#endif
253
254#ifdef NO_MEMMEM
255#define memmem gitmemmem
256void *gitmemmem(const void *haystack, size_t haystacklen,
257 const void *needle, size_t needlelen);
258#endif
259
260#ifdef FREAD_READS_DIRECTORIES
261#ifdef fopen
262#undef fopen
263#endif
264#define fopen(a,b) git_fopen(a,b)
265extern FILE *git_fopen(const char*, const char*);
266#endif
267
268#ifdef SNPRINTF_RETURNS_BOGUS
269#define snprintf git_snprintf
270extern int git_snprintf(char *str, size_t maxsize,
271 const char *format, ...);
272#define vsnprintf git_vsnprintf
273extern int git_vsnprintf(char *str, size_t maxsize,
274 const char *format, va_list ap);
275#endif
276
277#ifdef __GLIBC_PREREQ 165#ifdef __GLIBC_PREREQ
278#if __GLIBC_PREREQ(2, 1) 166#if __GLIBC_PREREQ(2, 1)
279#define HAVE_STRCHRNUL 167#define HAVE_STRCHRNUL
@@ -294,27 +182,19 @@ static inline char *gitstrchrnul(const char *s, int c)
294 * Wrappers: 182 * Wrappers:
295 */ 183 */
296extern char *xstrdup(const char *str); 184extern char *xstrdup(const char *str);
297extern void *xmalloc(size_t size); 185extern void *xrealloc(void *ptr, size_t size) __attribute__((weak));
298extern void *xmemdupz(const void *data, size_t len); 186
299extern char *xstrndup(const char *str, size_t len); 187
300extern void *xrealloc(void *ptr, size_t size); 188static inline void *zalloc(size_t size)
301extern void *xcalloc(size_t nmemb, size_t size);
302extern void *xmmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
303extern ssize_t xread(int fd, void *buf, size_t len);
304extern ssize_t xwrite(int fd, const void *buf, size_t len);
305extern int xdup(int fd);
306extern FILE *xfdopen(int fd, const char *mode);
307extern int xmkstemp(char *template);
308
309static inline size_t xsize_t(off_t len)
310{ 189{
311 return (size_t)len; 190 return calloc(1, size);
312} 191}
313 192
314static inline int has_extension(const char *filename, const char *ext) 193static inline int has_extension(const char *filename, const char *ext)
315{ 194{
316 size_t len = strlen(filename); 195 size_t len = strlen(filename);
317 size_t extlen = strlen(ext); 196 size_t extlen = strlen(ext);
197
318 return len > extlen && !memcmp(filename + len - extlen, ext, extlen); 198 return len > extlen && !memcmp(filename + len - extlen, ext, extlen);
319} 199}
320 200
@@ -322,10 +202,13 @@ static inline int has_extension(const char *filename, const char *ext)
322#undef isascii 202#undef isascii
323#undef isspace 203#undef isspace
324#undef isdigit 204#undef isdigit
205#undef isxdigit
325#undef isalpha 206#undef isalpha
207#undef isprint
326#undef isalnum 208#undef isalnum
327#undef tolower 209#undef tolower
328#undef toupper 210#undef toupper
211
329extern unsigned char sane_ctype[256]; 212extern unsigned char sane_ctype[256];
330#define GIT_SPACE 0x01 213#define GIT_SPACE 0x01
331#define GIT_DIGIT 0x02 214#define GIT_DIGIT 0x02
@@ -338,11 +221,11 @@ extern unsigned char sane_ctype[256];
338#define isascii(x) (((x) & ~0x7f) == 0) 221#define isascii(x) (((x) & ~0x7f) == 0)
339#define isspace(x) sane_istest(x,GIT_SPACE) 222#define isspace(x) sane_istest(x,GIT_SPACE)
340#define isdigit(x) sane_istest(x,GIT_DIGIT) 223#define isdigit(x) sane_istest(x,GIT_DIGIT)
224#define isxdigit(x) \
225 (sane_istest(toupper(x), GIT_ALPHA | GIT_DIGIT) && toupper(x) < 'G')
341#define isalpha(x) sane_istest(x,GIT_ALPHA) 226#define isalpha(x) sane_istest(x,GIT_ALPHA)
342#define isalnum(x) sane_istest(x,GIT_ALPHA | GIT_DIGIT) 227#define isalnum(x) sane_istest(x,GIT_ALPHA | GIT_DIGIT)
343#define isprint(x) sane_istest(x,GIT_PRINT) 228#define isprint(x) sane_istest(x,GIT_PRINT)
344#define is_glob_special(x) sane_istest(x,GIT_GLOB_SPECIAL)
345#define is_regex_special(x) sane_istest(x,GIT_GLOB_SPECIAL | GIT_REGEX_SPECIAL)
346#define tolower(x) sane_case((unsigned char)(x), 0x20) 229#define tolower(x) sane_case((unsigned char)(x), 0x20)
347#define toupper(x) sane_case((unsigned char)(x), 0) 230#define toupper(x) sane_case((unsigned char)(x), 0)
348 231
@@ -353,38 +236,6 @@ static inline int sane_case(int x, int high)
353 return x; 236 return x;
354} 237}
355 238
356static inline int strtoul_ui(char const *s, int base, unsigned int *result)
357{
358 unsigned long ul;
359 char *p;
360
361 errno = 0;
362 ul = strtoul(s, &p, base);
363 if (errno || *p || p == s || (unsigned int) ul != ul)
364 return -1;
365 *result = ul;
366 return 0;
367}
368
369static inline int strtol_i(char const *s, int base, int *result)
370{
371 long ul;
372 char *p;
373
374 errno = 0;
375 ul = strtol(s, &p, base);
376 if (errno || *p || p == s || (int) ul != ul)
377 return -1;
378 *result = ul;
379 return 0;
380}
381
382#ifdef INTERNAL_QSORT
383void git_qsort(void *base, size_t nmemb, size_t size,
384 int(*compar)(const void *, const void *));
385#define qsort git_qsort
386#endif
387
388#ifndef DIR_HAS_BSD_GROUP_SEMANTICS 239#ifndef DIR_HAS_BSD_GROUP_SEMANTICS
389# define FORCE_DIR_SET_GID S_ISGID 240# define FORCE_DIR_SET_GID S_ISGID
390#else 241#else
@@ -405,4 +256,18 @@ void git_qsort(void *base, size_t nmemb, size_t size,
405#endif 256#endif
406#endif 257#endif
407 258
259int mkdir_p(char *path, mode_t mode);
260int copyfile(const char *from, const char *to);
261
262s64 perf_atoll(const char *str);
263char **argv_split(const char *str, int *argcp);
264void argv_free(char **argv);
265bool strglobmatch(const char *str, const char *pat);
266bool strlazymatch(const char *str, const char *pat);
267unsigned long convert_unit(unsigned long value, char *unit);
268int readn(int fd, void *buf, size_t size);
269
270#define _STR(x) #x
271#define STR(x) _STR(x)
272
408#endif 273#endif
diff --git a/tools/perf/util/values.c b/tools/perf/util/values.c
new file mode 100644
index 00000000000..cfa55d686e3
--- /dev/null
+++ b/tools/perf/util/values.c
@@ -0,0 +1,231 @@
1#include <stdlib.h>
2
3#include "util.h"
4#include "values.h"
5
6void perf_read_values_init(struct perf_read_values *values)
7{
8 values->threads_max = 16;
9 values->pid = malloc(values->threads_max * sizeof(*values->pid));
10 values->tid = malloc(values->threads_max * sizeof(*values->tid));
11 values->value = malloc(values->threads_max * sizeof(*values->value));
12 if (!values->pid || !values->tid || !values->value)
13 die("failed to allocate read_values threads arrays");
14 values->threads = 0;
15
16 values->counters_max = 16;
17 values->counterrawid = malloc(values->counters_max
18 * sizeof(*values->counterrawid));
19 values->countername = malloc(values->counters_max
20 * sizeof(*values->countername));
21 if (!values->counterrawid || !values->countername)
22 die("failed to allocate read_values counters arrays");
23 values->counters = 0;
24}
25
26void perf_read_values_destroy(struct perf_read_values *values)
27{
28 int i;
29
30 if (!values->threads_max || !values->counters_max)
31 return;
32
33 for (i = 0; i < values->threads; i++)
34 free(values->value[i]);
35 free(values->pid);
36 free(values->tid);
37 free(values->counterrawid);
38 for (i = 0; i < values->counters; i++)
39 free(values->countername[i]);
40 free(values->countername);
41}
42
43static void perf_read_values__enlarge_threads(struct perf_read_values *values)
44{
45 values->threads_max *= 2;
46 values->pid = realloc(values->pid,
47 values->threads_max * sizeof(*values->pid));
48 values->tid = realloc(values->tid,
49 values->threads_max * sizeof(*values->tid));
50 values->value = realloc(values->value,
51 values->threads_max * sizeof(*values->value));
52 if (!values->pid || !values->tid || !values->value)
53 die("failed to enlarge read_values threads arrays");
54}
55
56static int perf_read_values__findnew_thread(struct perf_read_values *values,
57 u32 pid, u32 tid)
58{
59 int i;
60
61 for (i = 0; i < values->threads; i++)
62 if (values->pid[i] == pid && values->tid[i] == tid)
63 return i;
64
65 if (values->threads == values->threads_max)
66 perf_read_values__enlarge_threads(values);
67
68 i = values->threads++;
69 values->pid[i] = pid;
70 values->tid[i] = tid;
71 values->value[i] = malloc(values->counters_max * sizeof(**values->value));
72 if (!values->value[i])
73 die("failed to allocate read_values counters array");
74
75 return i;
76}
77
78static void perf_read_values__enlarge_counters(struct perf_read_values *values)
79{
80 int i;
81
82 values->counters_max *= 2;
83 values->counterrawid = realloc(values->counterrawid,
84 values->counters_max * sizeof(*values->counterrawid));
85 values->countername = realloc(values->countername,
86 values->counters_max * sizeof(*values->countername));
87 if (!values->counterrawid || !values->countername)
88 die("failed to enlarge read_values counters arrays");
89
90 for (i = 0; i < values->threads; i++) {
91 values->value[i] = realloc(values->value[i],
92 values->counters_max * sizeof(**values->value));
93 if (!values->value[i])
94 die("failed to enlarge read_values counters arrays");
95 }
96}
97
98static int perf_read_values__findnew_counter(struct perf_read_values *values,
99 u64 rawid, const char *name)
100{
101 int i;
102
103 for (i = 0; i < values->counters; i++)
104 if (values->counterrawid[i] == rawid)
105 return i;
106
107 if (values->counters == values->counters_max)
108 perf_read_values__enlarge_counters(values);
109
110 i = values->counters++;
111 values->counterrawid[i] = rawid;
112 values->countername[i] = strdup(name);
113
114 return i;
115}
116
117void perf_read_values_add_value(struct perf_read_values *values,
118 u32 pid, u32 tid,
119 u64 rawid, const char *name, u64 value)
120{
121 int tindex, cindex;
122
123 tindex = perf_read_values__findnew_thread(values, pid, tid);
124 cindex = perf_read_values__findnew_counter(values, rawid, name);
125
126 values->value[tindex][cindex] = value;
127}
128
129static void perf_read_values__display_pretty(FILE *fp,
130 struct perf_read_values *values)
131{
132 int i, j;
133 int pidwidth, tidwidth;
134 int *counterwidth;
135
136 counterwidth = malloc(values->counters * sizeof(*counterwidth));
137 if (!counterwidth)
138 die("failed to allocate counterwidth array");
139 tidwidth = 3;
140 pidwidth = 3;
141 for (j = 0; j < values->counters; j++)
142 counterwidth[j] = strlen(values->countername[j]);
143 for (i = 0; i < values->threads; i++) {
144 int width;
145
146 width = snprintf(NULL, 0, "%d", values->pid[i]);
147 if (width > pidwidth)
148 pidwidth = width;
149 width = snprintf(NULL, 0, "%d", values->tid[i]);
150 if (width > tidwidth)
151 tidwidth = width;
152 for (j = 0; j < values->counters; j++) {
153 width = snprintf(NULL, 0, "%Lu", values->value[i][j]);
154 if (width > counterwidth[j])
155 counterwidth[j] = width;
156 }
157 }
158
159 fprintf(fp, "# %*s %*s", pidwidth, "PID", tidwidth, "TID");
160 for (j = 0; j < values->counters; j++)
161 fprintf(fp, " %*s", counterwidth[j], values->countername[j]);
162 fprintf(fp, "\n");
163
164 for (i = 0; i < values->threads; i++) {
165 fprintf(fp, " %*d %*d", pidwidth, values->pid[i],
166 tidwidth, values->tid[i]);
167 for (j = 0; j < values->counters; j++)
168 fprintf(fp, " %*Lu",
169 counterwidth[j], values->value[i][j]);
170 fprintf(fp, "\n");
171 }
172 free(counterwidth);
173}
174
175static void perf_read_values__display_raw(FILE *fp,
176 struct perf_read_values *values)
177{
178 int width, pidwidth, tidwidth, namewidth, rawwidth, countwidth;
179 int i, j;
180
181 tidwidth = 3; /* TID */
182 pidwidth = 3; /* PID */
183 namewidth = 4; /* "Name" */
184 rawwidth = 3; /* "Raw" */
185 countwidth = 5; /* "Count" */
186
187 for (i = 0; i < values->threads; i++) {
188 width = snprintf(NULL, 0, "%d", values->pid[i]);
189 if (width > pidwidth)
190 pidwidth = width;
191 width = snprintf(NULL, 0, "%d", values->tid[i]);
192 if (width > tidwidth)
193 tidwidth = width;
194 }
195 for (j = 0; j < values->counters; j++) {
196 width = strlen(values->countername[j]);
197 if (width > namewidth)
198 namewidth = width;
199 width = snprintf(NULL, 0, "%llx", values->counterrawid[j]);
200 if (width > rawwidth)
201 rawwidth = width;
202 }
203 for (i = 0; i < values->threads; i++) {
204 for (j = 0; j < values->counters; j++) {
205 width = snprintf(NULL, 0, "%Lu", values->value[i][j]);
206 if (width > countwidth)
207 countwidth = width;
208 }
209 }
210
211 fprintf(fp, "# %*s %*s %*s %*s %*s\n",
212 pidwidth, "PID", tidwidth, "TID",
213 namewidth, "Name", rawwidth, "Raw",
214 countwidth, "Count");
215 for (i = 0; i < values->threads; i++)
216 for (j = 0; j < values->counters; j++)
217 fprintf(fp, " %*d %*d %*s %*llx %*Lu\n",
218 pidwidth, values->pid[i],
219 tidwidth, values->tid[i],
220 namewidth, values->countername[j],
221 rawwidth, values->counterrawid[j],
222 countwidth, values->value[i][j]);
223}
224
225void perf_read_values_display(FILE *fp, struct perf_read_values *values, int raw)
226{
227 if (raw)
228 perf_read_values__display_raw(fp, values);
229 else
230 perf_read_values__display_pretty(fp, values);
231}
diff --git a/tools/perf/util/values.h b/tools/perf/util/values.h
new file mode 100644
index 00000000000..2fa967e1a88
--- /dev/null
+++ b/tools/perf/util/values.h
@@ -0,0 +1,27 @@
1#ifndef __PERF_VALUES_H
2#define __PERF_VALUES_H
3
4#include "types.h"
5
6struct perf_read_values {
7 int threads;
8 int threads_max;
9 u32 *pid, *tid;
10 int counters;
11 int counters_max;
12 u64 *counterrawid;
13 char **countername;
14 u64 **value;
15};
16
17void perf_read_values_init(struct perf_read_values *values);
18void perf_read_values_destroy(struct perf_read_values *values);
19
20void perf_read_values_add_value(struct perf_read_values *values,
21 u32 pid, u32 tid,
22 u64 rawid, const char *name, u64 value);
23
24void perf_read_values_display(FILE *fp, struct perf_read_values *values,
25 int raw);
26
27#endif /* __PERF_VALUES_H */
diff --git a/tools/perf/util/wrapper.c b/tools/perf/util/wrapper.c
index 6350d65f6d9..73e900edb5a 100644
--- a/tools/perf/util/wrapper.c
+++ b/tools/perf/util/wrapper.c
@@ -7,7 +7,7 @@
7 * There's no pack memory to release - but stay close to the Git 7 * There's no pack memory to release - but stay close to the Git
8 * version so wrap this away: 8 * version so wrap this away:
9 */ 9 */
10static inline void release_pack_memory(size_t size, int flag) 10static inline void release_pack_memory(size_t size __used, int flag __used)
11{ 11{
12} 12}
13 13
@@ -23,45 +23,6 @@ char *xstrdup(const char *str)
23 return ret; 23 return ret;
24} 24}
25 25
26void *xmalloc(size_t size)
27{
28 void *ret = malloc(size);
29 if (!ret && !size)
30 ret = malloc(1);
31 if (!ret) {
32 release_pack_memory(size, -1);
33 ret = malloc(size);
34 if (!ret && !size)
35 ret = malloc(1);
36 if (!ret)
37 die("Out of memory, malloc failed");
38 }
39#ifdef XMALLOC_POISON
40 memset(ret, 0xA5, size);
41#endif
42 return ret;
43}
44
45/*
46 * xmemdupz() allocates (len + 1) bytes of memory, duplicates "len" bytes of
47 * "data" to the allocated memory, zero terminates the allocated memory,
48 * and returns a pointer to the allocated memory. If the allocation fails,
49 * the program dies.
50 */
51void *xmemdupz(const void *data, size_t len)
52{
53 char *p = xmalloc(len + 1);
54 memcpy(p, data, len);
55 p[len] = '\0';
56 return p;
57}
58
59char *xstrndup(const char *str, size_t len)
60{
61 char *p = memchr(str, '\0', len);
62 return xmemdupz(str, p ? p - str : len);
63}
64
65void *xrealloc(void *ptr, size_t size) 26void *xrealloc(void *ptr, size_t size)
66{ 27{
67 void *ret = realloc(ptr, size); 28 void *ret = realloc(ptr, size);
@@ -77,130 +38,3 @@ void *xrealloc(void *ptr, size_t size)
77 } 38 }
78 return ret; 39 return ret;
79} 40}
80
81void *xcalloc(size_t nmemb, size_t size)
82{
83 void *ret = calloc(nmemb, size);
84 if (!ret && (!nmemb || !size))
85 ret = calloc(1, 1);
86 if (!ret) {
87 release_pack_memory(nmemb * size, -1);
88 ret = calloc(nmemb, size);
89 if (!ret && (!nmemb || !size))
90 ret = calloc(1, 1);
91 if (!ret)
92 die("Out of memory, calloc failed");
93 }
94 return ret;
95}
96
97void *xmmap(void *start, size_t length,
98 int prot, int flags, int fd, off_t offset)
99{
100 void *ret = mmap(start, length, prot, flags, fd, offset);
101 if (ret == MAP_FAILED) {
102 if (!length)
103 return NULL;
104 release_pack_memory(length, fd);
105 ret = mmap(start, length, prot, flags, fd, offset);
106 if (ret == MAP_FAILED)
107 die("Out of memory? mmap failed: %s", strerror(errno));
108 }
109 return ret;
110}
111
112/*
113 * xread() is the same a read(), but it automatically restarts read()
114 * operations with a recoverable error (EAGAIN and EINTR). xread()
115 * DOES NOT GUARANTEE that "len" bytes is read even if the data is available.
116 */
117ssize_t xread(int fd, void *buf, size_t len)
118{
119 ssize_t nr;
120 while (1) {
121 nr = read(fd, buf, len);
122 if ((nr < 0) && (errno == EAGAIN || errno == EINTR))
123 continue;
124 return nr;
125 }
126}
127
128/*
129 * xwrite() is the same a write(), but it automatically restarts write()
130 * operations with a recoverable error (EAGAIN and EINTR). xwrite() DOES NOT
131 * GUARANTEE that "len" bytes is written even if the operation is successful.
132 */
133ssize_t xwrite(int fd, const void *buf, size_t len)
134{
135 ssize_t nr;
136 while (1) {
137 nr = write(fd, buf, len);
138 if ((nr < 0) && (errno == EAGAIN || errno == EINTR))
139 continue;
140 return nr;
141 }
142}
143
144ssize_t read_in_full(int fd, void *buf, size_t count)
145{
146 char *p = buf;
147 ssize_t total = 0;
148
149 while (count > 0) {
150 ssize_t loaded = xread(fd, p, count);
151 if (loaded <= 0)
152 return total ? total : loaded;
153 count -= loaded;
154 p += loaded;
155 total += loaded;
156 }
157
158 return total;
159}
160
161ssize_t write_in_full(int fd, const void *buf, size_t count)
162{
163 const char *p = buf;
164 ssize_t total = 0;
165
166 while (count > 0) {
167 ssize_t written = xwrite(fd, p, count);
168 if (written < 0)
169 return -1;
170 if (!written) {
171 errno = ENOSPC;
172 return -1;
173 }
174 count -= written;
175 p += written;
176 total += written;
177 }
178
179 return total;
180}
181
182int xdup(int fd)
183{
184 int ret = dup(fd);
185 if (ret < 0)
186 die("dup failed: %s", strerror(errno));
187 return ret;
188}
189
190FILE *xfdopen(int fd, const char *mode)
191{
192 FILE *stream = fdopen(fd, mode);
193 if (stream == NULL)
194 die("Out of memory? fdopen failed: %s", strerror(errno));
195 return stream;
196}
197
198int xmkstemp(char *template)
199{
200 int fd;
201
202 fd = mkstemp(template);
203 if (fd < 0)
204 die("Unable to create temporary file: %s", strerror(errno));
205 return fd;
206}
diff --git a/tools/perf/util/xyarray.c b/tools/perf/util/xyarray.c
new file mode 100644
index 00000000000..22afbf6c536
--- /dev/null
+++ b/tools/perf/util/xyarray.c
@@ -0,0 +1,20 @@
1#include "xyarray.h"
2#include "util.h"
3
4struct xyarray *xyarray__new(int xlen, int ylen, size_t entry_size)
5{
6 size_t row_size = ylen * entry_size;
7 struct xyarray *xy = zalloc(sizeof(*xy) + xlen * row_size);
8
9 if (xy != NULL) {
10 xy->entry_size = entry_size;
11 xy->row_size = row_size;
12 }
13
14 return xy;
15}
16
17void xyarray__delete(struct xyarray *xy)
18{
19 free(xy);
20}
diff --git a/tools/perf/util/xyarray.h b/tools/perf/util/xyarray.h
new file mode 100644
index 00000000000..c488a07275d
--- /dev/null
+++ b/tools/perf/util/xyarray.h
@@ -0,0 +1,20 @@
1#ifndef _PERF_XYARRAY_H_
2#define _PERF_XYARRAY_H_ 1
3
4#include <sys/types.h>
5
6struct xyarray {
7 size_t row_size;
8 size_t entry_size;
9 char contents[];
10};
11
12struct xyarray *xyarray__new(int xlen, int ylen, size_t entry_size);
13void xyarray__delete(struct xyarray *xy);
14
15static inline void *xyarray__entry(struct xyarray *xy, int x, int y)
16{
17 return &xy->contents[x * xy->row_size + y * xy->entry_size];
18}
19
20#endif /* _PERF_XYARRAY_H_ */