summaryrefslogtreecommitdiffstats
path: root/tools/perf
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2009-08-26 17:17:51 -0700
committerH. Peter Anvin <hpa@zytor.com>2009-08-26 17:24:28 -0700
commitb855192c08fcb14adbc5d3a7cab182022d433cca (patch)
tree3a10cafbfbf98cafacf667eb218c71300b351bfa /tools/perf
parentd886c73cd4cf02a71e1650cbcb6176799d78aac1 (diff)
parent3e0e1e9c5a327d4dba8490d83ef55c0564e6e8a7 (diff)
Merge branch 'x86/urgent' into x86/pat
Reason: Change to is_new_memtype_allowed() in x86/urgent Resolved semantic conflicts in: arch/x86/mm/pat.c arch/x86/mm/ioremap.c Signed-off-by: H. Peter Anvin <hpa@zytor.com>
Diffstat (limited to 'tools/perf')
-rw-r--r--tools/perf/Documentation/perf-examples.txt225
-rw-r--r--tools/perf/Documentation/perf-record.txt60
-rw-r--r--tools/perf/Documentation/perf-report.txt15
-rw-r--r--tools/perf/Documentation/perf-stat.txt2
-rw-r--r--tools/perf/Documentation/perf-top.txt112
-rw-r--r--tools/perf/Makefile60
-rw-r--r--tools/perf/builtin-annotate.c93
-rw-r--r--tools/perf/builtin-help.c6
-rw-r--r--tools/perf/builtin-list.c5
-rw-r--r--tools/perf/builtin-record.c125
-rw-r--r--tools/perf/builtin-report.c751
-rw-r--r--tools/perf/builtin-stat.c56
-rw-r--r--tools/perf/builtin-top.c625
-rw-r--r--tools/perf/perf.c82
-rw-r--r--tools/perf/perf.h16
-rw-r--r--tools/perf/util/alias.c2
-rw-r--r--tools/perf/util/cache.h2
-rw-r--r--tools/perf/util/callchain.c263
-rw-r--r--tools/perf/util/callchain.h47
-rw-r--r--tools/perf/util/color.c37
-rw-r--r--tools/perf/util/color.h5
-rw-r--r--tools/perf/util/config.c18
-rw-r--r--tools/perf/util/exec_cmd.c5
-rw-r--r--tools/perf/util/header.c5
-rw-r--r--tools/perf/util/header.h2
-rw-r--r--tools/perf/util/help.c26
-rw-r--r--tools/perf/util/help.h6
-rw-r--r--tools/perf/util/include/asm/system.h1
-rw-r--r--tools/perf/util/include/linux/kernel.h29
-rw-r--r--tools/perf/util/include/linux/list.h18
-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/list.h603
-rw-r--r--tools/perf/util/module.c509
-rw-r--r--tools/perf/util/module.h53
-rw-r--r--tools/perf/util/parse-events.c465
-rw-r--r--tools/perf/util/parse-events.h6
-rw-r--r--tools/perf/util/parse-options.c5
-rw-r--r--tools/perf/util/parse-options.h27
-rw-r--r--tools/perf/util/quote.c48
-rw-r--r--tools/perf/util/quote.h2
-rw-r--r--tools/perf/util/rbtree.c383
-rw-r--r--tools/perf/util/rbtree.h171
-rw-r--r--tools/perf/util/strbuf.c13
-rw-r--r--tools/perf/util/strbuf.h10
-rw-r--r--tools/perf/util/string.h3
-rw-r--r--tools/perf/util/strlist.c20
-rw-r--r--tools/perf/util/strlist.h13
-rw-r--r--tools/perf/util/symbol.c441
-rw-r--r--tools/perf/util/symbol.h38
-rw-r--r--tools/perf/util/util.h2
-rw-r--r--tools/perf/util/wrapper.c5
54 files changed, 3660 insertions, 1870 deletions
diff --git a/tools/perf/Documentation/perf-examples.txt b/tools/perf/Documentation/perf-examples.txt
new file mode 100644
index 00000000000..8eb6c489fb1
--- /dev/null
+++ b/tools/perf/Documentation/perf-examples.txt
@@ -0,0 +1,225 @@
+
+ ------------------------------
+ ****** perf by examples ******
+ ------------------------------
+
+[ From an e-mail by Ingo Molnar, http://lkml.org/lkml/2009/8/4/346 ]
+
+
+First, discovery/enumeration of available counters can be done via
+'perf list':
+
+titan:~> perf list
+ [...]
+ kmem:kmalloc [Tracepoint event]
+ kmem:kmem_cache_alloc [Tracepoint event]
+ kmem:kmalloc_node [Tracepoint event]
+ kmem:kmem_cache_alloc_node [Tracepoint event]
+ kmem:kfree [Tracepoint event]
+ kmem:kmem_cache_free [Tracepoint event]
+ kmem:mm_page_free_direct [Tracepoint event]
+ kmem:mm_pagevec_free [Tracepoint event]
+ kmem:mm_page_alloc [Tracepoint event]
+ kmem:mm_page_alloc_zone_locked [Tracepoint event]
+ kmem:mm_page_pcpu_drain [Tracepoint event]
+ kmem:mm_page_alloc_extfrag [Tracepoint event]
+
+Then any (or all) of the above event sources can be activated and
+measured. For example the page alloc/free properties of a 'hackbench
+run' are:
+
+ titan:~> perf stat -e kmem:mm_page_pcpu_drain -e kmem:mm_page_alloc
+ -e kmem:mm_pagevec_free -e kmem:mm_page_free_direct ./hackbench 10
+ Time: 0.575
+
+ Performance counter stats for './hackbench 10':
+
+ 13857 kmem:mm_page_pcpu_drain
+ 27576 kmem:mm_page_alloc
+ 6025 kmem:mm_pagevec_free
+ 20934 kmem:mm_page_free_direct
+
+ 0.613972165 seconds time elapsed
+
+You can observe the statistical properties as well, by using the
+'repeat the workload N times' feature of perf stat:
+
+ titan:~> perf stat --repeat 5 -e kmem:mm_page_pcpu_drain -e
+ kmem:mm_page_alloc -e kmem:mm_pagevec_free -e
+ kmem:mm_page_free_direct ./hackbench 10
+ Time: 0.627
+ Time: 0.644
+ Time: 0.564
+ Time: 0.559
+ Time: 0.626
+
+ Performance counter stats for './hackbench 10' (5 runs):
+
+ 12920 kmem:mm_page_pcpu_drain ( +- 3.359% )
+ 25035 kmem:mm_page_alloc ( +- 3.783% )
+ 6104 kmem:mm_pagevec_free ( +- 0.934% )
+ 18376 kmem:mm_page_free_direct ( +- 4.941% )
+
+ 0.643954516 seconds time elapsed ( +- 2.363% )
+
+Furthermore, these tracepoints can be used to sample the workload as
+well. For example the page allocations done by a 'git gc' can be
+captured the following way:
+
+ titan:~/git> perf record -f -e kmem:mm_page_alloc -c 1 ./git gc
+ Counting objects: 1148, done.
+ Delta compression using up to 2 threads.
+ Compressing objects: 100% (450/450), done.
+ Writing objects: 100% (1148/1148), done.
+ Total 1148 (delta 690), reused 1148 (delta 690)
+ [ perf record: Captured and wrote 0.267 MB perf.data (~11679 samples) ]
+
+To check which functions generated page allocations:
+
+ titan:~/git> perf report
+ # Samples: 10646
+ #
+ # Overhead Command Shared Object
+ # ........ ............... ..........................
+ #
+ 23.57% git-repack /lib64/libc-2.5.so
+ 21.81% git /lib64/libc-2.5.so
+ 14.59% git ./git
+ 11.79% git-repack ./git
+ 7.12% git /lib64/ld-2.5.so
+ 3.16% git-repack /lib64/libpthread-2.5.so
+ 2.09% git-repack /bin/bash
+ 1.97% rm /lib64/libc-2.5.so
+ 1.39% mv /lib64/ld-2.5.so
+ 1.37% mv /lib64/libc-2.5.so
+ 1.12% git-repack /lib64/ld-2.5.so
+ 0.95% rm /lib64/ld-2.5.so
+ 0.90% git-update-serv /lib64/libc-2.5.so
+ 0.73% git-update-serv /lib64/ld-2.5.so
+ 0.68% perf /lib64/libpthread-2.5.so
+ 0.64% git-repack /usr/lib64/libz.so.1.2.3
+
+Or to see it on a more finegrained level:
+
+titan:~/git> perf report --sort comm,dso,symbol
+# Samples: 10646
+#
+# Overhead Command Shared Object Symbol
+# ........ ............... .......................... ......
+#
+ 9.35% git-repack ./git [.] insert_obj_hash
+ 9.12% git ./git [.] insert_obj_hash
+ 7.31% git /lib64/libc-2.5.so [.] memcpy
+ 6.34% git-repack /lib64/libc-2.5.so [.] _int_malloc
+ 6.24% git-repack /lib64/libc-2.5.so [.] memcpy
+ 5.82% git-repack /lib64/libc-2.5.so [.] __GI___fork
+ 5.47% git /lib64/libc-2.5.so [.] _int_malloc
+ 2.99% git /lib64/libc-2.5.so [.] memset
+
+Furthermore, call-graph sampling can be done too, of page
+allocations - to see precisely what kind of page allocations there
+are:
+
+ titan:~/git> perf record -f -g -e kmem:mm_page_alloc -c 1 ./git gc
+ Counting objects: 1148, done.
+ Delta compression using up to 2 threads.
+ Compressing objects: 100% (450/450), done.
+ Writing objects: 100% (1148/1148), done.
+ Total 1148 (delta 690), reused 1148 (delta 690)
+ [ perf record: Captured and wrote 0.963 MB perf.data (~42069 samples) ]
+
+ titan:~/git> perf report -g
+ # Samples: 10686
+ #
+ # Overhead Command Shared Object
+ # ........ ............... ..........................
+ #
+ 23.25% git-repack /lib64/libc-2.5.so
+ |
+ |--50.00%-- _int_free
+ |
+ |--37.50%-- __GI___fork
+ | make_child
+ |
+ |--12.50%-- ptmalloc_unlock_all2
+ | make_child
+ |
+ --6.25%-- __GI_strcpy
+ 21.61% git /lib64/libc-2.5.so
+ |
+ |--30.00%-- __GI_read
+ | |
+ | --83.33%-- git_config_from_file
+ | git_config
+ | |
+ [...]
+
+Or you can observe the whole system's page allocations for 10
+seconds:
+
+titan:~/git> perf stat -a -e kmem:mm_page_pcpu_drain -e
+kmem:mm_page_alloc -e kmem:mm_pagevec_free -e
+kmem:mm_page_free_direct sleep 10
+
+ Performance counter stats for 'sleep 10':
+
+ 171585 kmem:mm_page_pcpu_drain
+ 322114 kmem:mm_page_alloc
+ 73623 kmem:mm_pagevec_free
+ 254115 kmem:mm_page_free_direct
+
+ 10.000591410 seconds time elapsed
+
+Or observe how fluctuating the page allocations are, via statistical
+analysis done over ten 1-second intervals:
+
+ titan:~/git> perf stat --repeat 10 -a -e kmem:mm_page_pcpu_drain -e
+ kmem:mm_page_alloc -e kmem:mm_pagevec_free -e
+ kmem:mm_page_free_direct sleep 1
+
+ Performance counter stats for 'sleep 1' (10 runs):
+
+ 17254 kmem:mm_page_pcpu_drain ( +- 3.709% )
+ 34394 kmem:mm_page_alloc ( +- 4.617% )
+ 7509 kmem:mm_pagevec_free ( +- 4.820% )
+ 25653 kmem:mm_page_free_direct ( +- 3.672% )
+
+ 1.058135029 seconds time elapsed ( +- 3.089% )
+
+Or you can annotate the recorded 'git gc' run on a per symbol basis
+and check which instructions/source-code generated page allocations:
+
+ titan:~/git> perf annotate __GI___fork
+ ------------------------------------------------
+ Percent | Source code & Disassembly of libc-2.5.so
+ ------------------------------------------------
+ :
+ :
+ : Disassembly of section .plt:
+ : Disassembly of section .text:
+ :
+ : 00000031a2e95560 <__fork>:
+ [...]
+ 0.00 : 31a2e95602: b8 38 00 00 00 mov $0x38,%eax
+ 0.00 : 31a2e95607: 0f 05 syscall
+ 83.42 : 31a2e95609: 48 3d 00 f0 ff ff cmp $0xfffffffffffff000,%rax
+ 0.00 : 31a2e9560f: 0f 87 4d 01 00 00 ja 31a2e95762 <__fork+0x202>
+ 0.00 : 31a2e95615: 85 c0 test %eax,%eax
+
+( this shows that 83.42% of __GI___fork's page allocations come from
+ the 0x38 system call it performs. )
+
+etc. etc. - a lot more is possible. I could list a dozen of
+other different usecases straight away - neither of which is
+possible via /proc/vmstat.
+
+/proc/vmstat is not in the same league really, in terms of
+expressive power of system analysis and performance
+analysis.
+
+All that the above results needed were those new tracepoints
+in include/tracing/events/kmem.h.
+
+ Ingo
+
+
diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt
index 1dbc1eeb4c0..6be696b0a2b 100644
--- a/tools/perf/Documentation/perf-record.txt
+++ b/tools/perf/Documentation/perf-record.txt
@@ -29,13 +29,67 @@ OPTIONS
Select the PMU event. Selection can be a symbolic event name
(use 'perf list' to list all events) or a raw PMU
event (eventsel+umask) in the form of rNNN where NNN is a
- hexadecimal event descriptor.
+ hexadecimal event descriptor.
-a::
- system-wide collection
+ System-wide collection.
-l::
- scale counter values
+ Scale counter values.
+
+-p::
+--pid=::
+ Record events on existing pid.
+
+-r::
+--realtime=::
+ Collect data with this RT SCHED_FIFO priority.
+-A::
+--append::
+ Append to the output file to do incremental profiling.
+
+-f::
+--force::
+ Overwrite existing data file.
+
+-c::
+--count=::
+ Event period to sample.
+
+-o::
+--output=::
+ Output file name.
+
+-i::
+--inherit::
+ Child tasks inherit counters.
+-F::
+--freq=::
+ Profile at this frequency.
+
+-m::
+--mmap-pages=::
+ Number of mmap data pages.
+
+-g::
+--call-graph::
+ Do call-graph (stack chain/backtrace) recording.
+
+-v::
+--verbose::
+ Be more verbose (show counter open errors, etc).
+
+-s::
+--stat::
+ Per thread counts.
+
+-d::
+--data::
+ Sample addresses.
+
+-n::
+--no-samples::
+ Don't sample.
SEE ALSO
--------
diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt
index 8aa3f8c8870..e72e9311078 100644
--- a/tools/perf/Documentation/perf-report.txt
+++ b/tools/perf/Documentation/perf-report.txt
@@ -24,6 +24,9 @@ OPTIONS
--dsos=::
Only consider symbols in these dsos. CSV that understands
file://filename entries.
+-n
+--show-nr-samples
+ Show the number of samples for each symbol
-C::
--comms=::
Only consider symbols in these comms. CSV that understands
@@ -33,6 +36,18 @@ OPTIONS
Only consider these symbols. CSV that understands
file://filename entries.
+-w::
+--field-width=::
+ Force each column width to the provided list, for large terminal
+ readability.
+
+-t::
+--field-separator=::
+
+ Use a special separator character and don't pad with spaces, replacing
+ all occurances of this separator in symbol names (and other output)
+ with a '.' character, that thus it's the only non valid separator.
+
SEE ALSO
--------
linkperf:perf-stat[1]
diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt
index 0d74346d21a..484080dd5b6 100644
--- a/tools/perf/Documentation/perf-stat.txt
+++ b/tools/perf/Documentation/perf-stat.txt
@@ -40,7 +40,7 @@ OPTIONS
-a::
system-wide collection
--S::
+-c::
scale counter values
EXAMPLES
diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt
index 539d0128972..4a7d558dc30 100644
--- a/tools/perf/Documentation/perf-top.txt
+++ b/tools/perf/Documentation/perf-top.txt
@@ -3,36 +3,122 @@ perf-top(1)
NAME
----
-perf-top - Run a command and profile it
+perf-top - System profiling tool.
SYNOPSIS
--------
[verse]
-'perf top' [-e <EVENT> | --event=EVENT] [-l] [-a] <command>
+'perf top' [-e <EVENT> | --event=EVENT] [<options>]
DESCRIPTION
-----------
-This command runs a command and gathers a performance counter profile
-from it.
+This command generates and displays a performance counter profile in realtime.
OPTIONS
-------
-<command>...::
- Any command you can specify in a shell.
+-a::
+--all-cpus::
+ System-wide collection. (default)
+
+-c <count>::
+--count=<count>::
+ Event period to sample.
+
+-C <cpu>::
+--CPU=<cpu>::
+ CPU to profile.
+
+-d <seconds>::
+--delay=<seconds>::
+ Number of seconds to delay between refreshes.
--e::
---event=::
+-e <event>::
+--event=<event>::
Select the PMU event. Selection can be a symbolic event name
(use 'perf list' to list all events) or a raw PMU
event (eventsel+umask) in the form of rNNN where NNN is a
- hexadecimal event descriptor.
+ hexadecimal event descriptor.
--a::
- system-wide collection
+-E <entries>::
+--entries=<entries>::
+ Display this many functions.
+
+-f <count>::
+--count-filter=<count>::
+ Only display functions with more events than this.
+
+-F <freq>::
+--freq=<freq>::
+ Profile at this frequency.
+
+-i::
+--inherit::
+ Child tasks inherit counters, only makes sens with -p option.
+
+-k <path>::
+--vmlinux=<path>::
+ Path to vmlinux. Required for annotation functionality.
+
+-m <pages>::
+--mmap-pages=<pages>::
+ Number of mmapped data pages.
+
+-p <pid>::
+--pid=<pid>::
+ Profile events on existing pid.
+
+-r <priority>::
+--realtime=<priority>::
+ Collect data with this RT SCHED_FIFO priority.
+
+-s <symbol>::
+--sym-annotate=<symbol>::
+ Annotate this symbol. Requires -k option.
+
+-v::
+--verbose::
+ Be more verbose (show counter open errors, etc).
+
+-z::
+--zero::
+ Zero history across display updates.
+
+INTERACTIVE PROMPTING KEYS
+--------------------------
+
+[d]::
+ Display refresh delay.
+
+[e]::
+ Number of entries to display.
+
+[E]::
+ Event to display when multiple counters are active.
+
+[f]::
+ Profile display filter (>= hit count).
+
+[F]::
+ Annotation display filter (>= % of total).
+
+[s]::
+ Annotate symbol.
+
+[S]::
+ Stop annotation, return to full profile display.
+
+[w]::
+ Toggle between weighted sum and individual count[E]r profile.
+
+[z]::
+ Toggle event count zeroing across display updates.
+
+[qQ]::
+ Quit.
+
+Pressing any unmapped key displays a menu, and prompts for input.
--l::
- scale counter values
SEE ALSO
--------
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index 9c6d0ae3708..c045b4271e5 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -158,13 +158,15 @@ uname_P := $(shell sh -c 'uname -p 2>/dev/null || echo not')
uname_V := $(shell sh -c 'uname -v 2>/dev/null || echo not')
# If we're on a 64-bit kernel, use -m64
-ifneq ($(patsubst %64,%,$(uname_M)),$(uname_M))
- M64 := -m64
+ifndef NO_64BIT
+ ifneq ($(patsubst %64,%,$(uname_M)),$(uname_M))
+ M64 := -m64
+ endif
endif
# CFLAGS and LDFLAGS are for the users to override from the command line.
-CFLAGS = $(M64) -ggdb3 -Wall -Wstrict-prototypes -Wmissing-declarations -Wmissing-prototypes -std=gnu99 -Wdeclaration-after-statement -Werror -O6
+CFLAGS = $(M64) -ggdb3 -Wall -Wextra -Wstrict-prototypes -Wmissing-declarations -Wmissing-prototypes -std=gnu99 -Wdeclaration-after-statement -Werror -O6
LDFLAGS = -lpthread -lrt -lelf -lm
ALL_CFLAGS = $(CFLAGS)
ALL_LDFLAGS = $(LDFLAGS)
@@ -223,7 +225,7 @@ SPARSE_FLAGS = -D__BIG_ENDIAN__ -D__powerpc__
# Those must not be GNU-specific; they are shared with perl/ which may
# be built by a different compiler. (Note that this is an artifact now
# but it still might be nice to keep that distinction.)
-BASIC_CFLAGS =
+BASIC_CFLAGS = -Iutil/include
BASIC_LDFLAGS =
# Guard against environment variables
@@ -289,10 +291,11 @@ export PERL_PATH
LIB_FILE=libperf.a
LIB_H += ../../include/linux/perf_counter.h
+LIB_H += ../../include/linux/rbtree.h
+LIB_H += ../../include/linux/list.h
+LIB_H += util/include/linux/list.h
LIB_H += perf.h
LIB_H += util/types.h
-LIB_H += util/list.h
-LIB_H += util/rbtree.h
LIB_H += util/levenshtein.h
LIB_H += util/parse-options.h
LIB_H += util/parse-events.h
@@ -305,6 +308,7 @@ LIB_H += util/strlist.h
LIB_H += util/run-command.h
LIB_H += util/sigchain.h
LIB_H += util/symbol.h
+LIB_H += util/module.h
LIB_H += util/color.h
LIB_OBJS += util/abspath.o
@@ -328,6 +332,7 @@ LIB_OBJS += util/usage.o
LIB_OBJS += util/wrapper.o
LIB_OBJS += util/sigchain.o
LIB_OBJS += util/symbol.o
+LIB_OBJS += util/module.o
LIB_OBJS += util/color.o
LIB_OBJS += util/pager.o
LIB_OBJS += util/header.o
@@ -342,7 +347,6 @@ BUILTIN_OBJS += builtin-stat.o
BUILTIN_OBJS += builtin-top.o
PERFLIBS = $(LIB_FILE)
-EXTLIBS =
#
# Platform specific tweaks
@@ -371,6 +375,39 @@ ifeq ($(uname_S),Darwin)
PTHREAD_LIBS =
endif
+ifneq ($(shell sh -c "(echo '\#include <libelf.h>'; echo 'int main(void) { Elf * elf = elf_begin(0, ELF_C_READ_MMAP, 0); return (long)elf; }') | $(CC) -x c - $(ALL_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -o /dev/null $(ALL_LDFLAGS) > /dev/null 2>&1 && echo y"), y)
+ msg := $(error No libelf.h/libelf found, please install libelf-dev/elfutils-libelf-devel);
+endif
+
+ifdef NO_DEMANGLE
+ BASIC_CFLAGS += -DNO_DEMANGLE
+else
+ has_bfd := $(shell sh -c "(echo '\#include <bfd.h>'; echo 'int main(void) { bfd_demangle(0, 0, 0); return 0; }') | $(CC) -x c - $(ALL_CFLAGS) -o /dev/null $(ALL_LDFLAGS) -lbfd > /dev/null 2>&1 && echo y")
+
+ ifeq ($(has_bfd),y)
+ EXTLIBS += -lbfd
+ else
+ has_bfd_iberty := $(shell sh -c "(echo '\#include <bfd.h>'; echo 'int main(void) { bfd_demangle(0, 0, 0); return 0; }') | $(CC) -x c - $(ALL_CFLAGS) -o /dev/null $(ALL_LDFLAGS) -lbfd -liberty > /dev/null 2>&1 && echo y")
+ ifeq ($(has_bfd_iberty),y)
+ EXTLIBS += -lbfd -liberty
+ else
+ has_bfd_iberty_z := $(shell sh -c "(echo '\#include <bfd.h>'; echo 'int main(void) { bfd_demangle(0, 0, 0); return 0; }') | $(CC) -x c - $(ALL_CFLAGS) -o /dev/null $(ALL_LDFLAGS) -lbfd -liberty -lz > /dev/null 2>&1 && echo y")
+ ifeq ($(has_bfd_iberty_z),y)
+ EXTLIBS += -lbfd -liberty -lz
+ else
+ has_cplus_demangle := $(shell sh -c "(echo 'extern char *cplus_demangle(const char *, int);'; echo 'int main(void) { cplus_demangle(0, 0); return 0; }') | $(CC) -x c - $(ALL_CFLAGS) -o /dev/null $(ALL_LDFLAGS) -liberty > /dev/null 2>&1 && echo y")
+ ifeq ($(has_cplus_demangle),y)
+ EXTLIBS += -liberty
+ BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE
+ else
+ msg := $(warning No bfd.h/libbfd found, install binutils-dev[el] to gain symbol demangling)
+ BASIC_CFLAGS += -DNO_DEMANGLE
+ endif
+ endif
+ endif
+ endif
+endif
+
ifndef CC_LD_DYNPATH
ifdef NO_R_TO_GCC_LINKER
# Some gcc does not accept and pass -R to the linker to specify
@@ -381,12 +418,6 @@ ifndef CC_LD_DYNPATH
endif
endif
-ifdef ZLIB_PATH
- BASIC_CFLAGS += -I$(ZLIB_PATH)/include
- EXTLIBS += -L$(ZLIB_PATH)/$(lib) $(CC_LD_DYNPATH)$(ZLIB_PATH)/$(lib)
-endif
-EXTLIBS += -lz
-
ifdef NEEDS_SOCKET
EXTLIBS += -lsocket
endif
@@ -697,6 +728,9 @@ builtin-init-db.o: builtin-init-db.c PERF-CFLAGS
util/config.o: util/config.c PERF-CFLAGS
$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $<
+util/rbtree.o: ../../lib/rbtree.c PERF-CFLAGS
+ $(QUIET_CC)$(CC) -o util/rbtree.o -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $<
+
perf-%$X: %.o $(PERFLIBS)
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index 722c0f54e54..1dba568e194 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -10,9 +10,9 @@
#include "util/util.h"
#include "util/color.h"
-#include "util/list.h"
+#include <linux/list.h>
#include "util/cache.h"
-#include "util/rbtree.h"
+#include <linux/rbtree.h>
#include "util/symbol.h"
#include "util/string.h"
@@ -25,10 +25,6 @@
#define SHOW_USER 2
#define SHOW_HV 4
-#define MIN_GREEN 0.5
-#define MIN_RED 5.0
-
-
static char const *input_name = "perf.data";
static char *vmlinux = "vmlinux";
@@ -43,6 +39,10 @@ static int dump_trace = 0;
static int verbose;
+static int modules;
+
+static int full_paths;
+
static int print_line;
static unsigned long page_size;
@@ -74,20 +74,12 @@ struct fork_event {
u32 pid, ppid;
};
-struct period_event {
- struct perf_event_header header;
- u64 time;
- u64 id;
- u64 sample_period;
-};
-
typedef union event_union {
struct perf_event_header header;
struct ip_event ip;
struct mmap_event mmap;
struct comm_event comm;
struct fork_event fork;
- struct period_event period;
} event_t;
@@ -160,7 +152,7 @@ static void dsos__fprintf(FILE *fp)
static struct symbol *vdso__find_symbol(struct dso *dso, u64 ip)
{
- return dso__find_symbol(kernel_dso, ip);
+ return dso__find_symbol(dso, ip);
}
static int load_kernel(void)
@@ -171,8 +163,8 @@ static int load_kernel(void)
if (!kernel_dso)
return -1;
- err = dso__load_kernel(kernel_dso, vmlinux, NULL, verbose);
- if (err) {
+ err = dso__load_kernel(kernel_dso, vmlinux, NULL, verbose, modules);
+ if (err <= 0) {
dso__delete(kernel_dso);
kernel_dso = NULL;
} else
@@ -203,7 +195,7 @@ static u64 map__map_ip(struct map *map, u64 ip)
return ip - map->start + map->pgoff;
}
-static u64 vdso__map_ip(struct map *map, u64 ip)
+static u64 vdso__map_ip(struct map *map __used, u64 ip)
{
return ip;
}
@@ -600,7 +592,7 @@ static LIST_HEAD(hist_entry__sort_list);
static int sort_dimension__add(char *tok)
{
- int i;
+ unsigned int i;
for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) {
struct sort_dimension *sd = &sort_dimensions[i];
@@ -998,19 +990,6 @@ process_fork_event(event_t *event, unsigned long offset, unsigned long head)
}
static int
-process_period_event(event_t *event, unsigned long offset, unsigned long head)
-{
- dprintf("%p [%p]: PERF_EVENT_PERIOD: time:%Ld, id:%Ld: period:%Ld\n",
- (void *)(offset + head),
- (void *)(long)(event->header.size),
- event->period.time,
- event->period.id,
- event->period.sample_period);
-
- return 0;
-}
-
-static int
process_event(event_t *event, unsigned long offset, unsigned long head)
{
switch (event->header.type) {
@@ -1025,9 +1004,6 @@ process_event(event_t *event, unsigned long offset, unsigned long head)
case PERF_EVENT_FORK:
return process_fork_event(event, offset, head);
-
- case PERF_EVENT_PERIOD:
- return process_period_event(event, offset, head);
/*
* We dont process them right now but they are fine:
*/
@@ -1043,24 +1019,6 @@ process_event(event_t *event, unsigned long offset, unsigned long head)
return 0;
}
-static char *get_color(double percent)
-{
- char *color = PERF_COLOR_NORMAL;
-
- /*
- * We color high-overhead entries in red, mid-overhead
- * entries in green - and keep the low overhead places
- * normal:
- */
- if (percent >= MIN_RED)
- color = PERF_COLOR_RED;
- else {
- if (percent > MIN_GREEN)
- color = PERF_COLOR_GREEN;
- }
- return color;
-}
-
static int
parse_line(FILE *file, struct symbol *sym, u64 start, u64 len)
{
@@ -1069,7 +1027,7 @@ parse_line(FILE *file, struct symbol *sym, u64 start, u64 len)
static const char *prev_color;
unsigned int offset;
size_t line_len;
- u64 line_ip;
+ s64 line_ip;
int ret;
char *c;
@@ -1122,7 +1080,7 @@ parse_line(FILE *file, struct symbol *sym, u64 start, u64 len)
} else if (sym->hist_sum)
percent = 100.0 * hits / sym->hist_sum;
- color = get_color(percent);
+ color = get_percent_color(percent);
/*
* Also color the filename and line if needed, with
@@ -1258,7 +1216,7 @@ static void print_summary(char *filename)
sym_ext = rb_entry(node, struct sym_ext, node);
percent = sym_ext->percent;
- color = get_color(percent);
+ color = get_percent_color(percent);
path = sym_ext->path;
color_fprintf(stdout, color, " %7.2f %s", percent, path);
@@ -1268,19 +1226,25 @@ static void print_summary(char *filename)
static void annotate_sym(struct dso *dso, struct symbol *sym)
{
- char *filename = dso->name;
+ char *filename = dso->name, *d_filename;
u64 start, end, len;
char command[PATH_MAX*2];
FILE *file;
if (!filename)
return;
- if (dso == kernel_dso)
+ if (sym->module)
+ filename = sym->module->path;
+ else if (dso == kernel_dso)
filename = vmlinux;
start = sym->obj_start;
if (!start)
start = sym->start;
+ if (full_paths)
+ d_filename = filename;
+ else
+ d_filename = basename(filename);
end = start + sym->end - sym->start + 1;
len = sym->end - sym->start;
@@ -1291,13 +1255,14 @@ static void annotate_sym(struct dso *dso, struct symbol *sym)
}
printf("\n\n------------------------------------------------\n");
- printf(" Percent | Source code & Disassembly of %s\n", filename);
+ printf(" Percent | Source code & Disassembly of %s\n", d_filename);
printf("------------------------------------------------\n");
if (verbose >= 2)
printf("annotating [%p] %30s : [%p] %30s\n", dso, dso->name, sym, sym->name);
- sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s", (u64)start, (u64)end, filename);
+ sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s|grep -v %s",
+ (u64)start, (u64)end, filename, filename);
if (verbose >= 3)
printf("doing: %s\n", command);
@@ -1428,7 +1393,7 @@ more:
head += size;
- if (offset + head < stat.st_size)
+ if (offset + head < (unsigned long)stat.st_size)
goto more;
rc = EXIT_SUCCESS;
@@ -1472,8 +1437,12 @@ static const struct option options[] = {
OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
"dump raw trace in ASCII"),
OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"),
+ OPT_BOOLEAN('m', "modules", &modules,
+ "load module symbols - WARNING: use only with -k and LIVE kernel"),
OPT_BOOLEAN('l', "print-line", &print_line,
"print matching source lines (may be slow)"),
+ OPT_BOOLEAN('P', "full-paths", &full_paths,
+ "Don't shorten the displayed pathnames"),
OPT_END()
};
@@ -1492,7 +1461,7 @@ static void setup_sorting(void)
free(str);
}
-int cmd_annotate(int argc, const char **argv, const char *prefix)
+int cmd_annotate(int argc, const char **argv, const char *prefix __used)
{
symbol__init();
diff --git a/tools/perf/builtin-help.c b/tools/perf/builtin-help.c
index 0f32dc3f3c4..2599d86a733 100644
--- a/tools/perf/builtin-help.c
+++ b/tools/perf/builtin-help.c
@@ -3,6 +3,7 @@
*
* Builtin help command
*/
+#include "perf.h"
#include "util/cache.h"
#include "builtin.h"
#include "util/exec_cmd.h"
@@ -277,7 +278,7 @@ static struct cmdnames main_cmds, other_cmds;
void list_common_cmds_help(void)
{
- int i, longest = 0;
+ unsigned int i, longest = 0;
for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
if (longest < strlen(common_cmds[i].name))
@@ -415,9 +416,10 @@ static void show_html_page(const char *perf_cmd)
open_html(page_path.buf);
}
-int cmd_help(int argc, const char **argv, const char *prefix)
+int cmd_help(int argc, const char **argv, const char *prefix __used)
{
const char *alias;
+
load_command_list("perf-", &main_cmds, &other_cmds);
perf_config(perf_help_config, NULL);
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 @@
#include "perf.h"
-#include "util/parse-options.h"
#include "util/parse-events.h"
+#include "util/cache.h"
-int cmd_list(int argc, const char **argv, const char *prefix)
+int cmd_list(int argc __used, const char **argv __used, const char *prefix __used)
{
+ setup_pager();
print_events();
return 0;
}
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index d18546f37d7..3d051b9cf25 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -34,7 +34,9 @@ static int output;
static const char *output_name = "perf.data";
static int group = 0;
static unsigned int realtime_prio = 0;
+static int raw_samples = 0;
static int system_wide = 0;
+static int profile_cpu = -1;
static pid_t target_pid = -1;
static int inherit = 1;
static int force = 0;
@@ -43,6 +45,7 @@ static int call_graph = 0;
static int verbose = 0;
static int inherit_stat = 0;
static int no_samples = 0;
+static int sample_address = 0;
static long samples;
static struct timeval last_read;
@@ -202,46 +205,48 @@ static void sig_atexit(void)
kill(getpid(), signr);
}
-static void pid_synthesize_comm_event(pid_t pid, int full)
+static pid_t pid_synthesize_comm_event(pid_t pid, int full)
{
struct comm_event comm_ev;
char filename[PATH_MAX];
char bf[BUFSIZ];
- int fd;
- size_t size;
- char *field, *sep;
+ FILE *fp;
+ size_t size = 0;
DIR *tasks;
struct dirent dirent, *next;
+ pid_t tgid = 0;
- snprintf(filename, sizeof(filename), "/proc/%d/stat", pid);
+ snprintf(filename, sizeof(filename), "/proc/%d/status", pid);
- fd = open(filename, O_RDONLY);
- if (fd < 0) {
+ fp = fopen(filename, "r");
+ if (fd == NULL) {
/*
* We raced with a task exiting - just return:
*/
if (verbose)
fprintf(stderr, "couldn't open %s\n", filename);
- return;
+ return 0;
}
- if (read(fd, bf, sizeof(bf)) < 0) {
- fprintf(stderr, "couldn't read %s\n", filename);
- exit(EXIT_FAILURE);
- }
- close(fd);
- /* 9027 (cat) R 6747 9027 6747 34816 9027 ... */
memset(&comm_ev, 0, sizeof(comm_ev));
- field = strchr(bf, '(');
- if (field == NULL)
- goto out_failure;
- sep = strchr(++field, ')');
- if (sep == NULL)
- goto out_failure;
- size = sep - field;
- memcpy(comm_ev.comm, field, size++);
-
- comm_ev.pid = pid;
+ while (!comm_ev.comm[0] || !comm_ev.pid) {
+ if (fgets(bf, sizeof(bf), fp) == NULL)
+ goto out_failure;
+
+ if (memcmp(bf, "Name:", 5) == 0) {
+ char *name = bf + 5;
+ while (*name && isspace(*name))
+ ++name;
+ size = strlen(name) - 1;
+ memcpy(comm_ev.comm, name, size++);
+ } else if (memcmp(bf, "Tgid:", 5) == 0) {
+ char *tgids = bf + 5;
+ while (*tgids && isspace(*tgids))
+ ++tgids;
+ tgid = comm_ev.pid = atoi(tgids);
+ }
+ }
+
comm_ev.header.type = PERF_EVENT_COMM;
size = ALIGN(size, sizeof(u64));
comm_ev.header.size = sizeof(comm_ev) - (sizeof(comm_ev.comm) - size);
@@ -250,7 +255,7 @@ static void pid_synthesize_comm_event(pid_t pid, int full)
comm_ev.tid = pid;
write_output(&comm_ev, comm_ev.header.size);
- return;
+ goto out_fclose;
}
snprintf(filename, sizeof(filename), "/proc/%d/task", pid);
@@ -267,7 +272,10 @@ static void pid_synthesize_comm_event(pid_t pid, int full)
write_output(&comm_ev, comm_ev.header.size);
}
closedir(tasks);
- return;
+
+out_fclose:
+ fclose(fp);
+ return tgid;
out_failure:
fprintf(stderr, "couldn't get COMM and pgid, malformed %s\n",
@@ -275,7 +283,7 @@ out_failure:
exit(EXIT_FAILURE);
}
-static void pid_synthesize_mmap_samples(pid_t pid)
+static void pid_synthesize_mmap_samples(pid_t pid, pid_t tgid)
{
char filename[PATH_MAX];
FILE *fp;
@@ -294,7 +302,7 @@ static void pid_synthesize_mmap_samples(pid_t pid)
while (1) {
char bf[BUFSIZ], *pbf = bf;
struct mmap_event mmap_ev = {
- .header.type = PERF_EVENT_MMAP,
+ .header = { .type = PERF_EVENT_MMAP },
};
int n;
size_t size;
@@ -313,6 +321,10 @@ static void pid_synthesize_mmap_samples(pid_t pid)
if (*pbf == 'x') { /* vm_exec */
char *execname = strchr(bf, '/');
+ /* Catch VDSO */
+ if (execname == NULL)
+ execname = strstr(bf, "[vdso]");
+
if (execname == NULL)
continue;
@@ -323,7 +335,7 @@ static void pid_synthesize_mmap_samples(pid_t pid)
mmap_ev.len -= mmap_ev.start;
mmap_ev.header.size = (sizeof(mmap_ev) -
(sizeof(mmap_ev.filename) - size));
- mmap_ev.pid = pid;
+ mmap_ev.pid = tgid;
mmap_ev.tid = pid;
write_output(&mmap_ev, mmap_ev.header.size);
@@ -342,14 +354,14 @@ static void synthesize_all(void)
while (!readdir_r(proc, &dirent, &next) && next) {
char *end;
- pid_t pid;
+ pid_t pid, tgid;
pid = strtol(dirent.d_name, &end, 10);
if (*end) /* only interested in proper numerical dirents */
continue;
- pid_synthesize_comm_event(pid, 1);
- pid_synthesize_mmap_samples(pid);
+ tgid = pid_synthesize_comm_event(pid, 1);
+ pid_synthesize_mmap_samples(pid, tgid);
}
closedir(proc);
@@ -387,7 +399,7 @@ static void create_counter(int counter, int cpu, pid_t pid)
PERF_FORMAT_TOTAL_TIME_RUNNING |
PERF_FORMAT_ID;
- attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID;
+ attr->sample_type |= PERF_SAMPLE_IP | PERF_SAMPLE_TID;
if (freq) {
attr->sample_type |= PERF_SAMPLE_PERIOD;
@@ -401,9 +413,15 @@ static void create_counter(int counter, int cpu, pid_t pid)
if (inherit_stat)
attr->inherit_stat = 1;
+ if (sample_address)
+ attr->sample_type |= PERF_SAMPLE_ADDR;
+
if (call_graph)
attr->sample_type |= PERF_SAMPLE_CALLCHAIN;
+ if (raw_samples)
+ attr->sample_type |= PERF_SAMPLE_RAW;
+
attr->mmap = track;
attr->comm = track;
attr->inherit = (cpu < 0) && inherit;
@@ -417,6 +435,8 @@ try_again:
if (err == EPERM)
die("Permission error - are you root?\n");
+ else if (err == ENODEV && profile_cpu != -1)
+ die("No such device - did you specify an out-of-range profile CPU?\n");
/*
* If it's cycles then fall back to hrtimer
@@ -516,10 +536,14 @@ static int __cmd_record(int argc, const char **argv)
signal(SIGCHLD, sig_handler);
signal(SIGINT, sig_handler);
- if (!stat(output_name, &st) && !force && !append_file) {
- fprintf(stderr, "Error, output file %s exists, use -A to append or -f to overwrite.\n",
- output_name);
- exit(-1);
+ if (!stat(output_name, &st) && st.st_size) {
+ if (!force && !append_file) {
+ fprintf(stderr, "Error, output file %s exists, use -A to append or -f to overwrite.\n",
+ output_name);
+ exit(-1);
+ }
+ } else {
+ append_file = 0;
}
flags = O_CREAT|O_RDWR;
@@ -546,16 +570,22 @@ static int __cmd_record(int argc, const char **argv)
if (pid == -1)
pid = getpid();
- open_counters(-1, pid);
- } else for (i = 0; i < nr_cpus; i++)
- open_counters(i, target_pid);
+ open_counters(profile_cpu, pid);
+ } else {
+ if (profile_cpu != -1) {
+ open_counters(profile_cpu, target_pid);
+ } else {
+ for (i = 0; i < nr_cpus; i++)
+ open_counters(i, target_pid);
+ }
+ }
if (file_new)
perf_header__write(header, output);
if (!system_wide) {
- pid_synthesize_comm_event(pid, 0);
- pid_synthesize_mmap_samples(pid);
+ pid_t tgid = pid_synthesize_comm_event(pid, 0);
+ pid_synthesize_mmap_samples(pid, tgid);
} else
synthesize_all();
@@ -623,10 +653,14 @@ static const struct option options[] = {
"record events on existing pid"),
OPT_INTEGER('r', "realtime", &realtime_prio,
"collect data with this RT SCHED_FIFO priority"),
+ OPT_BOOLEAN('R', "raw-samples", &raw_samples,
+ "collect raw sample records from all opened counters"),
OPT_BOOLEAN('a', "all-cpus", &system_wide,
"system-wide collection from all CPUs"),
OPT_BOOLEAN('A', "append", &append_file,
"append to the output file to do incremental profiling"),
+ OPT_INTEGER('C', "profile_cpu", &profile_cpu,
+ "CPU to profile on"),
OPT_BOOLEAN('f', "force", &force,
"overwrite existing data file"),
OPT_LONG('c', "count", &default_interval,
@@ -645,16 +679,19 @@ static const struct option options[] = {
"be more verbose (show counter open errors, etc)"),
OPT_BOOLEAN('s', "stat", &inherit_stat,
"per thread counts"),
+ OPT_BOOLEAN('d', "data", &sample_address,
+ "Sample addresses"),
OPT_BOOLEAN('n', "no-samples", &no_samples,
"don't sample"),
OPT_END()
};
-int cmd_record(int argc, const char **argv, const char *prefix)
+int cmd_record(int argc, const char **argv, const char *prefix __used)
{
int counter;
- argc = parse_options(argc, argv, options, record_usage, 0);
+ argc = parse_options(argc, argv, options, record_usage,
+ PARSE_OPT_STOP_AT_NON_OPTION);
if (!argc && target_pid == -1 && !system_wide)
usage_with_options(record_usage, options);
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 135b7837e6b..b53a60fc12d 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -10,9 +10,9 @@
#include "util/util.h"
#include "util/color.h"
-#include "util/list.h"
+#include <linux/list.h>
#include "util/cache.h"
-#include "util/rbtree.h"
+#include <linux/rbtree.h>
#include "util/symbol.h"
#include "util/string.h"
#include "util/callchain.h"
@@ -31,10 +31,12 @@
static char const *input_name = "perf.data";
static char *vmlinux = NULL;
-static char default_sort_order[] = "comm,dso";
+static char default_sort_order[] = "comm,dso,symbol";
static char *sort_order = default_sort_order;
-static char *dso_list_str, *comm_list_str, *sym_list_str;
+static char *dso_list_str, *comm_list_str, *sym_list_str,
+ *col_width_list_str;
static struct strlist *dso_list, *comm_list, *sym_list;
+static char *field_sep;
static int input;
static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV;
@@ -46,7 +48,10 @@ static int dump_trace = 0;
static int verbose;
#define eprintf(x...) do { if (verbose) fprintf(stderr, x); } while (0)
+static int modules;
+
static int full_paths;
+static int show_nr_samples;
static unsigned long page_size;
static unsigned long mmap_window = 32;
@@ -56,8 +61,17 @@ static char *parent_pattern = default_parent_pattern;
static regex_t parent_regex;
static int exclude_other = 1;
+
+static char callchain_default_opt[] = "fractal,0.5";
+
static int callchain;
+static
+struct callchain_param callchain_param = {
+ .mode = CHAIN_GRAPH_REL,
+ .min_percent = 0.5
+};
+
static u64 sample_type;
struct ip_event {
@@ -85,13 +99,7 @@ struct comm_event {
struct fork_event {
struct perf_event_header header;
u32 pid, ppid;
-};
-
-struct period_event {
- struct perf_event_header header;
- u64 time;
- u64 id;
- u64 sample_period;
+ u32 tid, ptid;
};
struct lost_event {
@@ -104,7 +112,9 @@ struct read_event {
struct perf_event_header header;
u32 pid,tid;
u64 value;
- u64 format[3];
+ u64 time_enabled;
+ u64 time_running;
+ u64 id;
};
typedef union event_union {
@@ -113,14 +123,41 @@ typedef union event_union {
struct mmap_event mmap;
struct comm_event comm;
struct fork_event fork;
- struct period_event period;
struct lost_event lost;
struct read_event read;
} event_t;
+static int repsep_fprintf(FILE *fp, const char *fmt, ...)
+{
+ int n;
+ va_list ap;
+
+ va_start(ap, fmt);
+ if (!field_sep)
+ n = vfprintf(fp, fmt, ap);
+ else {
+ char *bf = NULL;
+ n = vasprintf(&bf, fmt, ap);
+ if (n > 0) {
+ char *sep = bf;
+ while (1) {
+ sep = strchr(sep, *field_sep);
+ if (sep == NULL)
+ break;
+ *sep = '.';
+ }
+ }
+ fputs(bf, fp);
+ free(bf);
+ }
+ va_end(ap);
+ return n;
+}
+
static LIST_HEAD(dsos);
static struct dso *kernel_dso;
static struct dso *vdso;
+static struct dso *hypervisor_dso;
static void dsos__add(struct dso *dso)
{
@@ -176,7 +213,7 @@ static void dsos__fprintf(FILE *fp)
static struct symbol *vdso__find_symbol(struct dso *dso, u64 ip)
{
- return dso__find_symbol(kernel_dso, ip);
+ return dso__find_symbol(dso, ip);
}
static int load_kernel(void)
@@ -187,8 +224,8 @@ static int load_kernel(void)
if (!kernel_dso)
return -1;
- err = dso__load_kernel(kernel_dso, vmlinux, NULL, verbose);
- if (err) {
+ err = dso__load_kernel(kernel_dso, vmlinux, NULL, verbose, modules);
+ if (err <= 0) {
dso__delete(kernel_dso);
kernel_dso = NULL;
} else
@@ -202,6 +239,11 @@ static int load_kernel(void)
dsos__add(vdso);
+ hypervisor_dso = dso__new("[hypervisor]", 0);
+ if (!hypervisor_dso)
+ return -1;
+ dsos__add(hypervisor_dso);
+
return err;
}
@@ -213,7 +255,7 @@ static int strcommon(const char *pathname)
{
int n = 0;
- while (pathname[n] == cwd[n] && n < cwdlen)
+ while (n < cwdlen && pathname[n] == cwd[n])
++n;
return n;
@@ -233,7 +275,7 @@ static u64 map__map_ip(struct map *map, u64 ip)
return ip - map->start + map->pgoff;
}
-static u64 vdso__map_ip(struct map *map, u64 ip)
+static u64 vdso__map_ip(struct map *map __used, u64 ip)
{
return ip;
}
@@ -343,12 +385,28 @@ static struct thread *thread__new(pid_t pid)
return self;
}
+static unsigned int dsos__col_width,
+ comms__col_width,
+ threads__col_width;
+
static int thread__set_comm(struct thread *self, const char *comm)
{
if (self->comm)
free(self->comm);
self->comm = strdup(comm);
- return self->comm ? 0 : -ENOMEM;
+ if (!self->comm)
+ return -ENOMEM;
+
+ if (!col_width_list_str && !field_sep &&
+ (!comm_list || strlist__has_entry(comm_list, comm))) {
+ unsigned int slen = strlen(comm);
+ if (slen > comms__col_width) {
+ comms__col_width = slen;
+ threads__col_width = slen + 6;
+ }
+ }
+
+ return 0;
}
static size_t thread__fprintf(struct thread *self, FILE *fp)
@@ -519,7 +577,9 @@ struct sort_entry {
int64_t (*cmp)(struct hist_entry *, struct hist_entry *);
int64_t (*collapse)(struct hist_entry *, struct hist_entry *);
- size_t (*print)(FILE *fp, struct hist_entry *);
+ size_t (*print)(FILE *fp, struct hist_entry *, unsigned int width);
+ unsigned int *width;
+ bool elide;
};
static int64_t cmp_null(void *l, void *r)
@@ -541,15 +601,17 @@ sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)
}
static size_t
-sort__thread_print(FILE *fp, struct hist_entry *self)
+sort__thread_print(FILE *fp, struct hist_entry *self, unsigned int width)
{
- return fprintf(fp, "%16s:%5d", self->thread->comm ?: "", self->thread->pid);
+ return repsep_fprintf(fp, "%*s:%5d", width - 6,
+ self->thread->comm ?: "", self->thread->pid);
}
static struct sort_entry sort_thread = {
- .header = " Command: Pid",
+ .header = "Command: Pid",
.cmp = sort__thread_cmp,
.print = sort__thread_print,
+ .width = &threads__col_width,
};
/* --sort comm */
@@ -573,16 +635,17 @@ sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)
}
static size_t
-sort__comm_print(FILE *fp, struct hist_entry *self)
+sort__comm_print(FILE *fp, struct hist_entry *self, unsigned int width)
{
- return fprintf(fp, "%16s", self->thread->comm);
+ return repsep_fprintf(fp, "%*s", width, self->thread->comm);
}
static struct sort_entry sort_comm = {
- .header = " Command",
+ .header = "Command",
.cmp = sort__comm_cmp,
.collapse = sort__comm_collapse,
.print = sort__comm_print,
+ .width = &comms__col_width,
};
/* --sort dso */
@@ -600,18 +663,19 @@ sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
}
static size_t
-sort__dso_print(FILE *fp, struct hist_entry *self)
+sort__dso_print(FILE *fp, struct hist_entry *self, unsigned int width)
{
if (self->dso)
- return fprintf(fp, "%-25s", self->dso->name);
+ return repsep_fprintf(fp, "%-*s", width, self->dso->name);
- return fprintf(fp, "%016llx ", (u64)self->ip);
+ return repsep_fprintf(fp, "%*llx", width, (u64)self->ip);
}
static struct sort_entry sort_dso = {
- .header = "Shared Object ",
+ .header = "Shared Object",
.cmp = sort__dso_cmp,
.print = sort__dso_print,
+ .width = &dsos__col_width,
};
/* --sort symbol */
@@ -631,18 +695,23 @@ sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
}
static size_t
-sort__sym_print(FILE *fp, struct hist_entry *self)
+sort__sym_print(FILE *fp, struct hist_entry *self, unsigned int width __used)
{
size_t ret = 0;
if (verbose)
- ret += fprintf(fp, "%#018llx ", (u64)self->ip);
+ ret += repsep_fprintf(fp, "%#018llx %c ", (u64)self->ip,
+ dso__symtab_origin(self->dso));
+ ret += repsep_fprintf(fp, "[%c] ", self->level);
if (self->sym) {
- ret += fprintf(fp, "[%c] %s",
- self->dso == kernel_dso ? 'k' : '.', self->sym->name);
+ ret += repsep_fprintf(fp, "%s", self->sym->name);
+
+ if (self->sym->module)
+ ret += repsep_fprintf(fp, "\t[%s]",
+ self->sym->module->name);
} else {
- ret += fprintf(fp, "%#016llx", (u64)self->ip);
+ ret += repsep_fprintf(fp, "%#016llx", (u64)self->ip);
}
return ret;
@@ -669,19 +738,19 @@ sort__parent_cmp(struct hist_entry *left, struct hist_entry *right)
}
static size_t
-sort__parent_print(FILE *fp, struct hist_entry *self)
+sort__parent_print(FILE *fp, struct hist_entry *self, unsigned int width)
{
- size_t ret = 0;
-
- ret += fprintf(fp, "%-20s", self->parent ? self->parent->name : "[other]");
-
- return ret;
+ return repsep_fprintf(fp, "%-*s", width,
+ self->parent ? self->parent->name : "[other]");
}
+static unsigned int parent_symbol__col_width;
+
static struct sort_entry sort_parent = {
- .header = "Parent symbol ",
+ .header = "Parent symbol",
.cmp = sort__parent_cmp,
.print = sort__parent_print,
+ .width = &parent_symbol__col_width,
};
static int sort__need_collapse = 0;
@@ -705,7 +774,7 @@ static LIST_HEAD(hist_entry__sort_list);
static int sort_dimension__add(char *tok)
{
- int i;
+ unsigned int i;
for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) {
struct sort_dimension *sd = &sort_dimensions[i];
@@ -775,8 +844,146 @@ hist_entry__collapse(struct hist_entry *left, struct hist_entry *right)
return cmp;
}
+static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask)
+{
+ int i;
+ size_t ret = 0;
+
+ ret += fprintf(fp, "%s", " ");
+
+ for (i = 0; i < depth; i++)
+ if (depth_mask & (1 << i))
+ ret += fprintf(fp, "| ");
+ else
+ ret += fprintf(fp, " ");
+
+ ret += fprintf(fp, "\n");
+
+ return ret;
+}
static size_t
-callchain__fprintf(FILE *fp, struct callchain_node *self, u64 total_samples)
+ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, int depth,
+ int depth_mask, int count, u64 total_samples,
+ int hits)
+{
+ int i;
+ size_t ret = 0;
+
+ ret += fprintf(fp, "%s", " ");
+ for (i = 0; i < depth; i++) {
+ if (depth_mask & (1 << i))
+ ret += fprintf(fp, "|");
+ else
+ ret += fprintf(fp, " ");
+ if (!count && i == depth - 1) {
+ double percent;
+
+ percent = hits * 100.0 / total_samples;
+ ret += percent_color_fprintf(fp, "--%2.2f%%-- ", percent);
+ } else
+ ret += fprintf(fp, "%s", " ");
+ }
+ if (chain->sym)
+ ret += fprintf(fp, "%s\n", chain->sym->name);
+ else
+ ret += fprintf(fp, "%p\n", (void *)(long)chain->ip);
+
+ return ret;
+}
+
+static struct symbol *rem_sq_bracket;
+static struct callchain_list rem_hits;
+
+static void init_rem_hits(void)
+{
+ rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6);
+ if (!rem_sq_bracket) {
+ fprintf(stderr, "Not enough memory to display remaining hits\n");
+ return;
+ }
+
+ strcpy(rem_sq_bracket->name, "[...]");
+ rem_hits.sym = rem_sq_bracket;
+}
+
+static size_t
+callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
+ u64 total_samples, int depth, int depth_mask)
+{
+ struct rb_node *node, *next;
+ struct callchain_node *child;
+ struct callchain_list *chain;
+ int new_depth_mask = depth_mask;
+ u64 new_total;
+ u64 remaining;
+ size_t ret = 0;
+ int i;
+
+ if (callchain_param.mode == CHAIN_GRAPH_REL)
+ new_total = self->children_hit;
+ else
+ new_total = total_samples;
+
+ remaining = new_total;
+
+ node = rb_first(&self->rb_root);
+ while (node) {
+ u64 cumul;
+
+ child = rb_entry(node, struct callchain_node, rb_node);
+ cumul = cumul_hits(child);
+ remaining -= cumul;
+
+ /*
+ * The depth mask manages the output of pipes that show
+ * the depth. We don't want to keep the pipes of the current
+ * level for the last child of this depth.
+ * Except if we have remaining filtered hits. They will
+ * supersede the last child
+ */
+ next = rb_next(node);
+ if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining))
+ new_depth_mask &= ~(1 << (depth - 1));
+
+ /*
+ * But we keep the older depth mask for the line seperator
+ * to keep the level link until we reach the last child
+ */
+ ret += ipchain__fprintf_graph_line(fp, depth, depth_mask);
+ i = 0;
+ list_for_each_entry(chain, &child->val, list) {
+ if (chain->ip >= PERF_CONTEXT_MAX)
+ continue;
+ ret += ipchain__fprintf_graph(fp, chain, depth,
+ new_depth_mask, i++,
+ new_total,
+ cumul);
+ }
+ ret += callchain__fprintf_graph(fp, child, new_total,
+ depth + 1,
+ new_depth_mask | (1 << depth));
+ node = next;
+ }
+
+ if (callchain_param.mode == CHAIN_GRAPH_REL &&
+ remaining && remaining != new_total) {
+
+ if (!rem_sq_bracket)
+ return ret;
+
+ new_depth_mask &= ~(1 << (depth - 1));
+
+ ret += ipchain__fprintf_graph(fp, &rem_hits, depth,
+ new_depth_mask, 0, new_total,
+ remaining);
+ }
+
+ return ret;
+}
+
+static size_t
+callchain__fprintf_flat(FILE *fp, struct callchain_node *self,
+ u64 total_samples)
{
struct callchain_list *chain;
size_t ret = 0;
@@ -784,11 +991,18 @@ callchain__fprintf(FILE *fp, struct callchain_node *self, u64 total_samples)
if (!self)
return 0;
- ret += callchain__fprintf(fp, self->parent, total_samples);
+ ret += callchain__fprintf_flat(fp, self->parent, total_samples);
- list_for_each_entry(chain, &self->val, list)
- ret += fprintf(fp, " %p\n", (void *)chain->ip);
+ list_for_each_entry(chain, &self->val, list) {
+ if (chain->ip >= PERF_CONTEXT_MAX)
+ continue;
+ if (chain->sym)
+ ret += fprintf(fp, " %s\n", chain->sym->name);
+ else
+ ret += fprintf(fp, " %p\n",
+ (void *)(long)chain->ip);
+ }
return ret;
}
@@ -807,8 +1021,19 @@ hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self,
chain = rb_entry(rb_node, struct callchain_node, rb_node);
percent = chain->hit * 100.0 / total_samples;
- ret += fprintf(fp, " %6.2f%%\n", percent);
- ret += callchain__fprintf(fp, chain, total_samples);
+ switch (callchain_param.mode) {
+ case CHAIN_FLAT:
+ ret += percent_color_fprintf(fp, " %6.2f%%\n",
+ percent);
+ ret += callchain__fprintf_flat(fp, chain, total_samples);
+ break;
+ case CHAIN_GRAPH_ABS: /* Falldown */
+ case CHAIN_GRAPH_REL:
+ ret += callchain__fprintf_graph(fp, chain,
+ total_samples, 1, 1);
+ default:
+ break;
+ }
ret += fprintf(fp, "\n");
rb_node = rb_next(rb_node);
}
@@ -826,33 +1051,26 @@ hist_entry__fprintf(FILE *fp, struct hist_entry *self, u64 total_samples)
if (exclude_other && !self->parent)
return 0;
- if (total_samples) {
- double percent = self->count * 100.0 / total_samples;
- char *color = PERF_COLOR_NORMAL;
-
- /*
- * We color high-overhead entries in red, mid-overhead
- * entries in green - and keep the low overhead places
- * normal:
- */
- if (percent >= 5.0) {
- color = PERF_COLOR_RED;
- } else {
- if (percent >= 0.5)
- color = PERF_COLOR_GREEN;
- }
+ if (total_samples)
+ ret = percent_color_fprintf(fp,
+ field_sep ? "%.2f" : " %6.2f%%",
+ (self->count * 100.0) / total_samples);
+ else
+ ret = fprintf(fp, field_sep ? "%lld" : "%12lld ", self->count);
- ret = color_fprintf(fp, color, " %6.2f%%",
- (self->count * 100.0) / total_samples);
- } else
- ret = fprintf(fp, "%12Ld ", self->count);
+ if (show_nr_samples) {
+ if (field_sep)
+ fprintf(fp, "%c%lld", *field_sep, self->count);
+ else
+ fprintf(fp, "%11lld", self->count);
+ }
list_for_each_entry(se, &hist_entry__sort_list, list) {
- if (exclude_other && (se == &sort_parent))
+ if (se->elide)
continue;
- fprintf(fp, " ");
- ret += se->print(fp, self);
+ fprintf(fp, "%s", field_sep ?: " ");
+ ret += se->print(fp, self, se->width ? *se->width : 0);
}
ret += fprintf(fp, "\n");
@@ -867,6 +1085,18 @@ hist_entry__fprintf(FILE *fp, struct hist_entry *self, u64 total_samples)
*
*/
+static void dso__calc_col_width(struct dso *self)
+{
+ if (!col_width_list_str && !field_sep &&
+ (!dso_list || strlist__has_entry(dso_list, self->name))) {
+ unsigned int slen = strlen(self->name);
+ if (slen > dsos__col_width)
+ dsos__col_width = slen;
+ }
+
+ self->slen_calculated = 1;
+}
+
static struct symbol *
resolve_symbol(struct thread *thread, struct map **mapp,
struct dso **dsop, u64 *ipp)
@@ -886,6 +1116,14 @@ resolve_symbol(struct thread *thread, struct map **mapp,
map = thread__find_map(thread, ip);
if (map != NULL) {
+ /*
+ * We have to do this here as we may have a dso
+ * with no symbol hit that has a name longer than
+ * the ones with symbols sampled.
+ */
+ if (!sort_dso.elide && !map->dso->slen_calculated)
+ dso__calc_col_width(map->dso);
+
if (mapp)
*mapp = map;
got_map:
@@ -923,6 +1161,58 @@ static int call__match(struct symbol *sym)
return 0;
}
+static struct symbol **
+resolve_callchain(struct thread *thread, struct map *map __used,
+ struct ip_callchain *chain, struct hist_entry *entry)
+{
+ u64 context = PERF_CONTEXT_MAX;
+ struct symbol **syms = NULL;
+ unsigned int i;
+
+ if (callchain) {
+ syms = calloc(chain->nr, sizeof(*syms));
+ if (!syms) {
+ fprintf(stderr, "Can't allocate memory for symbols\n");
+ exit(-1);
+ }
+ }
+
+ for (i = 0; i < chain->nr; i++) {
+ u64 ip = chain->ips[i];
+ struct dso *dso = NULL;
+ struct symbol *sym;
+
+ if (ip >= PERF_CONTEXT_MAX) {
+ context = ip;
+ continue;
+ }
+
+ switch (context) {
+ case PERF_CONTEXT_HV:
+ dso = hypervisor_dso;
+ break;
+ case PERF_CONTEXT_KERNEL:
+ dso = kernel_dso;
+ break;
+ default:
+ break;
+ }
+
+ sym = resolve_symbol(thread, NULL, &dso, &ip);
+
+ if (sym) {
+ if (sort__has_parent && call__match(sym) &&
+ !entry->parent)
+ entry->parent = sym;
+ if (!callchain)
+ break;
+ syms[i] = sym;
+ }
+ }
+
+ return syms;
+}
+
/*
* collect histogram counts
*/
@@ -935,6 +1225,7 @@ hist_entry__add(struct thread *thread, struct map *map, struct dso *dso,
struct rb_node **p = &hist.rb_node;
struct rb_node *parent = NULL;
struct hist_entry *he;
+ struct symbol **syms = NULL;
struct hist_entry entry = {
.thread = thread,
.map = map,
@@ -948,36 +1239,8 @@ hist_entry__add(struct thread *thread, struct map *map, struct dso *dso,
};
int cmp;
- if (sort__has_parent && chain) {
- u64 context = PERF_CONTEXT_MAX;
- int i;
-
- for (i = 0; i < chain->nr; i++) {
- u64 ip = chain->ips[i];
- struct dso *dso = NULL;
- struct symbol *sym;
-
- if (ip >= PERF_CONTEXT_MAX) {
- context = ip;
- continue;
- }
-
- switch (context) {
- case PERF_CONTEXT_KERNEL:
- dso = kernel_dso;
- break;
- default:
- break;
- }
-
- sym = resolve_symbol(thread, NULL, &dso, &ip);
-
- if (sym && call__match(sym)) {
- entry.parent = sym;
- break;
- }
- }
- }
+ if ((sort__has_parent || callchain) && chain)
+ syms = resolve_callchain(thread, map, chain, &entry);
while (*p != NULL) {
parent = *p;
@@ -987,8 +1250,10 @@ hist_entry__add(struct thread *thread, struct map *map, struct dso *dso,
if (!cmp) {
he->count += count;
- if (callchain)
- append_chain(&he->callchain, chain);
+ if (callchain) {
+ append_chain(&he->callchain, chain, syms);
+ free(syms);
+ }
return 0;
}
@@ -1004,7 +1269,8 @@ hist_entry__add(struct thread *thread, struct map *map, struct dso *dso,
*he = entry;
if (callchain) {
callchain_init(&he->callchain);
- append_chain(&he->callchain, chain);
+ append_chain(&he->callchain, chain, syms);
+ free(syms);
}
rb_link_node(&he->rb_node, parent, p);
rb_insert_color(&he->rb_node, &hist);
@@ -1076,14 +1342,15 @@ static void collapse__resort(void)
static struct rb_root output_hists;
-static void output__insert_entry(struct hist_entry *he)
+static void output__insert_entry(struct hist_entry *he, u64 min_callchain_hits)
{
struct rb_node **p = &output_hists.rb_node;
struct rb_node *parent = NULL;
struct hist_entry *iter;
if (callchain)
- sort_chain_to_rbtree(&he->sorted_chain, &he->callchain);
+ callchain_param.sort(&he->sorted_chain, &he->callchain,
+ min_callchain_hits, &callchain_param);
while (*p != NULL) {
parent = *p;
@@ -1099,11 +1366,14 @@ static void output__insert_entry(struct hist_entry *he)
rb_insert_color(&he->rb_node, &output_hists);
}
-static void output__resort(void)
+static void output__resort(u64 total_samples)
{
struct rb_node *next;
struct hist_entry *n;
struct rb_root *tree = &hist;
+ u64 min_callchain_hits;
+
+ min_callchain_hits = total_samples * (callchain_param.min_percent / 100);
if (sort__need_collapse)
tree = &collapse_hists;
@@ -1115,7 +1385,7 @@ static void output__resort(void)
next = rb_next(&n->rb_node);
rb_erase(&n->rb_node, tree);
- output__insert_entry(n);
+ output__insert_entry(n, min_callchain_hits);
}
}
@@ -1125,35 +1395,69 @@ static size_t output__fprintf(FILE *fp, u64 total_samples)
struct sort_entry *se;
struct rb_node *nd;
size_t ret = 0;
+ unsigned int width;
+ char *col_width = col_width_list_str;
- fprintf(fp, "\n");
- fprintf(fp, "#\n");
- fprintf(fp, "# (%Ld samples)\n", (u64)total_samples);
+ init_rem_hits();
+
+ fprintf(fp, "# Samples: %Ld\n", (u64)total_samples);
fprintf(fp, "#\n");
fprintf(fp, "# Overhead");
+ if (show_nr_samples) {
+ if (field_sep)
+ fprintf(fp, "%cSamples", *field_sep);
+ else
+ fputs(" Samples ", fp);
+ }
list_for_each_entry(se, &hist_entry__sort_list, list) {
- if (exclude_other && (se == &sort_parent))
+ if (se->elide)
continue;
- fprintf(fp, " %s", se->header);
+ if (field_sep) {
+ fprintf(fp, "%c%s", *field_sep, se->header);
+ continue;
+ }
+ width = strlen(se->header);
+ if (se->width) {
+ if (col_width_list_str) {
+ if (col_width) {
+ *se->width = atoi(col_width);
+ col_width = strchr(col_width, ',');
+ if (col_width)
+ ++col_width;
+ }
+ }
+ width = *se->width = max(*se->width, width);
+ }
+ fprintf(fp, " %*s", width, se->header);
}
fprintf(fp, "\n");
+ if (field_sep)
+ goto print_entries;
+
fprintf(fp, "# ........");
+ if (show_nr_samples)
+ fprintf(fp, " ..........");
list_for_each_entry(se, &hist_entry__sort_list, list) {
- int i;
+ unsigned int i;
- if (exclude_other && (se == &sort_parent))
+ if (se->elide)
continue;
fprintf(fp, " ");
- for (i = 0; i < strlen(se->header); i++)
+ if (se->width)
+ width = *se->width;
+ else
+ width = strlen(se->header);
+ for (i = 0; i < width; i++)
fprintf(fp, ".");
}
fprintf(fp, "\n");
fprintf(fp, "#\n");
+print_entries:
for (nd = rb_first(&output_hists); nd; nd = rb_next(nd)) {
pos = rb_entry(nd, struct hist_entry, rb_node);
ret += hist_entry__fprintf(fp, pos, total_samples);
@@ -1162,11 +1466,13 @@ static size_t output__fprintf(FILE *fp, u64 total_samples)
if (sort_order == default_sort_order &&
parent_pattern == default_parent_pattern) {
fprintf(fp, "#\n");
- fprintf(fp, "# (For more details, try: perf report --sort comm,dso,symbol)\n");
+ fprintf(fp, "# (For a higher level overview, try: perf report --sort comm,dso)\n");
fprintf(fp, "#\n");
}
fprintf(fp, "\n");
+ free(rem_sq_bracket);
+
return ret;
}
@@ -1213,22 +1519,23 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head)
struct map *map = NULL;
void *more_data = event->ip.__more_data;
struct ip_callchain *chain = NULL;
+ int cpumode;
if (sample_type & PERF_SAMPLE_PERIOD) {
period = *(u64 *)more_data;
more_data += sizeof(u64);
}
- dprintf("%p [%p]: PERF_EVENT_SAMPLE (IP, %d): %d: %p period: %Ld\n",
+ dprintf("%p [%p]: PERF_EVENT_SAMPLE (IP, %d): %d/%d: %p period: %Ld\n",
(void *)(offset + head),
(void *)(long)(event->header.size),
event->header.misc,
- event->ip.pid,
+ event->ip.pid, event->ip.tid,
(void *)(long)ip,
(long long)period);
if (sample_type & PERF_SAMPLE_CALLCHAIN) {
- int i;
+ unsigned int i;
chain = (void *)more_data;
@@ -1256,7 +1563,9 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head)
if (comm_list && !strlist__has_entry(comm_list, thread->comm))
return 0;
- if (event->header.misc & PERF_EVENT_MISC_KERNEL) {
+ cpumode = event->header.misc & PERF_EVENT_MISC_CPUMODE_MASK;
+
+ if (cpumode == PERF_EVENT_MISC_KERNEL) {
show = SHOW_KERNEL;
level = 'k';
@@ -1264,7 +1573,7 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head)
dprintf(" ...... dso: %s\n", dso->name);
- } else if (event->header.misc & PERF_EVENT_MISC_USER) {
+ } else if (cpumode == PERF_EVENT_MISC_USER) {
show = SHOW_USER;
level = '.';
@@ -1272,16 +1581,20 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head)
} else {
show = SHOW_HV;
level = 'H';
+
+ dso = hypervisor_dso;
+
dprintf(" ...... dso: [hypervisor]\n");
}
if (show & show_mask) {
struct symbol *sym = resolve_symbol(thread, &map, &dso, &ip);
- if (dso_list && dso && dso->name && !strlist__has_entry(dso_list, dso->name))
+ if (dso_list && (!dso || !dso->name ||
+ !strlist__has_entry(dso_list, dso->name)))
return 0;
- if (sym_list && sym && !strlist__has_entry(sym_list, sym->name))
+ if (sym_list && (!sym || !strlist__has_entry(sym_list, sym->name)))
return 0;
if (hist_entry__add(thread, map, dso, sym, ip, chain, level, period)) {
@@ -1300,10 +1613,11 @@ process_mmap_event(event_t *event, unsigned long offset, unsigned long head)
struct thread *thread = threads__findnew(event->mmap.pid);
struct map *map = map__new(&event->mmap);
- dprintf("%p [%p]: PERF_EVENT_MMAP %d: [%p(%p) @ %p]: %s\n",
+ dprintf("%p [%p]: PERF_EVENT_MMAP %d/%d: [%p(%p) @ %p]: %s\n",
(void *)(offset + head),
(void *)(long)(event->header.size),
event->mmap.pid,
+ event->mmap.tid,
(void *)(long)event->mmap.start,
(void *)(long)event->mmap.len,
(void *)(long)event->mmap.pgoff,
@@ -1341,15 +1655,27 @@ process_comm_event(event_t *event, unsigned long offset, unsigned long head)
}
static int
-process_fork_event(event_t *event, unsigned long offset, unsigned long head)
+process_task_event(event_t *event, unsigned long offset, unsigned long head)
{
struct thread *thread = threads__findnew(event->fork.pid);
struct thread *parent = threads__findnew(event->fork.ppid);
- dprintf("%p [%p]: PERF_EVENT_FORK: %d:%d\n",
+ dprintf("%p [%p]: PERF_EVENT_%s: (%d:%d):(%d:%d)\n",
(void *)(offset + head),
(void *)(long)(event->header.size),
- event->fork.pid, event->fork.ppid);
+ event->header.type == PERF_EVENT_FORK ? "FORK" : "EXIT",
+ event->fork.pid, event->fork.tid,
+ event->fork.ppid, event->fork.ptid);
+
+ /*
+ * A thread clone will have the same PID for both
+ * parent and child.
+ */
+ if (thread == parent)
+ return 0;
+
+ if (event->header.type == PERF_EVENT_EXIT)
+ return 0;
if (!thread || !parent || thread__fork(thread, parent)) {
dprintf("problem processing PERF_EVENT_FORK, skipping event.\n");
@@ -1361,19 +1687,6 @@ process_fork_event(event_t *event, unsigned long offset, unsigned long head)
}
static int
-process_period_event(event_t *event, unsigned long offset, unsigned long head)
-{
- dprintf("%p [%p]: PERF_EVENT_PERIOD: time:%Ld, id:%Ld: period:%Ld\n",
- (void *)(offset + head),
- (void *)(long)(event->header.size),
- event->period.time,
- event->period.id,
- event->period.sample_period);
-
- return 0;
-}
-
-static int
process_lost_event(event_t *event, unsigned long offset, unsigned long head)
{
dprintf("%p [%p]: PERF_EVENT_LOST: id:%Ld: lost:%Ld\n",
@@ -1423,14 +1736,37 @@ static void trace_event(event_t *event)
dprintf(".\n");
}
+static struct perf_header *header;
+
+static struct perf_counter_attr *perf_header__find_attr(u64 id)
+{
+ int i;
+
+ for (i = 0; i < header->attrs; i++) {
+ struct perf_header_attr *attr = header->attr[i];
+ int j;
+
+ for (j = 0; j < attr->ids; j++) {
+ if (attr->id[j] == id)
+ return &attr->attr;
+ }
+ }
+
+ return NULL;
+}
+
static int
process_read_event(event_t *event, unsigned long offset, unsigned long head)
{
- dprintf("%p [%p]: PERF_EVENT_READ: %d %d %Lu\n",
+ struct perf_counter_attr *attr = perf_header__find_attr(event->read.id);
+
+ dprintf("%p [%p]: PERF_EVENT_READ: %d %d %s %Lu\n",
(void *)(offset + head),
(void *)(long)(event->header.size),
event->read.pid,
event->read.tid,
+ attr ? __event_name(attr->type, attr->config)
+ : "FAIL",
event->read.value);
return 0;
@@ -1452,10 +1788,8 @@ process_event(event_t *event, unsigned long offset, unsigned long head)
return process_comm_event(event, offset, head);
case PERF_EVENT_FORK:
- return process_fork_event(event, offset, head);
-
- case PERF_EVENT_PERIOD:
- return process_period_event(event, offset, head);
+ case PERF_EVENT_EXIT:
+ return process_task_event(event, offset, head);
case PERF_EVENT_LOST:
return process_lost_event(event, offset, head);
@@ -1478,8 +1812,6 @@ process_event(event_t *event, unsigned long offset, unsigned long head)
return 0;
}
-static struct perf_header *header;
-
static u64 perf_header__sample_type(void)
{
u64 sample_type = 0;
@@ -1534,9 +1866,26 @@ static int __cmd_report(void)
sample_type = perf_header__sample_type();
- if (sort__has_parent && !(sample_type & PERF_SAMPLE_CALLCHAIN)) {
- fprintf(stderr, "selected --sort parent, but no callchain data\n");
- exit(-1);
+ if (!(sample_type & PERF_SAMPLE_CALLCHAIN)) {
+ if (sort__has_parent) {
+ fprintf(stderr, "selected --sort parent, but no"
+ " callchain data. Did you call"
+ " perf record without -g?\n");
+ exit(-1);
+ }
+ if (callchain) {
+ fprintf(stderr, "selected -c but no callchain data."
+ " Did you call perf record without"
+ " -g?\n");
+ exit(-1);
+ }
+ } else if (callchain_param.mode != CHAIN_NONE && !callchain) {
+ callchain = 1;
+ if (register_callchain_param(&callchain_param) < 0) {
+ fprintf(stderr, "Can't register callchain"
+ " params\n");
+ exit(-1);
+ }
}
if (load_kernel() < 0) {
@@ -1619,7 +1968,7 @@ more:
if (offset + head >= header->data_offset + header->data_size)
goto done;
- if (offset + head < stat.st_size)
+ if (offset + head < (unsigned long)stat.st_size)
goto more;
done:
@@ -1643,12 +1992,65 @@ done:
dsos__fprintf(stdout);
collapse__resort();
- output__resort();
+ output__resort(total);
output__fprintf(stdout, total);
return rc;
}
+static int
+parse_callchain_opt(const struct option *opt __used, const char *arg,
+ int unset __used)
+{
+ char *tok;
+ char *endptr;
+
+ callchain = 1;
+
+ if (!arg)
+ return 0;
+
+ tok = strtok((char *)arg, ",");
+ if (!tok)
+ return -1;
+
+ /* get the output mode */
+ if (!strncmp(tok, "graph", strlen(arg)))
+ callchain_param.mode = CHAIN_GRAPH_ABS;
+
+ else if (!strncmp(tok, "flat", strlen(arg)))
+ callchain_param.mode = CHAIN_FLAT;
+
+ else if (!strncmp(tok, "fractal", strlen(arg)))
+ callchain_param.mode = CHAIN_GRAPH_REL;
+
+ else if (!strncmp(tok, "none", strlen(arg))) {
+ callchain_param.mode = CHAIN_NONE;
+ callchain = 0;
+
+ return 0;
+ }
+
+ else
+ return -1;
+
+ /* get the min percentage */
+ tok = strtok(NULL, ",");
+ if (!tok)
+ goto setup;
+
+ callchain_param.min_percent = strtod(tok, &endptr);
+ if (tok == endptr)
+ return -1;
+
+setup:
+ if (register_callchain_param(&callchain_param) < 0) {
+ fprintf(stderr, "Can't register callchain params\n");
+ return -1;
+ }
+ return 0;
+}
+
static const char * const report_usage[] = {
"perf report [<options>] <command>",
NULL
@@ -1662,6 +2064,10 @@ static const struct option options[] = {
OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
"dump raw trace in ASCII"),
OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"),
+ OPT_BOOLEAN('m', "modules", &modules,
+ "load module symbols - WARNING: use only with -k and LIVE kernel"),
+ OPT_BOOLEAN('n', "show-nr-samples", &show_nr_samples,
+ "Show a column with the number of samples"),
OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
"sort by key(s): pid, comm, dso, symbol, parent"),
OPT_BOOLEAN('P', "full-paths", &full_paths,
@@ -1670,13 +2076,21 @@ static const struct option options[] = {
"regex filter to identify parent, see: '--sort parent'"),
OPT_BOOLEAN('x', "exclude-other", &exclude_other,
"Only display entries with parent-match"),
- OPT_BOOLEAN('c', "callchain", &callchain, "Display callchains"),
+ OPT_CALLBACK_DEFAULT('g', "call-graph", NULL, "output_type,min_percent",
+ "Display callchains using output_type and min percent threshold. "
+ "Default: fractal,0.5", &parse_callchain_opt, callchain_default_opt),
OPT_STRING('d', "dsos", &dso_list_str, "dso[,dso...]",
"only consider symbols in these dsos"),
OPT_STRING('C', "comms", &comm_list_str, "comm[,comm...]",
"only consider symbols in these comms"),
OPT_STRING('S', "symbols", &sym_list_str, "symbol[,symbol...]",
"only consider these symbols"),
+ OPT_STRING('w', "column-widths", &col_width_list_str,
+ "width[,width...]",
+ "don't try to adjust column width, use these fixed values"),
+ OPT_STRING('t', "field-separator", &field_sep, "separator",
+ "separator for columns, no spaces will be added between "
+ "columns '.' is reserved."),
OPT_END()
};
@@ -1696,7 +2110,8 @@ static void setup_sorting(void)
}
static void setup_list(struct strlist **list, const char *list_str,
- const char *list_name)
+ struct sort_entry *se, const char *list_name,
+ FILE *fp)
{
if (list_str) {
*list = strlist__new(true, list_str);
@@ -1705,10 +2120,15 @@ static void setup_list(struct strlist **list, const char *list_str,
list_name);
exit(129);
}
+ if (strlist__nr_entries(*list) == 1) {
+ fprintf(fp, "# %s: %s\n", list_name,
+ strlist__entry(*list, 0)->s);
+ se->elide = true;
+ }
}
}
-int cmd_report(int argc, const char **argv, const char *prefix)
+int cmd_report(int argc, const char **argv, const char *prefix __used)
{
symbol__init();
@@ -1718,9 +2138,10 @@ int cmd_report(int argc, const char **argv, const char *prefix)
setup_sorting();
- if (parent_pattern != default_parent_pattern)
+ if (parent_pattern != default_parent_pattern) {
sort_dimension__add("parent");
- else
+ sort_parent.elide = 1;
+ } else
exclude_other = 0;
/*
@@ -1729,11 +2150,17 @@ int cmd_report(int argc, const char **argv, const char *prefix)
if (argc)
usage_with_options(report_usage, options);
- setup_list(&dso_list, dso_list_str, "dso");
- setup_list(&comm_list, comm_list_str, "comm");
- setup_list(&sym_list, sym_list_str, "symbol");
-
setup_pager();
+ setup_list(&dso_list, dso_list_str, &sort_dso, "dso", stdout);
+ setup_list(&comm_list, comm_list_str, &sort_comm, "comm", stdout);
+ setup_list(&sym_list, sym_list_str, &sort_sym, "symbol", stdout);
+
+ if (field_sep && *field_sep == '.') {
+ fputs("'.' is the only non valid --field-separator argument\n",
+ stderr);
+ exit(129);
+ }
+
return __cmd_report();
}
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c
index 2e03524a1de..b4b06c7903e 100644
--- a/tools/perf/builtin-stat.c
+++ b/tools/perf/builtin-stat.c
@@ -64,7 +64,7 @@ static struct perf_counter_attr default_attrs[] = {
static int system_wide = 0;
static int verbose = 0;
-static int nr_cpus = 0;
+static unsigned int nr_cpus = 0;
static int run_idx = 0;
static int run_count = 1;
@@ -96,6 +96,10 @@ static u64 walltime_nsecs_noise;
static u64 runtime_cycles_avg;
static u64 runtime_cycles_noise;
+#define MATCH_EVENT(t, c, counter) \
+ (attrs[counter].type == PERF_TYPE_##t && \
+ attrs[counter].config == PERF_COUNT_##c)
+
#define ERR_PERF_OPEN \
"Error: counter %d, sys_perf_counter_open() syscall returned with %d (%s)\n"
@@ -108,7 +112,8 @@ static void create_perf_stat_counter(int counter, int pid)
PERF_FORMAT_TOTAL_TIME_RUNNING;
if (system_wide) {
- int cpu;
+ unsigned int cpu;
+
for (cpu = 0; cpu < nr_cpus; cpu++) {
fd[cpu][counter] = sys_perf_counter_open(attr, -1, cpu, -1, 0);
if (fd[cpu][counter] < 0 && verbose)
@@ -132,13 +137,8 @@ static void create_perf_stat_counter(int counter, int pid)
*/
static inline int nsec_counter(int counter)
{
- if (attrs[counter].type != PERF_TYPE_SOFTWARE)
- return 0;
-
- if (attrs[counter].config == PERF_COUNT_SW_CPU_CLOCK)
- return 1;
-
- if (attrs[counter].config == PERF_COUNT_SW_TASK_CLOCK)
+ if (MATCH_EVENT(SOFTWARE, SW_CPU_CLOCK, counter) ||
+ MATCH_EVENT(SOFTWARE, SW_TASK_CLOCK, counter))
return 1;
return 0;
@@ -150,8 +150,8 @@ static inline int nsec_counter(int counter)
static void read_counter(int counter)
{
u64 *count, single_count[3];
- ssize_t res;
- int cpu, nv;
+ unsigned int cpu;
+ size_t res, nv;
int scaled;
count = event_res[run_idx][counter];
@@ -165,6 +165,7 @@ static void read_counter(int counter)
res = read(fd[cpu][counter], single_count, nv * sizeof(u64));
assert(res == nv * sizeof(u64));
+
close(fd[cpu][counter]);
fd[cpu][counter] = -1;
@@ -192,15 +193,13 @@ static void read_counter(int counter)
/*
* Save the full runtime - to allow normalization during printout:
*/
- if (attrs[counter].type == PERF_TYPE_SOFTWARE &&
- attrs[counter].config == PERF_COUNT_SW_TASK_CLOCK)
+ if (MATCH_EVENT(SOFTWARE, SW_TASK_CLOCK, counter))
runtime_nsecs[run_idx] = count[0];
- if (attrs[counter].type == PERF_TYPE_HARDWARE &&
- attrs[counter].config == PERF_COUNT_HW_CPU_CYCLES)
+ if (MATCH_EVENT(HARDWARE, HW_CPU_CYCLES, counter))
runtime_cycles[run_idx] = count[0];
}
-static int run_perf_stat(int argc, const char **argv)
+static int run_perf_stat(int argc __used, const char **argv)
{
unsigned long long t0, t1;
int status = 0;
@@ -240,7 +239,8 @@ static int run_perf_stat(int argc, const char **argv)
/*
* Wait until the parent tells us to go.
*/
- read(go_pipe[0], &buf, 1);
+ if (read(go_pipe[0], &buf, 1) == -1)
+ perror("unable to read pipe");
execvp(argv[0], (char **)argv);
@@ -253,7 +253,8 @@ static int run_perf_stat(int argc, const char **argv)
*/
close(child_ready_pipe[1]);
close(go_pipe[0]);
- read(child_ready_pipe[0], &buf, 1);
+ if (read(child_ready_pipe[0], &buf, 1) == -1)
+ perror("unable to read pipe");
close(child_ready_pipe[0]);
for (counter = 0; counter < nr_counters; counter++)
@@ -290,9 +291,7 @@ static void nsec_printout(int counter, u64 *count, u64 *noise)
fprintf(stderr, " %14.6f %-24s", msecs, event_name(counter));
- if (attrs[counter].type == PERF_TYPE_SOFTWARE &&
- attrs[counter].config == PERF_COUNT_SW_TASK_CLOCK) {
-
+ if (MATCH_EVENT(SOFTWARE, SW_TASK_CLOCK, counter)) {
if (walltime_nsecs_avg)
fprintf(stderr, " # %10.3f CPUs ",
(double)count[0] / (double)walltime_nsecs_avg);
@@ -305,9 +304,7 @@ static void abs_printout(int counter, u64 *count, u64 *noise)
fprintf(stderr, " %14Ld %-24s", count[0], event_name(counter));
if (runtime_cycles_avg &&
- attrs[counter].type == PERF_TYPE_HARDWARE &&
- attrs[counter].config == PERF_COUNT_HW_INSTRUCTIONS) {
-
+ MATCH_EVENT(HARDWARE, HW_INSTRUCTIONS, counter)) {
fprintf(stderr, " # %10.3f IPC ",
(double)count[0] / (double)runtime_cycles_avg);
} else {
@@ -390,7 +387,7 @@ static void calc_avg(void)
event_res_avg[j]+1, event_res[i][j]+1);
update_avg("counter/2", j,
event_res_avg[j]+2, event_res[i][j]+2);
- if (event_scaled[i][j] != -1)
+ if (event_scaled[i][j] != (u64)-1)
update_avg("scaled", j,
event_scaled_avg + j, event_scaled[i]+j);
else
@@ -499,7 +496,7 @@ static const struct option options[] = {
"stat events on existing pid"),
OPT_BOOLEAN('a', "all-cpus", &system_wide,
"system-wide collection from all CPUs"),
- OPT_BOOLEAN('S', "scale", &scale,
+ OPT_BOOLEAN('c', "scale", &scale,
"scale/normalize counters"),
OPT_BOOLEAN('v', "verbose", &verbose,
"be more verbose (show counter open errors, etc)"),
@@ -510,11 +507,12 @@ static const struct option options[] = {
OPT_END()
};
-int cmd_stat(int argc, const char **argv, const char *prefix)
+int cmd_stat(int argc, const char **argv, const char *prefix __used)
{
int status;
- argc = parse_options(argc, argv, options, stat_usage, 0);
+ argc = parse_options(argc, argv, options, stat_usage,
+ PARSE_OPT_STOP_AT_NON_OPTION);
if (!argc)
usage_with_options(stat_usage, options);
if (run_count <= 0 || run_count > MAX_RUN)
@@ -528,7 +526,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix)
nr_cpus = sysconf(_SC_NPROCESSORS_ONLN);
assert(nr_cpus <= MAX_NR_CPUS);
- assert(nr_cpus >= 0);
+ assert((int)nr_cpus >= 0);
/*
* We dont want to block the signals - that would cause
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index cf0d21f1ae1..7de28ce9ca2 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -23,7 +23,7 @@
#include "util/symbol.h"
#include "util/color.h"
#include "util/util.h"
-#include "util/rbtree.h"
+#include <linux/rbtree.h>
#include "util/parse-options.h"
#include "util/parse-events.h"
@@ -31,6 +31,8 @@
#include <fcntl.h>
#include <stdio.h>
+#include <termios.h>
+#include <unistd.h>
#include <errno.h>
#include <time.h>
@@ -54,10 +56,11 @@ static int system_wide = 0;
static int default_interval = 100000;
-static u64 count_filter = 5;
+static int count_filter = 5;
static int print_entries = 15;
static int target_pid = -1;
+static int inherit = 0;
static int profile_cpu = -1;
static int nr_cpus = 0;
static unsigned int realtime_prio = 0;
@@ -66,16 +69,30 @@ static unsigned int page_size;
static unsigned int mmap_pages = 16;
static int freq = 0;
static int verbose = 0;
-
-static char *sym_filter;
-static unsigned long filter_start;
-static unsigned long filter_end;
+static char *vmlinux = NULL;
static int delay_secs = 2;
static int zero;
static int dump_symtab;
/*
+ * Source
+ */
+
+struct source_line {
+ u64 eip;
+ unsigned long count[MAX_COUNTERS];
+ char *line;
+ struct source_line *next;
+};
+
+static char *sym_filter = NULL;
+struct sym_entry *sym_filter_entry = NULL;
+static int sym_pcnt_filter = 5;
+static int sym_counter = 0;
+static int display_weighted = -1;
+
+/*
* Symbols
*/
@@ -89,9 +106,237 @@ struct sym_entry {
unsigned long snap_count;
double weight;
int skip;
+ struct source_line *source;
+ struct source_line *lines;
+ struct source_line **lines_tail;
+ pthread_mutex_t source_lock;
};
-struct sym_entry *sym_filter_entry;
+/*
+ * Source functions
+ */
+
+static void parse_source(struct sym_entry *syme)
+{
+ struct symbol *sym;
+ struct module *module;
+ struct section *section = NULL;
+ FILE *file;
+ char command[PATH_MAX*2], *path = vmlinux;
+ u64 start, end, len;
+
+ if (!syme)
+ return;
+
+ if (syme->lines) {
+ pthread_mutex_lock(&syme->source_lock);
+ goto out_assign;
+ }
+
+ sym = (struct symbol *)(syme + 1);
+ module = sym->module;
+
+ if (module)
+ path = module->path;
+ if (!path)
+ return;
+
+ start = sym->obj_start;
+ if (!start)
+ start = sym->start;
+
+ if (module) {
+ section = module->sections->find_section(module->sections, ".text");
+ if (section)
+ start -= section->vma;
+ }
+
+ end = start + sym->end - sym->start + 1;
+ len = sym->end - sym->start;
+
+ sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s", start, end, path);
+
+ file = popen(command, "r");
+ if (!file)
+ return;
+
+ pthread_mutex_lock(&syme->source_lock);
+ syme->lines_tail = &syme->lines;
+ while (!feof(file)) {
+ struct source_line *src;
+ size_t dummy = 0;
+ char *c;
+
+ src = malloc(sizeof(struct source_line));
+ assert(src != NULL);
+ memset(src, 0, sizeof(struct source_line));
+
+ if (getline(&src->line, &dummy, file) < 0)
+ break;
+ if (!src->line)
+ break;
+
+ c = strchr(src->line, '\n');
+ if (c)
+ *c = 0;
+
+ src->next = NULL;
+ *syme->lines_tail = src;
+ syme->lines_tail = &src->next;
+
+ if (strlen(src->line)>8 && src->line[8] == ':') {
+ src->eip = strtoull(src->line, NULL, 16);
+ if (section)
+ src->eip += section->vma;
+ }
+ if (strlen(src->line)>8 && src->line[16] == ':') {
+ src->eip = strtoull(src->line, NULL, 16);
+ if (section)
+ src->eip += section->vma;
+ }
+ }
+ pclose(file);
+out_assign:
+ sym_filter_entry = syme;
+ pthread_mutex_unlock(&syme->source_lock);
+}
+
+static void __zero_source_counters(struct sym_entry *syme)
+{
+ int i;
+ struct source_line *line;
+
+ line = syme->lines;
+ while (line) {
+ for (i = 0; i < nr_counters; i++)
+ line->count[i] = 0;
+ line = line->next;
+ }
+}
+
+static void record_precise_ip(struct sym_entry *syme, int counter, u64 ip)
+{
+ struct source_line *line;
+
+ if (syme != sym_filter_entry)
+ return;
+
+ if (pthread_mutex_trylock(&syme->source_lock))
+ return;
+
+ if (!syme->source)
+ goto out_unlock;
+
+ for (line = syme->lines; line; line = line->next) {
+ if (line->eip == ip) {
+ line->count[counter]++;
+ break;
+ }
+ if (line->eip > ip)
+ break;
+ }
+out_unlock:
+ pthread_mutex_unlock(&syme->source_lock);
+}
+
+static void lookup_sym_source(struct sym_entry *syme)
+{
+ struct symbol *symbol = (struct symbol *)(syme + 1);
+ struct source_line *line;
+ char pattern[PATH_MAX];
+ char *idx;
+
+ sprintf(pattern, "<%s>:", symbol->name);
+
+ if (symbol->module) {
+ idx = strstr(pattern, "\t");
+ if (idx)
+ *idx = 0;
+ }
+
+ pthread_mutex_lock(&syme->source_lock);
+ for (line = syme->lines; line; line = line->next) {
+ if (strstr(line->line, pattern)) {
+ syme->source = line;
+ break;
+ }
+ }
+ pthread_mutex_unlock(&syme->source_lock);
+}
+
+static void show_lines(struct source_line *queue, int count, int total)
+{
+ int i;
+ struct source_line *line;
+
+ line = queue;
+ for (i = 0; i < count; i++) {
+ float pcnt = 100.0*(float)line->count[sym_counter]/(float)total;
+
+ printf("%8li %4.1f%%\t%s\n", line->count[sym_counter], pcnt, line->line);
+ line = line->next;
+ }
+}
+
+#define TRACE_COUNT 3
+
+static void show_details(struct sym_entry *syme)
+{
+ struct symbol *symbol;
+ struct source_line *line;
+ struct source_line *line_queue = NULL;
+ int displayed = 0;
+ int line_queue_count = 0, total = 0, more = 0;
+
+ if (!syme)
+ return;
+
+ if (!syme->source)
+ lookup_sym_source(syme);
+
+ if (!syme->source)
+ return;
+
+ symbol = (struct symbol *)(syme + 1);
+ printf("Showing %s for %s\n", event_name(sym_counter), symbol->name);
+ printf(" Events Pcnt (>=%d%%)\n", sym_pcnt_filter);
+
+ pthread_mutex_lock(&syme->source_lock);
+ line = syme->source;
+ while (line) {
+ total += line->count[sym_counter];
+ line = line->next;
+ }
+
+ line = syme->source;
+ while (line) {
+ float pcnt = 0.0;
+
+ if (!line_queue_count)
+ line_queue = line;
+ line_queue_count++;
+
+ if (line->count[sym_counter])
+ pcnt = 100.0 * line->count[sym_counter] / (float)total;
+ if (pcnt >= (float)sym_pcnt_filter) {
+ if (displayed <= print_entries)
+ show_lines(line_queue, line_queue_count, total);
+ else more++;
+ displayed += line_queue_count;
+ line_queue_count = 0;
+ line_queue = NULL;
+ } else if (line_queue_count > TRACE_COUNT) {
+ line_queue = line_queue->next;
+ line_queue_count--;
+ }
+
+ line->count[sym_counter] = zero ? 0 : line->count[sym_counter] * 7 / 8;
+ line = line->next;
+ }
+ pthread_mutex_unlock(&syme->source_lock);
+ if (more)
+ printf("%d lines not displayed, maybe increase display entries [e]\n", more);
+}
struct dso *kernel_dso;
@@ -110,6 +355,9 @@ static double sym_weight(const struct sym_entry *sym)
double weight = sym->snap_count;
int counter;
+ if (!display_weighted)
+ return weight;
+
for (counter = 1; counter < nr_counters-1; counter++)
weight *= sym->count[counter];
@@ -157,7 +405,7 @@ static void rb_insert_active_sym(struct rb_root *tree, struct sym_entry *se)
static void print_sym_table(void)
{
int printed = 0, j;
- int counter;
+ int counter, snap = !display_weighted ? sym_counter : 0;
float samples_per_sec = samples/delay_secs;
float ksamples_per_sec = (samples-userspace_samples)/delay_secs;
float sum_ksamples = 0.0;
@@ -173,7 +421,7 @@ static void print_sym_table(void)
pthread_mutex_unlock(&active_symbols_lock);
list_for_each_entry_safe_from(syme, n, &active_symbols, node) {
- syme->snap_count = syme->count[0];
+ syme->snap_count = syme->count[snap];
if (syme->snap_count != 0) {
syme->weight = sym_weight(syme);
rb_insert_active_sym(&tmp, syme);
@@ -193,7 +441,7 @@ static void print_sym_table(void)
samples_per_sec,
100.0 - (100.0*((samples_per_sec-ksamples_per_sec)/samples_per_sec)));
- if (nr_counters == 1) {
+ if (nr_counters == 1 || !display_weighted) {
printf("%Ld", (u64)attrs[0].sample_period);
if (freq)
printf("Hz ");
@@ -201,7 +449,9 @@ static void print_sym_table(void)
printf(" ");
}
- for (counter = 0; counter < nr_counters; counter++) {
+ if (!display_weighted)
+ printf("%s", event_name(sym_counter));
+ else for (counter = 0; counter < nr_counters; counter++) {
if (counter)
printf("/");
@@ -226,6 +476,11 @@ static void print_sym_table(void)
printf("------------------------------------------------------------------------------\n\n");
+ if (sym_filter_entry) {
+ show_details(sym_filter_entry);
+ return;
+ }
+
if (nr_counters == 1)
printf(" samples pcnt");
else
@@ -238,59 +493,300 @@ static void print_sym_table(void)
for (nd = rb_first(&tmp); nd; nd = rb_next(nd)) {
struct sym_entry *syme = rb_entry(nd, struct sym_entry, rb_node);
struct symbol *sym = (struct symbol *)(syme + 1);
- char *color = PERF_COLOR_NORMAL;
double pcnt;
- if (++printed > print_entries || syme->snap_count < count_filter)
+ if (++printed > print_entries || (int)syme->snap_count < count_filter)
continue;
pcnt = 100.0 - (100.0 * ((sum_ksamples - syme->snap_count) /
sum_ksamples));
- /*
- * We color high-overhead entries in red, mid-overhead
- * entries in green - and keep the low overhead places
- * normal:
- */
- if (pcnt >= 5.0) {
- color = PERF_COLOR_RED;
- } else {
- if (pcnt >= 0.5)
- color = PERF_COLOR_GREEN;
- }
-
- if (nr_counters == 1)
+ if (nr_counters == 1 || !display_weighted)
printf("%20.2f - ", syme->weight);
else
printf("%9.1f %10ld - ", syme->weight, syme->snap_count);
- color_fprintf(stdout, color, "%4.1f%%", pcnt);
- printf(" - %016llx : %s\n", sym->start, sym->name);
+ percent_color_fprintf(stdout, "%4.1f%%", pcnt);
+ printf(" - %016llx : %s", sym->start, sym->name);
+ if (sym->module)
+ printf("\t[%s]", sym->module->name);
+ printf("\n");
+ }
+}
+
+static void prompt_integer(int *target, const char *msg)
+{
+ char *buf = malloc(0), *p;
+ size_t dummy = 0;
+ int tmp;
+
+ fprintf(stdout, "\n%s: ", msg);
+ if (getline(&buf, &dummy, stdin) < 0)
+ return;
+
+ p = strchr(buf, '\n');
+ if (p)
+ *p = 0;
+
+ p = buf;
+ while(*p) {
+ if (!isdigit(*p))
+ goto out_free;
+ p++;
+ }
+ tmp = strtoul(buf, NULL, 10);
+ *target = tmp;
+out_free:
+ free(buf);
+}
+
+static void prompt_percent(int *target, const char *msg)
+{
+ int tmp = 0;
+
+ prompt_integer(&tmp, msg);
+ if (tmp >= 0 && tmp <= 100)
+ *target = tmp;
+}
+
+static void prompt_symbol(struct sym_entry **target, const char *msg)
+{
+ char *buf = malloc(0), *p;
+ struct sym_entry *syme = *target, *n, *found = NULL;
+ size_t dummy = 0;
+
+ /* zero counters of active symbol */
+ if (syme) {
+ pthread_mutex_lock(&syme->source_lock);
+ __zero_source_counters(syme);
+ *target = NULL;
+ pthread_mutex_unlock(&syme->source_lock);
+ }
+
+ fprintf(stdout, "\n%s: ", msg);
+ if (getline(&buf, &dummy, stdin) < 0)
+ goto out_free;
+
+ p = strchr(buf, '\n');
+ if (p)
+ *p = 0;
+
+ pthread_mutex_lock(&active_symbols_lock);
+ syme = list_entry(active_symbols.next, struct sym_entry, node);
+ pthread_mutex_unlock(&active_symbols_lock);
+
+ list_for_each_entry_safe_from(syme, n, &active_symbols, node) {
+ struct symbol *sym = (struct symbol *)(syme + 1);
+
+ if (!strcmp(buf, sym->name)) {
+ found = syme;
+ break;
+ }
}
+
+ if (!found) {
+ fprintf(stderr, "Sorry, %s is not active.\n", sym_filter);
+ sleep(1);
+ return;
+ } else
+ parse_source(found);
+
+out_free:
+ free(buf);
}
-static void *display_thread(void *arg)
+static void print_mapped_keys(void)
+{
+ char *name = NULL;
+
+ if (sym_filter_entry) {
+ struct symbol *sym = (struct symbol *)(sym_filter_entry+1);
+ name = sym->name;
+ }
+
+ fprintf(stdout, "\nMapped keys:\n");
+ fprintf(stdout, "\t[d] display refresh delay. \t(%d)\n", delay_secs);
+ fprintf(stdout, "\t[e] display entries (lines). \t(%d)\n", print_entries);
+
+ if (nr_counters > 1)
+ fprintf(stdout, "\t[E] active event counter. \t(%s)\n", event_name(sym_counter));
+
+ fprintf(stdout, "\t[f] profile display filter (count). \t(%d)\n", count_filter);
+
+ if (vmlinux) {
+ fprintf(stdout, "\t[F] annotate display filter (percent). \t(%d%%)\n", sym_pcnt_filter);
+ fprintf(stdout, "\t[s] annotate symbol. \t(%s)\n", name?: "NULL");
+ fprintf(stdout, "\t[S] stop annotation.\n");
+ }
+
+ if (nr_counters > 1)
+ fprintf(stdout, "\t[w] toggle display weighted/count[E]r. \t(%d)\n", display_weighted ? 1 : 0);
+
+ fprintf(stdout, "\t[z] toggle sample zeroing. \t(%d)\n", zero ? 1 : 0);
+ fprintf(stdout, "\t[qQ] quit.\n");
+}
+
+static int key_mapped(int c)
+{
+ switch (c) {
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'z':
+ case 'q':
+ case 'Q':
+ return 1;
+ case 'E':
+ case 'w':
+ return nr_counters > 1 ? 1 : 0;
+ case 'F':
+ case 's':
+ case 'S':
+ return vmlinux ? 1 : 0;
+ }
+
+ return 0;
+}
+
+static void handle_keypress(int c)
+{
+ if (!key_mapped(c)) {
+ struct pollfd stdin_poll = { .fd = 0, .events = POLLIN };
+ struct termios tc, save;
+
+ print_mapped_keys();
+ fprintf(stdout, "\nEnter selection, or unmapped key to continue: ");
+ fflush(stdout);
+
+ tcgetattr(0, &save);
+ tc = save;
+ tc.c_lflag &= ~(ICANON | ECHO);
+ tc.c_cc[VMIN] = 0;
+ tc.c_cc[VTIME] = 0;
+ tcsetattr(0, TCSANOW, &tc);
+
+ poll(&stdin_poll, 1, -1);
+ c = getc(stdin);
+
+ tcsetattr(0, TCSAFLUSH, &save);
+ if (!key_mapped(c))
+ return;
+ }
+
+ switch (c) {
+ case 'd':
+ prompt_integer(&delay_secs, "Enter display delay");
+ break;
+ case 'e':
+ prompt_integer(&print_entries, "Enter display entries (lines)");
+ break;
+ case 'E':
+ if (nr_counters > 1) {
+ int i;
+
+ fprintf(stderr, "\nAvailable events:");
+ for (i = 0; i < nr_counters; i++)
+ fprintf(stderr, "\n\t%d %s", i, event_name(i));
+
+ prompt_integer(&sym_counter, "Enter details event counter");
+
+ if (sym_counter >= nr_counters) {
+ fprintf(stderr, "Sorry, no such event, using %s.\n", event_name(0));
+ sym_counter = 0;
+ sleep(1);
+ }
+ } else sym_counter = 0;
+ break;
+ case 'f':
+ prompt_integer(&count_filter, "Enter display event count filter");
+ break;
+ case 'F':
+ prompt_percent(&sym_pcnt_filter, "Enter details display event filter (percent)");
+ break;
+ case 'q':
+ case 'Q':
+ printf("exiting.\n");
+ exit(0);
+ case 's':
+ prompt_symbol(&sym_filter_entry, "Enter details symbol");
+ break;
+ case 'S':
+ if (!sym_filter_entry)
+ break;
+ else {
+ struct sym_entry *syme = sym_filter_entry;
+
+ pthread_mutex_lock(&syme->source_lock);
+ sym_filter_entry = NULL;
+ __zero_source_counters(syme);
+ pthread_mutex_unlock(&syme->source_lock);
+ }
+ break;
+ case 'w':
+ display_weighted = ~display_weighted;
+ break;
+ case 'z':
+ zero = ~zero;
+ break;
+ }
+}
+
+static void *display_thread(void *arg __used)
{
struct pollfd stdin_poll = { .fd = 0, .events = POLLIN };
- int delay_msecs = delay_secs * 1000;
+ struct termios tc, save;
+ int delay_msecs, c;
- printf("PerfTop refresh period: %d seconds\n", delay_secs);
+ tcgetattr(0, &save);
+ tc = save;
+ tc.c_lflag &= ~(ICANON | ECHO);
+ tc.c_cc[VMIN] = 0;
+ tc.c_cc[VTIME] = 0;
+
+repeat:
+ delay_msecs = delay_secs * 1000;
+ tcsetattr(0, TCSANOW, &tc);
+ /* trash return*/
+ getc(stdin);
do {
print_sym_table();
} while (!poll(&stdin_poll, 1, delay_msecs) == 1);
- printf("key pressed - exiting.\n");
- exit(0);
+ c = getc(stdin);
+ tcsetattr(0, TCSAFLUSH, &save);
+
+ handle_keypress(c);
+ goto repeat;
return NULL;
}
+/* Tag samples to be skipped. */
+static const char *skip_symbols[] = {
+ "default_idle",
+ "cpu_idle",
+ "enter_idle",
+ "exit_idle",
+ "mwait_idle",
+ "mwait_idle_with_hints",
+ "ppc64_runlatch_off",
+ "pseries_dedicated_idle_sleep",
+ NULL
+};
+
static int symbol_filter(struct dso *self, struct symbol *sym)
{
- static int filter_match;
struct sym_entry *syme;
const char *name = sym->name;
+ int i;
+
+ /*
+ * ppc64 uses function descriptors and appends a '.' to the
+ * start of every instruction address. Remove it.
+ */
+ if (name[0] == '.')
+ name++;
if (!strcmp(name, "_text") ||
!strcmp(name, "_etext") ||
@@ -302,37 +798,17 @@ static int symbol_filter(struct dso *self, struct symbol *sym)
return 1;
syme = dso__sym_priv(self, sym);
- /* Tag samples to be skipped. */
- if (!strcmp("default_idle", name) ||
- !strcmp("cpu_idle", name) ||
- !strcmp("enter_idle", name) ||
- !strcmp("exit_idle", name) ||
- !strcmp("mwait_idle", name))
- syme->skip = 1;
-
- if (filter_match == 1) {
- filter_end = sym->start;
- filter_match = -1;
- if (filter_end - filter_start > 10000) {
- fprintf(stderr,
- "hm, too large filter symbol <%s> - skipping.\n",
- sym_filter);
- fprintf(stderr, "symbol filter start: %016lx\n",
- filter_start);
- fprintf(stderr, " end: %016lx\n",
- filter_end);
- filter_end = filter_start = 0;
- sym_filter = NULL;
- sleep(1);
+ pthread_mutex_init(&syme->source_lock, NULL);
+ if (!sym_filter_entry && sym_filter && !strcmp(name, sym_filter))
+ sym_filter_entry = syme;
+
+ for (i = 0; skip_symbols[i]; i++) {
+ if (!strcmp(skip_symbols[i], name)) {
+ syme->skip = 1;
+ break;
}
}
- if (filter_match == 0 && sym_filter && !strcmp(name, sym_filter)) {
- filter_match = 1;
- filter_start = sym->start;
- }
-
-
return 0;
}
@@ -340,12 +816,13 @@ static int parse_symbols(void)
{
struct rb_node *node;
struct symbol *sym;
+ int modules = vmlinux ? 1 : 0;
kernel_dso = dso__new("[kernel]", sizeof(struct sym_entry));
if (kernel_dso == NULL)
return -1;
- if (dso__load_kernel(kernel_dso, NULL, symbol_filter, 1) != 0)
+ if (dso__load_kernel(kernel_dso, vmlinux, symbol_filter, verbose, modules) <= 0)
goto out_delete_dso;
node = rb_first(&kernel_dso->syms);
@@ -367,8 +844,6 @@ out_delete_dso:
return -1;
}
-#define TRACE_COUNT 3
-
/*
* Binary search in the histogram table and record the hit:
*/
@@ -381,6 +856,7 @@ static void record_ip(u64 ip, int counter)
if (!syme->skip) {
syme->count[counter]++;
+ record_precise_ip(syme, counter, ip);
pthread_mutex_lock(&active_symbols_lock);
if (list_empty(&syme->node) || !syme->node.next)
__list_insert_active_sym(syme);
@@ -407,7 +883,7 @@ static void process_event(u64 ip, int counter, int user)
struct mmap_data {
int counter;
void *base;
- unsigned int mask;
+ int mask;
unsigned int prev;
};
@@ -538,7 +1014,7 @@ int group_fd;
static void start_counter(int i, int counter)
{
struct perf_counter_attr *attr;
- unsigned int cpu;
+ int cpu;
cpu = profile_cpu;
if (target_pid == -1 && profile_cpu == -1)
@@ -548,6 +1024,7 @@ static void start_counter(int i, int counter)
attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID;
attr->freq = freq;
+ attr->inherit = (cpu < 0) && inherit;
try_again:
fd[i][counter] = sys_perf_counter_open(attr, target_pid, cpu, group_fd, 0);
@@ -661,6 +1138,7 @@ static const struct option options[] = {
"system-wide collection from all CPUs"),
OPT_INTEGER('C', "CPU", &profile_cpu,
"CPU to profile on"),
+ OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"),
OPT_INTEGER('m', "mmap-pages", &mmap_pages,
"number of mmap data pages"),
OPT_INTEGER('r', "realtime", &realtime_prio,
@@ -673,9 +1151,11 @@ static const struct option options[] = {
"only display functions with more events than this"),
OPT_BOOLEAN('g', "group", &group,
"put the counters into a counter group"),
- OPT_STRING('s', "sym-filter", &sym_filter, "pattern",
- "only display symbols matchig this pattern"),
- OPT_BOOLEAN('z', "zero", &group,
+ OPT_BOOLEAN('i', "inherit", &inherit,
+ "child tasks inherit counters"),
+ OPT_STRING('s', "sym-annotate", &sym_filter, "symbol name",
+ "symbol to annotate - requires -k option"),
+ OPT_BOOLEAN('z', "zero", &zero,
"zero history across updates"),
OPT_INTEGER('F', "freq", &freq,
"profile at this frequency"),
@@ -686,10 +1166,12 @@ static const struct option options[] = {
OPT_END()
};
-int cmd_top(int argc, const char **argv, const char *prefix)
+int cmd_top(int argc, const char **argv, const char *prefix __used)
{
int counter;
+ symbol__init();
+
page_size = sysconf(_SC_PAGE_SIZE);
argc = parse_options(argc, argv, options, top_usage, 0);
@@ -715,6 +1197,7 @@ int cmd_top(int argc, const char **argv, const char *prefix)
delay_secs = 1;
parse_symbols();
+ parse_source(sym_filter_entry);
/*
* Fill in the ones not specifically initialized via -c:
diff --git a/tools/perf/perf.c b/tools/perf/perf.c
index 4eb72593370..31982ad064b 100644
--- a/tools/perf/perf.c
+++ b/tools/perf/perf.c
@@ -12,6 +12,8 @@
#include "util/cache.h"
#include "util/quote.h"
#include "util/run-command.h"
+#include "util/parse-events.h"
+#include "util/string.h"
const char perf_usage_string[] =
"perf [--version] [--help] COMMAND [ARGS]";
@@ -25,6 +27,8 @@ struct pager_config {
int val;
};
+static char debugfs_mntpt[MAXPATHLEN];
+
static int pager_command_config(const char *var, const char *value, void *data)
{
struct pager_config *c = data;
@@ -56,6 +60,15 @@ static void commit_pager_choice(void) {
}
}
+static void set_debugfs_path(void)
+{
+ char *path;
+
+ path = getenv(PERF_DEBUGFS_ENVIRONMENT);
+ snprintf(debugfs_path, MAXPATHLEN, "%s/%s", path ?: debugfs_mntpt,
+ "tracing/events");
+}
+
static int handle_options(const char*** argv, int* argc, int* envchanged)
{
int handled = 0;
@@ -122,6 +135,22 @@ static int handle_options(const char*** argv, int* argc, int* envchanged)
setenv(PERF_WORK_TREE_ENVIRONMENT, cmd + 12, 1);
if (envchanged)
*envchanged = 1;
+ } else if (!strcmp(cmd, "--debugfs-dir")) {
+ if (*argc < 2) {
+ fprintf(stderr, "No directory given for --debugfs-dir.\n");
+ usage(perf_usage_string);
+ }
+ strncpy(debugfs_mntpt, (*argv)[1], MAXPATHLEN);
+ debugfs_mntpt[MAXPATHLEN - 1] = '\0';
+ if (envchanged)
+ *envchanged = 1;
+ (*argv)++;
+ (*argc)--;
+ } else if (!prefixcmp(cmd, "--debugfs-dir=")) {
+ strncpy(debugfs_mntpt, cmd + 14, MAXPATHLEN);
+ debugfs_mntpt[MAXPATHLEN - 1] = '\0';
+ if (envchanged)
+ *envchanged = 1;
} else {
fprintf(stderr, "Unknown option: %s\n", cmd);
usage(perf_usage_string);
@@ -228,9 +257,7 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
if (use_pager == -1 && p->option & USE_PAGER)
use_pager = 1;
commit_pager_choice();
-
- if (p->option & NEED_WORK_TREE)
- /* setup_work_tree() */;
+ set_debugfs_path();
status = p->fn(argc, argv, prefix);
if (status)
@@ -266,7 +293,7 @@ static void handle_internal_command(int argc, const char **argv)
{ "annotate", cmd_annotate, 0 },
{ "version", cmd_version, 0 },
};
- int i;
+ unsigned int i;
static const char ext[] = STRIP_EXTENSION;
if (sizeof(ext) > 1) {
@@ -349,6 +376,49 @@ static int run_argv(int *argcp, const char ***argv)
return done_alias;
}
+/* mini /proc/mounts parser: searching for "^blah /mount/point debugfs" */
+static void get_debugfs_mntpt(void)
+{
+ FILE *file;
+ char fs_type[100];
+ char debugfs[MAXPATHLEN];
+
+ /*
+ * try the standard location
+ */
+ if (valid_debugfs_mount("/sys/kernel/debug/") == 0) {
+ strcpy(debugfs_mntpt, "/sys/kernel/debug/");
+ return;
+ }
+
+ /*
+ * try the sane location
+ */
+ if (valid_debugfs_mount("/debug/") == 0) {
+ strcpy(debugfs_mntpt, "/debug/");
+ return;
+ }
+
+ /*
+ * give up and parse /proc/mounts
+ */
+ file = fopen("/proc/mounts", "r");
+ if (file == NULL)
+ return;
+
+ while (fscanf(file, "%*s %"
+ STR(MAXPATHLEN)
+ "s %99s %*s %*d %*d\n",
+ debugfs, fs_type) == 2) {
+ if (strcmp(fs_type, "debugfs") == 0)
+ break;
+ }
+ fclose(file);
+ if (strcmp(fs_type, "debugfs") == 0) {
+ strncpy(debugfs_mntpt, debugfs, MAXPATHLEN);
+ debugfs_mntpt[MAXPATHLEN - 1] = '\0';
+ }
+}
int main(int argc, const char **argv)
{
@@ -357,7 +427,8 @@ int main(int argc, const char **argv)
cmd = perf_extract_argv0_path(argv[0]);
if (!cmd)
cmd = "perf-help";
-
+ /* get debugfs mount point from /proc/mounts */
+ get_debugfs_mntpt();
/*
* "perf-xxxx" is the same as "perf xxxx", but we obviously:
*
@@ -380,6 +451,7 @@ int main(int argc, const char **argv)
argc--;
handle_options(&argv, &argc, NULL);
commit_pager_choice();
+ set_debugfs_path();
if (argc > 0) {
if (!prefixcmp(argv[0], "--"))
argv[0] += 2;
diff --git a/tools/perf/perf.h b/tools/perf/perf.h
index 8f729aedc1a..e5148e2b613 100644
--- a/tools/perf/perf.h
+++ b/tools/perf/perf.h
@@ -1,7 +1,13 @@
#ifndef _PERF_PERF_H
#define _PERF_PERF_H
-#if defined(__x86_64__) || defined(__i386__)
+#if defined(__i386__)
+#include "../../arch/x86/include/asm/unistd.h"
+#define rmb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory")
+#define cpu_relax() asm volatile("rep; nop" ::: "memory");
+#endif
+
+#if defined(__x86_64__)
#include "../../arch/x86/include/asm/unistd.h"
#define rmb() asm volatile("lfence" ::: "memory")
#define cpu_relax() asm volatile("rep; nop" ::: "memory");
@@ -29,6 +35,12 @@
#define cpu_relax() asm volatile("" ::: "memory")
#endif
+#ifdef __hppa__
+#include "../../arch/parisc/include/asm/unistd.h"
+#define rmb() asm volatile("" ::: "memory")
+#define cpu_relax() asm volatile("" ::: "memory");
+#endif
+
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
@@ -62,6 +74,8 @@ static inline unsigned long long rdclock(void)
#define __user
#define asmlinkage
+#define __used __attribute__((__unused__))
+
#define unlikely(x) __builtin_expect(!!(x), 0)
#define min(x, y) ({ \
typeof(x) _min1 = (x); \
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 @@
static const char *alias_key;
static char *alias_val;
-static int alias_lookup_cb(const char *k, const char *v, void *cb)
+static int alias_lookup_cb(const char *k, const char *v, void *cb __used)
{
if (!prefixcmp(k, "alias.") && !strcmp(k+6, alias_key)) {
if (!v)
diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h
index 393d6146d13..4b50c412b9c 100644
--- a/tools/perf/util/cache.h
+++ b/tools/perf/util/cache.h
@@ -3,6 +3,7 @@
#include "util.h"
#include "strbuf.h"
+#include "../perf.h"
#define PERF_DIR_ENVIRONMENT "PERF_DIR"
#define PERF_WORK_TREE_ENVIRONMENT "PERF_WORK_TREE"
@@ -17,6 +18,7 @@
#define PERFATTRIBUTES_FILE ".perfattributes"
#define INFOATTRIBUTES_FILE "info/attributes"
#define ATTRIBUTE_MACRO_PREFIX "[attr]"
+#define PERF_DEBUGFS_ENVIRONMENT "PERF_DEBUGFS_DIR"
typedef int (*config_fn_t)(const char *, const char *, void *);
extern int perf_default_config(const char *, const char *, void *);
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c
index ad3c2857896..01147341164 100644
--- a/tools/perf/util/callchain.c
+++ b/tools/perf/util/callchain.c
@@ -4,52 +4,157 @@
* Handle the callchains from the stream in an ad-hoc radix tree and then
* sort them in an rbtree.
*
+ * Using a radix for code path provides a fast retrieval and factorizes
+ * memory use. Also that lets us use the paths in a hierarchical graph view.
+ *
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <errno.h>
+#include <math.h>
#include "callchain.h"
+#define chain_for_each_child(child, parent) \
+ list_for_each_entry(child, &parent->children, brothers)
-static void rb_insert_callchain(struct rb_root *root, struct callchain_node *chain)
+static void
+rb_insert_callchain(struct rb_root *root, struct callchain_node *chain,
+ enum chain_mode mode)
{
struct rb_node **p = &root->rb_node;
struct rb_node *parent = NULL;
struct callchain_node *rnode;
+ u64 chain_cumul = cumul_hits(chain);
while (*p) {
+ u64 rnode_cumul;
+
parent = *p;
rnode = rb_entry(parent, struct callchain_node, rb_node);
-
- if (rnode->hit < chain->hit)
- p = &(*p)->rb_left;
- else
- p = &(*p)->rb_right;
+ rnode_cumul = cumul_hits(rnode);
+
+ switch (mode) {
+ case CHAIN_FLAT:
+ if (rnode->hit < chain->hit)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ break;
+ case CHAIN_GRAPH_ABS: /* Falldown */
+ case CHAIN_GRAPH_REL:
+ if (rnode_cumul < chain_cumul)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ break;
+ default:
+ break;
+ }
}
rb_link_node(&chain->rb_node, parent, p);
rb_insert_color(&chain->rb_node, root);
}
+static void
+__sort_chain_flat(struct rb_root *rb_root, struct callchain_node *node,
+ u64 min_hit)
+{
+ struct callchain_node *child;
+
+ chain_for_each_child(child, node)
+ __sort_chain_flat(rb_root, child, min_hit);
+
+ if (node->hit && node->hit >= min_hit)
+ rb_insert_callchain(rb_root, node, CHAIN_FLAT);
+}
+
/*
* Once we get every callchains from the stream, we can now
* sort them by hit
*/
-void sort_chain_to_rbtree(struct rb_root *rb_root, struct callchain_node *node)
+static void
+sort_chain_flat(struct rb_root *rb_root, struct callchain_node *node,
+ u64 min_hit, struct callchain_param *param __used)
+{
+ __sort_chain_flat(rb_root, node, min_hit);
+}
+
+static void __sort_chain_graph_abs(struct callchain_node *node,
+ u64 min_hit)
+{
+ struct callchain_node *child;
+
+ node->rb_root = RB_ROOT;
+
+ chain_for_each_child(child, node) {
+ __sort_chain_graph_abs(child, min_hit);
+ if (cumul_hits(child) >= min_hit)
+ rb_insert_callchain(&node->rb_root, child,
+ CHAIN_GRAPH_ABS);
+ }
+}
+
+static void
+sort_chain_graph_abs(struct rb_root *rb_root, struct callchain_node *chain_root,
+ u64 min_hit, struct callchain_param *param __used)
+{
+ __sort_chain_graph_abs(chain_root, min_hit);
+ rb_root->rb_node = chain_root->rb_root.rb_node;
+}
+
+static void __sort_chain_graph_rel(struct callchain_node *node,
+ double min_percent)
{
struct callchain_node *child;
+ u64 min_hit;
+
+ node->rb_root = RB_ROOT;
+ min_hit = ceil(node->children_hit * min_percent);
+
+ chain_for_each_child(child, node) {
+ __sort_chain_graph_rel(child, min_percent);
+ if (cumul_hits(child) >= min_hit)
+ rb_insert_callchain(&node->rb_root, child,
+ CHAIN_GRAPH_REL);
+ }
+}
- list_for_each_entry(child, &node->children, brothers)
- sort_chain_to_rbtree(rb_root, child);
+static void
+sort_chain_graph_rel(struct rb_root *rb_root, struct callchain_node *chain_root,
+ u64 min_hit __used, struct callchain_param *param)
+{
+ __sort_chain_graph_rel(chain_root, param->min_percent / 100.0);
+ rb_root->rb_node = chain_root->rb_root.rb_node;
+}
- if (node->hit)
- rb_insert_callchain(rb_root, node);
+int register_callchain_param(struct callchain_param *param)
+{
+ switch (param->mode) {
+ case CHAIN_GRAPH_ABS:
+ param->sort = sort_chain_graph_abs;
+ break;
+ case CHAIN_GRAPH_REL:
+ param->sort = sort_chain_graph_rel;
+ break;
+ case CHAIN_FLAT:
+ param->sort = sort_chain_flat;
+ break;
+ default:
+ return -1;
+ }
+ return 0;
}
-static struct callchain_node *create_child(struct callchain_node *parent)
+/*
+ * Create a child for a parent. If inherit_children, then the new child
+ * will become the new parent of it's parent children
+ */
+static struct callchain_node *
+create_child(struct callchain_node *parent, bool inherit_children)
{
struct callchain_node *new;
@@ -61,91 +166,150 @@ static struct callchain_node *create_child(struct callchain_node *parent)
new->parent = parent;
INIT_LIST_HEAD(&new->children);
INIT_LIST_HEAD(&new->val);
+
+ if (inherit_children) {
+ struct callchain_node *next;
+
+ list_splice(&parent->children, &new->children);
+ INIT_LIST_HEAD(&parent->children);
+
+ chain_for_each_child(next, new)
+ next->parent = new;
+ }
list_add_tail(&new->brothers, &parent->children);
return new;
}
+/*
+ * Fill the node with callchain values
+ */
static void
-fill_node(struct callchain_node *node, struct ip_callchain *chain, int start)
+fill_node(struct callchain_node *node, struct ip_callchain *chain,
+ int start, struct symbol **syms)
{
- int i;
+ unsigned int i;
for (i = start; i < chain->nr; i++) {
struct callchain_list *call;
- call = malloc(sizeof(*chain));
+ call = malloc(sizeof(*call));
if (!call) {
perror("not enough memory for the code path tree");
return;
}
call->ip = chain->ips[i];
+ call->sym = syms[i];
list_add_tail(&call->list, &node->val);
}
- node->val_nr = i - start;
+ node->val_nr = chain->nr - start;
+ if (!node->val_nr)
+ printf("Warning: empty node in callchain tree\n");
}
-static void add_child(struct callchain_node *parent, struct ip_callchain *chain)
+static void
+add_child(struct callchain_node *parent, struct ip_callchain *chain,
+ int start, struct symbol **syms)
{
struct callchain_node *new;
- new = create_child(parent);
- fill_node(new, chain, parent->val_nr);
+ new = create_child(parent, false);
+ fill_node(new, chain, start, syms);
+ new->children_hit = 0;
new->hit = 1;
}
+/*
+ * Split the parent in two parts (a new child is created) and
+ * give a part of its callchain to the created child.
+ * Then create another child to host the given callchain of new branch
+ */
static void
split_add_child(struct callchain_node *parent, struct ip_callchain *chain,
- struct callchain_list *to_split, int idx)
+ struct callchain_list *to_split, int idx_parents, int idx_local,
+ struct symbol **syms)
{
struct callchain_node *new;
+ struct list_head *old_tail;
+ unsigned int idx_total = idx_parents + idx_local;
/* split */
- new = create_child(parent);
- list_move_tail(&to_split->list, &new->val);
- new->hit = parent->hit;
- parent->hit = 0;
- parent->val_nr = idx;
+ new = create_child(parent, true);
- /* create the new one */
- add_child(parent, chain);
+ /* split the callchain and move a part to the new child */
+ old_tail = parent->val.prev;
+ list_del_range(&to_split->list, old_tail);
+ new->val.next = &to_split->list;
+ new->val.prev = old_tail;
+ to_split->list.prev = &new->val;
+ old_tail->next = &new->val;
+
+ /* split the hits */
+ new->hit = parent->hit;
+ new->children_hit = parent->children_hit;
+ parent->children_hit = cumul_hits(new);
+ new->val_nr = parent->val_nr - idx_local;
+ parent->val_nr = idx_local;
+
+ /* create a new child for the new branch if any */
+ if (idx_total < chain->nr) {
+ parent->hit = 0;
+ add_child(parent, chain, idx_total, syms);
+ parent->children_hit++;
+ } else {
+ parent->hit = 1;
+ }
}
static int
__append_chain(struct callchain_node *root, struct ip_callchain *chain,
- int start);
+ unsigned int start, struct symbol **syms);
-static int
-__append_chain_children(struct callchain_node *root, struct ip_callchain *chain)
+static void
+__append_chain_children(struct callchain_node *root, struct ip_callchain *chain,
+ struct symbol **syms, unsigned int start)
{
struct callchain_node *rnode;
/* lookup in childrens */
- list_for_each_entry(rnode, &root->children, brothers) {
- int ret = __append_chain(rnode, chain, root->val_nr);
+ chain_for_each_child(rnode, root) {
+ unsigned int ret = __append_chain(rnode, chain, start, syms);
+
if (!ret)
- return 0;
+ goto inc_children_hit;
}
- return -1;
+ /* nothing in children, add to the current node */
+ add_child(root, chain, start, syms);
+
+inc_children_hit:
+ root->children_hit++;
}
static int
__append_chain(struct callchain_node *root, struct ip_callchain *chain,
- int start)
+ unsigned int start, struct symbol **syms)
{
struct callchain_list *cnode;
- int i = start;
+ unsigned int i = start;
bool found = false;
- /* lookup in the current node */
+ /*
+ * Lookup in the current node
+ * If we have a symbol, then compare the start to match
+ * anywhere inside a function.
+ */
list_for_each_entry(cnode, &root->val, list) {
- if (cnode->ip != chain->ips[i++])
+ if (i == chain->nr)
+ break;
+ if (cnode->sym && syms[i]) {
+ if (cnode->sym->start != syms[i]->start)
+ break;
+ } else if (cnode->ip != chain->ips[i])
break;
if (!found)
found = true;
- if (i == chain->nr)
- break;
+ i++;
}
/* matches not, relay on the parent */
@@ -153,22 +317,27 @@ __append_chain(struct callchain_node *root, struct ip_callchain *chain,
return -1;
/* we match only a part of the node. Split it and add the new chain */
- if (i < root->val_nr) {
- split_add_child(root, chain, cnode, i);
+ if (i - start < root->val_nr) {
+ split_add_child(root, chain, cnode, start, i - start, syms);
return 0;
}
/* we match 100% of the path, increment the hit */
- if (i == root->val_nr) {
+ if (i - start == root->val_nr && i == chain->nr) {
root->hit++;
return 0;
}
- return __append_chain_children(root, chain);
+ /* We match the node and still have a part remaining */
+ __append_chain_children(root, chain, syms, i);
+
+ return 0;
}
-void append_chain(struct callchain_node *root, struct ip_callchain *chain)
+void append_chain(struct callchain_node *root, struct ip_callchain *chain,
+ struct symbol **syms)
{
- if (__append_chain_children(root, chain) == -1)
- add_child(root, chain);
+ if (!chain->nr)
+ return;
+ __append_chain_children(root, chain, syms, 0);
}
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h
index fa1cd2f71fd..a926ae4f5a1 100644
--- a/tools/perf/util/callchain.h
+++ b/tools/perf/util/callchain.h
@@ -2,22 +2,43 @@
#define __PERF_CALLCHAIN_H
#include "../perf.h"
-#include "list.h"
-#include "rbtree.h"
+#include <linux/list.h>
+#include <linux/rbtree.h>
+#include "symbol.h"
+enum chain_mode {
+ CHAIN_NONE,
+ CHAIN_FLAT,
+ CHAIN_GRAPH_ABS,
+ CHAIN_GRAPH_REL
+};
struct callchain_node {
struct callchain_node *parent;
struct list_head brothers;
- struct list_head children;
- struct list_head val;
- struct rb_node rb_node;
- int val_nr;
- int hit;
+ struct list_head children;
+ struct list_head val;
+ struct rb_node rb_node; /* to sort nodes in an rbtree */
+ struct rb_root rb_root; /* sorted tree of children */
+ unsigned int val_nr;
+ u64 hit;
+ u64 children_hit;
+};
+
+struct callchain_param;
+
+typedef void (*sort_chain_func_t)(struct rb_root *, struct callchain_node *,
+ u64, struct callchain_param *);
+
+struct callchain_param {
+ enum chain_mode mode;
+ double min_percent;
+ sort_chain_func_t sort;
};
struct callchain_list {
- unsigned long ip;
+ u64 ip;
+ struct symbol *sym;
struct list_head list;
};
@@ -28,6 +49,12 @@ static inline void callchain_init(struct callchain_node *node)
INIT_LIST_HEAD(&node->val);
}
-void append_chain(struct callchain_node *root, struct ip_callchain *chain);
-void sort_chain_to_rbtree(struct rb_root *rb_root, struct callchain_node *node);
+static inline u64 cumul_hits(struct callchain_node *node)
+{
+ return node->hit + node->children_hit;
+}
+
+int register_callchain_param(struct callchain_param *param);
+void append_chain(struct callchain_node *root, struct ip_callchain *chain,
+ struct symbol **syms);
#endif
diff --git a/tools/perf/util/color.c b/tools/perf/util/color.c
index 9a8c20ccc53..90a044d1fe7 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)
};
char *end;
int i;
- for (i = 0; i < ARRAY_SIZE(color_names); i++) {
+
+ for (i = 0; i < (int)ARRAY_SIZE(color_names); i++) {
const char *str = color_names[i];
if (!strncasecmp(name, str, len) && !str[len])
return i - 1;
@@ -28,7 +29,8 @@ static int parse_attr(const char *name, int len)
static const char * const attr_names[] = {
"bold", "dim", "ul", "blink", "reverse"
};
- int i;
+ unsigned int i;
+
for (i = 0; i < ARRAY_SIZE(attr_names); i++) {
const char *str = attr_names[i];
if (!strncasecmp(name, str, len) && !str[len])
@@ -222,10 +224,12 @@ int color_fwrite_lines(FILE *fp, const char *color,
{
if (!*color)
return fwrite(buf, count, 1, fp) != 1;
+
while (count) {
char *p = memchr(buf, '\n', count);
+
if (p != buf && (fputs(color, fp) < 0 ||
- fwrite(buf, p ? p - buf : count, 1, fp) != 1 ||
+ fwrite(buf, p ? (size_t)(p - buf) : count, 1, fp) != 1 ||
fputs(PERF_COLOR_RESET, fp) < 0))
return -1;
if (!p)
@@ -238,4 +242,31 @@ int color_fwrite_lines(FILE *fp, const char *color,
return 0;
}
+char *get_percent_color(double percent)
+{
+ char *color = PERF_COLOR_NORMAL;
+ /*
+ * We color high-overhead entries in red, mid-overhead
+ * entries in green - and keep the low overhead places
+ * normal:
+ */
+ if (percent >= MIN_RED)
+ color = PERF_COLOR_RED;
+ else {
+ if (percent > MIN_GREEN)
+ color = PERF_COLOR_GREEN;
+ }
+ return color;
+}
+
+int percent_color_fprintf(FILE *fp, const char *fmt, double percent)
+{
+ int r;
+ char *color;
+
+ color = get_percent_color(percent);
+ r = color_fprintf(fp, color, fmt, percent);
+
+ return r;
+}
diff --git a/tools/perf/util/color.h b/tools/perf/util/color.h
index 5abfd379582..706cec50bd2 100644
--- a/tools/perf/util/color.h
+++ b/tools/perf/util/color.h
@@ -15,6 +15,9 @@
#define PERF_COLOR_CYAN "\033[36m"
#define PERF_COLOR_BG_RED "\033[41m"
+#define MIN_GREEN 0.5
+#define MIN_RED 5.0
+
/*
* This variable stores the value of color.ui
*/
@@ -32,5 +35,7 @@ void color_parse_mem(const char *value, int len, const char *var, char *dst);
int color_fprintf(FILE *fp, const char *color, const char *fmt, ...);
int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...);
int color_fwrite_lines(FILE *fp, const char *color, size_t count, const char *buf);
+int percent_color_fprintf(FILE *fp, const char *fmt, double percent);
+char *get_percent_color(double percent);
#endif /* COLOR_H */
diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c
index 3dd13faa6a2..780df541006 100644
--- a/tools/perf/util/config.c
+++ b/tools/perf/util/config.c
@@ -47,10 +47,12 @@ static int get_next_char(void)
static char *parse_value(void)
{
static char value[1024];
- int quote = 0, comment = 0, len = 0, space = 0;
+ int quote = 0, comment = 0, space = 0;
+ size_t len = 0;
for (;;) {
int c = get_next_char();
+
if (len >= sizeof(value) - 1)
return NULL;
if (c == '\n') {
@@ -353,13 +355,13 @@ int perf_config_string(const char **dest, const char *var, const char *value)
return 0;
}
-static int perf_default_core_config(const char *var, const char *value)
+static int perf_default_core_config(const char *var __used, const char *value __used)
{
/* Add other config variables here and to Documentation/config.txt. */
return 0;
}
-int perf_default_config(const char *var, const char *value, void *dummy)
+int perf_default_config(const char *var, const char *value, void *dummy __used)
{
if (!prefixcmp(var, "core."))
return perf_default_core_config(var, value);
@@ -471,10 +473,10 @@ static int matches(const char* key, const char* value)
!regexec(store.value_regex, value, 0, NULL, 0)));
}
-static int store_aux(const char* key, const char* value, void *cb)
+static int store_aux(const char* key, const char* value, void *cb __used)
{
+ int section_len;
const char *ep;
- size_t section_len;
switch (store.state) {
case KEY_SEEN:
@@ -551,7 +553,7 @@ static int store_write_section(int fd, const char* key)
strbuf_addf(&sb, "[%.*s]\n", store.baselen, key);
}
- success = write_in_full(fd, sb.buf, sb.len) == sb.len;
+ success = (write_in_full(fd, sb.buf, sb.len) == (ssize_t)sb.len);
strbuf_release(&sb);
return success;
@@ -599,7 +601,7 @@ static int store_write_pair(int fd, const char* key, const char* value)
}
strbuf_addf(&sb, "%s\n", quote);
- success = write_in_full(fd, sb.buf, sb.len) == sb.len;
+ success = (write_in_full(fd, sb.buf, sb.len) == (ssize_t)sb.len);
strbuf_release(&sb);
return success;
@@ -741,7 +743,7 @@ int perf_config_set_multivar(const char* key, const char* value,
} else {
struct stat st;
char* contents;
- size_t contents_sz, copy_begin, copy_end;
+ ssize_t contents_sz, copy_begin, copy_end;
int i, new_line = 0;
if (value_regex == NULL)
diff --git a/tools/perf/util/exec_cmd.c b/tools/perf/util/exec_cmd.c
index d3929226315..34a35286738 100644
--- a/tools/perf/util/exec_cmd.c
+++ b/tools/perf/util/exec_cmd.c
@@ -1,6 +1,9 @@
#include "cache.h"
#include "exec_cmd.h"
#include "quote.h"
+
+#include <string.h>
+
#define MAX_ARGS 32
extern char **environ;
@@ -51,7 +54,7 @@ const char *perf_extract_argv0_path(const char *argv0)
slash--;
if (slash >= argv0) {
- argv0_path = strndup(argv0, slash - argv0);
+ argv0_path = xstrndup(argv0, slash - argv0);
return slash + 1;
}
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 450384b3bbe..b92a457ca32 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -185,6 +185,8 @@ static void do_read(int fd, void *buf, size_t size)
if (ret < 0)
die("failed to read");
+ if (ret == 0)
+ die("failed to read: missing data");
size -= ret;
buf += ret;
@@ -213,9 +215,10 @@ struct perf_header *perf_header__read(int fd)
for (i = 0; i < nr_attrs; i++) {
struct perf_header_attr *attr;
- off_t tmp = lseek(fd, 0, SEEK_CUR);
+ off_t tmp;
do_read(fd, &f_attr, sizeof(f_attr));
+ tmp = lseek(fd, 0, SEEK_CUR);
attr = perf_header_attr__new(&f_attr.attr);
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h
index b5ef53ad4c7..bf280449fcf 100644
--- a/tools/perf/util/header.h
+++ b/tools/perf/util/header.h
@@ -16,7 +16,7 @@ struct perf_header {
int frozen;
int attrs, size;
struct perf_header_attr **attr;
- off_t attr_offset;
+ s64 attr_offset;
u64 data_offset;
u64 data_size;
};
diff --git a/tools/perf/util/help.c b/tools/perf/util/help.c
index 17a00e0df2c..fbb00978b2e 100644
--- a/tools/perf/util/help.c
+++ b/tools/perf/util/help.c
@@ -26,7 +26,7 @@ static int term_columns(void)
return 80;
}
-void add_cmdname(struct cmdnames *cmds, const char *name, int len)
+void add_cmdname(struct cmdnames *cmds, const char *name, size_t len)
{
struct cmdname *ent = malloc(sizeof(*ent) + len + 1);
@@ -40,7 +40,8 @@ void add_cmdname(struct cmdnames *cmds, const char *name, int len)
static void clean_cmdnames(struct cmdnames *cmds)
{
- int i;
+ unsigned int i;
+
for (i = 0; i < cmds->cnt; ++i)
free(cmds->names[i]);
free(cmds->names);
@@ -57,7 +58,7 @@ static int cmdname_compare(const void *a_, const void *b_)
static void uniq(struct cmdnames *cmds)
{
- int i, j;
+ unsigned int i, j;
if (!cmds->cnt)
return;
@@ -71,7 +72,7 @@ static void uniq(struct cmdnames *cmds)
void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes)
{
- int ci, cj, ei;
+ size_t ci, cj, ei;
int cmp;
ci = cj = ei = 0;
@@ -106,8 +107,9 @@ static void pretty_print_string_list(struct cmdnames *cmds, int longest)
printf(" ");
for (j = 0; j < cols; j++) {
- int n = j * rows + i;
- int size = space;
+ unsigned int n = j * rows + i;
+ unsigned int size = space;
+
if (n >= cmds->cnt)
break;
if (j == cols-1 || n + rows >= cmds->cnt)
@@ -208,7 +210,7 @@ void load_command_list(const char *prefix,
void list_commands(const char *title, struct cmdnames *main_cmds,
struct cmdnames *other_cmds)
{
- int i, longest = 0;
+ unsigned int i, longest = 0;
for (i = 0; i < main_cmds->cnt; i++)
if (longest < main_cmds->names[i]->len)
@@ -239,7 +241,8 @@ void list_commands(const char *title, struct cmdnames *main_cmds,
int is_in_cmdlist(struct cmdnames *c, const char *s)
{
- int i;
+ unsigned int i;
+
for (i = 0; i < c->cnt; i++)
if (!strcmp(s, c->names[i]->name))
return 1;
@@ -271,7 +274,8 @@ static int levenshtein_compare(const void *p1, const void *p2)
static void add_cmd_list(struct cmdnames *cmds, struct cmdnames *old)
{
- int i;
+ unsigned int i;
+
ALLOC_GROW(cmds->names, cmds->cnt + old->cnt, cmds->alloc);
for (i = 0; i < old->cnt; i++)
@@ -283,7 +287,7 @@ static void add_cmd_list(struct cmdnames *cmds, struct cmdnames *old)
const char *help_unknown_cmd(const char *cmd)
{
- int i, n = 0, best_similarity = 0;
+ unsigned int i, n = 0, best_similarity = 0;
struct cmdnames main_cmds, other_cmds;
memset(&main_cmds, 0, sizeof(main_cmds));
@@ -345,7 +349,7 @@ const char *help_unknown_cmd(const char *cmd)
exit(1);
}
-int cmd_version(int argc, const char **argv, const char *prefix)
+int cmd_version(int argc __used, const char **argv __used, const char *prefix __used)
{
printf("perf version %s\n", perf_version_string);
return 0;
diff --git a/tools/perf/util/help.h b/tools/perf/util/help.h
index 56bc15406ff..7128783637b 100644
--- a/tools/perf/util/help.h
+++ b/tools/perf/util/help.h
@@ -2,8 +2,8 @@
#define HELP_H
struct cmdnames {
- int alloc;
- int cnt;
+ size_t alloc;
+ size_t cnt;
struct cmdname {
size_t len; /* also used for similarity index in help.c */
char name[FLEX_ARRAY];
@@ -19,7 +19,7 @@ static inline void mput_char(char c, unsigned int num)
void load_command_list(const char *prefix,
struct cmdnames *main_cmds,
struct cmdnames *other_cmds);
-void add_cmdname(struct cmdnames *cmds, const char *name, int len);
+void add_cmdname(struct cmdnames *cmds, const char *name, size_t len);
/* Here we require that excludes is a sorted list. */
void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes);
int is_in_cmdlist(struct cmdnames *c, const char *s);
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/linux/kernel.h b/tools/perf/util/include/linux/kernel.h
new file mode 100644
index 00000000000..a6b87390cb5
--- /dev/null
+++ b/tools/perf/util/include/linux/kernel.h
@@ -0,0 +1,29 @@
+#ifndef PERF_LINUX_KERNEL_H_
+#define PERF_LINUX_KERNEL_H_
+
+#ifndef offsetof
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+#endif
+
+#ifndef container_of
+/**
+ * container_of - cast a member of a structure out to the containing structure
+ * @ptr: the pointer to the member.
+ * @type: the type of the container struct this is embedded in.
+ * @member: the name of the member within the struct.
+ *
+ */
+#define container_of(ptr, type, member) ({ \
+ const typeof(((type *)0)->member) * __mptr = (ptr); \
+ (type *)((char *)__mptr - offsetof(type, member)); })
+#endif
+
+#ifndef max
+#define max(x, y) ({ \
+ typeof(x) _max1 = (x); \
+ typeof(y) _max2 = (y); \
+ (void) (&_max1 == &_max2); \
+ _max1 > _max2 ? _max1 : _max2; })
+#endif
+
+#endif
diff --git a/tools/perf/util/include/linux/list.h b/tools/perf/util/include/linux/list.h
new file mode 100644
index 00000000000..dbe4b814382
--- /dev/null
+++ b/tools/perf/util/include/linux/list.h
@@ -0,0 +1,18 @@
+#include "../../../../include/linux/list.h"
+
+#ifndef PERF_LIST_H
+#define PERF_LIST_H
+/**
+ * list_del_range - deletes range of entries from list.
+ * @begin: first element in the range to delete from the list.
+ * @end: last element in the range to delete from the list.
+ * Note: list_empty on the range of entries does not return true after this,
+ * the entries is in an undefined state.
+ */
+static inline void list_del_range(struct list_head *begin,
+ struct list_head *end)
+{
+ begin->prev->next = end->next;
+ end->next->prev = begin->prev;
+}
+#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 @@
+#ifndef PERF_LINUX_MODULE_H
+#define PERF_LINUX_MODULE_H
+
+#define EXPORT_SYMBOL(name)
+
+#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 @@
+#ifndef PERF_LINUX_PREFETCH_H
+#define PERF_LINUX_PREFETCH_H
+
+static inline void prefetch(void *a __attribute__((unused))) { }
+
+#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/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 @@
-#ifndef _LINUX_LIST_H
-#define _LINUX_LIST_H
-/*
- Copyright (C) Cast of dozens, comes from the Linux kernel
-
- This program is free software; you can redistribute it and/or modify it
- under the terms of version 2 of the GNU General Public License as
- published by the Free Software Foundation.
-*/
-
-#include <stddef.h>
-
-/*
- * These are non-NULL pointers that will result in page faults
- * under normal circumstances, used to verify that nobody uses
- * non-initialized list entries.
- */
-#define LIST_POISON1 ((void *)0x00100100)
-#define LIST_POISON2 ((void *)0x00200200)
-
-/**
- * container_of - cast a member of a structure out to the containing structure
- * @ptr: the pointer to the member.
- * @type: the type of the container struct this is embedded in.
- * @member: the name of the member within the struct.
- *
- */
-#define container_of(ptr, type, member) ({ \
- const typeof( ((type *)0)->member ) *__mptr = (ptr); \
- (type *)( (char *)__mptr - offsetof(type,member) );})
-
-/*
- * Simple doubly linked list implementation.
- *
- * Some of the internal functions ("__xxx") are useful when
- * manipulating whole lists rather than single entries, as
- * sometimes we already know the next/prev entries and we can
- * generate better code by using them directly rather than
- * using the generic single-entry routines.
- */
-
-struct list_head {
- struct list_head *next, *prev;
-};
-
-#define LIST_HEAD_INIT(name) { &(name), &(name) }
-
-#define LIST_HEAD(name) \
- struct list_head name = LIST_HEAD_INIT(name)
-
-static inline void INIT_LIST_HEAD(struct list_head *list)
-{
- list->next = list;
- list->prev = list;
-}
-
-/*
- * Insert a new entry between two known consecutive entries.
- *
- * This is only for internal list manipulation where we know
- * the prev/next entries already!
- */
-static inline void __list_add(struct list_head *new,
- struct list_head *prev,
- struct list_head *next)
-{
- next->prev = new;
- new->next = next;
- new->prev = prev;
- prev->next = new;
-}
-
-/**
- * list_add - add a new entry
- * @new: new entry to be added
- * @head: list head to add it after
- *
- * Insert a new entry after the specified head.
- * This is good for implementing stacks.
- */
-static inline void list_add(struct list_head *new, struct list_head *head)
-{
- __list_add(new, head, head->next);
-}
-
-/**
- * list_add_tail - add a new entry
- * @new: new entry to be added
- * @head: list head to add it before
- *
- * Insert a new entry before the specified head.
- * This is useful for implementing queues.
- */
-static inline void list_add_tail(struct list_head *new, struct list_head *head)
-{
- __list_add(new, head->prev, head);
-}
-
-/*
- * Delete a list entry by making the prev/next entries
- * point to each other.
- *
- * This is only for internal list manipulation where we know
- * the prev/next entries already!
- */
-static inline void __list_del(struct list_head * prev, struct list_head * next)
-{
- next->prev = prev;
- prev->next = next;
-}
-
-/**
- * list_del - deletes entry from list.
- * @entry: the element to delete from the list.
- * Note: list_empty on entry does not return true after this, the entry is
- * in an undefined state.
- */
-static inline void list_del(struct list_head *entry)
-{
- __list_del(entry->prev, entry->next);
- entry->next = LIST_POISON1;
- entry->prev = LIST_POISON2;
-}
-
-/**
- * list_del_range - deletes range of entries from list.
- * @beging: first element in the range to delete from the list.
- * @beging: first element in the range to delete from the list.
- * Note: list_empty on the range of entries does not return true after this,
- * the entries is in an undefined state.
- */
-static inline void list_del_range(struct list_head *begin,
- struct list_head *end)
-{
- begin->prev->next = end->next;
- end->next->prev = begin->prev;
-}
-
-/**
- * list_replace - replace old entry by new one
- * @old : the element to be replaced
- * @new : the new element to insert
- * Note: if 'old' was empty, it will be overwritten.
- */
-static inline void list_replace(struct list_head *old,
- struct list_head *new)
-{
- new->next = old->next;
- new->next->prev = new;
- new->prev = old->prev;
- new->prev->next = new;
-}
-
-static inline void list_replace_init(struct list_head *old,
- struct list_head *new)
-{
- list_replace(old, new);
- INIT_LIST_HEAD(old);
-}
-
-/**
- * list_del_init - deletes entry from list and reinitialize it.
- * @entry: the element to delete from the list.
- */
-static inline void list_del_init(struct list_head *entry)
-{
- __list_del(entry->prev, entry->next);
- INIT_LIST_HEAD(entry);
-}
-
-/**
- * list_move - delete from one list and add as another's head
- * @list: the entry to move
- * @head: the head that will precede our entry
- */
-static inline void list_move(struct list_head *list, struct list_head *head)
-{
- __list_del(list->prev, list->next);
- list_add(list, head);
-}
-
-/**
- * list_move_tail - delete from one list and add as another's tail
- * @list: the entry to move
- * @head: the head that will follow our entry
- */
-static inline void list_move_tail(struct list_head *list,
- struct list_head *head)
-{
- __list_del(list->prev, list->next);
- list_add_tail(list, head);
-}
-
-/**
- * list_is_last - tests whether @list is the last entry in list @head
- * @list: the entry to test
- * @head: the head of the list
- */
-static inline int list_is_last(const struct list_head *list,
- const struct list_head *head)
-{
- return list->next == head;
-}
-
-/**
- * list_empty - tests whether a list is empty
- * @head: the list to test.
- */
-static inline int list_empty(const struct list_head *head)
-{
- return head->next == head;
-}
-
-/**
- * list_empty_careful - tests whether a list is empty and not being modified
- * @head: the list to test
- *
- * Description:
- * tests whether a list is empty _and_ checks that no other CPU might be
- * in the process of modifying either member (next or prev)
- *
- * NOTE: using list_empty_careful() without synchronization
- * can only be safe if the only activity that can happen
- * to the list entry is list_del_init(). Eg. it cannot be used
- * if another CPU could re-list_add() it.
- */
-static inline int list_empty_careful(const struct list_head *head)
-{
- struct list_head *next = head->next;
- return (next == head) && (next == head->prev);
-}
-
-static inline void __list_splice(struct list_head *list,
- struct list_head *head)
-{
- struct list_head *first = list->next;
- struct list_head *last = list->prev;
- struct list_head *at = head->next;
-
- first->prev = head;
- head->next = first;
-
- last->next = at;
- at->prev = last;
-}
-
-/**
- * list_splice - join two lists
- * @list: the new list to add.
- * @head: the place to add it in the first list.
- */
-static inline void list_splice(struct list_head *list, struct list_head *head)
-{
- if (!list_empty(list))
- __list_splice(list, head);
-}
-
-/**
- * list_splice_init - join two lists and reinitialise the emptied list.
- * @list: the new list to add.
- * @head: the place to add it in the first list.
- *
- * The list at @list is reinitialised
- */
-static inline void list_splice_init(struct list_head *list,
- struct list_head *head)
-{
- if (!list_empty(list)) {
- __list_splice(list, head);
- INIT_LIST_HEAD(list);
- }
-}
-
-/**
- * list_entry - get the struct for this entry
- * @ptr: the &struct list_head pointer.
- * @type: the type of the struct this is embedded in.
- * @member: the name of the list_struct within the struct.
- */
-#define list_entry(ptr, type, member) \
- container_of(ptr, type, member)
-
-/**
- * list_first_entry - get the first element from a list
- * @ptr: the list head to take the element from.
- * @type: the type of the struct this is embedded in.
- * @member: the name of the list_struct within the struct.
- *
- * Note, that list is expected to be not empty.
- */
-#define list_first_entry(ptr, type, member) \
- list_entry((ptr)->next, type, member)
-
-/**
- * list_for_each - iterate over a list
- * @pos: the &struct list_head to use as a loop cursor.
- * @head: the head for your list.
- */
-#define list_for_each(pos, head) \
- for (pos = (head)->next; pos != (head); \
- pos = pos->next)
-
-/**
- * __list_for_each - iterate over a list
- * @pos: the &struct list_head to use as a loop cursor.
- * @head: the head for your list.
- *
- * This variant differs from list_for_each() in that it's the
- * simplest possible list iteration code, no prefetching is done.
- * Use this for code that knows the list to be very short (empty
- * or 1 entry) most of the time.
- */
-#define __list_for_each(pos, head) \
- for (pos = (head)->next; pos != (head); pos = pos->next)
-
-/**
- * list_for_each_prev - iterate over a list backwards
- * @pos: the &struct list_head to use as a loop cursor.
- * @head: the head for your list.
- */
-#define list_for_each_prev(pos, head) \
- for (pos = (head)->prev; pos != (head); \
- pos = pos->prev)
-
-/**
- * list_for_each_safe - iterate over a list safe against removal of list entry
- * @pos: the &struct list_head to use as a loop cursor.
- * @n: another &struct list_head to use as temporary storage
- * @head: the head for your list.
- */
-#define list_for_each_safe(pos, n, head) \
- for (pos = (head)->next, n = pos->next; pos != (head); \
- pos = n, n = pos->next)
-
-/**
- * list_for_each_entry - iterate over list of given type
- * @pos: the type * to use as a loop cursor.
- * @head: the head for your list.
- * @member: the name of the list_struct within the struct.
- */
-#define list_for_each_entry(pos, head, member) \
- for (pos = list_entry((head)->next, typeof(*pos), member); \
- &pos->member != (head); \
- pos = list_entry(pos->member.next, typeof(*pos), member))
-
-/**
- * list_for_each_entry_reverse - iterate backwards over list of given type.
- * @pos: the type * to use as a loop cursor.
- * @head: the head for your list.
- * @member: the name of the list_struct within the struct.
- */
-#define list_for_each_entry_reverse(pos, head, member) \
- for (pos = list_entry((head)->prev, typeof(*pos), member); \
- &pos->member != (head); \
- pos = list_entry(pos->member.prev, typeof(*pos), member))
-
-/**
- * list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue
- * @pos: the type * to use as a start point
- * @head: the head of the list
- * @member: the name of the list_struct within the struct.
- *
- * Prepares a pos entry for use as a start point in list_for_each_entry_continue.
- */
-#define list_prepare_entry(pos, head, member) \
- ((pos) ? : list_entry(head, typeof(*pos), member))
-
-/**
- * list_for_each_entry_continue - continue iteration over list of given type
- * @pos: the type * to use as a loop cursor.
- * @head: the head for your list.
- * @member: the name of the list_struct within the struct.
- *
- * Continue to iterate over list of given type, continuing after
- * the current position.
- */
-#define list_for_each_entry_continue(pos, head, member) \
- for (pos = list_entry(pos->member.next, typeof(*pos), member); \
- &pos->member != (head); \
- pos = list_entry(pos->member.next, typeof(*pos), member))
-
-/**
- * list_for_each_entry_from - iterate over list of given type from the current point
- * @pos: the type * to use as a loop cursor.
- * @head: the head for your list.
- * @member: the name of the list_struct within the struct.
- *
- * Iterate over list of given type, continuing from current position.
- */
-#define list_for_each_entry_from(pos, head, member) \
- for (; &pos->member != (head); \
- pos = list_entry(pos->member.next, typeof(*pos), member))
-
-/**
- * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
- * @pos: the type * to use as a loop cursor.
- * @n: another type * to use as temporary storage
- * @head: the head for your list.
- * @member: the name of the list_struct within the struct.
- */
-#define list_for_each_entry_safe(pos, n, head, member) \
- for (pos = list_entry((head)->next, typeof(*pos), member), \
- n = list_entry(pos->member.next, typeof(*pos), member); \
- &pos->member != (head); \
- pos = n, n = list_entry(n->member.next, typeof(*n), member))
-
-/**
- * list_for_each_entry_safe_continue
- * @pos: the type * to use as a loop cursor.
- * @n: another type * to use as temporary storage
- * @head: the head for your list.
- * @member: the name of the list_struct within the struct.
- *
- * Iterate over list of given type, continuing after current point,
- * safe against removal of list entry.
- */
-#define list_for_each_entry_safe_continue(pos, n, head, member) \
- for (pos = list_entry(pos->member.next, typeof(*pos), member), \
- n = list_entry(pos->member.next, typeof(*pos), member); \
- &pos->member != (head); \
- pos = n, n = list_entry(n->member.next, typeof(*n), member))
-
-/**
- * list_for_each_entry_safe_from
- * @pos: the type * to use as a loop cursor.
- * @n: another type * to use as temporary storage
- * @head: the head for your list.
- * @member: the name of the list_struct within the struct.
- *
- * Iterate over list of given type from current point, safe against
- * removal of list entry.
- */
-#define list_for_each_entry_safe_from(pos, n, head, member) \
- for (n = list_entry(pos->member.next, typeof(*pos), member); \
- &pos->member != (head); \
- pos = n, n = list_entry(n->member.next, typeof(*n), member))
-
-/**
- * list_for_each_entry_safe_reverse
- * @pos: the type * to use as a loop cursor.
- * @n: another type * to use as temporary storage
- * @head: the head for your list.
- * @member: the name of the list_struct within the struct.
- *
- * Iterate backwards over list of given type, safe against removal
- * of list entry.
- */
-#define list_for_each_entry_safe_reverse(pos, n, head, member) \
- for (pos = list_entry((head)->prev, typeof(*pos), member), \
- n = list_entry(pos->member.prev, typeof(*pos), member); \
- &pos->member != (head); \
- pos = n, n = list_entry(n->member.prev, typeof(*n), member))
-
-/*
- * Double linked lists with a single pointer list head.
- * Mostly useful for hash tables where the two pointer list head is
- * too wasteful.
- * You lose the ability to access the tail in O(1).
- */
-
-struct hlist_head {
- struct hlist_node *first;
-};
-
-struct hlist_node {
- struct hlist_node *next, **pprev;
-};
-
-#define HLIST_HEAD_INIT { .first = NULL }
-#define HLIST_HEAD(name) struct hlist_head name = { .first = NULL }
-#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
-static inline void INIT_HLIST_NODE(struct hlist_node *h)
-{
- h->next = NULL;
- h->pprev = NULL;
-}
-
-static inline int hlist_unhashed(const struct hlist_node *h)
-{
- return !h->pprev;
-}
-
-static inline int hlist_empty(const struct hlist_head *h)
-{
- return !h->first;
-}
-
-static inline void __hlist_del(struct hlist_node *n)
-{
- struct hlist_node *next = n->next;
- struct hlist_node **pprev = n->pprev;
- *pprev = next;
- if (next)
- next->pprev = pprev;
-}
-
-static inline void hlist_del(struct hlist_node *n)
-{
- __hlist_del(n);
- n->next = LIST_POISON1;
- n->pprev = LIST_POISON2;
-}
-
-static inline void hlist_del_init(struct hlist_node *n)
-{
- if (!hlist_unhashed(n)) {
- __hlist_del(n);
- INIT_HLIST_NODE(n);
- }
-}
-
-static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
-{
- struct hlist_node *first = h->first;
- n->next = first;
- if (first)
- first->pprev = &n->next;
- h->first = n;
- n->pprev = &h->first;
-}
-
-/* next must be != NULL */
-static inline void hlist_add_before(struct hlist_node *n,
- struct hlist_node *next)
-{
- n->pprev = next->pprev;
- n->next = next;
- next->pprev = &n->next;
- *(n->pprev) = n;
-}
-
-static inline void hlist_add_after(struct hlist_node *n,
- struct hlist_node *next)
-{
- next->next = n->next;
- n->next = next;
- next->pprev = &n->next;
-
- if(next->next)
- next->next->pprev = &next->next;
-}
-
-#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
-
-#define hlist_for_each(pos, head) \
- for (pos = (head)->first; pos; \
- pos = pos->next)
-
-#define hlist_for_each_safe(pos, n, head) \
- for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \
- pos = n)
-
-/**
- * hlist_for_each_entry - iterate over list of given type
- * @tpos: the type * to use as a loop cursor.
- * @pos: the &struct hlist_node to use as a loop cursor.
- * @head: the head for your list.
- * @member: the name of the hlist_node within the struct.
- */
-#define hlist_for_each_entry(tpos, pos, head, member) \
- for (pos = (head)->first; \
- pos && \
- ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
- pos = pos->next)
-
-/**
- * hlist_for_each_entry_continue - iterate over a hlist continuing after current point
- * @tpos: the type * to use as a loop cursor.
- * @pos: the &struct hlist_node to use as a loop cursor.
- * @member: the name of the hlist_node within the struct.
- */
-#define hlist_for_each_entry_continue(tpos, pos, member) \
- for (pos = (pos)->next; \
- pos && \
- ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
- pos = pos->next)
-
-/**
- * hlist_for_each_entry_from - iterate over a hlist continuing from current point
- * @tpos: the type * to use as a loop cursor.
- * @pos: the &struct hlist_node to use as a loop cursor.
- * @member: the name of the hlist_node within the struct.
- */
-#define hlist_for_each_entry_from(tpos, pos, member) \
- for (; pos && \
- ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
- pos = pos->next)
-
-/**
- * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry
- * @tpos: the type * to use as a loop cursor.
- * @pos: the &struct hlist_node to use as a loop cursor.
- * @n: another &struct hlist_node to use as temporary storage
- * @head: the head for your list.
- * @member: the name of the hlist_node within the struct.
- */
-#define hlist_for_each_entry_safe(tpos, pos, n, head, member) \
- for (pos = (head)->first; \
- pos && ({ n = pos->next; 1; }) && \
- ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
- pos = n)
-
-#endif
diff --git a/tools/perf/util/module.c b/tools/perf/util/module.c
new file mode 100644
index 00000000000..ddabe925d65
--- /dev/null
+++ b/tools/perf/util/module.c
@@ -0,0 +1,509 @@
+#include "util.h"
+#include "../perf.h"
+#include "string.h"
+#include "module.h"
+
+#include <libelf.h>
+#include <gelf.h>
+#include <elf.h>
+#include <dirent.h>
+#include <sys/utsname.h>
+
+static unsigned int crc32(const char *p, unsigned int len)
+{
+ int i;
+ unsigned int crc = 0;
+
+ while (len--) {
+ crc ^= *p++;
+ for (i = 0; i < 8; i++)
+ crc = (crc >> 1) ^ ((crc & 1) ? 0xedb88320 : 0);
+ }
+ return crc;
+}
+
+/* module section methods */
+
+struct sec_dso *sec_dso__new_dso(const char *name)
+{
+ struct sec_dso *self = malloc(sizeof(*self) + strlen(name) + 1);
+
+ if (self != NULL) {
+ strcpy(self->name, name);
+ self->secs = RB_ROOT;
+ self->find_section = sec_dso__find_section;
+ }
+
+ return self;
+}
+
+static void sec_dso__delete_section(struct section *self)
+{
+ free(((void *)self));
+}
+
+void sec_dso__delete_sections(struct sec_dso *self)
+{
+ struct section *pos;
+ struct rb_node *next = rb_first(&self->secs);
+
+ while (next) {
+ pos = rb_entry(next, struct section, rb_node);
+ next = rb_next(&pos->rb_node);
+ rb_erase(&pos->rb_node, &self->secs);
+ sec_dso__delete_section(pos);
+ }
+}
+
+void sec_dso__delete_self(struct sec_dso *self)
+{
+ sec_dso__delete_sections(self);
+ free(self);
+}
+
+static void sec_dso__insert_section(struct sec_dso *self, struct section *sec)
+{
+ struct rb_node **p = &self->secs.rb_node;
+ struct rb_node *parent = NULL;
+ const u64 hash = sec->hash;
+ struct section *s;
+
+ while (*p != NULL) {
+ parent = *p;
+ s = rb_entry(parent, struct section, rb_node);
+ if (hash < s->hash)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+ rb_link_node(&sec->rb_node, parent, p);
+ rb_insert_color(&sec->rb_node, &self->secs);
+}
+
+struct section *sec_dso__find_section(struct sec_dso *self, const char *name)
+{
+ struct rb_node *n;
+ u64 hash;
+ int len;
+
+ if (self == NULL)
+ return NULL;
+
+ len = strlen(name);
+ hash = crc32(name, len);
+
+ n = self->secs.rb_node;
+
+ while (n) {
+ struct section *s = rb_entry(n, struct section, rb_node);
+
+ if (hash < s->hash)
+ n = n->rb_left;
+ else if (hash > s->hash)
+ n = n->rb_right;
+ else {
+ if (!strcmp(name, s->name))
+ return s;
+ else
+ n = rb_next(&s->rb_node);
+ }
+ }
+
+ return NULL;
+}
+
+static size_t sec_dso__fprintf_section(struct section *self, FILE *fp)
+{
+ return fprintf(fp, "name:%s vma:%llx path:%s\n",
+ self->name, self->vma, self->path);
+}
+
+size_t sec_dso__fprintf(struct sec_dso *self, FILE *fp)
+{
+ size_t ret = fprintf(fp, "dso: %s\n", self->name);
+
+ struct rb_node *nd;
+ for (nd = rb_first(&self->secs); nd; nd = rb_next(nd)) {
+ struct section *pos = rb_entry(nd, struct section, rb_node);
+ ret += sec_dso__fprintf_section(pos, fp);
+ }
+
+ return ret;
+}
+
+static struct section *section__new(const char *name, const char *path)
+{
+ struct section *self = calloc(1, sizeof(*self));
+
+ if (!self)
+ goto out_failure;
+
+ self->name = calloc(1, strlen(name) + 1);
+ if (!self->name)
+ goto out_failure;
+
+ self->path = calloc(1, strlen(path) + 1);
+ if (!self->path)
+ goto out_failure;
+
+ strcpy(self->name, name);
+ strcpy(self->path, path);
+ self->hash = crc32(self->name, strlen(name));
+
+ return self;
+
+out_failure:
+ if (self) {
+ if (self->name)
+ free(self->name);
+ if (self->path)
+ free(self->path);
+ free(self);
+ }
+
+ return NULL;
+}
+
+/* module methods */
+
+struct mod_dso *mod_dso__new_dso(const char *name)
+{
+ struct mod_dso *self = malloc(sizeof(*self) + strlen(name) + 1);
+
+ if (self != NULL) {
+ strcpy(self->name, name);
+ self->mods = RB_ROOT;
+ self->find_module = mod_dso__find_module;
+ }
+
+ return self;
+}
+
+static void mod_dso__delete_module(struct module *self)
+{
+ free(((void *)self));
+}
+
+void mod_dso__delete_modules(struct mod_dso *self)
+{
+ struct module *pos;
+ struct rb_node *next = rb_first(&self->mods);
+
+ while (next) {
+ pos = rb_entry(next, struct module, rb_node);
+ next = rb_next(&pos->rb_node);
+ rb_erase(&pos->rb_node, &self->mods);
+ mod_dso__delete_module(pos);
+ }
+}
+
+void mod_dso__delete_self(struct mod_dso *self)
+{
+ mod_dso__delete_modules(self);
+ free(self);
+}
+
+static void mod_dso__insert_module(struct mod_dso *self, struct module *mod)
+{
+ struct rb_node **p = &self->mods.rb_node;
+ struct rb_node *parent = NULL;
+ const u64 hash = mod->hash;
+ struct module *m;
+
+ while (*p != NULL) {
+ parent = *p;
+ m = rb_entry(parent, struct module, rb_node);
+ if (hash < m->hash)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+ rb_link_node(&mod->rb_node, parent, p);
+ rb_insert_color(&mod->rb_node, &self->mods);
+}
+
+struct module *mod_dso__find_module(struct mod_dso *self, const char *name)
+{
+ struct rb_node *n;
+ u64 hash;
+ int len;
+
+ if (self == NULL)
+ return NULL;
+
+ len = strlen(name);
+ hash = crc32(name, len);
+
+ n = self->mods.rb_node;
+
+ while (n) {
+ struct module *m = rb_entry(n, struct module, rb_node);
+
+ if (hash < m->hash)
+ n = n->rb_left;
+ else if (hash > m->hash)
+ n = n->rb_right;
+ else {
+ if (!strcmp(name, m->name))
+ return m;
+ else
+ n = rb_next(&m->rb_node);
+ }
+ }
+
+ return NULL;
+}
+
+static size_t mod_dso__fprintf_module(struct module *self, FILE *fp)
+{
+ return fprintf(fp, "name:%s path:%s\n", self->name, self->path);
+}
+
+size_t mod_dso__fprintf(struct mod_dso *self, FILE *fp)
+{
+ struct rb_node *nd;
+ size_t ret;
+
+ ret = fprintf(fp, "dso: %s\n", self->name);
+
+ for (nd = rb_first(&self->mods); nd; nd = rb_next(nd)) {
+ struct module *pos = rb_entry(nd, struct module, rb_node);
+
+ ret += mod_dso__fprintf_module(pos, fp);
+ }
+
+ return ret;
+}
+
+static struct module *module__new(const char *name, const char *path)
+{
+ struct module *self = calloc(1, sizeof(*self));
+
+ if (!self)
+ goto out_failure;
+
+ self->name = calloc(1, strlen(name) + 1);
+ if (!self->name)
+ goto out_failure;
+
+ self->path = calloc(1, strlen(path) + 1);
+ if (!self->path)
+ goto out_failure;
+
+ strcpy(self->name, name);
+ strcpy(self->path, path);
+ self->hash = crc32(self->name, strlen(name));
+
+ return self;
+
+out_failure:
+ if (self) {
+ if (self->name)
+ free(self->name);
+ if (self->path)
+ free(self->path);
+ free(self);
+ }
+
+ return NULL;
+}
+
+static int mod_dso__load_sections(struct module *mod)
+{
+ int count = 0, path_len;
+ struct dirent *entry;
+ char *line = NULL;
+ char *dir_path;
+ DIR *dir;
+ size_t n;
+
+ path_len = strlen("/sys/module/");
+ path_len += strlen(mod->name);
+ path_len += strlen("/sections/");
+
+ dir_path = calloc(1, path_len + 1);
+ if (dir_path == NULL)
+ goto out_failure;
+
+ strcat(dir_path, "/sys/module/");
+ strcat(dir_path, mod->name);
+ strcat(dir_path, "/sections/");
+
+ dir = opendir(dir_path);
+ if (dir == NULL)
+ goto out_free;
+
+ while ((entry = readdir(dir))) {
+ struct section *section;
+ char *path, *vma;
+ int line_len;
+ FILE *file;
+
+ if (!strcmp(".", entry->d_name) || !strcmp("..", entry->d_name))
+ continue;
+
+ path = calloc(1, path_len + strlen(entry->d_name) + 1);
+ if (path == NULL)
+ break;
+ strcat(path, dir_path);
+ strcat(path, entry->d_name);
+
+ file = fopen(path, "r");
+ if (file == NULL) {
+ free(path);
+ break;
+ }
+
+ line_len = getline(&line, &n, file);
+ if (line_len < 0) {
+ free(path);
+ fclose(file);
+ break;
+ }
+
+ if (!line) {
+ free(path);
+ fclose(file);
+ break;
+ }
+
+ line[--line_len] = '\0'; /* \n */
+
+ vma = strstr(line, "0x");
+ if (!vma) {
+ free(path);
+ fclose(file);
+ break;
+ }
+ vma += 2;
+
+ section = section__new(entry->d_name, path);
+ if (!section) {
+ fprintf(stderr, "load_sections: allocation error\n");
+ free(path);
+ fclose(file);
+ break;
+ }
+
+ hex2u64(vma, &section->vma);
+ sec_dso__insert_section(mod->sections, section);
+
+ free(path);
+ fclose(file);
+ count++;
+ }
+
+ closedir(dir);
+ free(line);
+ free(dir_path);
+
+ return count;
+
+out_free:
+ free(dir_path);
+
+out_failure:
+ return count;
+}
+
+static int mod_dso__load_module_paths(struct mod_dso *self)
+{
+ struct utsname uts;
+ int count = 0, len;
+ char *line = NULL;
+ FILE *file;
+ char *path;
+ size_t n;
+
+ if (uname(&uts) < 0)
+ goto out_failure;
+
+ len = strlen("/lib/modules/");
+ len += strlen(uts.release);
+ len += strlen("/modules.dep");
+
+ path = calloc(1, len);
+ if (path == NULL)
+ goto out_failure;
+
+ strcat(path, "/lib/modules/");
+ strcat(path, uts.release);
+ strcat(path, "/modules.dep");
+
+ file = fopen(path, "r");
+ free(path);
+ if (file == NULL)
+ goto out_failure;
+
+ while (!feof(file)) {
+ char *path, *name, *tmp;
+ struct module *module;
+ int line_len, len;
+
+ line_len = getline(&line, &n, file);
+ if (line_len < 0)
+ break;
+
+ if (!line)
+ goto out_failure;
+
+ line[--line_len] = '\0'; /* \n */
+
+ path = strtok(line, ":");
+ if (!path)
+ goto out_failure;
+
+ name = strdup(path);
+ name = strtok(name, "/");
+
+ tmp = name;
+
+ while (tmp) {
+ tmp = strtok(NULL, "/");
+ if (tmp)
+ name = tmp;
+ }
+ name = strsep(&name, ".");
+
+ /* Quirk: replace '-' with '_' in sound modules */
+ for (len = strlen(name); len; len--) {
+ if (*(name+len) == '-')
+ *(name+len) = '_';
+ }
+
+ module = module__new(name, path);
+ if (!module) {
+ fprintf(stderr, "load_module_paths: allocation error\n");
+ goto out_failure;
+ }
+ mod_dso__insert_module(self, module);
+
+ module->sections = sec_dso__new_dso("sections");
+ if (!module->sections) {
+ fprintf(stderr, "load_module_paths: allocation error\n");
+ goto out_failure;
+ }
+
+ module->active = mod_dso__load_sections(module);
+
+ if (module->active > 0)
+ count++;
+ }
+
+ free(line);
+ fclose(file);
+
+ return count;
+
+out_failure:
+ return -1;
+}
+
+int mod_dso__load_modules(struct mod_dso *dso)
+{
+ int err;
+
+ err = mod_dso__load_module_paths(dso);
+
+ return err;
+}
diff --git a/tools/perf/util/module.h b/tools/perf/util/module.h
new file mode 100644
index 00000000000..8a592ef641c
--- /dev/null
+++ b/tools/perf/util/module.h
@@ -0,0 +1,53 @@
+#ifndef _PERF_MODULE_
+#define _PERF_MODULE_ 1
+
+#include <linux/types.h>
+#include "../types.h"
+#include <linux/list.h>
+#include <linux/rbtree.h>
+
+struct section {
+ struct rb_node rb_node;
+ u64 hash;
+ u64 vma;
+ char *name;
+ char *path;
+};
+
+struct sec_dso {
+ struct list_head node;
+ struct rb_root secs;
+ struct section *(*find_section)(struct sec_dso *, const char *name);
+ char name[0];
+};
+
+struct module {
+ struct rb_node rb_node;
+ u64 hash;
+ char *name;
+ char *path;
+ struct sec_dso *sections;
+ int active;
+};
+
+struct mod_dso {
+ struct list_head node;
+ struct rb_root mods;
+ struct module *(*find_module)(struct mod_dso *, const char *name);
+ char name[0];
+};
+
+struct sec_dso *sec_dso__new_dso(const char *name);
+void sec_dso__delete_sections(struct sec_dso *self);
+void sec_dso__delete_self(struct sec_dso *self);
+size_t sec_dso__fprintf(struct sec_dso *self, FILE *fp);
+struct section *sec_dso__find_section(struct sec_dso *self, const char *name);
+
+struct mod_dso *mod_dso__new_dso(const char *name);
+void mod_dso__delete_modules(struct mod_dso *self);
+void mod_dso__delete_self(struct mod_dso *self);
+size_t mod_dso__fprintf(struct mod_dso *self, FILE *fp);
+struct module *mod_dso__find_module(struct mod_dso *self, const char *name);
+int mod_dso__load_modules(struct mod_dso *dso);
+
+#endif /* _PERF_MODULE_ */
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 4d042f104cd..04417840878 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -5,6 +5,7 @@
#include "parse-events.h"
#include "exec_cmd.h"
#include "string.h"
+#include "cache.h"
extern char *strcasestr(const char *haystack, const char *needle);
@@ -19,6 +20,8 @@ struct event_symbol {
char *alias;
};
+char debugfs_path[MAXPATHLEN];
+
#define CHW(x) .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_##x
#define CSW(x) .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_##x
@@ -71,8 +74,8 @@ static char *sw_event_names[] = {
#define MAX_ALIASES 8
static char *hw_cache[][MAX_ALIASES] = {
- { "L1-d$", "l1-d", "l1d", "L1-data", },
- { "L1-i$", "l1-i", "l1i", "L1-instruction", },
+ { "L1-dcache", "l1-d", "l1d", "L1-data", },
+ { "L1-icache", "l1-i", "l1i", "L1-instruction", },
{ "LLC", "L2" },
{ "dTLB", "d-tlb", "Data-TLB", },
{ "iTLB", "i-tlb", "Instruction-TLB", },
@@ -110,6 +113,104 @@ static unsigned long hw_cache_stat[C(MAX)] = {
[C(BPU)] = (CACHE_READ),
};
+#define for_each_subsystem(sys_dir, sys_dirent, sys_next, file, st) \
+ while (!readdir_r(sys_dir, &sys_dirent, &sys_next) && sys_next) \
+ if (snprintf(file, MAXPATHLEN, "%s/%s", debugfs_path, \
+ sys_dirent.d_name) && \
+ (!stat(file, &st)) && (S_ISDIR(st.st_mode)) && \
+ (strcmp(sys_dirent.d_name, ".")) && \
+ (strcmp(sys_dirent.d_name, "..")))
+
+static int tp_event_has_id(struct dirent *sys_dir, struct dirent *evt_dir)
+{
+ char evt_path[MAXPATHLEN];
+ int fd;
+
+ snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", debugfs_path,
+ sys_dir->d_name, evt_dir->d_name);
+ fd = open(evt_path, O_RDONLY);
+ if (fd < 0)
+ return -EINVAL;
+ close(fd);
+
+ return 0;
+}
+
+#define for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next, file, st) \
+ while (!readdir_r(evt_dir, &evt_dirent, &evt_next) && evt_next) \
+ if (snprintf(file, MAXPATHLEN, "%s/%s/%s", debugfs_path, \
+ sys_dirent.d_name, evt_dirent.d_name) && \
+ (!stat(file, &st)) && (S_ISDIR(st.st_mode)) && \
+ (strcmp(evt_dirent.d_name, ".")) && \
+ (strcmp(evt_dirent.d_name, "..")) && \
+ (!tp_event_has_id(&sys_dirent, &evt_dirent)))
+
+#define MAX_EVENT_LENGTH 30
+
+int valid_debugfs_mount(const char *debugfs)
+{
+ struct statfs st_fs;
+
+ if (statfs(debugfs, &st_fs) < 0)
+ return -ENOENT;
+ else if (st_fs.f_type != (long) DEBUGFS_MAGIC)
+ return -ENOENT;
+ return 0;
+}
+
+static char *tracepoint_id_to_name(u64 config)
+{
+ static char tracepoint_name[2 * MAX_EVENT_LENGTH];
+ DIR *sys_dir, *evt_dir;
+ struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent;
+ struct stat st;
+ char id_buf[4];
+ int fd;
+ u64 id;
+ char evt_path[MAXPATHLEN];
+
+ if (valid_debugfs_mount(debugfs_path))
+ return "unkown";
+
+ sys_dir = opendir(debugfs_path);
+ if (!sys_dir)
+ goto cleanup;
+
+ for_each_subsystem(sys_dir, sys_dirent, sys_next, evt_path, st) {
+ evt_dir = opendir(evt_path);
+ if (!evt_dir)
+ goto cleanup;
+ for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next,
+ evt_path, st) {
+ snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id",
+ debugfs_path, sys_dirent.d_name,
+ evt_dirent.d_name);
+ fd = open(evt_path, O_RDONLY);
+ if (fd < 0)
+ continue;
+ if (read(fd, id_buf, sizeof(id_buf)) < 0) {
+ close(fd);
+ continue;
+ }
+ close(fd);
+ id = atoll(id_buf);
+ if (id == config) {
+ closedir(evt_dir);
+ closedir(sys_dir);
+ snprintf(tracepoint_name, 2 * MAX_EVENT_LENGTH,
+ "%s:%s", sys_dirent.d_name,
+ evt_dirent.d_name);
+ return tracepoint_name;
+ }
+ }
+ closedir(evt_dir);
+ }
+
+cleanup:
+ closedir(sys_dir);
+ return "unkown";
+}
+
static int is_cache_op_valid(u8 cache_type, u8 cache_op)
{
if (hw_cache_stat[cache_type] & COP(cache_op))
@@ -138,9 +239,15 @@ char *event_name(int counter)
{
u64 config = attrs[counter].config;
int type = attrs[counter].type;
+
+ return __event_name(type, config);
+}
+
+char *__event_name(int type, u64 config)
+{
static char buf[32];
- if (attrs[counter].type == PERF_TYPE_RAW) {
+ if (type == PERF_TYPE_RAW) {
sprintf(buf, "raw 0x%llx", config);
return buf;
}
@@ -177,6 +284,9 @@ char *event_name(int counter)
return sw_event_names[config];
return "unknown-software";
+ case PERF_TYPE_TRACEPOINT:
+ return tracepoint_id_to_name(config);
+
default:
break;
}
@@ -184,16 +294,20 @@ char *event_name(int counter)
return "unknown";
}
-static int parse_aliases(const char *str, char *names[][MAX_ALIASES], int size)
+static int parse_aliases(const char **str, char *names[][MAX_ALIASES], int size)
{
int i, j;
+ int n, longest = -1;
for (i = 0; i < size; i++) {
- for (j = 0; j < MAX_ALIASES; j++) {
- if (!names[i][j])
- break;
- if (strcasestr(str, names[i][j]))
- return i;
+ for (j = 0; j < MAX_ALIASES && names[i][j]; j++) {
+ n = strlen(names[i][j]);
+ if (n > longest && !strncasecmp(*str, names[i][j], n))
+ longest = n;
+ }
+ if (longest > 0) {
+ *str += longest;
+ return i;
}
}
@@ -201,30 +315,53 @@ static int parse_aliases(const char *str, char *names[][MAX_ALIASES], int size)
}
static int
-parse_generic_hw_symbols(const char *str, struct perf_counter_attr *attr)
+parse_generic_hw_event(const char **str, struct perf_counter_attr *attr)
{
- int cache_type = -1, cache_op = 0, cache_result = 0;
+ const char *s = *str;
+ int cache_type = -1, cache_op = -1, cache_result = -1;
- cache_type = parse_aliases(str, hw_cache, PERF_COUNT_HW_CACHE_MAX);
+ cache_type = parse_aliases(&s, hw_cache, PERF_COUNT_HW_CACHE_MAX);
/*
* No fallback - if we cannot get a clear cache type
* then bail out:
*/
if (cache_type == -1)
- return -EINVAL;
+ return 0;
+
+ while ((cache_op == -1 || cache_result == -1) && *s == '-') {
+ ++s;
+
+ if (cache_op == -1) {
+ cache_op = parse_aliases(&s, hw_cache_op,
+ PERF_COUNT_HW_CACHE_OP_MAX);
+ if (cache_op >= 0) {
+ if (!is_cache_op_valid(cache_type, cache_op))
+ return 0;
+ continue;
+ }
+ }
+
+ if (cache_result == -1) {
+ cache_result = parse_aliases(&s, hw_cache_result,
+ PERF_COUNT_HW_CACHE_RESULT_MAX);
+ if (cache_result >= 0)
+ continue;
+ }
+
+ /*
+ * Can't parse this as a cache op or result, so back up
+ * to the '-'.
+ */
+ --s;
+ break;
+ }
- cache_op = parse_aliases(str, hw_cache_op, PERF_COUNT_HW_CACHE_OP_MAX);
/*
* Fall back to reads:
*/
if (cache_op == -1)
cache_op = PERF_COUNT_HW_CACHE_OP_READ;
- if (!is_cache_op_valid(cache_type, cache_op))
- return -EINVAL;
-
- cache_result = parse_aliases(str, hw_cache_result,
- PERF_COUNT_HW_CACHE_RESULT_MAX);
/*
* Fall back to accesses:
*/
@@ -234,93 +371,212 @@ parse_generic_hw_symbols(const char *str, struct perf_counter_attr *attr)
attr->config = cache_type | (cache_op << 8) | (cache_result << 16);
attr->type = PERF_TYPE_HW_CACHE;
- return 0;
+ *str = s;
+ return 1;
+}
+
+static int parse_tracepoint_event(const char **strp,
+ struct perf_counter_attr *attr)
+{
+ const char *evt_name;
+ char *flags;
+ char sys_name[MAX_EVENT_LENGTH];
+ char id_buf[4];
+ int fd;
+ unsigned int sys_length, evt_length;
+ u64 id;
+ char evt_path[MAXPATHLEN];
+
+ if (valid_debugfs_mount(debugfs_path))
+ return 0;
+
+ evt_name = strchr(*strp, ':');
+ if (!evt_name)
+ return 0;
+
+ sys_length = evt_name - *strp;
+ if (sys_length >= MAX_EVENT_LENGTH)
+ return 0;
+
+ strncpy(sys_name, *strp, sys_length);
+ sys_name[sys_length] = '\0';
+ evt_name = evt_name + 1;
+
+ flags = strchr(evt_name, ':');
+ if (flags) {
+ *flags = '\0';
+ flags++;
+ if (!strncmp(flags, "record", strlen(flags)))
+ attr->sample_type |= PERF_SAMPLE_RAW;
+ }
+
+ evt_length = strlen(evt_name);
+ if (evt_length >= MAX_EVENT_LENGTH)
+ return 0;
+
+ snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", debugfs_path,
+ sys_name, evt_name);
+ fd = open(evt_path, O_RDONLY);
+ if (fd < 0)
+ return 0;
+
+ if (read(fd, id_buf, sizeof(id_buf)) < 0) {
+ close(fd);
+ return 0;
+ }
+ close(fd);
+ id = atoll(id_buf);
+ attr->config = id;
+ attr->type = PERF_TYPE_TRACEPOINT;
+ *strp = evt_name + evt_length;
+ return 1;
}
static int check_events(const char *str, unsigned int i)
{
- if (!strncmp(str, event_symbols[i].symbol,
- strlen(event_symbols[i].symbol)))
- return 1;
+ int n;
- if (strlen(event_symbols[i].alias))
- if (!strncmp(str, event_symbols[i].alias,
- strlen(event_symbols[i].alias)))
- return 1;
+ n = strlen(event_symbols[i].symbol);
+ if (!strncmp(str, event_symbols[i].symbol, n))
+ return n;
+
+ n = strlen(event_symbols[i].alias);
+ if (n)
+ if (!strncmp(str, event_symbols[i].alias, n))
+ return n;
return 0;
}
-/*
- * Each event can have multiple symbolic names.
- * Symbolic names are (almost) exactly matched.
- */
-static int parse_event_symbols(const char *str, struct perf_counter_attr *attr)
+static int
+parse_symbolic_event(const char **strp, struct perf_counter_attr *attr)
{
- u64 config, id;
- int type;
+ const char *str = *strp;
unsigned int i;
- const char *sep, *pstr;
+ int n;
- if (str[0] == 'r' && hex2u64(str + 1, &config) > 0) {
- attr->type = PERF_TYPE_RAW;
- attr->config = config;
+ for (i = 0; i < ARRAY_SIZE(event_symbols); i++) {
+ n = check_events(str, i);
+ if (n > 0) {
+ attr->type = event_symbols[i].type;
+ attr->config = event_symbols[i].config;
+ *strp = str + n;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int parse_raw_event(const char **strp, struct perf_counter_attr *attr)
+{
+ const char *str = *strp;
+ u64 config;
+ int n;
+ if (*str != 'r')
return 0;
+ n = hex2u64(str + 1, &config);
+ if (n > 0) {
+ *strp = str + n + 1;
+ attr->type = PERF_TYPE_RAW;
+ attr->config = config;
+ return 1;
}
+ return 0;
+}
- pstr = str;
- sep = strchr(pstr, ':');
- if (sep) {
- type = atoi(pstr);
- pstr = sep + 1;
- id = atoi(pstr);
- sep = strchr(pstr, ':');
- if (sep) {
- pstr = sep + 1;
- if (strchr(pstr, 'k'))
- attr->exclude_user = 1;
- if (strchr(pstr, 'u'))
- attr->exclude_kernel = 1;
+static int
+parse_numeric_event(const char **strp, struct perf_counter_attr *attr)
+{
+ const char *str = *strp;
+ char *endp;
+ unsigned long type;
+ u64 config;
+
+ type = strtoul(str, &endp, 0);
+ if (endp > str && type < PERF_TYPE_MAX && *endp == ':') {
+ str = endp + 1;
+ config = strtoul(str, &endp, 0);
+ if (endp > str) {
+ attr->type = type;
+ attr->config = config;
+ *strp = endp;
+ return 1;
}
- attr->type = type;
- attr->config = id;
+ }
+ return 0;
+}
+static int
+parse_event_modifier(const char **strp, struct perf_counter_attr *attr)
+{
+ const char *str = *strp;
+ int eu = 1, ek = 1, eh = 1;
+
+ if (*str++ != ':')
return 0;
+ while (*str) {
+ if (*str == 'u')
+ eu = 0;
+ else if (*str == 'k')
+ ek = 0;
+ else if (*str == 'h')
+ eh = 0;
+ else
+ break;
+ ++str;
}
+ if (str >= *strp + 2) {
+ *strp = str;
+ attr->exclude_user = eu;
+ attr->exclude_kernel = ek;
+ attr->exclude_hv = eh;
+ return 1;
+ }
+ return 0;
+}
- for (i = 0; i < ARRAY_SIZE(event_symbols); i++) {
- if (check_events(str, i)) {
- attr->type = event_symbols[i].type;
- attr->config = event_symbols[i].config;
+/*
+ * Each event can have multiple symbolic names.
+ * Symbolic names are (almost) exactly matched.
+ */
+static int parse_event_symbols(const char **str, struct perf_counter_attr *attr)
+{
+ if (!(parse_tracepoint_event(str, attr) ||
+ parse_raw_event(str, attr) ||
+ parse_numeric_event(str, attr) ||
+ parse_symbolic_event(str, attr) ||
+ parse_generic_hw_event(str, attr)))
+ return 0;
- return 0;
- }
- }
+ parse_event_modifier(str, attr);
- return parse_generic_hw_symbols(str, attr);
+ return 1;
}
-int parse_events(const struct option *opt, const char *str, int unset)
+int parse_events(const struct option *opt __used, const char *str, int unset __used)
{
struct perf_counter_attr attr;
- int ret;
- memset(&attr, 0, sizeof(attr));
-again:
- if (nr_counters == MAX_COUNTERS)
- return -1;
+ for (;;) {
+ if (nr_counters == MAX_COUNTERS)
+ return -1;
- ret = parse_event_symbols(str, &attr);
- if (ret < 0)
- return ret;
+ memset(&attr, 0, sizeof(attr));
+ if (!parse_event_symbols(&str, &attr))
+ return -1;
- attrs[nr_counters] = attr;
- nr_counters++;
+ if (!(*str == 0 || *str == ',' || isspace(*str)))
+ return -1;
- str = strstr(str, ",");
- if (str) {
- str++;
- goto again;
+ attrs[nr_counters] = attr;
+ nr_counters++;
+
+ if (*str == 0)
+ break;
+ if (*str == ',')
+ ++str;
+ while (isspace(*str))
+ ++str;
}
return 0;
@@ -335,12 +591,48 @@ static const char * const event_type_descriptors[] = {
};
/*
+ * Print the events from <debugfs_mount_point>/tracing/events
+ */
+
+static void print_tracepoint_events(void)
+{
+ DIR *sys_dir, *evt_dir;
+ struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent;
+ struct stat st;
+ char evt_path[MAXPATHLEN];
+
+ if (valid_debugfs_mount(debugfs_path))
+ return;
+
+ sys_dir = opendir(debugfs_path);
+ if (!sys_dir)
+ goto cleanup;
+
+ for_each_subsystem(sys_dir, sys_dirent, sys_next, evt_path, st) {
+ evt_dir = opendir(evt_path);
+ if (!evt_dir)
+ goto cleanup;
+ for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next,
+ evt_path, st) {
+ snprintf(evt_path, MAXPATHLEN, "%s:%s",
+ sys_dirent.d_name, evt_dirent.d_name);
+ fprintf(stderr, " %-40s [%s]\n", evt_path,
+ event_type_descriptors[PERF_TYPE_TRACEPOINT+1]);
+ }
+ closedir(evt_dir);
+ }
+
+cleanup:
+ closedir(sys_dir);
+}
+
+/*
* Print the help text for the event symbols:
*/
void print_events(void)
{
struct event_symbol *syms = event_symbols;
- unsigned int i, type, prev_type = -1;
+ unsigned int i, type, op, prev_type = -1;
char name[40];
fprintf(stderr, "\n");
@@ -348,7 +640,7 @@ void print_events(void)
for (i = 0; i < ARRAY_SIZE(event_symbols); i++, syms++) {
type = syms->type + 1;
- if (type > ARRAY_SIZE(event_type_descriptors))
+ if (type >= ARRAY_SIZE(event_type_descriptors))
type = 0;
if (type != prev_type)
@@ -365,9 +657,26 @@ void print_events(void)
}
fprintf(stderr, "\n");
+ for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) {
+ for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) {
+ /* skip invalid cache type */
+ if (!is_cache_op_valid(type, op))
+ continue;
+
+ for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) {
+ fprintf(stderr, " %-40s [%s]\n",
+ event_cache_name(type, op, i),
+ event_type_descriptors[4]);
+ }
+ }
+ }
+
+ fprintf(stderr, "\n");
fprintf(stderr, " %-40s [raw hardware event descriptor]\n",
"rNNN");
fprintf(stderr, "\n");
+ print_tracepoint_events();
+
exit(129);
}
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h
index e3d552908e6..192a962e3a0 100644
--- a/tools/perf/util/parse-events.h
+++ b/tools/perf/util/parse-events.h
@@ -3,11 +3,14 @@
* Parse symbolic events/counts passed in as options:
*/
+struct option;
+
extern int nr_counters;
extern struct perf_counter_attr attrs[MAX_COUNTERS];
extern char *event_name(int ctr);
+extern char *__event_name(int type, u64 config);
extern int parse_events(const struct option *opt, const char *str, int unset);
@@ -15,3 +18,6 @@ extern int parse_events(const struct option *opt, const char *str, int unset);
extern void print_events(void);
+extern char debugfs_path[];
+extern int valid_debugfs_mount(const char *debugfs);
+
diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c
index b3affb1658d..1bf67190c82 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,
if (p->opt) {
*arg = p->opt;
p->opt = NULL;
- } else if (p->argc == 1 && (opt->flags & PARSE_OPT_LASTARG_DEFAULT)) {
+ } else if ((opt->flags & PARSE_OPT_LASTARG_DEFAULT) && (p->argc == 1 ||
+ **(p->argv + 1) == '-')) {
*arg = (const char *)opt->defval;
} else if (p->argc > 1) {
p->argc--;
@@ -485,7 +486,7 @@ int parse_options_usage(const char * const *usagestr,
}
-int parse_opt_verbosity_cb(const struct option *opt, const char *arg,
+int parse_opt_verbosity_cb(const struct option *opt, const char *arg __used,
int unset)
{
int *target = opt->value;
diff --git a/tools/perf/util/parse-options.h b/tools/perf/util/parse-options.h
index a1039a6ce0e..8aa3464c709 100644
--- a/tools/perf/util/parse-options.h
+++ b/tools/perf/util/parse-options.h
@@ -90,21 +90,22 @@ struct option {
intptr_t defval;
};
-#define OPT_END() { OPTION_END }
-#define OPT_ARGUMENT(l, h) { OPTION_ARGUMENT, 0, (l), NULL, NULL, (h) }
-#define OPT_GROUP(h) { OPTION_GROUP, 0, NULL, NULL, NULL, (h) }
-#define OPT_BIT(s, l, v, h, b) { OPTION_BIT, (s), (l), (v), NULL, (h), 0, NULL, (b) }
-#define OPT_BOOLEAN(s, l, v, h) { OPTION_BOOLEAN, (s), (l), (v), NULL, (h) }
-#define OPT_SET_INT(s, l, v, h, i) { OPTION_SET_INT, (s), (l), (v), NULL, (h), 0, NULL, (i) }
-#define OPT_SET_PTR(s, l, v, h, p) { OPTION_SET_PTR, (s), (l), (v), NULL, (h), 0, NULL, (p) }
-#define OPT_INTEGER(s, l, v, h) { OPTION_INTEGER, (s), (l), (v), NULL, (h) }
-#define OPT_LONG(s, l, v, h) { OPTION_LONG, (s), (l), (v), NULL, (h) }
-#define OPT_STRING(s, l, v, a, h) { OPTION_STRING, (s), (l), (v), (a), (h) }
+#define OPT_END() { .type = OPTION_END }
+#define OPT_ARGUMENT(l, h) { .type = OPTION_ARGUMENT, .long_name = (l), .help = (h) }
+#define OPT_GROUP(h) { .type = OPTION_GROUP, .help = (h) }
+#define OPT_BIT(s, l, v, h, b) { .type = OPTION_BIT, .short_name = (s), .long_name = (l), .value = (v), .help = (h), .defval = (b) }
+#define OPT_BOOLEAN(s, l, v, h) { .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), .value = (v), .help = (h) }
+#define OPT_SET_INT(s, l, v, h, i) { .type = OPTION_SET_INT, .short_name = (s), .long_name = (l), .value = (v), .help = (h), .defval = (i) }
+#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) }
+#define OPT_INTEGER(s, l, v, h) { .type = OPTION_INTEGER, .short_name = (s), .long_name = (l), .value = (v), .help = (h) }
+#define OPT_LONG(s, l, v, h) { .type = OPTION_LONG, .short_name = (s), .long_name = (l), .value = (v), .help = (h) }
+#define OPT_STRING(s, l, v, a, h) { .type = OPTION_STRING, .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h) }
#define OPT_DATE(s, l, v, h) \
- { OPTION_CALLBACK, (s), (l), (v), "time",(h), 0, \
- parse_opt_approxidate_cb }
+ { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), .argh = "time", .help = (h), .callback = parse_opt_approxidate_cb }
#define OPT_CALLBACK(s, l, v, a, h, f) \
- { OPTION_CALLBACK, (s), (l), (v), (a), (h), 0, (f) }
+ { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h), .callback = (f) }
+#define OPT_CALLBACK_DEFAULT(s, l, v, a, h, f, d) \
+ { .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 }
/* parse_options() will filter out the processed options and leave the
* non-option argments in argv[].
diff --git a/tools/perf/util/quote.c b/tools/perf/util/quote.c
index f18c5212bc9..2726fe40eb5 100644
--- a/tools/perf/util/quote.c
+++ b/tools/perf/util/quote.c
@@ -162,12 +162,16 @@ static inline int sq_must_quote(char c)
return sq_lookup[(unsigned char)c] + quote_path_fully > 0;
}
-/* returns the longest prefix not needing a quote up to maxlen if positive.
- This stops at the first \0 because it's marked as a character needing an
- escape */
-static size_t next_quote_pos(const char *s, ssize_t maxlen)
+/*
+ * Returns the longest prefix not needing a quote up to maxlen if
+ * positive.
+ * This stops at the first \0 because it's marked as a character
+ * needing an escape.
+ */
+static ssize_t next_quote_pos(const char *s, ssize_t maxlen)
{
- size_t len;
+ ssize_t len;
+
if (maxlen < 0) {
for (len = 0; !sq_must_quote(s[len]); len++);
} else {
@@ -192,22 +196,22 @@ static size_t next_quote_pos(const char *s, ssize_t maxlen)
static size_t quote_c_style_counted(const char *name, ssize_t maxlen,
struct strbuf *sb, FILE *fp, int no_dq)
{
-#undef EMIT
-#define EMIT(c) \
- do { \
- if (sb) strbuf_addch(sb, (c)); \
- if (fp) fputc((c), fp); \
- count++; \
+#define EMIT(c) \
+ do { \
+ if (sb) strbuf_addch(sb, (c)); \
+ if (fp) fputc((c), fp); \
+ count++; \
} while (0)
-#define EMITBUF(s, l) \
- do { \
- int __ret; \
- if (sb) strbuf_add(sb, (s), (l)); \
- if (fp) __ret = fwrite((s), (l), 1, fp); \
- count += (l); \
+
+#define EMITBUF(s, l) \
+ do { \
+ int __ret; \
+ if (sb) strbuf_add(sb, (s), (l)); \
+ if (fp) __ret = fwrite((s), (l), 1, fp); \
+ count += (l); \
} while (0)
- size_t len, count = 0;
+ ssize_t len, count = 0;
const char *p = name;
for (;;) {
@@ -273,8 +277,8 @@ void write_name_quoted(const char *name, FILE *fp, int terminator)
fputc(terminator, fp);
}
-extern void write_name_quotedpfx(const char *pfx, size_t pfxlen,
- const char *name, FILE *fp, int terminator)
+void write_name_quotedpfx(const char *pfx, ssize_t pfxlen,
+ const char *name, FILE *fp, int terminator)
{
int needquote = 0;
@@ -306,7 +310,7 @@ char *quote_path_relative(const char *in, int len,
len = strlen(in);
/* "../" prefix itself does not need quoting, but "in" might. */
- needquote = next_quote_pos(in, len) < len;
+ needquote = (next_quote_pos(in, len) < len);
strbuf_setlen(out, 0);
strbuf_grow(out, len);
@@ -314,7 +318,7 @@ char *quote_path_relative(const char *in, int len,
strbuf_addch(out, '"');
if (prefix) {
int off = 0;
- while (prefix[off] && off < len && prefix[off] == in[off])
+ while (off < len && prefix[off] && prefix[off] == in[off])
if (prefix[off] == '/') {
prefix += off + 1;
in += off + 1;
diff --git a/tools/perf/util/quote.h b/tools/perf/util/quote.h
index 5dfad89816d..a5454a1d1c1 100644
--- a/tools/perf/util/quote.h
+++ b/tools/perf/util/quote.h
@@ -53,7 +53,7 @@ extern size_t quote_c_style(const char *name, struct strbuf *, FILE *, int no_dq
extern void quote_two_c_style(struct strbuf *, const char *, const char *, int);
extern void write_name_quoted(const char *name, FILE *, int terminator);
-extern void write_name_quotedpfx(const char *pfx, size_t pfxlen,
+extern void write_name_quotedpfx(const char *pfx, ssize_t pfxlen,
const char *name, FILE *, int terminator);
/* quote path as relative to the given prefix */
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 @@
-/*
- Red Black Trees
- (C) 1999 Andrea Arcangeli <andrea@suse.de>
- (C) 2002 David Woodhouse <dwmw2@infradead.org>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
- linux/lib/rbtree.c
-*/
-
-#include "rbtree.h"
-
-static void __rb_rotate_left(struct rb_node *node, struct rb_root *root)
-{
- struct rb_node *right = node->rb_right;
- struct rb_node *parent = rb_parent(node);
-
- if ((node->rb_right = right->rb_left))
- rb_set_parent(right->rb_left, node);
- right->rb_left = node;
-
- rb_set_parent(right, parent);
-
- if (parent)
- {
- if (node == parent->rb_left)
- parent->rb_left = right;
- else
- parent->rb_right = right;
- }
- else
- root->rb_node = right;
- rb_set_parent(node, right);
-}
-
-static void __rb_rotate_right(struct rb_node *node, struct rb_root *root)
-{
- struct rb_node *left = node->rb_left;
- struct rb_node *parent = rb_parent(node);
-
- if ((node->rb_left = left->rb_right))
- rb_set_parent(left->rb_right, node);
- left->rb_right = node;
-
- rb_set_parent(left, parent);
-
- if (parent)
- {
- if (node == parent->rb_right)
- parent->rb_right = left;
- else
- parent->rb_left = left;
- }
- else
- root->rb_node = left;
- rb_set_parent(node, left);
-}
-
-void rb_insert_color(struct rb_node *node, struct rb_root *root)
-{
- struct rb_node *parent, *gparent;
-
- while ((parent = rb_parent(node)) && rb_is_red(parent))
- {
- gparent = rb_parent(parent);
-
- if (parent == gparent->rb_left)
- {
- {
- register struct rb_node *uncle = gparent->rb_right;
- if (uncle && rb_is_red(uncle))
- {
- rb_set_black(uncle);
- rb_set_black(parent);
- rb_set_red(gparent);
- node = gparent;
- continue;
- }
- }
-
- if (parent->rb_right == node)
- {
- register struct rb_node *tmp;
- __rb_rotate_left(parent, root);
- tmp = parent;
- parent = node;
- node = tmp;
- }
-
- rb_set_black(parent);
- rb_set_red(gparent);
- __rb_rotate_right(gparent, root);
- } else {
- {
- register struct rb_node *uncle = gparent->rb_left;
- if (uncle && rb_is_red(uncle))
- {
- rb_set_black(uncle);
- rb_set_black(parent);
- rb_set_red(gparent);
- node = gparent;
- continue;
- }
- }
-
- if (parent->rb_left == node)
- {
- register struct rb_node *tmp;
- __rb_rotate_right(parent, root);
- tmp = parent;
- parent = node;
- node = tmp;
- }
-
- rb_set_black(parent);
- rb_set_red(gparent);
- __rb_rotate_left(gparent, root);
- }
- }
-
- rb_set_black(root->rb_node);
-}
-
-static void __rb_erase_color(struct rb_node *node, struct rb_node *parent,
- struct rb_root *root)
-{
- struct rb_node *other;
-
- while ((!node || rb_is_black(node)) && node != root->rb_node)
- {
- if (parent->rb_left == node)
- {
- other = parent->rb_right;
- if (rb_is_red(other))
- {
- rb_set_black(other);
- rb_set_red(parent);
- __rb_rotate_left(parent, root);
- other = parent->rb_right;
- }
- if ((!other->rb_left || rb_is_black(other->rb_left)) &&
- (!other->rb_right || rb_is_black(other->rb_right)))
- {
- rb_set_red(other);
- node = parent;
- parent = rb_parent(node);
- }
- else
- {
- if (!other->rb_right || rb_is_black(other->rb_right))
- {
- rb_set_black(other->rb_left);
- rb_set_red(other);
- __rb_rotate_right(other, root);
- other = parent->rb_right;
- }
- rb_set_color(other, rb_color(parent));
- rb_set_black(parent);
- rb_set_black(other->rb_right);
- __rb_rotate_left(parent, root);
- node = root->rb_node;
- break;
- }
- }
- else
- {
- other = parent->rb_left;
- if (rb_is_red(other))
- {
- rb_set_black(other);
- rb_set_red(parent);
- __rb_rotate_right(parent, root);
- other = parent->rb_left;
- }
- if ((!other->rb_left || rb_is_black(other->rb_left)) &&
- (!other->rb_right || rb_is_black(other->rb_right)))
- {
- rb_set_red(other);
- node = parent;
- parent = rb_parent(node);
- }
- else
- {
- if (!other->rb_left || rb_is_black(other->rb_left))
- {
- rb_set_black(other->rb_right);
- rb_set_red(other);
- __rb_rotate_left(other, root);
- other = parent->rb_left;
- }
- rb_set_color(other, rb_color(parent));
- rb_set_black(parent);
- rb_set_black(other->rb_left);
- __rb_rotate_right(parent, root);
- node = root->rb_node;
- break;
- }
- }
- }
- if (node)
- rb_set_black(node);
-}
-
-void rb_erase(struct rb_node *node, struct rb_root *root)
-{
- struct rb_node *child, *parent;
- int color;
-
- if (!node->rb_left)
- child = node->rb_right;
- else if (!node->rb_right)
- child = node->rb_left;
- else
- {
- struct rb_node *old = node, *left;
-
- node = node->rb_right;
- while ((left = node->rb_left) != NULL)
- node = left;
- child = node->rb_right;
- parent = rb_parent(node);
- color = rb_color(node);
-
- if (child)
- rb_set_parent(child, parent);
- if (parent == old) {
- parent->rb_right = child;
- parent = node;
- } else
- parent->rb_left = child;
-
- node->rb_parent_color = old->rb_parent_color;
- node->rb_right = old->rb_right;
- node->rb_left = old->rb_left;
-
- if (rb_parent(old))
- {
- if (rb_parent(old)->rb_left == old)
- rb_parent(old)->rb_left = node;
- else
- rb_parent(old)->rb_right = node;
- } else
- root->rb_node = node;
-
- rb_set_parent(old->rb_left, node);
- if (old->rb_right)
- rb_set_parent(old->rb_right, node);
- goto color;
- }
-
- parent = rb_parent(node);
- color = rb_color(node);
-
- if (child)
- rb_set_parent(child, parent);
- if (parent)
- {
- if (parent->rb_left == node)
- parent->rb_left = child;
- else
- parent->rb_right = child;
- }
- else
- root->rb_node = child;
-
- color:
- if (color == RB_BLACK)
- __rb_erase_color(child, parent, root);
-}
-
-/*
- * This function returns the first node (in sort order) of the tree.
- */
-struct rb_node *rb_first(const struct rb_root *root)
-{
- struct rb_node *n;
-
- n = root->rb_node;
- if (!n)
- return NULL;
- while (n->rb_left)
- n = n->rb_left;
- return n;
-}
-
-struct rb_node *rb_last(const struct rb_root *root)
-{
- struct rb_node *n;
-
- n = root->rb_node;
- if (!n)
- return NULL;
- while (n->rb_right)
- n = n->rb_right;
- return n;
-}
-
-struct rb_node *rb_next(const struct rb_node *node)
-{
- struct rb_node *parent;
-
- if (rb_parent(node) == node)
- return NULL;
-
- /* If we have a right-hand child, go down and then left as far
- as we can. */
- if (node->rb_right) {
- node = node->rb_right;
- while (node->rb_left)
- node=node->rb_left;
- return (struct rb_node *)node;
- }
-
- /* No right-hand children. Everything down and left is
- smaller than us, so any 'next' node must be in the general
- direction of our parent. Go up the tree; any time the
- ancestor is a right-hand child of its parent, keep going
- up. First time it's a left-hand child of its parent, said
- parent is our 'next' node. */
- while ((parent = rb_parent(node)) && node == parent->rb_right)
- node = parent;
-
- return parent;
-}
-
-struct rb_node *rb_prev(const struct rb_node *node)
-{
- struct rb_node *parent;
-
- if (rb_parent(node) == node)
- return NULL;
-
- /* If we have a left-hand child, go down and then right as far
- as we can. */
- if (node->rb_left) {
- node = node->rb_left;
- while (node->rb_right)
- node=node->rb_right;
- return (struct rb_node *)node;
- }
-
- /* No left-hand children. Go up till we find an ancestor which
- is a right-hand child of its parent */
- while ((parent = rb_parent(node)) && node == parent->rb_left)
- node = parent;
-
- return parent;
-}
-
-void rb_replace_node(struct rb_node *victim, struct rb_node *new,
- struct rb_root *root)
-{
- struct rb_node *parent = rb_parent(victim);
-
- /* Set the surrounding nodes to point to the replacement */
- if (parent) {
- if (victim == parent->rb_left)
- parent->rb_left = new;
- else
- parent->rb_right = new;
- } else {
- root->rb_node = new;
- }
- if (victim->rb_left)
- rb_set_parent(victim->rb_left, new);
- if (victim->rb_right)
- rb_set_parent(victim->rb_right, new);
-
- /* Copy the pointers/colour from the victim to the replacement */
- *new = *victim;
-}
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 @@
-/*
- Red Black Trees
- (C) 1999 Andrea Arcangeli <andrea@suse.de>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
- linux/include/linux/rbtree.h
-
- To use rbtrees you'll have to implement your own insert and search cores.
- This will avoid us to use callbacks and to drop drammatically performances.
- I know it's not the cleaner way, but in C (not in C++) to get
- performances and genericity...
-
- Some example of insert and search follows here. The search is a plain
- normal search over an ordered tree. The insert instead must be implemented
- int two steps: as first thing the code must insert the element in
- order as a red leaf in the tree, then the support library function
- rb_insert_color() must be called. Such function will do the
- not trivial work to rebalance the rbtree if necessary.
-
------------------------------------------------------------------------
-static inline struct page * rb_search_page_cache(struct inode * inode,
- unsigned long offset)
-{
- struct rb_node * n = inode->i_rb_page_cache.rb_node;
- struct page * page;
-
- while (n)
- {
- page = rb_entry(n, struct page, rb_page_cache);
-
- if (offset < page->offset)
- n = n->rb_left;
- else if (offset > page->offset)
- n = n->rb_right;
- else
- return page;
- }
- return NULL;
-}
-
-static inline struct page * __rb_insert_page_cache(struct inode * inode,
- unsigned long offset,
- struct rb_node * node)
-{
- struct rb_node ** p = &inode->i_rb_page_cache.rb_node;
- struct rb_node * parent = NULL;
- struct page * page;
-
- while (*p)
- {
- parent = *p;
- page = rb_entry(parent, struct page, rb_page_cache);
-
- if (offset < page->offset)
- p = &(*p)->rb_left;
- else if (offset > page->offset)
- p = &(*p)->rb_right;
- else
- return page;
- }
-
- rb_link_node(node, parent, p);
-
- return NULL;
-}
-
-static inline struct page * rb_insert_page_cache(struct inode * inode,
- unsigned long offset,
- struct rb_node * node)
-{
- struct page * ret;
- if ((ret = __rb_insert_page_cache(inode, offset, node)))
- goto out;
- rb_insert_color(node, &inode->i_rb_page_cache);
- out:
- return ret;
-}
------------------------------------------------------------------------
-*/
-
-#ifndef _LINUX_RBTREE_H
-#define _LINUX_RBTREE_H
-
-#include <stddef.h>
-
-/**
- * container_of - cast a member of a structure out to the containing structure
- * @ptr: the pointer to the member.
- * @type: the type of the container struct this is embedded in.
- * @member: the name of the member within the struct.
- *
- */
-#define container_of(ptr, type, member) ({ \
- const typeof( ((type *)0)->member ) *__mptr = (ptr); \
- (type *)( (char *)__mptr - offsetof(type,member) );})
-
-struct rb_node
-{
- unsigned long rb_parent_color;
-#define RB_RED 0
-#define RB_BLACK 1
- struct rb_node *rb_right;
- struct rb_node *rb_left;
-} __attribute__((aligned(sizeof(long))));
- /* The alignment might seem pointless, but allegedly CRIS needs it */
-
-struct rb_root
-{
- struct rb_node *rb_node;
-};
-
-
-#define rb_parent(r) ((struct rb_node *)((r)->rb_parent_color & ~3))
-#define rb_color(r) ((r)->rb_parent_color & 1)
-#define rb_is_red(r) (!rb_color(r))
-#define rb_is_black(r) rb_color(r)
-#define rb_set_red(r) do { (r)->rb_parent_color &= ~1; } while (0)
-#define rb_set_black(r) do { (r)->rb_parent_color |= 1; } while (0)
-
-static inline void rb_set_parent(struct rb_node *rb, struct rb_node *p)
-{
- rb->rb_parent_color = (rb->rb_parent_color & 3) | (unsigned long)p;
-}
-static inline void rb_set_color(struct rb_node *rb, int color)
-{
- rb->rb_parent_color = (rb->rb_parent_color & ~1) | color;
-}
-
-#define RB_ROOT (struct rb_root) { NULL, }
-#define rb_entry(ptr, type, member) container_of(ptr, type, member)
-
-#define RB_EMPTY_ROOT(root) ((root)->rb_node == NULL)
-#define RB_EMPTY_NODE(node) (rb_parent(node) == node)
-#define RB_CLEAR_NODE(node) (rb_set_parent(node, node))
-
-extern void rb_insert_color(struct rb_node *, struct rb_root *);
-extern void rb_erase(struct rb_node *, struct rb_root *);
-
-/* Find logical next and previous nodes in a tree */
-extern struct rb_node *rb_next(const struct rb_node *);
-extern struct rb_node *rb_prev(const struct rb_node *);
-extern struct rb_node *rb_first(const struct rb_root *);
-extern struct rb_node *rb_last(const struct rb_root *);
-
-/* Fast replacement of a single node without remove/rebalance/add/rebalance */
-extern void rb_replace_node(struct rb_node *victim, struct rb_node *new,
- struct rb_root *root);
-
-static inline void rb_link_node(struct rb_node * node, struct rb_node * parent,
- struct rb_node ** rb_link)
-{
- node->rb_parent_color = (unsigned long )parent;
- node->rb_left = node->rb_right = NULL;
-
- *rb_link = node;
-}
-
-#endif /* _LINUX_RBTREE_H */
diff --git a/tools/perf/util/strbuf.c b/tools/perf/util/strbuf.c
index 464e7ca898c..5249d5a1b0c 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)
*/
char strbuf_slopbuf[1];
-void strbuf_init(struct strbuf *sb, size_t hint)
+void strbuf_init(struct strbuf *sb, ssize_t hint)
{
sb->alloc = sb->len = 0;
sb->buf = strbuf_slopbuf;
@@ -92,7 +92,8 @@ void strbuf_ltrim(struct strbuf *sb)
void strbuf_tolower(struct strbuf *sb)
{
- int i;
+ unsigned int i;
+
for (i = 0; i < sb->len; i++)
sb->buf[i] = tolower(sb->buf[i]);
}
@@ -264,7 +265,7 @@ size_t strbuf_fread(struct strbuf *sb, size_t size, FILE *f)
return res;
}
-ssize_t strbuf_read(struct strbuf *sb, int fd, size_t hint)
+ssize_t strbuf_read(struct strbuf *sb, int fd, ssize_t hint)
{
size_t oldlen = sb->len;
size_t oldalloc = sb->alloc;
@@ -293,7 +294,7 @@ ssize_t strbuf_read(struct strbuf *sb, int fd, size_t hint)
#define STRBUF_MAXLINK (2*PATH_MAX)
-int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint)
+int strbuf_readlink(struct strbuf *sb, const char *path, ssize_t hint)
{
size_t oldalloc = sb->alloc;
@@ -301,7 +302,7 @@ int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint)
hint = 32;
while (hint < STRBUF_MAXLINK) {
- int len;
+ ssize_t len;
strbuf_grow(sb, hint);
len = readlink(path, sb->buf, hint);
@@ -343,7 +344,7 @@ int strbuf_getline(struct strbuf *sb, FILE *fp, int term)
return 0;
}
-int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint)
+int strbuf_read_file(struct strbuf *sb, const char *path, ssize_t hint)
{
int fd, len;
diff --git a/tools/perf/util/strbuf.h b/tools/perf/util/strbuf.h
index 9ee908a3ec5..d2aa86c014c 100644
--- a/tools/perf/util/strbuf.h
+++ b/tools/perf/util/strbuf.h
@@ -50,7 +50,7 @@ struct strbuf {
#define STRBUF_INIT { 0, 0, strbuf_slopbuf }
/*----- strbuf life cycle -----*/
-extern void strbuf_init(struct strbuf *, size_t);
+extern void strbuf_init(struct strbuf *buf, ssize_t hint);
extern void strbuf_release(struct strbuf *);
extern char *strbuf_detach(struct strbuf *, size_t *);
extern void strbuf_attach(struct strbuf *, void *, size_t, size_t);
@@ -61,7 +61,7 @@ static inline void strbuf_swap(struct strbuf *a, struct strbuf *b) {
}
/*----- strbuf size related -----*/
-static inline size_t strbuf_avail(const struct strbuf *sb) {
+static inline ssize_t strbuf_avail(const struct strbuf *sb) {
return sb->alloc ? sb->alloc - sb->len - 1 : 0;
}
@@ -122,9 +122,9 @@ extern void strbuf_addf(struct strbuf *sb, const char *fmt, ...);
extern size_t strbuf_fread(struct strbuf *, size_t, FILE *);
/* XXX: if read fails, any partial read is undone */
-extern ssize_t strbuf_read(struct strbuf *, int fd, size_t hint);
-extern int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint);
-extern int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint);
+extern ssize_t strbuf_read(struct strbuf *, int fd, ssize_t hint);
+extern int strbuf_read_file(struct strbuf *sb, const char *path, ssize_t hint);
+extern int strbuf_readlink(struct strbuf *sb, const char *path, ssize_t hint);
extern int strbuf_getline(struct strbuf *, FILE *, int);
diff --git a/tools/perf/util/string.h b/tools/perf/util/string.h
index 3dca2f654cd..bf39dfadfd2 100644
--- a/tools/perf/util/string.h
+++ b/tools/perf/util/string.h
@@ -5,4 +5,7 @@
int hex2u64(const char *ptr, u64 *val);
+#define _STR(x) #x
+#define STR(x) _STR(x)
+
#endif
diff --git a/tools/perf/util/strlist.c b/tools/perf/util/strlist.c
index 025a78edfff..7ad38171dc2 100644
--- a/tools/perf/util/strlist.c
+++ b/tools/perf/util/strlist.c
@@ -64,6 +64,7 @@ int strlist__add(struct strlist *self, const char *new_entry)
rb_link_node(&sn->rb_node, parent, p);
rb_insert_color(&sn->rb_node, &self->entries);
+ ++self->nr_entries;
return 0;
}
@@ -155,8 +156,9 @@ struct strlist *strlist__new(bool dupstr, const char *slist)
struct strlist *self = malloc(sizeof(*self));
if (self != NULL) {
- self->entries = RB_ROOT;
- self->dupstr = dupstr;
+ self->entries = RB_ROOT;
+ self->dupstr = dupstr;
+ self->nr_entries = 0;
if (slist && strlist__parse_list(self, slist) != 0)
goto out_error;
}
@@ -182,3 +184,17 @@ void strlist__delete(struct strlist *self)
free(self);
}
}
+
+struct str_node *strlist__entry(const struct strlist *self, unsigned int idx)
+{
+ struct rb_node *nd;
+
+ for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) {
+ struct str_node *pos = rb_entry(nd, struct str_node, rb_node);
+
+ if (!idx--)
+ return pos;
+ }
+
+ return NULL;
+}
diff --git a/tools/perf/util/strlist.h b/tools/perf/util/strlist.h
index 2fb117fb4b6..921818e44a5 100644
--- a/tools/perf/util/strlist.h
+++ b/tools/perf/util/strlist.h
@@ -1,7 +1,7 @@
#ifndef STRLIST_H_
#define STRLIST_H_
-#include "rbtree.h"
+#include <linux/rbtree.h>
#include <stdbool.h>
struct str_node {
@@ -11,7 +11,8 @@ struct str_node {
struct strlist {
struct rb_root entries;
- bool dupstr;
+ unsigned int nr_entries;
+ bool dupstr;
};
struct strlist *strlist__new(bool dupstr, const char *slist);
@@ -21,11 +22,17 @@ void strlist__remove(struct strlist *self, struct str_node *sn);
int strlist__load(struct strlist *self, const char *filename);
int strlist__add(struct strlist *self, const char *str);
+struct str_node *strlist__entry(const struct strlist *self, unsigned int idx);
bool strlist__has_entry(struct strlist *self, const char *entry);
static inline bool strlist__empty(const struct strlist *self)
{
- return rb_first(&self->entries) == NULL;
+ return self->nr_entries == 0;
+}
+
+static inline unsigned int strlist__nr_entries(const struct strlist *self)
+{
+ return self->nr_entries;
}
int strlist__parse_list(struct strlist *self, const char *s);
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 78c2efde01b..5c0f42e6b33 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -9,6 +9,16 @@
const char *sym_hist_filter;
+enum dso_origin {
+ DSO__ORIG_KERNEL = 0,
+ DSO__ORIG_JAVA_JIT,
+ DSO__ORIG_FEDORA,
+ DSO__ORIG_UBUNTU,
+ DSO__ORIG_BUILDID,
+ DSO__ORIG_DSO,
+ DSO__ORIG_NOT_FOUND,
+};
+
static struct symbol *symbol__new(u64 start, u64 len,
const char *name, unsigned int priv_size,
u64 obj_start, int verbose)
@@ -35,7 +45,7 @@ static struct symbol *symbol__new(u64 start, u64 len,
self = ((void *)self) + priv_size;
}
self->start = start;
- self->end = start + len - 1;
+ self->end = len ? start + len - 1 : start;
memcpy(self->name, name, namelen);
return self;
@@ -48,8 +58,12 @@ static void symbol__delete(struct symbol *self, unsigned int priv_size)
static size_t symbol__fprintf(struct symbol *self, FILE *fp)
{
- return fprintf(fp, " %llx-%llx %s\n",
+ if (!self->module)
+ return fprintf(fp, " %llx-%llx %s\n",
self->start, self->end, self->name);
+ else
+ return fprintf(fp, " %llx-%llx %s \t[%s]\n",
+ self->start, self->end, self->name, self->module->name);
}
struct dso *dso__new(const char *name, unsigned int sym_priv_size)
@@ -61,6 +75,8 @@ struct dso *dso__new(const char *name, unsigned int sym_priv_size)
self->syms = RB_ROOT;
self->sym_priv_size = sym_priv_size;
self->find_symbol = dso__find_symbol;
+ self->slen_calculated = 0;
+ self->origin = DSO__ORIG_NOT_FOUND;
}
return self;
@@ -146,6 +162,7 @@ static int dso__load_kallsyms(struct dso *self, symbol_filter_t filter, int verb
char *line = NULL;
size_t n;
FILE *file = fopen("/proc/kallsyms", "r");
+ int count = 0;
if (file == NULL)
goto out_failure;
@@ -188,8 +205,10 @@ static int dso__load_kallsyms(struct dso *self, symbol_filter_t filter, int verb
if (filter && filter(self, sym))
symbol__delete(sym, self->sym_priv_size);
- else
+ else {
dso__insert_symbol(self, sym);
+ count++;
+ }
}
/*
@@ -212,7 +231,7 @@ static int dso__load_kallsyms(struct dso *self, symbol_filter_t filter, int verb
free(line);
fclose(file);
- return 0;
+ return count;
out_delete_line:
free(line);
@@ -307,6 +326,26 @@ static inline int elf_sym__is_function(const GElf_Sym *sym)
sym->st_size != 0;
}
+static inline int elf_sym__is_label(const GElf_Sym *sym)
+{
+ return elf_sym__type(sym) == STT_NOTYPE &&
+ sym->st_name != 0 &&
+ sym->st_shndx != SHN_UNDEF &&
+ sym->st_shndx != SHN_ABS;
+}
+
+static inline const char *elf_sec__name(const GElf_Shdr *shdr,
+ const Elf_Data *secstrs)
+{
+ return secstrs->d_buf + shdr->sh_name;
+}
+
+static inline int elf_sec__is_text(const GElf_Shdr *shdr,
+ const Elf_Data *secstrs)
+{
+ return strstr(elf_sec__name(shdr, secstrs), "text") != NULL;
+}
+
static inline const char *elf_sym__name(const GElf_Sym *sym,
const Elf_Data *symstrs)
{
@@ -346,36 +385,61 @@ static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep,
idx < nr_entries; \
++idx, pos = gelf_getrela(reldata, idx, &pos_mem))
-static int dso__synthesize_plt_symbols(struct dso *self, Elf *elf,
- GElf_Ehdr *ehdr, Elf_Scn *scn_dynsym,
- GElf_Shdr *shdr_dynsym,
- size_t dynsym_idx, int verbose)
+/*
+ * We need to check if we have a .dynsym, so that we can handle the
+ * .plt, synthesizing its symbols, that aren't on the symtabs (be it
+ * .dynsym or .symtab).
+ * And always look at the original dso, not at debuginfo packages, that
+ * have the PLT data stripped out (shdr_rel_plt.sh_type == SHT_NOBITS).
+ */
+static int dso__synthesize_plt_symbols(struct dso *self, int verbose)
{
uint32_t nr_rel_entries, idx;
GElf_Sym sym;
u64 plt_offset;
GElf_Shdr shdr_plt;
struct symbol *f;
- GElf_Shdr shdr_rel_plt;
+ GElf_Shdr shdr_rel_plt, shdr_dynsym;
Elf_Data *reldata, *syms, *symstrs;
- Elf_Scn *scn_plt_rel, *scn_symstrs;
+ Elf_Scn *scn_plt_rel, *scn_symstrs, *scn_dynsym;
+ size_t dynsym_idx;
+ GElf_Ehdr ehdr;
char sympltname[1024];
- int nr = 0, symidx;
+ Elf *elf;
+ int nr = 0, symidx, fd, err = 0;
- scn_plt_rel = elf_section_by_name(elf, ehdr, &shdr_rel_plt,
+ fd = open(self->name, O_RDONLY);
+ if (fd < 0)
+ goto out;
+
+ elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
+ if (elf == NULL)
+ goto out_close;
+
+ if (gelf_getehdr(elf, &ehdr) == NULL)
+ goto out_elf_end;
+
+ scn_dynsym = elf_section_by_name(elf, &ehdr, &shdr_dynsym,
+ ".dynsym", &dynsym_idx);
+ if (scn_dynsym == NULL)
+ goto out_elf_end;
+
+ scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt,
".rela.plt", NULL);
if (scn_plt_rel == NULL) {
- scn_plt_rel = elf_section_by_name(elf, ehdr, &shdr_rel_plt,
+ scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt,
".rel.plt", NULL);
if (scn_plt_rel == NULL)
- return 0;
+ goto out_elf_end;
}
+ err = -1;
+
if (shdr_rel_plt.sh_link != dynsym_idx)
- return 0;
+ goto out_elf_end;
- if (elf_section_by_name(elf, ehdr, &shdr_plt, ".plt", NULL) == NULL)
- return 0;
+ if (elf_section_by_name(elf, &ehdr, &shdr_plt, ".plt", NULL) == NULL)
+ goto out_elf_end;
/*
* Fetch the relocation section to find the indexes to the GOT
@@ -383,19 +447,19 @@ static int dso__synthesize_plt_symbols(struct dso *self, Elf *elf,
*/
reldata = elf_getdata(scn_plt_rel, NULL);
if (reldata == NULL)
- return -1;
+ goto out_elf_end;
syms = elf_getdata(scn_dynsym, NULL);
if (syms == NULL)
- return -1;
+ goto out_elf_end;
- scn_symstrs = elf_getscn(elf, shdr_dynsym->sh_link);
+ scn_symstrs = elf_getscn(elf, shdr_dynsym.sh_link);
if (scn_symstrs == NULL)
- return -1;
+ goto out_elf_end;
symstrs = elf_getdata(scn_symstrs, NULL);
if (symstrs == NULL)
- return -1;
+ goto out_elf_end;
nr_rel_entries = shdr_rel_plt.sh_size / shdr_rel_plt.sh_entsize;
plt_offset = shdr_plt.sh_offset;
@@ -414,7 +478,7 @@ static int dso__synthesize_plt_symbols(struct dso *self, Elf *elf,
f = symbol__new(plt_offset, shdr_plt.sh_entsize,
sympltname, self->sym_priv_size, 0, verbose);
if (!f)
- return -1;
+ goto out_elf_end;
dso__insert_symbol(self, f);
++nr;
@@ -432,25 +496,31 @@ static int dso__synthesize_plt_symbols(struct dso *self, Elf *elf,
f = symbol__new(plt_offset, shdr_plt.sh_entsize,
sympltname, self->sym_priv_size, 0, verbose);
if (!f)
- return -1;
+ goto out_elf_end;
dso__insert_symbol(self, f);
++nr;
}
- } else {
- /*
- * TODO: There are still one more shdr_rel_plt.sh_type
- * I have to investigate, but probably should be ignored.
- */
}
- return nr;
+ err = 0;
+out_elf_end:
+ elf_end(elf);
+out_close:
+ close(fd);
+
+ if (err == 0)
+ return nr;
+out:
+ fprintf(stderr, "%s: problems reading %s PLT info.\n",
+ __func__, self->name);
+ return 0;
}
static int dso__load_sym(struct dso *self, int fd, const char *name,
- symbol_filter_t filter, int verbose)
+ symbol_filter_t filter, int verbose, struct module *mod)
{
- Elf_Data *symstrs;
+ Elf_Data *symstrs, *secstrs;
uint32_t nr_syms;
int err = -1;
uint32_t index;
@@ -458,10 +528,9 @@ static int dso__load_sym(struct dso *self, int fd, const char *name,
GElf_Shdr shdr;
Elf_Data *syms;
GElf_Sym sym;
- Elf_Scn *sec, *sec_dynsym;
+ Elf_Scn *sec, *sec_strndx;
Elf *elf;
- size_t dynsym_idx;
- int nr = 0;
+ int nr = 0, kernel = !strcmp("[kernel]", self->name);
elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
if (elf == NULL) {
@@ -477,32 +546,11 @@ static int dso__load_sym(struct dso *self, int fd, const char *name,
goto out_elf_end;
}
- /*
- * We need to check if we have a .dynsym, so that we can handle the
- * .plt, synthesizing its symbols, that aren't on the symtabs (be it
- * .dynsym or .symtab)
- */
- sec_dynsym = elf_section_by_name(elf, &ehdr, &shdr,
- ".dynsym", &dynsym_idx);
- if (sec_dynsym != NULL) {
- nr = dso__synthesize_plt_symbols(self, elf, &ehdr,
- sec_dynsym, &shdr,
- dynsym_idx, verbose);
- if (nr < 0)
- goto out_elf_end;
- }
-
- /*
- * But if we have a full .symtab (that is a superset of .dynsym) we
- * should add the symbols not in the .dynsyn
- */
sec = elf_section_by_name(elf, &ehdr, &shdr, ".symtab", NULL);
if (sec == NULL) {
- if (sec_dynsym == NULL)
+ sec = elf_section_by_name(elf, &ehdr, &shdr, ".dynsym", NULL);
+ if (sec == NULL)
goto out_elf_end;
-
- sec = sec_dynsym;
- gelf_getshdr(sec, &shdr);
}
syms = elf_getdata(sec, NULL);
@@ -517,17 +565,34 @@ static int dso__load_sym(struct dso *self, int fd, const char *name,
if (symstrs == NULL)
goto out_elf_end;
+ sec_strndx = elf_getscn(elf, ehdr.e_shstrndx);
+ if (sec_strndx == NULL)
+ goto out_elf_end;
+
+ secstrs = elf_getdata(sec_strndx, NULL);
+ if (secstrs == NULL)
+ goto out_elf_end;
+
nr_syms = shdr.sh_size / shdr.sh_entsize;
memset(&sym, 0, sizeof(sym));
- self->prelinked = elf_section_by_name(elf, &ehdr, &shdr,
- ".gnu.prelink_undo",
- NULL) != NULL;
+ if (!kernel) {
+ self->adjust_symbols = (ehdr.e_type == ET_EXEC ||
+ elf_section_by_name(elf, &ehdr, &shdr,
+ ".gnu.prelink_undo",
+ NULL) != NULL);
+ } else self->adjust_symbols = 0;
+
elf_symtab__for_each_symbol(syms, nr_syms, index, sym) {
struct symbol *f;
+ const char *name;
+ char *demangled;
u64 obj_start;
+ struct section *section = NULL;
+ int is_label = elf_sym__is_label(&sym);
+ const char *section_name;
- if (!elf_sym__is_function(&sym))
+ if (!is_label && !elf_sym__is_function(&sym))
continue;
sec = elf_getscn(elf, sym.st_shndx);
@@ -535,9 +600,14 @@ static int dso__load_sym(struct dso *self, int fd, const char *name,
goto out_elf_end;
gelf_getshdr(sec, &shdr);
+
+ if (is_label && !elf_sec__is_text(&shdr, secstrs))
+ continue;
+
+ section_name = elf_sec__name(&shdr, secstrs);
obj_start = sym.st_value;
- if (self->prelinked) {
+ if (self->adjust_symbols) {
if (verbose >= 2)
printf("adjusting symbol: st_value: %Lx sh_addr: %Lx sh_offset: %Lx\n",
(u64)sym.st_value, (u64)shdr.sh_addr, (u64)shdr.sh_offset);
@@ -545,15 +615,36 @@ static int dso__load_sym(struct dso *self, int fd, const char *name,
sym.st_value -= shdr.sh_addr - shdr.sh_offset;
}
- f = symbol__new(sym.st_value, sym.st_size,
- elf_sym__name(&sym, symstrs),
+ if (mod) {
+ section = mod->sections->find_section(mod->sections, section_name);
+ if (section)
+ sym.st_value += section->vma;
+ else {
+ fprintf(stderr, "dso__load_sym() module %s lookup of %s failed\n",
+ mod->name, section_name);
+ goto out_elf_end;
+ }
+ }
+ /*
+ * We need to figure out if the object was created from C++ sources
+ * DWARF DW_compile_unit has this, but we don't always have access
+ * to it...
+ */
+ name = elf_sym__name(&sym, symstrs);
+ demangled = bfd_demangle(NULL, name, DMGL_PARAMS | DMGL_ANSI);
+ if (demangled != NULL)
+ name = demangled;
+
+ f = symbol__new(sym.st_value, sym.st_size, name,
self->sym_priv_size, obj_start, verbose);
+ free(demangled);
if (!f)
goto out_elf_end;
if (filter && filter(self, f))
symbol__delete(f, self->sym_priv_size);
else {
+ f->module = mod;
dso__insert_symbol(self, f);
nr++;
}
@@ -566,44 +657,135 @@ out_close:
return err;
}
+#define BUILD_ID_SIZE 128
+
+static char *dso__read_build_id(struct dso *self, int verbose)
+{
+ int i;
+ GElf_Ehdr ehdr;
+ GElf_Shdr shdr;
+ Elf_Data *build_id_data;
+ Elf_Scn *sec;
+ char *build_id = NULL, *bid;
+ unsigned char *raw;
+ Elf *elf;
+ int fd = open(self->name, O_RDONLY);
+
+ if (fd < 0)
+ goto out;
+
+ elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
+ if (elf == NULL) {
+ if (verbose)
+ fprintf(stderr, "%s: cannot read %s ELF file.\n",
+ __func__, self->name);
+ goto out_close;
+ }
+
+ if (gelf_getehdr(elf, &ehdr) == NULL) {
+ if (verbose)
+ fprintf(stderr, "%s: cannot get elf header.\n", __func__);
+ goto out_elf_end;
+ }
+
+ sec = elf_section_by_name(elf, &ehdr, &shdr, ".note.gnu.build-id", NULL);
+ if (sec == NULL)
+ goto out_elf_end;
+
+ build_id_data = elf_getdata(sec, NULL);
+ if (build_id_data == NULL)
+ goto out_elf_end;
+ build_id = malloc(BUILD_ID_SIZE);
+ if (build_id == NULL)
+ goto out_elf_end;
+ raw = build_id_data->d_buf + 16;
+ bid = build_id;
+
+ for (i = 0; i < 20; ++i) {
+ sprintf(bid, "%02x", *raw);
+ ++raw;
+ bid += 2;
+ }
+ if (verbose >= 2)
+ printf("%s(%s): %s\n", __func__, self->name, build_id);
+out_elf_end:
+ elf_end(elf);
+out_close:
+ close(fd);
+out:
+ return build_id;
+}
+
+char dso__symtab_origin(const struct dso *self)
+{
+ static const char origin[] = {
+ [DSO__ORIG_KERNEL] = 'k',
+ [DSO__ORIG_JAVA_JIT] = 'j',
+ [DSO__ORIG_FEDORA] = 'f',
+ [DSO__ORIG_UBUNTU] = 'u',
+ [DSO__ORIG_BUILDID] = 'b',
+ [DSO__ORIG_DSO] = 'd',
+ };
+
+ if (self == NULL || self->origin == DSO__ORIG_NOT_FOUND)
+ return '!';
+ return origin[self->origin];
+}
+
int dso__load(struct dso *self, symbol_filter_t filter, int verbose)
{
- int size = strlen(self->name) + sizeof("/usr/lib/debug%s.debug");
- char *name = malloc(size);
- int variant = 0;
+ int size = PATH_MAX;
+ char *name = malloc(size), *build_id = NULL;
int ret = -1;
int fd;
if (!name)
return -1;
- self->prelinked = 0;
+ self->adjust_symbols = 0;
+
+ if (strncmp(self->name, "/tmp/perf-", 10) == 0) {
+ ret = dso__load_perf_map(self, filter, verbose);
+ self->origin = ret > 0 ? DSO__ORIG_JAVA_JIT :
+ DSO__ORIG_NOT_FOUND;
+ return ret;
+ }
- if (strncmp(self->name, "/tmp/perf-", 10) == 0)
- return dso__load_perf_map(self, filter, verbose);
+ self->origin = DSO__ORIG_FEDORA - 1;
more:
do {
- switch (variant) {
- case 0: /* Fedora */
+ self->origin++;
+ switch (self->origin) {
+ case DSO__ORIG_FEDORA:
snprintf(name, size, "/usr/lib/debug%s.debug", self->name);
break;
- case 1: /* Ubuntu */
+ case DSO__ORIG_UBUNTU:
snprintf(name, size, "/usr/lib/debug%s", self->name);
break;
- case 2: /* Sane people */
+ case DSO__ORIG_BUILDID:
+ build_id = dso__read_build_id(self, verbose);
+ if (build_id != NULL) {
+ snprintf(name, size,
+ "/usr/lib/debug/.build-id/%.2s/%s.debug",
+ build_id, build_id + 2);
+ free(build_id);
+ break;
+ }
+ self->origin++;
+ /* Fall thru */
+ case DSO__ORIG_DSO:
snprintf(name, size, "%s", self->name);
break;
default:
goto out;
}
- variant++;
fd = open(name, O_RDONLY);
} while (fd < 0);
- ret = dso__load_sym(self, fd, name, filter, verbose);
+ ret = dso__load_sym(self, fd, name, filter, verbose, NULL);
close(fd);
/*
@@ -612,11 +794,98 @@ more:
if (!ret)
goto more;
+ if (ret > 0) {
+ int nr_plt = dso__synthesize_plt_symbols(self, verbose);
+ if (nr_plt > 0)
+ ret += nr_plt;
+ }
out:
free(name);
+ if (ret < 0 && strstr(self->name, " (deleted)") != NULL)
+ return 0;
return ret;
}
+static int dso__load_module(struct dso *self, struct mod_dso *mods, const char *name,
+ symbol_filter_t filter, int verbose)
+{
+ struct module *mod = mod_dso__find_module(mods, name);
+ int err = 0, fd;
+
+ if (mod == NULL || !mod->active)
+ return err;
+
+ fd = open(mod->path, O_RDONLY);
+
+ if (fd < 0)
+ return err;
+
+ err = dso__load_sym(self, fd, name, filter, verbose, mod);
+ close(fd);
+
+ return err;
+}
+
+int dso__load_modules(struct dso *self, symbol_filter_t filter, int verbose)
+{
+ struct mod_dso *mods = mod_dso__new_dso("modules");
+ struct module *pos;
+ struct rb_node *next;
+ int err;
+
+ err = mod_dso__load_modules(mods);
+
+ if (err <= 0)
+ return err;
+
+ /*
+ * Iterate over modules, and load active symbols.
+ */
+ next = rb_first(&mods->mods);
+ while (next) {
+ pos = rb_entry(next, struct module, rb_node);
+ err = dso__load_module(self, mods, pos->name, filter, verbose);
+
+ if (err < 0)
+ break;
+
+ next = rb_next(&pos->rb_node);
+ }
+
+ if (err < 0) {
+ mod_dso__delete_modules(mods);
+ mod_dso__delete_self(mods);
+ }
+
+ return err;
+}
+
+static inline void dso__fill_symbol_holes(struct dso *self)
+{
+ struct symbol *prev = NULL;
+ struct rb_node *nd;
+
+ for (nd = rb_last(&self->syms); nd; nd = rb_prev(nd)) {
+ struct symbol *pos = rb_entry(nd, struct symbol, rb_node);
+
+ if (prev) {
+ u64 hole = 0;
+ int alias = pos->start == prev->start;
+
+ if (!alias)
+ hole = prev->start - pos->end - 1;
+
+ if (hole || alias) {
+ if (alias)
+ pos->end = prev->end;
+ else if (hole)
+ pos->end = prev->start - 1;
+ }
+ }
+ prev = pos;
+ }
+}
+
static int dso__load_vmlinux(struct dso *self, const char *vmlinux,
symbol_filter_t filter, int verbose)
{
@@ -625,23 +894,33 @@ static int dso__load_vmlinux(struct dso *self, const char *vmlinux,
if (fd < 0)
return -1;
- err = dso__load_sym(self, fd, vmlinux, filter, verbose);
+ err = dso__load_sym(self, fd, vmlinux, filter, verbose, NULL);
+
+ if (err > 0)
+ dso__fill_symbol_holes(self);
+
close(fd);
return err;
}
int dso__load_kernel(struct dso *self, const char *vmlinux,
- symbol_filter_t filter, int verbose)
+ symbol_filter_t filter, int verbose, int modules)
{
int err = -1;
- if (vmlinux)
+ if (vmlinux) {
err = dso__load_vmlinux(self, vmlinux, filter, verbose);
+ if (err > 0 && modules)
+ err = dso__load_modules(self, filter, verbose);
+ }
- if (err < 0)
+ if (err <= 0)
err = dso__load_kallsyms(self, filter, verbose);
+ if (err > 0)
+ self->origin = DSO__ORIG_KERNEL;
+
return err;
}
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 2c48ace8203..b53bf0125c1 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -3,8 +3,33 @@
#include <linux/types.h>
#include "types.h"
-#include "list.h"
-#include "rbtree.h"
+#include <linux/list.h>
+#include <linux/rbtree.h>
+#include "module.h"
+
+#ifdef HAVE_CPLUS_DEMANGLE
+extern char *cplus_demangle(const char *, int);
+
+static inline char *bfd_demangle(void __used *v, const char *c, int i)
+{
+ return cplus_demangle(c, i);
+}
+#else
+#ifdef NO_DEMANGLE
+static inline char *bfd_demangle(void __used *v, const char __used *c,
+ int __used i)
+{
+ return NULL;
+}
+#else
+#include <bfd.h>
+#endif
+#endif
+
+#ifndef DMGL_PARAMS
+#define DMGL_PARAMS (1 << 0) /* Include function args */
+#define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */
+#endif
struct symbol {
struct rb_node rb_node;
@@ -13,6 +38,7 @@ struct symbol {
u64 obj_start;
u64 hist_sum;
u64 *hist;
+ struct module *module;
void *priv;
char name[0];
};
@@ -22,7 +48,9 @@ struct dso {
struct rb_root syms;
struct symbol *(*find_symbol)(struct dso *, u64 ip);
unsigned int sym_priv_size;
- unsigned char prelinked;
+ unsigned char adjust_symbols;
+ unsigned char slen_calculated;
+ unsigned char origin;
char name[0];
};
@@ -41,10 +69,12 @@ static inline void *dso__sym_priv(struct dso *self, struct symbol *sym)
struct symbol *dso__find_symbol(struct dso *self, u64 ip);
int dso__load_kernel(struct dso *self, const char *vmlinux,
- symbol_filter_t filter, int verbose);
+ symbol_filter_t filter, int verbose, int modules);
+int dso__load_modules(struct dso *self, symbol_filter_t filter, int verbose);
int dso__load(struct dso *self, symbol_filter_t filter, int verbose);
size_t dso__fprintf(struct dso *self, FILE *fp);
+char dso__symtab_origin(const struct dso *self);
void symbol__init(void);
#endif /* _PERF_SYMBOL_ */
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h
index b4be6071c10..68fe157d72f 100644
--- a/tools/perf/util/util.h
+++ b/tools/perf/util/util.h
@@ -50,6 +50,7 @@
#include <unistd.h>
#include <stdio.h>
#include <sys/stat.h>
+#include <sys/statfs.h>
#include <fcntl.h>
#include <stddef.h>
#include <stdlib.h>
@@ -80,6 +81,7 @@
#include <netdb.h>
#include <pwd.h>
#include <inttypes.h>
+#include "../../../include/linux/magic.h"
#ifndef NO_ICONV
#include <iconv.h>
diff --git a/tools/perf/util/wrapper.c b/tools/perf/util/wrapper.c
index 6350d65f6d9..4574ac28396 100644
--- a/tools/perf/util/wrapper.c
+++ b/tools/perf/util/wrapper.c
@@ -7,7 +7,7 @@
* There's no pack memory to release - but stay close to the Git
* version so wrap this away:
*/
-static inline void release_pack_memory(size_t size, int flag)
+static inline void release_pack_memory(size_t size __used, int flag __used)
{
}
@@ -59,7 +59,8 @@ void *xmemdupz(const void *data, size_t len)
char *xstrndup(const char *str, size_t len)
{
char *p = memchr(str, '\0', len);
- return xmemdupz(str, p ? p - str : len);
+
+ return xmemdupz(str, p ? (size_t)(p - str) : len);
}
void *xrealloc(void *ptr, size_t size)