summaryrefslogtreecommitdiff
path: root/tools/perf
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf')
-rw-r--r--tools/perf/.gitignore2
-rw-r--r--tools/perf/Documentation/perf-record.txt30
-rw-r--r--tools/perf/Documentation/perf-report.txt15
-rw-r--r--tools/perf/Documentation/perfconfig.example1
-rw-r--r--tools/perf/Makefile162
-rw-r--r--tools/perf/arch/powerpc/util/header.c2
-rw-r--r--tools/perf/arch/x86/util/header.c2
-rw-r--r--tools/perf/builtin-diff.c60
-rw-r--r--tools/perf/builtin-record.c97
-rw-r--r--tools/perf/builtin-report.c229
-rw-r--r--tools/perf/builtin-sched.c1
-rw-r--r--tools/perf/builtin-stat.c28
-rw-r--r--tools/perf/builtin-test.c174
-rw-r--r--tools/perf/builtin-top.c36
-rw-r--r--tools/perf/config/feature-tests.mak15
-rw-r--r--tools/perf/perf.h24
-rw-r--r--tools/perf/ui/browser.c (renamed from tools/perf/util/ui/browser.c)11
-rw-r--r--tools/perf/ui/browser.h (renamed from tools/perf/util/ui/browser.h)6
-rw-r--r--tools/perf/ui/browsers/annotate.c (renamed from tools/perf/util/ui/browsers/annotate.c)380
-rw-r--r--tools/perf/ui/browsers/hists.c (renamed from tools/perf/util/ui/browsers/hists.c)151
-rw-r--r--tools/perf/ui/browsers/map.c (renamed from tools/perf/util/ui/browsers/map.c)6
-rw-r--r--tools/perf/ui/browsers/map.h (renamed from tools/perf/util/ui/browsers/map.h)0
-rw-r--r--tools/perf/ui/gtk/browser.c189
-rw-r--r--tools/perf/ui/gtk/gtk.h8
-rw-r--r--tools/perf/ui/helpline.c (renamed from tools/perf/util/ui/helpline.c)2
-rw-r--r--tools/perf/ui/helpline.h (renamed from tools/perf/util/ui/helpline.h)0
-rw-r--r--tools/perf/ui/keysyms.h (renamed from tools/perf/util/ui/keysyms.h)2
-rw-r--r--tools/perf/ui/libslang.h (renamed from tools/perf/util/ui/libslang.h)0
-rw-r--r--tools/perf/ui/progress.c (renamed from tools/perf/util/ui/progress.c)0
-rw-r--r--tools/perf/ui/progress.h (renamed from tools/perf/util/ui/progress.h)0
-rw-r--r--tools/perf/ui/setup.c (renamed from tools/perf/util/ui/setup.c)0
-rw-r--r--tools/perf/ui/ui.h (renamed from tools/perf/util/ui/ui.h)0
-rw-r--r--tools/perf/ui/util.c (renamed from tools/perf/util/ui/util.c)82
-rw-r--r--tools/perf/ui/util.h (renamed from tools/perf/util/ui/util.h)0
-rw-r--r--tools/perf/util/annotate.c57
-rw-r--r--tools/perf/util/cache.h12
-rw-r--r--tools/perf/util/color.c9
-rw-r--r--tools/perf/util/debug.h2
-rw-r--r--tools/perf/util/event.h1
-rw-r--r--tools/perf/util/evlist.c7
-rw-r--r--tools/perf/util/evsel.c24
-rw-r--r--tools/perf/util/evsel.h5
-rw-r--r--tools/perf/util/header.c209
-rw-r--r--tools/perf/util/header.h2
-rw-r--r--tools/perf/util/hist.c356
-rw-r--r--tools/perf/util/hist.h32
-rw-r--r--tools/perf/util/include/asm/unistd_32.h1
-rw-r--r--tools/perf/util/include/asm/unistd_64.h1
-rw-r--r--tools/perf/util/include/linux/bitops.h2
-rw-r--r--tools/perf/util/include/linux/export.h (renamed from tools/perf/util/include/linux/module.h)0
-rw-r--r--tools/perf/util/map.c1
-rw-r--r--tools/perf/util/map.h1
-rw-r--r--tools/perf/util/parse-events.c605
-rw-r--r--tools/perf/util/parse-events.h49
-rw-r--r--tools/perf/util/parse-events.l127
-rw-r--r--tools/perf/util/parse-events.y229
-rw-r--r--tools/perf/util/pmu.c469
-rw-r--r--tools/perf/util/pmu.h41
-rw-r--r--tools/perf/util/pmu.l43
-rw-r--r--tools/perf/util/pmu.y93
-rw-r--r--tools/perf/util/probe-event.c1
-rw-r--r--tools/perf/util/probe-finder.c4
-rw-r--r--tools/perf/util/session.c78
-rw-r--r--tools/perf/util/session.h4
-rw-r--r--tools/perf/util/sort.c290
-rw-r--r--tools/perf/util/sort.h11
-rw-r--r--tools/perf/util/strbuf.c7
-rw-r--r--tools/perf/util/symbol.c3
-rw-r--r--tools/perf/util/symbol.h20
-rw-r--r--tools/perf/util/thread_map.c2
-rw-r--r--tools/perf/util/thread_map.h2
-rw-r--r--tools/perf/util/trace-event-parse.c22
72 files changed, 3695 insertions, 842 deletions
diff --git a/tools/perf/.gitignore b/tools/perf/.gitignore
index 416684be0ad3..26b823b61aa1 100644
--- a/tools/perf/.gitignore
+++ b/tools/perf/.gitignore
@@ -19,3 +19,5 @@ TAGS
cscope*
config.mak
config.mak.autogen
+*-bison.*
+*-flex.*
diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt
index a5766b4b0125..a1386b2fff00 100644
--- a/tools/perf/Documentation/perf-record.txt
+++ b/tools/perf/Documentation/perf-record.txt
@@ -152,6 +152,36 @@ an empty cgroup (monitor all the time) using, e.g., -G foo,,bar. Cgroups must ha
corresponding events, i.e., they always refer to events defined earlier on the command
line.
+-b::
+--branch-any::
+Enable taken branch stack sampling. Any type of taken branch may be sampled.
+This is a shortcut for --branch-filter any. See --branch-filter for more infos.
+
+-j::
+--branch-filter::
+Enable taken branch stack sampling. Each sample captures a series of consecutive
+taken branches. The number of branches captured with each sample depends on the
+underlying hardware, the type of branches of interest, and the executed code.
+It is possible to select the types of branches captured by enabling filters. The
+following filters are defined:
+
+ - any: any type of branches
+ - any_call: any function call or system call
+ - any_ret: any function return or system call return
+ - any_ind: any indirect branch
+ - u: only when the branch target is at the user level
+ - k: only when the branch target is in the kernel
+ - hv: only when the target is at the hypervisor level
+
++
+The option requires at least one branch type among any, any_call, any_ret, ind_call.
+The privilege levels may be ommitted, in which case, the privilege levels of the associated
+event are applied to the branch filter. Both kernel (k) and hypervisor (hv) privilege
+levels are subject to permissions. When sampling on multiple events, branch stack sampling
+is enabled for all the sampling events. The sampled branch type is the same for all events.
+The various filters must be specified as a comma separated list: --branch-filter any_ret,u,k
+Note that this feature may not be available on all processors.
+
SEE ALSO
--------
linkperf:perf-stat[1], linkperf:perf-list[1]
diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt
index 9b430e98712e..2d89f02719b5 100644
--- a/tools/perf/Documentation/perf-report.txt
+++ b/tools/perf/Documentation/perf-report.txt
@@ -48,6 +48,9 @@ OPTIONS
Only consider these symbols. CSV that understands
file://filename entries.
+--symbol-filter=::
+ Only show symbols that match (partially) with this filter.
+
-U::
--hide-unresolved::
Only display entries resolved to a symbol.
@@ -110,6 +113,8 @@ OPTIONS
requires a tty, if one is not present, as when piping to other
commands, the stdio interface is used.
+--gtk:: Use the GTK2 interface.
+
-k::
--vmlinux=<file>::
vmlinux pathname
@@ -153,6 +158,16 @@ OPTIONS
information which may be very large and thus may clutter the display.
It currently includes: cpu and numa topology of the host system.
+-b::
+--branch-stack::
+ Use the addresses of sampled taken branches instead of the instruction
+ address to build the histograms. To generate meaningful output, the
+ perf.data file must have been obtained using perf record -b or
+ perf record --branch-filter xxx where xxx is a branch filter option.
+ perf report is able to auto-detect whether a perf.data file contains
+ branch stacks and it will automatically switch to the branch view mode,
+ unless --no-branch-stack is used.
+
SEE ALSO
--------
linkperf:perf-stat[1], linkperf:perf-annotate[1]
diff --git a/tools/perf/Documentation/perfconfig.example b/tools/perf/Documentation/perfconfig.example
index d1448668f4d4..42c6fd2ae85d 100644
--- a/tools/perf/Documentation/perfconfig.example
+++ b/tools/perf/Documentation/perfconfig.example
@@ -6,6 +6,7 @@
normal = black, lightgray
selected = lightgray, magenta
code = blue, lightgray
+ addr = magenta, lightgray
[tui]
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index e011b5060f92..e98e14c88532 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -1,20 +1,22 @@
-ifeq ("$(origin O)", "command line")
- OUTPUT := $(O)/
-endif
+include ../scripts/Makefile.include
# The default target of this Makefile is...
all:
include config/utilities.mak
-ifneq ($(OUTPUT),)
-# check that the output directory actually exists
-OUTDIR := $(shell cd $(OUTPUT) && /bin/pwd)
-$(if $(OUTDIR),, $(error output directory "$(OUTPUT)" does not exist))
-endif
-
# Define V to have a more verbose compile.
#
+# Define O to save output files in a separate directory.
+#
+# Define ARCH as name of target architecture if you want cross-builds.
+#
+# Define CROSS_COMPILE as prefix name of compiler if you want cross-builds.
+#
+# Define NO_LIBPERL to disable perl script extension.
+#
+# Define NO_LIBPYTHON to disable python script extension.
+#
# Define PYTHON to point to the python binary if the default
# `python' is not correct; for example: PYTHON=python2
#
@@ -32,6 +34,10 @@ endif
# Define NO_DWARF if you do not want debug-info analysis feature at all.
#
# Define WERROR=0 to disable treating any warnings as errors.
+#
+# Define NO_NEWT if you do not want TUI support.
+#
+# Define NO_DEMANGLE if you do not want C++ symbol demangling.
$(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE
@$(SHELL_PATH) util/PERF-VERSION-GEN $(OUTPUT)
@@ -70,31 +76,6 @@ ifneq ($(WERROR),0)
CFLAGS_WERROR := -Werror
endif
-#
-# Include saner warnings here, which can catch bugs:
-#
-
-EXTRA_WARNINGS := -Wformat
-EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wformat-security
-EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wformat-y2k
-EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wshadow
-EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Winit-self
-EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wpacked
-EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wredundant-decls
-EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wstrict-aliasing=3
-EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wswitch-default
-EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wswitch-enum
-EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wno-system-headers
-EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wundef
-EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wwrite-strings
-EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wbad-function-cast
-EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wmissing-declarations
-EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wmissing-prototypes
-EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wnested-externs
-EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wold-style-definition
-EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wstrict-prototypes
-EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wdeclaration-after-statement
-
ifeq ("$(origin DEBUG)", "command line")
PERF_DEBUG = $(DEBUG)
endif
@@ -168,7 +149,7 @@ endif
### --- END CONFIGURATION SECTION ---
-BASIC_CFLAGS = -Iutil/include -Iarch/$(ARCH)/include -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE
+BASIC_CFLAGS = -Iutil/include -Iarch/$(ARCH)/include -I$(OUTPUT)/util -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE
BASIC_LDFLAGS =
# Guard against environment variables
@@ -220,6 +201,24 @@ endif
export PERL_PATH
+FLEX = $(CROSS_COMPILE)flex
+BISON= $(CROSS_COMPILE)bison
+
+$(OUTPUT)util/parse-events-flex.c: util/parse-events.l
+ $(QUIET_FLEX)$(FLEX) --header-file=$(OUTPUT)util/parse-events-flex.h -t util/parse-events.l > $(OUTPUT)util/parse-events-flex.c
+
+$(OUTPUT)util/parse-events-bison.c: util/parse-events.y
+ $(QUIET_BISON)$(BISON) -v util/parse-events.y -d -o $(OUTPUT)util/parse-events-bison.c
+
+$(OUTPUT)util/pmu-flex.c: util/pmu.l
+ $(QUIET_FLEX)$(FLEX) --header-file=$(OUTPUT)util/pmu-flex.h -t util/pmu.l > $(OUTPUT)util/pmu-flex.c
+
+$(OUTPUT)util/pmu-bison.c: util/pmu.y
+ $(QUIET_BISON)$(BISON) -v util/pmu.y -d -o $(OUTPUT)util/pmu-bison.c
+
+$(OUTPUT)util/parse-events.o: $(OUTPUT)util/parse-events-flex.c $(OUTPUT)util/parse-events-bison.c
+$(OUTPUT)util/pmu.o: $(OUTPUT)util/pmu-flex.c $(OUTPUT)util/pmu-bison.c
+
LIB_FILE=$(OUTPUT)libperf.a
LIB_H += ../../include/linux/perf_event.h
@@ -235,7 +234,7 @@ LIB_H += util/include/linux/const.h
LIB_H += util/include/linux/ctype.h
LIB_H += util/include/linux/kernel.h
LIB_H += util/include/linux/list.h
-LIB_H += util/include/linux/module.h
+LIB_H += util/include/linux/export.h
LIB_H += util/include/linux/poison.h
LIB_H += util/include/linux/prefetch.h
LIB_H += util/include/linux/rbtree.h
@@ -252,6 +251,8 @@ LIB_H += util/include/asm/uaccess.h
LIB_H += util/include/dwarf-regs.h
LIB_H += util/include/asm/dwarf2.h
LIB_H += util/include/asm/cpufeature.h
+LIB_H += util/include/asm/unistd_32.h
+LIB_H += util/include/asm/unistd_64.h
LIB_H += perf.h
LIB_H += util/annotate.h
LIB_H += util/cache.h
@@ -260,6 +261,7 @@ LIB_H += util/build-id.h
LIB_H += util/debug.h
LIB_H += util/debugfs.h
LIB_H += util/sysfs.h
+LIB_H += util/pmu.h
LIB_H += util/event.h
LIB_H += util/evsel.h
LIB_H += util/evlist.h
@@ -307,6 +309,7 @@ LIB_OBJS += $(OUTPUT)util/config.o
LIB_OBJS += $(OUTPUT)util/ctype.o
LIB_OBJS += $(OUTPUT)util/debugfs.o
LIB_OBJS += $(OUTPUT)util/sysfs.o
+LIB_OBJS += $(OUTPUT)util/pmu.o
LIB_OBJS += $(OUTPUT)util/environment.o
LIB_OBJS += $(OUTPUT)util/event.o
LIB_OBJS += $(OUTPUT)util/evlist.o
@@ -343,6 +346,10 @@ LIB_OBJS += $(OUTPUT)util/session.o
LIB_OBJS += $(OUTPUT)util/thread.o
LIB_OBJS += $(OUTPUT)util/thread_map.o
LIB_OBJS += $(OUTPUT)util/trace-event-parse.o
+LIB_OBJS += $(OUTPUT)util/parse-events-flex.o
+LIB_OBJS += $(OUTPUT)util/parse-events-bison.o
+LIB_OBJS += $(OUTPUT)util/pmu-flex.o
+LIB_OBJS += $(OUTPUT)util/pmu-bison.o
LIB_OBJS += $(OUTPUT)util/trace-event-read.o
LIB_OBJS += $(OUTPUT)util/trace-event-info.o
LIB_OBJS += $(OUTPUT)util/trace-event-scripting.o
@@ -466,22 +473,36 @@ else
# Fedora has /usr/include/slang/slang.h, but ubuntu /usr/include/slang.h
BASIC_CFLAGS += -I/usr/include/slang
EXTLIBS += -lnewt -lslang
- LIB_OBJS += $(OUTPUT)util/ui/setup.o
- LIB_OBJS += $(OUTPUT)util/ui/browser.o
- LIB_OBJS += $(OUTPUT)util/ui/browsers/annotate.o
- LIB_OBJS += $(OUTPUT)util/ui/browsers/hists.o
- LIB_OBJS += $(OUTPUT)util/ui/browsers/map.o
- LIB_OBJS += $(OUTPUT)util/ui/helpline.o
- LIB_OBJS += $(OUTPUT)util/ui/progress.o
- LIB_OBJS += $(OUTPUT)util/ui/util.o
- LIB_H += util/ui/browser.h
- LIB_H += util/ui/browsers/map.h
- LIB_H += util/ui/helpline.h
- LIB_H += util/ui/keysyms.h
- LIB_H += util/ui/libslang.h
- LIB_H += util/ui/progress.h
- LIB_H += util/ui/util.h
- LIB_H += util/ui/ui.h
+ LIB_OBJS += $(OUTPUT)ui/setup.o
+ LIB_OBJS += $(OUTPUT)ui/browser.o
+ LIB_OBJS += $(OUTPUT)ui/browsers/annotate.o
+ LIB_OBJS += $(OUTPUT)ui/browsers/hists.o
+ LIB_OBJS += $(OUTPUT)ui/browsers/map.o
+ LIB_OBJS += $(OUTPUT)ui/helpline.o
+ LIB_OBJS += $(OUTPUT)ui/progress.o
+ LIB_OBJS += $(OUTPUT)ui/util.o
+ LIB_H += ui/browser.h
+ LIB_H += ui/browsers/map.h
+ LIB_H += ui/helpline.h
+ LIB_H += ui/keysyms.h
+ LIB_H += ui/libslang.h
+ LIB_H += ui/progress.h
+ LIB_H += ui/util.h
+ LIB_H += ui/ui.h
+ endif
+endif
+
+ifdef NO_GTK2
+ BASIC_CFLAGS += -DNO_GTK2_SUPPORT
+else
+ FLAGS_GTK2=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) $(shell pkg-config --libs --cflags gtk+-2.0)
+ ifneq ($(call try-cc,$(SOURCE_GTK2),$(FLAGS_GTK2)),y)
+ msg := $(warning GTK2 not found, disables GTK2 support. Please install gtk2-devel or libgtk2.0-dev);
+ BASIC_CFLAGS += -DNO_GTK2_SUPPORT
+ else
+ BASIC_CFLAGS += $(shell pkg-config --cflags gtk+-2.0)
+ EXTLIBS += $(shell pkg-config --libs gtk+-2.0)
+ LIB_OBJS += $(OUTPUT)ui/gtk/browser.o
endif
endif
@@ -624,16 +645,6 @@ else
endif
endif
-ifneq ($(findstring $(MAKEFLAGS),s),s)
-ifndef V
- QUIET_CC = @echo ' ' CC $@;
- QUIET_AR = @echo ' ' AR $@;
- QUIET_LINK = @echo ' ' LINK $@;
- QUIET_MKDIR = @echo ' ' MKDIR $@;
- QUIET_GEN = @echo ' ' GEN $@;
-endif
-endif
-
ifdef ASCIIDOC8
export ASCIIDOC8
endif
@@ -711,12 +722,28 @@ $(OUTPUT)perf.o perf.spec \
$(SCRIPTS) \
: $(OUTPUT)PERF-VERSION-FILE
+.SUFFIXES:
+.SUFFIXES: .o .c .S .s
+
+# These two need to be here so that when O= is not used they take precedence
+# over the general rule for .o
+
+$(OUTPUT)util/%-flex.o: $(OUTPUT)util/%-flex.c $(OUTPUT)PERF-CFLAGS
+ $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -Iutil/ -Wno-redundant-decls -Wno-switch-default -Wno-unused-function $<
+
+$(OUTPUT)util/%-bison.o: $(OUTPUT)util/%-bison.c $(OUTPUT)PERF-CFLAGS
+ $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DYYENABLE_NLS=0 -DYYLTYPE_IS_TRIVIAL=0 -Iutil/ -Wno-redundant-decls -Wno-switch-default -Wno-unused-function $<
+
$(OUTPUT)%.o: %.c $(OUTPUT)PERF-CFLAGS
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $<
+$(OUTPUT)%.i: %.c $(OUTPUT)PERF-CFLAGS
+ $(QUIET_CC)$(CC) -o $@ -E $(ALL_CFLAGS) $<
$(OUTPUT)%.s: %.c $(OUTPUT)PERF-CFLAGS
- $(QUIET_CC)$(CC) -S $(ALL_CFLAGS) $<
+ $(QUIET_CC)$(CC) -o $@ -S $(ALL_CFLAGS) $<
$(OUTPUT)%.o: %.S
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $<
+$(OUTPUT)%.s: %.S
+ $(QUIET_CC)$(CC) -o $@ -E $(ALL_CFLAGS) $<
$(OUTPUT)util/exec_cmd.o: util/exec_cmd.c $(OUTPUT)PERF-CFLAGS
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \
@@ -728,16 +755,16 @@ $(OUTPUT)util/exec_cmd.o: util/exec_cmd.c $(OUTPUT)PERF-CFLAGS
$(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $<
-$(OUTPUT)util/ui/browser.o: util/ui/browser.c $(OUTPUT)PERF-CFLAGS
+$(OUTPUT)ui/browser.o: ui/browser.c $(OUTPUT)PERF-CFLAGS
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $<
-$(OUTPUT)util/ui/browsers/annotate.o: util/ui/browsers/annotate.c $(OUTPUT)PERF-CFLAGS
+$(OUTPUT)ui/browsers/annotate.o: ui/browsers/annotate.c $(OUTPUT)PERF-CFLAGS
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $<
-$(OUTPUT)util/ui/browsers/hists.o: util/ui/browsers/hists.c $(OUTPUT)PERF-CFLAGS
+$(OUTPUT)ui/browsers/hists.o: ui/browsers/hists.c $(OUTPUT)PERF-CFLAGS
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $<
-$(OUTPUT)util/ui/browsers/map.o: util/ui/browsers/map.c $(OUTPUT)PERF-CFLAGS
+$(OUTPUT)ui/browsers/map.o: ui/browsers/map.c $(OUTPUT)PERF-CFLAGS
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $<
$(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS
@@ -915,6 +942,7 @@ clean:
$(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope*
$(MAKE) -C Documentation/ clean
$(RM) $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)PERF-CFLAGS
+ $(RM) $(OUTPUT)util/*-{bison,flex}*
$(python-clean)
.PHONY: all install clean strip
diff --git a/tools/perf/arch/powerpc/util/header.c b/tools/perf/arch/powerpc/util/header.c
index eba80c292945..2f7073d107fd 100644
--- a/tools/perf/arch/powerpc/util/header.c
+++ b/tools/perf/arch/powerpc/util/header.c
@@ -25,7 +25,7 @@ get_cpuid(char *buffer, size_t sz)
pvr = mfspr(SPRN_PVR);
- nb = snprintf(buffer, sz, "%lu,%lu$", PVR_VER(pvr), PVR_REV(pvr));
+ nb = scnprintf(buffer, sz, "%lu,%lu$", PVR_VER(pvr), PVR_REV(pvr));
/* look for end marker to ensure the entire data fit */
if (strchr(buffer, '$')) {
diff --git a/tools/perf/arch/x86/util/header.c b/tools/perf/arch/x86/util/header.c
index f94006068d2b..146d12a1cec0 100644
--- a/tools/perf/arch/x86/util/header.c
+++ b/tools/perf/arch/x86/util/header.c
@@ -48,7 +48,7 @@ get_cpuid(char *buffer, size_t sz)
if (family >= 0x6)
model += ((a >> 16) & 0xf) << 4;
}
- nb = snprintf(buffer, sz, "%s,%u,%u,%u$", vendor, family, model, step);
+ nb = scnprintf(buffer, sz, "%s,%u,%u,%u$", vendor, family, model, step);
/* look for end marker to ensure the entire data fit */
if (strchr(buffer, '$')) {
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c
index 4f19513d7dda..d29d350fb2b7 100644
--- a/tools/perf/builtin-diff.c
+++ b/tools/perf/builtin-diff.c
@@ -24,6 +24,11 @@ static char diff__default_sort_order[] = "dso,symbol";
static bool force;
static bool show_displacement;
+struct perf_diff {
+ struct perf_tool tool;
+ struct perf_session *session;
+};
+
static int hists__add_entry(struct hists *self,
struct addr_location *al, u64 period)
{
@@ -32,12 +37,14 @@ static int hists__add_entry(struct hists *self,
return -ENOMEM;
}
-static int diff__process_sample_event(struct perf_tool *tool __used,
+static int diff__process_sample_event(struct perf_tool *tool,
union perf_event *event,
struct perf_sample *sample,
struct perf_evsel *evsel __used,
struct machine *machine)
{
+ struct perf_diff *_diff = container_of(tool, struct perf_diff, tool);
+ struct perf_session *session = _diff->session;
struct addr_location al;
if (perf_event__preprocess_sample(event, machine, &al, sample, NULL) < 0) {
@@ -49,24 +56,26 @@ static int diff__process_sample_event(struct perf_tool *tool __used,
if (al.filtered || al.sym == NULL)
return 0;
- if (hists__add_entry(&evsel->hists, &al, sample->period)) {
+ if (hists__add_entry(&session->hists, &al, sample->period)) {
pr_warning("problem incrementing symbol period, skipping event\n");
return -1;
}
- evsel->hists.stats.total_period += sample->period;
+ session->hists.stats.total_period += sample->period;
return 0;
}
-static struct perf_tool perf_diff = {
- .sample = diff__process_sample_event,
- .mmap = perf_event__process_mmap,
- .comm = perf_event__process_comm,
- .exit = perf_event__process_task,
- .fork = perf_event__process_task,
- .lost = perf_event__process_lost,
- .ordered_samples = true,
- .ordering_requires_timestamps = true,
+static struct perf_diff diff = {
+ .tool = {
+ .sample = diff__process_sample_event,
+ .mmap = perf_event__process_mmap,
+ .comm = perf_event__process_comm,
+ .exit = perf_event__process_task,
+ .fork = perf_event__process_task,
+ .lost = perf_event__process_lost,
+ .ordered_samples = true,
+ .ordering_requires_timestamps = true,
+ },
};
static void perf_session__insert_hist_entry_by_name(struct rb_root *root,
@@ -107,12 +116,6 @@ static void hists__resort_entries(struct hists *self)
self->entries = tmp;
}
-static void hists__set_positions(struct hists *self)
-{
- hists__output_resort(self);
- hists__resort_entries(self);
-}
-
static struct hist_entry *hists__find_entry(struct hists *self,
struct hist_entry *he)
{
@@ -146,30 +149,37 @@ static void hists__match(struct hists *older, struct hists *newer)
static int __cmd_diff(void)
{
int ret, i;
+#define older (session[0])
+#define newer (session[1])
struct perf_session *session[2];
- session[0] = perf_session__new(input_old, O_RDONLY, force, false, &perf_diff);
- session[1] = perf_session__new(input_new, O_RDONLY, force, false, &perf_diff);
+ older = perf_session__new(input_old, O_RDONLY, force, false,
+ &diff.tool);
+ newer = perf_session__new(input_new, O_RDONLY, force, false,
+ &diff.tool);
if (session[0] == NULL || session[1] == NULL)
return -ENOMEM;
for (i = 0; i < 2; ++i) {
- ret = perf_session__process_events(session[i], &perf_diff);
+ diff.session = session[i];
+ ret = perf_session__process_events(session[i], &diff.tool);
if (ret)
goto out_delete;
+ hists__output_resort(&session[i]->hists);
}
- hists__output_resort(&session[1]->hists);
if (show_displacement)
- hists__set_positions(&session[0]->hists);
+ hists__resort_entries(&older->hists);
- hists__match(&session[0]->hists, &session[1]->hists);
- hists__fprintf(&session[1]->hists, &session[0]->hists,
+ hists__match(&older->hists, &newer->hists);
+ hists__fprintf(&newer->hists, &older->hists,
show_displacement, true, 0, 0, stdout);
out_delete:
for (i = 0; i < 2; ++i)
perf_session__delete(session[i]);
return ret;
+#undef older
+#undef newer
}
static const char * const diff_usage[] = {
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 75d230fef202..10b1f1f25ed7 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -245,7 +245,7 @@ try_again:
* based cpu-clock-tick sw counter, which
* is always available even if no PMU support:
*/
- if (attr->type == PERF_TYPE_HARDWARE
+ if (err == ENOENT && attr->type == PERF_TYPE_HARDWARE
&& attr->config == PERF_COUNT_HW_CPU_CYCLES) {
if (verbose)
@@ -473,6 +473,9 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
if (!have_tracepoints(&evsel_list->entries))
perf_header__clear_feat(&session->header, HEADER_TRACE_INFO);
+ if (!rec->opts.branch_stack)
+ perf_header__clear_feat(&session->header, HEADER_BRANCH_STACK);
+
if (!rec->file_new) {
err = perf_session__read_header(session, output);
if (err < 0)
@@ -638,6 +641,90 @@ out_delete_session:
return err;
}
+#define BRANCH_OPT(n, m) \
+ { .name = n, .mode = (m) }
+
+#define BRANCH_END { .name = NULL }
+
+struct branch_mode {
+ const char *name;
+ int mode;
+};
+
+static const struct branch_mode branch_modes[] = {
+ BRANCH_OPT("u", PERF_SAMPLE_BRANCH_USER),
+ BRANCH_OPT("k", PERF_SAMPLE_BRANCH_KERNEL),
+ BRANCH_OPT("hv", PERF_SAMPLE_BRANCH_HV),
+ BRANCH_OPT("any", PERF_SAMPLE_BRANCH_ANY),
+ BRANCH_OPT("any_call", PERF_SAMPLE_BRANCH_ANY_CALL),
+ BRANCH_OPT("any_ret", PERF_SAMPLE_BRANCH_ANY_RETURN),
+ BRANCH_OPT("ind_call", PERF_SAMPLE_BRANCH_IND_CALL),
+ BRANCH_END
+};
+
+static int
+parse_branch_stack(const struct option *opt, const char *str, int unset)
+{
+#define ONLY_PLM \
+ (PERF_SAMPLE_BRANCH_USER |\
+ PERF_SAMPLE_BRANCH_KERNEL |\
+ PERF_SAMPLE_BRANCH_HV)
+
+ uint64_t *mode = (uint64_t *)opt->value;
+ const struct branch_mode *br;
+ char *s, *os = NULL, *p;
+ int ret = -1;
+
+ if (unset)
+ return 0;
+
+ /*
+ * cannot set it twice, -b + --branch-filter for instance
+ */
+ if (*mode)
+ return -1;
+
+ /* str may be NULL in case no arg is passed to -b */
+ if (str) {
+ /* because str is read-only */
+ s = os = strdup(str);
+ if (!s)
+ return -1;
+
+ for (;;) {
+ p = strchr(s, ',');
+ if (p)
+ *p = '\0';
+
+ for (br = branch_modes; br->name; br++) {
+ if (!strcasecmp(s, br->name))
+ break;
+ }
+ if (!br->name) {
+ ui__warning("unknown branch filter %s,"
+ " check man page\n", s);
+ goto error;
+ }
+
+ *mode |= br->mode;
+
+ if (!p)
+ break;
+
+ s = p + 1;
+ }
+ }
+ ret = 0;
+
+ /* default to any branch */
+ if ((*mode & ~ONLY_PLM) == 0) {
+ *mode = PERF_SAMPLE_BRANCH_ANY;
+ }
+error:
+ free(os);
+ return ret;
+}
+
static const char * const record_usage[] = {
"perf record [<options>] [<command>]",
"perf record [<options>] -- <command> [<options>]",
@@ -727,6 +814,14 @@ const struct option record_options[] = {
"monitor event in cgroup name only",
parse_cgroups),
OPT_STRING('u', "uid", &record.uid_str, "user", "user to profile"),
+
+ OPT_CALLBACK_NOOPT('b', "branch-any", &record.opts.branch_stack,
+ "branch any", "sample any taken branches",
+ parse_branch_stack),
+
+ OPT_CALLBACK('j', "branch-filter", &record.opts.branch_stack,
+ "branch filter mask", "branch stack filter modes",
+ parse_branch_stack),
OPT_END()
};
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 25d34d483e49..cec2b8cee80c 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -40,7 +40,7 @@ struct perf_report {
struct perf_tool tool;
struct perf_session *session;
char const *input_name;
- bool force, use_tui, use_stdio;
+ bool force, use_tui, use_gtk, use_stdio;
bool hide_unresolved;
bool dont_use_callchains;
bool show_full_info;
@@ -50,9 +50,86 @@ struct perf_report {
const char *pretty_printing_style;
symbol_filter_t annotate_init;
const char *cpu_list;
+ const char *symbol_filter_str;
DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
};
+static int perf_report__add_branch_hist_entry(struct perf_tool *tool,
+ struct addr_location *al,
+ struct perf_sample *sample,
+ struct perf_evsel *evsel,
+ struct machine *machine)
+{
+ struct perf_report *rep = container_of(tool, struct perf_report, tool);
+ struct symbol *parent = NULL;
+ int err = 0;
+ unsigned i;
+ struct hist_entry *he;
+ struct branch_info *bi, *bx;
+
+ if ((sort__has_parent || symbol_conf.use_callchain)
+ && sample->callchain) {
+ err = machine__resolve_callchain(machine, evsel, al->thread,
+ sample->callchain, &parent);
+ if (err)
+ return err;
+ }
+
+ bi = machine__resolve_bstack(machine, al->thread,
+ sample->branch_stack);
+ if (!bi)
+ return -ENOMEM;
+
+ for (i = 0; i < sample->branch_stack->nr; i++) {
+ if (rep->hide_unresolved && !(bi[i].from.sym && bi[i].to.sym))
+ continue;
+ /*
+ * The report shows the percentage of total branches captured
+ * and not events sampled. Thus we use a pseudo period of 1.
+ */
+ he = __hists__add_branch_entry(&evsel->hists, al, parent,
+ &bi[i], 1);
+ if (he) {
+ struct annotation *notes;
+ err = -ENOMEM;
+ bx = he->branch_info;
+ if (bx->from.sym && use_browser > 0) {
+ notes = symbol__annotation(bx->from.sym);
+ if (!notes->src
+ && symbol__alloc_hist(bx->from.sym) < 0)
+ goto out;
+
+ err = symbol__inc_addr_samples(bx->from.sym,
+ bx->from.map,
+ evsel->idx,
+ bx->from.al_addr);
+ if (err)
+ goto out;
+ }
+
+ if (bx->to.sym && use_browser > 0) {
+ notes = symbol__annotation(bx->to.sym);
+ if (!notes->src
+ && symbol__alloc_hist(bx->to.sym) < 0)
+ goto out;
+
+ err = symbol__inc_addr_samples(bx->to.sym,
+ bx->to.map,
+ evsel->idx,
+ bx->to.al_addr);
+ if (err)
+ goto out;
+ }
+ evsel->hists.stats.total_period += 1;
+ hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE);
+ err = 0;
+ } else
+ return -ENOMEM;
+ }
+out:
+ return err;
+}
+
static int perf_evsel__add_hist_entry(struct perf_evsel *evsel,
struct addr_location *al,
struct perf_sample *sample,
@@ -126,14 +203,21 @@ static int process_sample_event(struct perf_tool *tool,
if (rep->cpu_list && !test_bit(sample->cpu, rep->cpu_bitmap))
return 0;
- if (al.map != NULL)
- al.map->dso->hit = 1;
+ if (sort__branch_mode == 1) {
+ if (perf_report__add_branch_hist_entry(tool, &al, sample,
+ evsel, machine)) {
+ pr_debug("problem adding lbr entry, skipping event\n");
+ return -1;
+ }
+ } else {
+ if (al.map != NULL)
+ al.map->dso->hit = 1;
- if (perf_evsel__add_hist_entry(evsel, &al, sample, machine)) {
- pr_debug("problem incrementing symbol period, skipping event\n");
- return -1;
+ if (perf_evsel__add_hist_entry(evsel, &al, sample, machine)) {
+ pr_debug("problem incrementing symbol period, skipping event\n");
+ return -1;
+ }
}
-
return 0;
}
@@ -188,6 +272,15 @@ static int perf_report__setup_sample_type(struct perf_report *rep)
}
}
+ if (sort__branch_mode == 1) {
+ if (!(self->sample_type & PERF_SAMPLE_BRANCH_STACK)) {
+ fprintf(stderr, "selected -b but no branch data."
+ " Did you call perf record without"
+ " -b?\n");
+ return -1;
+ }
+ }
+
return 0;
}
@@ -203,12 +296,15 @@ static size_t hists__fprintf_nr_sample_events(struct hists *self,
{
size_t ret;
char unit;
- unsigned long nr_events = self->stats.nr_events[PERF_RECORD_SAMPLE];
+ unsigned long nr_samples = self->stats.nr_events[PERF_RECORD_SAMPLE];
+ u64 nr_events = self->stats.total_period;
- nr_events = convert_unit(nr_events, &unit);
- ret = fprintf(fp, "# Events: %lu%c", nr_events, unit);
+ nr_samples = convert_unit(nr_samples, &unit);
+ ret = fprintf(fp, "# Samples: %lu%c", nr_samples, unit);
if (evname != NULL)
- ret += fprintf(fp, " %s", evname);
+ ret += fprintf(fp, " of event '%s'", evname);
+
+ ret += fprintf(fp, "\n# Event count (approx.): %lu", nr_events);
return ret + fprintf(fp, "\n#\n");
}
@@ -246,7 +342,7 @@ static int __cmd_report(struct perf_report *rep)
{
int ret = -EINVAL;
u64 nr_samples;
- struct perf_session *session;
+ struct perf_session *session = rep->session;
struct perf_evsel *pos;
struct map *kernel_map;
struct kmap *kernel_kmap;
@@ -254,13 +350,6 @@ static int __cmd_report(struct perf_report *rep)
signal(SIGINT, sig_handler);
- session = perf_session__new(rep->input_name, O_RDONLY,
- rep->force, false, &rep->tool);
- if (session == NULL)
- return -ENOMEM;
-
- rep->session = session;
-
if (rep->cpu_list) {
ret = perf_session__cpu_bitmap(session, rep->cpu_list,
rep->cpu_bitmap);
@@ -315,6 +404,9 @@ static int __cmd_report(struct perf_report *rep)
list_for_each_entry(pos, &session->evlist->entries, node) {
struct hists *hists = &pos->hists;
+ if (pos->idx == 0)
+ hists->symbol_filter_str = rep->symbol_filter_str;
+
hists__collapse_resort(hists);
hists__output_resort(hists);
nr_samples += hists->stats.nr_events[PERF_RECORD_SAMPLE];
@@ -326,8 +418,13 @@ static int __cmd_report(struct perf_report *rep)
}
if (use_browser > 0) {
- perf_evlist__tui_browse_hists(session->evlist, help,
- NULL, NULL, 0);
+ if (use_browser == 1) {
+ perf_evlist__tui_browse_hists(session->evlist, help,
+ NULL, NULL, 0);
+ } else if (use_browser == 2) {
+ perf_evlist__gtk_browse_hists(session->evlist, help,
+ NULL, NULL, 0);
+ }
} else
perf_evlist__tty_browse_hists(session->evlist, rep, help);
@@ -427,9 +524,19 @@ setup:
return 0;
}
+static int
+parse_branch_mode(const struct option *opt __used, const char *str __used, int unset)
+{
+ sort__branch_mode = !unset;
+ return 0;
+}
+
int cmd_report(int argc, const char **argv, const char *prefix __used)
{
+ struct perf_session *session;
struct stat st;
+ bool has_br_stack = false;
+ int ret = -1;
char callchain_default_opt[] = "fractal,0.5,callee";
const char * const report_usage[] = {
"perf report [<options>]",
@@ -474,10 +581,12 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
OPT_STRING(0, "pretty", &report.pretty_printing_style, "key",
"pretty printing style key: normal raw"),
OPT_BOOLEAN(0, "tui", &report.use_tui, "Use the TUI interface"),
+ OPT_BOOLEAN(0, "gtk", &report.use_gtk, "Use the GTK2 interface"),
OPT_BOOLEAN(0, "stdio", &report.use_stdio,
"Use the stdio interface"),
OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
- "sort by key(s): pid, comm, dso, symbol, parent"),
+ "sort by key(s): pid, comm, dso, symbol, parent, dso_to,"
+ " dso_from, symbol_to, symbol_from, mispredict"),
OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization,
"Show sample percentage for different cpu modes"),
OPT_STRING('p', "parent", &parent_pattern, "regex",
@@ -495,6 +604,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
"only consider symbols in these comms"),
OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]",
"only consider these symbols"),
+ OPT_STRING(0, "symbol-filter", &report.symbol_filter_str, "filter",
+ "only show symbols that (partially) match with this filter"),
OPT_STRING('w', "column-widths", &symbol_conf.col_width_list_str,
"width[,width...]",
"don't try to adjust column width, use these fixed values"),
@@ -517,6 +628,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
"Specify disassembler style (e.g. -M intel for intel syntax)"),
OPT_BOOLEAN(0, "show-total-period", &symbol_conf.show_total_period,
"Show a column with the sum of periods"),
+ OPT_CALLBACK_NOOPT('b', "branch-stack", &sort__branch_mode, "",
+ "use branch records for histogram filling", parse_branch_mode),
OPT_END()
};
@@ -526,6 +639,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
use_browser = 0;
else if (report.use_tui)
use_browser = 1;
+ else if (report.use_gtk)
+ use_browser = 2;
if (report.inverted_callchain)
callchain_param.order = ORDER_CALLER;
@@ -536,11 +651,39 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
else
report.input_name = "perf.data";
}
+ session = perf_session__new(report.input_name, O_RDONLY,
+ report.force, false, &report.tool);
+ if (session == NULL)
+ return -ENOMEM;
- if (strcmp(report.input_name, "-") != 0)
- setup_browser(true);
- else
+ report.session = session;
+
+ has_br_stack = perf_header__has_feat(&session->header,
+ HEADER_BRANCH_STACK);
+
+ if (sort__branch_mode == -1 && has_br_stack)
+ sort__branch_mode = 1;
+
+ /* sort__branch_mode could be 0 if --no-branch-stack */
+ if (sort__branch_mode == 1) {
+ /*
+ * if no sort_order is provided, then specify
+ * branch-mode specific order
+ */
+ if (sort_order == default_sort_order)
+ sort_order = "comm,dso_from,symbol_from,"
+ "dso_to,symbol_to";
+
+ }
+
+ if (strcmp(report.input_name, "-") != 0) {
+ if (report.use_gtk)
+ perf_gtk_setup_browser(argc, argv, true);
+ else
+ setup_browser(true);
+ } else {
use_browser = 0;
+ }
/*
* Only in the newt browser we are doing integrated annotation,
@@ -568,13 +711,13 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
}
if (symbol__init() < 0)
- return -1;
+ goto error;
setup_sorting(report_usage, options);
if (parent_pattern != default_parent_pattern) {
if (sort_dimension__add("parent") < 0)
- return -1;
+ goto error;
/*
* Only show the parent fields if we explicitly
@@ -586,15 +729,31 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
} else
symbol_conf.exclude_other = false;
- /*
- * Any (unrecognized) arguments left?
- */
- if (argc)
- usage_with_options(report_usage, options);
+ if (argc) {
+ /*
+ * Special case: if there's an argument left then assume that
+ * it's a symbol filter:
+ */
+ if (argc > 1)
+ usage_with_options(report_usage, options);
+
+ report.symbol_filter_str = argv[0];
+ }
- sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso", stdout);
sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, "comm", stdout);
- sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list, "symbol", stdout);
- return __cmd_report(&report);
+ if (sort__branch_mode == 1) {
+ sort_entry__setup_elide(&sort_dso_from, symbol_conf.dso_from_list, "dso_from", stdout);
+ sort_entry__setup_elide(&sort_dso_to, symbol_conf.dso_to_list, "dso_to", stdout);
+ sort_entry__setup_elide(&sort_sym_from, symbol_conf.sym_from_list, "sym_from", stdout);
+ sort_entry__setup_elide(&sort_sym_to, symbol_conf.sym_to_list, "sym_to", stdout);
+ } else {
+ sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso", stdout);
+ sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list, "symbol", stdout);
+ }
+
+ ret = __cmd_report(&report);
+error:
+ perf_session__delete(session);
+ return ret;
}
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c
index fb8b5f83b4a0..1cad3af4bf4c 100644
--- a/tools/perf/builtin-sched.c
+++ b/tools/perf/builtin-sched.c
@@ -17,6 +17,7 @@
#include "util/debug.h"
#include <sys/prctl.h>
+#include <sys/resource.h>
#include <semaphore.h>
#include <pthread.h>
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c
index ea40e4e8b227..dde9e17c018b 100644
--- a/tools/perf/builtin-stat.c
+++ b/tools/perf/builtin-stat.c
@@ -173,7 +173,7 @@ static struct perf_event_attr very_very_detailed_attrs[] = {
-struct perf_evlist *evsel_list;
+static struct perf_evlist *evsel_list;
static bool system_wide = false;
static int run_idx = 0;
@@ -265,18 +265,18 @@ static double stddev_stats(struct stats *stats)
return sqrt(variance_mean);
}
-struct stats runtime_nsecs_stats[MAX_NR_CPUS];
-struct stats runtime_cycles_stats[MAX_NR_CPUS];
-struct stats runtime_stalled_cycles_front_stats[MAX_NR_CPUS];
-struct stats runtime_stalled_cycles_back_stats[MAX_NR_CPUS];
-struct stats runtime_branches_stats[MAX_NR_CPUS];
-struct stats runtime_cacherefs_stats[MAX_NR_CPUS];
-struct stats runtime_l1_dcache_stats[MAX_NR_CPUS];
-struct stats runtime_l1_icache_stats[MAX_NR_CPUS];
-struct stats runtime_ll_cache_stats[MAX_NR_CPUS];
-struct stats runtime_itlb_cache_stats[MAX_NR_CPUS];
-struct stats runtime_dtlb_cache_stats[MAX_NR_CPUS];
-struct stats walltime_nsecs_stats;
+static struct stats runtime_nsecs_stats[MAX_NR_CPUS];
+static struct stats runtime_cycles_stats[MAX_NR_CPUS];
+static struct stats runtime_stalled_cycles_front_stats[MAX_NR_CPUS];
+static struct stats runtime_stalled_cycles_back_stats[MAX_NR_CPUS];
+static struct stats runtime_branches_stats[MAX_NR_CPUS];
+static struct stats runtime_cacherefs_stats[MAX_NR_CPUS];
+static struct stats runtime_l1_dcache_stats[MAX_NR_CPUS];
+static struct stats runtime_l1_icache_stats[MAX_NR_CPUS];
+static struct stats runtime_ll_cache_stats[MAX_NR_CPUS];
+static struct stats runtime_itlb_cache_stats[MAX_NR_CPUS];
+static struct stats runtime_dtlb_cache_stats[MAX_NR_CPUS];
+static struct stats walltime_nsecs_stats;
static int create_perf_stat_counter(struct perf_evsel *evsel,
struct perf_evsel *first)
@@ -296,7 +296,7 @@ static int create_perf_stat_counter(struct perf_evsel *evsel,
if (system_wide)
return perf_evsel__open_per_cpu(evsel, evsel_list->cpus,
group, group_fd);
- if (!target_pid && !target_tid) {
+ if (!target_pid && !target_tid && (!group || evsel == first)) {
attr->disabled = 1;
attr->enable_on_exec = 1;
}
diff --git a/tools/perf/builtin-test.c b/tools/perf/builtin-test.c
index 3e087ce8daa6..1c5b9801ac61 100644
--- a/tools/perf/builtin-test.c
+++ b/tools/perf/builtin-test.c
@@ -13,6 +13,7 @@
#include "util/parse-events.h"
#include "util/symbol.h"
#include "util/thread_map.h"
+#include "util/pmu.h"
#include "../../include/linux/hw_breakpoint.h"
#include <sys/mman.h>
@@ -650,7 +651,7 @@ static int test__checkevent_raw(struct perf_evlist *evlist)
TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type);
- TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config);
+ TEST_ASSERT_VAL("wrong config", 0x1a == evsel->attr.config);
return 0;
}
@@ -677,6 +678,24 @@ static int test__checkevent_symbolic_name(struct perf_evlist *evlist)
return 0;
}
+static int test__checkevent_symbolic_name_config(struct perf_evlist *evlist)
+{
+ struct perf_evsel *evsel = list_entry(evlist->entries.next,
+ struct perf_evsel, node);
+
+ TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong config",
+ PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config);
+ TEST_ASSERT_VAL("wrong period",
+ 100000 == evsel->attr.sample_period);
+ TEST_ASSERT_VAL("wrong config1",
+ 0 == evsel->attr.config1);
+ TEST_ASSERT_VAL("wrong config2",
+ 1 == evsel->attr.config2);
+ return 0;
+}
+
static int test__checkevent_symbolic_alias(struct perf_evlist *evlist)
{
struct perf_evsel *evsel = list_entry(evlist->entries.next,
@@ -858,6 +877,115 @@ static int test__checkevent_genhw_modifier(struct perf_evlist *evlist)
return test__checkevent_genhw(evlist);
}
+static int test__checkevent_breakpoint_modifier(struct perf_evlist *evlist)
+{
+ struct perf_evsel *evsel = list_entry(evlist->entries.next,
+ struct perf_evsel, node);
+
+ TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+
+ return test__checkevent_breakpoint(evlist);
+}
+
+static int test__checkevent_breakpoint_x_modifier(struct perf_evlist *evlist)
+{
+ struct perf_evsel *evsel = list_entry(evlist->entries.next,
+ struct perf_evsel, node);
+
+ TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+
+ return test__checkevent_breakpoint_x(evlist);
+}
+
+static int test__checkevent_breakpoint_r_modifier(struct perf_evlist *evlist)
+{
+ struct perf_evsel *evsel = list_entry(evlist->entries.next,
+ struct perf_evsel, node);
+
+ TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip);
+
+ return test__checkevent_breakpoint_r(evlist);
+}
+
+static int test__checkevent_breakpoint_w_modifier(struct perf_evlist *evlist)
+{
+ struct perf_evsel *evsel = list_entry(evlist->entries.next,
+ struct perf_evsel, node);
+
+ TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip);
+
+ return test__checkevent_breakpoint_w(evlist);
+}
+
+static int test__checkevent_pmu(struct perf_evlist *evlist)
+{
+
+ struct perf_evsel *evsel = list_entry(evlist->entries.next,
+ struct perf_evsel, node);
+
+ TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong config", 10 == evsel->attr.config);
+ TEST_ASSERT_VAL("wrong config1", 1 == evsel->attr.config1);
+ TEST_ASSERT_VAL("wrong config2", 3 == evsel->attr.config2);
+ TEST_ASSERT_VAL("wrong period", 1000 == evsel->attr.sample_period);
+
+ return 0;
+}
+
+static int test__checkevent_list(struct perf_evlist *evlist)
+{
+ struct perf_evsel *evsel;
+
+ TEST_ASSERT_VAL("wrong number of entries", 3 == evlist->nr_entries);
+
+ /* r1 */
+ evsel = list_entry(evlist->entries.next, struct perf_evsel, node);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config);
+ TEST_ASSERT_VAL("wrong config1", 0 == evsel->attr.config1);
+ TEST_ASSERT_VAL("wrong config2", 0 == evsel->attr.config2);
+ TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+
+ /* syscalls:sys_enter_open:k */
+ evsel = list_entry(evsel->node.next, struct perf_evsel, node);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_TRACEPOINT == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong sample_type",
+ (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | PERF_SAMPLE_CPU) ==
+ evsel->attr.sample_type);
+ TEST_ASSERT_VAL("wrong sample_period", 1 == evsel->attr.sample_period);
+ TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+
+ /* 1:1:hp */
+ evsel = list_entry(evsel->node.next, struct perf_evsel, node);
+ TEST_ASSERT_VAL("wrong type", 1 == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config);
+ TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip);
+
+ return 0;
+}
+
static struct test__event_st {
const char *name;
__u32 type;
@@ -872,7 +1000,7 @@ static struct test__event_st {
.check = test__checkevent_tracepoint_multi,
},
{
- .name = "r1",
+ .name = "r1a",
.check = test__checkevent_raw,
},
{
@@ -884,6 +1012,10 @@ static struct test__event_st {
.check = test__checkevent_symbolic_name,
},
{
+ .name = "cycles/period=100000,config2/",
+ .check = test__checkevent_symbolic_name_config,
+ },
+ {
.name = "faults",
.check = test__checkevent_symbolic_alias,
},
@@ -916,7 +1048,7 @@ static struct test__event_st {
.check = test__checkevent_tracepoint_multi_modifier,
},
{
- .name = "r1:kp",
+ .name = "r1a:kp",
.check = test__checkevent_raw_modifier,
},
{
@@ -935,6 +1067,30 @@ static struct test__event_st {
.name = "L1-dcache-load-miss:kp",
.check = test__checkevent_genhw_modifier,
},
+ {
+ .name = "mem:0:u",
+ .check = test__checkevent_breakpoint_modifier,
+ },
+ {
+ .name = "mem:0:x:k",
+ .check = test__checkevent_breakpoint_x_modifier,
+ },
+ {
+ .name = "mem:0:r:hp",
+ .check = test__checkevent_breakpoint_r_modifier,
+ },
+ {
+ .name = "mem:0:w:up",
+ .check = test__checkevent_breakpoint_w_modifier,
+ },
+ {
+ .name = "cpu/config=10,config1,config2=3,period=1000/u",
+ .check = test__checkevent_pmu,
+ },
+ {
+ .name = "r1,syscalls:sys_enter_open:k,1:1:hp",
+ .check = test__checkevent_list,
+ },
};
#define TEST__EVENTS_CNT (sizeof(test__events) / sizeof(struct test__event_st))
@@ -960,10 +1116,9 @@ static int test__parse_events(void)
}
ret = e->check(evlist);
+ perf_evlist__delete(evlist);
if (ret)
break;
-
- perf_evlist__delete(evlist);
}
return ret;
@@ -1462,6 +1617,11 @@ static int test__rdpmc(void)
#endif
+static int test__perf_pmu(void)
+{
+ return perf_pmu__test();
+}
+
static struct test {
const char *desc;
int (*func)(void);
@@ -1497,6 +1657,10 @@ static struct test {
.func = test__PERF_RECORD,
},
{
+ .desc = "Test perf pmu format parsing",
+ .func = test__perf_pmu,
+ },
+ {
.func = NULL,
},
};
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index e3c63aef8efc..8ef59f8262bb 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -42,6 +42,7 @@
#include "util/debug.h"
#include <assert.h>
+#include <elf.h>
#include <fcntl.h>
#include <stdio.h>
@@ -59,6 +60,7 @@
#include <sys/prctl.h>
#include <sys/wait.h>
#include <sys/uio.h>
+#include <sys/utsname.h>
#include <sys/mman.h>
#include <linux/unistd.h>
@@ -162,12 +164,40 @@ static void __zero_source_counters(struct hist_entry *he)
symbol__annotate_zero_histograms(sym);
}
+static void ui__warn_map_erange(struct map *map, struct symbol *sym, u64 ip)
+{
+ struct utsname uts;
+ int err = uname(&uts);
+
+ ui__warning("Out of bounds address found:\n\n"
+ "Addr: %" PRIx64 "\n"
+ "DSO: %s %c\n"
+ "Map: %" PRIx64 "-%" PRIx64 "\n"
+ "Symbol: %" PRIx64 "-%" PRIx64 " %c %s\n"
+ "Arch: %s\n"
+ "Kernel: %s\n"
+ "Tools: %s\n\n"
+ "Not all samples will be on the annotation output.\n\n"
+ "Please report to linux-kernel@vger.kernel.org\n",
+ ip, map->dso->long_name, dso__symtab_origin(map->dso),
+ map->start, map->end, sym->start, sym->end,
+ sym->binding == STB_GLOBAL ? 'g' :
+ sym->binding == STB_LOCAL ? 'l' : 'w', sym->name,
+ err ? "[unknown]" : uts.machine,
+ err ? "[unknown]" : uts.release, perf_version_string);
+ if (use_browser <= 0)
+ sleep(5);
+
+ map->erange_warned = true;
+}
+
static void perf_top__record_precise_ip(struct perf_top *top,
struct hist_entry *he,
int counter, u64 ip)
{
struct annotation *notes;
struct symbol *sym;
+ int err;
if (he == NULL || he->ms.sym == NULL ||
((top->sym_filter_entry == NULL ||
@@ -189,9 +219,12 @@ static void perf_top__record_precise_ip(struct perf_top *top,
}
ip = he->ms.map->map_ip(he->ms.map, ip);
- symbol__inc_addr_samples(sym, he->ms.map, counter, ip);
+ err = symbol__inc_addr_samples(sym, he->ms.map, counter, ip);
pthread_mutex_unlock(&notes->lock);
+
+ if (err == -ERANGE && !he->ms.map->erange_warned)
+ ui__warn_map_erange(he->ms.map, sym, ip);
}
static void perf_top__show_details(struct perf_top *top)
@@ -615,6 +648,7 @@ process_hotkey:
/* Tag samples to be skipped. */
static const char *skip_symbols[] = {
+ "intel_idle",
"default_idle",
"native_safe_halt",
"cpu_idle",
diff --git a/tools/perf/config/feature-tests.mak b/tools/perf/config/feature-tests.mak
index 6170fd2531b5..d9084e03ce56 100644
--- a/tools/perf/config/feature-tests.mak
+++ b/tools/perf/config/feature-tests.mak
@@ -65,6 +65,21 @@ int main(void)
endef
endif
+ifndef NO_GTK2
+define SOURCE_GTK2
+#pragma GCC diagnostic ignored \"-Wstrict-prototypes\"
+#include <gtk/gtk.h>
+#pragma GCC diagnostic error \"-Wstrict-prototypes\"
+
+int main(int argc, char *argv[])
+{
+ gtk_init(&argc, &argv);
+
+ return 0;
+}
+endef
+endif
+
ifndef NO_LIBPERL
define SOURCE_PERL_EMBED
#include <EXTERN.h>
diff --git a/tools/perf/perf.h b/tools/perf/perf.h
index f0227e93665d..89e3355ab173 100644
--- a/tools/perf/perf.h
+++ b/tools/perf/perf.h
@@ -10,6 +10,9 @@ void get_term_dimensions(struct winsize *ws);
#define rmb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory")
#define cpu_relax() asm volatile("rep; nop" ::: "memory");
#define CPUINFO_PROC "model name"
+#ifndef __NR_perf_event_open
+# define __NR_perf_event_open 336
+#endif
#endif
#if defined(__x86_64__)
@@ -17,6 +20,9 @@ void get_term_dimensions(struct winsize *ws);
#define rmb() asm volatile("lfence" ::: "memory")
#define cpu_relax() asm volatile("rep; nop" ::: "memory");
#define CPUINFO_PROC "model name"
+#ifndef __NR_perf_event_open
+# define __NR_perf_event_open 298
+#endif
#endif
#ifdef __powerpc__
@@ -179,6 +185,23 @@ struct ip_callchain {
u64 ips[0];
};
+struct branch_flags {
+ u64 mispred:1;
+ u64 predicted:1;
+ u64 reserved:62;
+};
+
+struct branch_entry {
+ u64 from;
+ u64 to;
+ struct branch_flags flags;
+};
+
+struct branch_stack {
+ u64 nr;
+ struct branch_entry entries[0];
+};
+
extern bool perf_host, perf_guest;
extern const char perf_version_string[];
@@ -205,6 +228,7 @@ struct perf_record_opts {
unsigned int freq;
unsigned int mmap_pages;
unsigned int user_freq;
+ int branch_stack;
u64 default_interval;
u64 user_interval;
const char *cpu_list;
diff --git a/tools/perf/util/ui/browser.c b/tools/perf/ui/browser.c
index 556829124b02..a1b140cf75ac 100644
--- a/tools/perf/util/ui/browser.c
+++ b/tools/perf/ui/browser.c
@@ -27,9 +27,12 @@ static int ui_browser__percent_color(struct ui_browser *browser,
return HE_COLORSET_NORMAL;
}
-void ui_browser__set_color(struct ui_browser *self __used, int color)
+int ui_browser__set_color(struct ui_browser *browser, int color)
{
+ int ret = browser->current_color;
+ browser->current_color = color;
SLsmg_set_color(color);
+ return ret;
}
void ui_browser__set_percent_color(struct ui_browser *self,
@@ -503,6 +506,12 @@ static struct ui_browser__colorset {
.bg = "default",
},
{
+ .colorset = HE_COLORSET_ADDR,
+ .name = "addr",
+ .fg = "magenta",
+ .bg = "default",
+ },
+ {
.name = NULL,
}
};
diff --git a/tools/perf/util/ui/browser.h b/tools/perf/ui/browser.h
index 84d761b730c1..2550277db9f9 100644
--- a/tools/perf/util/ui/browser.h
+++ b/tools/perf/ui/browser.h
@@ -10,11 +10,13 @@
#define HE_COLORSET_NORMAL 52
#define HE_COLORSET_SELECTED 53
#define HE_COLORSET_CODE 54
+#define HE_COLORSET_ADDR 55
struct ui_browser {
u64 index, top_idx;
void *top, *entries;
u16 y, x, width, height;
+ int current_color;
void *priv;
const char *title;
char *helpline;
@@ -27,7 +29,7 @@ struct ui_browser {
bool use_navkeypressed;
};
-void ui_browser__set_color(struct ui_browser *self, int color);
+int ui_browser__set_color(struct ui_browser *browser, int color);
void ui_browser__set_percent_color(struct ui_browser *self,
double percent, bool current);
bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row);
@@ -49,6 +51,8 @@ int ui_browser__warning(struct ui_browser *browser, int timeout,
const char *format, ...);
int ui_browser__help_window(struct ui_browser *browser, const char *text);
bool ui_browser__dialog_yesno(struct ui_browser *browser, const char *text);
+int ui_browser__input_window(const char *title, const char *text, char *input,
+ const char *exit_msg, int delay_sec);
void ui_browser__argv_seek(struct ui_browser *browser, off_t offset, int whence);
unsigned int ui_browser__argv_refresh(struct ui_browser *browser);
diff --git a/tools/perf/util/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c
index 295a9c93f945..4db5186472b5 100644
--- a/tools/perf/util/ui/browsers/annotate.c
+++ b/tools/perf/ui/browsers/annotate.c
@@ -1,13 +1,13 @@
-#include "../../util.h"
+#include "../../util/util.h"
#include "../browser.h"
#include "../helpline.h"
#include "../libslang.h"
#include "../ui.h"
#include "../util.h"
-#include "../../annotate.h"
-#include "../../hist.h"
-#include "../../sort.h"
-#include "../../symbol.h"
+#include "../../util/annotate.h"
+#include "../../util/hist.h"
+#include "../../util/sort.h"
+#include "../../util/symbol.h"
#include <pthread.h>
#include <newt.h>
@@ -16,9 +16,13 @@ struct annotate_browser {
struct rb_root entries;
struct rb_node *curr_hot;
struct objdump_line *selection;
+ u64 start;
int nr_asm_entries;
int nr_entries;
bool hide_src_code;
+ bool use_offset;
+ bool searching_backwards;
+ char search_bf[128];
};
struct objdump_line_rb_node {
@@ -51,6 +55,9 @@ static void annotate_browser__write(struct ui_browser *self, void *entry, int ro
struct annotate_browser *ab = container_of(self, struct annotate_browser, b);
struct objdump_line *ol = list_entry(entry, struct objdump_line, node);
bool current_entry = ui_browser__is_current_entry(self, row);
+ bool change_color = (!ab->hide_src_code &&
+ (!current_entry || (self->use_navkeypressed &&
+ !self->navkeypressed)));
int width = self->width;
if (ol->offset != -1) {
@@ -69,14 +76,31 @@ static void annotate_browser__write(struct ui_browser *self, void *entry, int ro
if (!self->navkeypressed)
width += 1;
+ if (ol->offset != -1 && change_color)
+ ui_browser__set_color(self, HE_COLORSET_CODE);
+
if (!*ol->line)
slsmg_write_nstring(" ", width - 18);
- else
+ else if (ol->offset == -1)
slsmg_write_nstring(ol->line, width - 18);
+ else {
+ char bf[64];
+ u64 addr = ol->offset;
+ int printed, color = -1;
+
+ if (!ab->use_offset)
+ addr += ab->start;
+
+ printed = scnprintf(bf, sizeof(bf), " %" PRIx64 ":", addr);
+ if (change_color)
+ color = ui_browser__set_color(self, HE_COLORSET_ADDR);
+ slsmg_write_nstring(bf, printed);
+ if (change_color)
+ ui_browser__set_color(self, color);
+ slsmg_write_nstring(ol->line, width - 18 - printed);
+ }
- if (!current_entry)
- ui_browser__set_color(self, HE_COLORSET_CODE);
- else
+ if (current_entry)
ab->selection = ol;
}
@@ -135,27 +159,38 @@ static void objdump__insert_line(struct rb_root *self,
}
static void annotate_browser__set_top(struct annotate_browser *self,
- struct rb_node *nd)
+ struct objdump_line *pos, u32 idx)
{
- struct objdump_line_rb_node *rbpos;
- struct objdump_line *pos;
unsigned back;
ui_browser__refresh_dimensions(&self->b);
back = self->b.height / 2;
- rbpos = rb_entry(nd, struct objdump_line_rb_node, rb_node);
- pos = ((struct objdump_line *)rbpos) - 1;
- self->b.top_idx = self->b.index = rbpos->idx;
+ self->b.top_idx = self->b.index = idx;
while (self->b.top_idx != 0 && back != 0) {
pos = list_entry(pos->node.prev, struct objdump_line, node);
+ if (objdump_line__filter(&self->b, &pos->node))
+ continue;
+
--self->b.top_idx;
--back;
}
self->b.top = pos;
- self->curr_hot = nd;
+ self->b.navkeypressed = true;
+}
+
+static void annotate_browser__set_rb_top(struct annotate_browser *browser,
+ struct rb_node *nd)
+{
+ struct objdump_line_rb_node *rbpos;
+ struct objdump_line *pos;
+
+ rbpos = rb_entry(nd, struct objdump_line_rb_node, rb_node);
+ pos = ((struct objdump_line *)rbpos) - 1;
+ annotate_browser__set_top(browser, pos, rbpos->idx);
+ browser->curr_hot = nd;
}
static void annotate_browser__calc_percent(struct annotate_browser *browser,
@@ -223,6 +258,231 @@ static bool annotate_browser__toggle_source(struct annotate_browser *browser)
return true;
}
+static bool annotate_browser__callq(struct annotate_browser *browser,
+ int evidx, void (*timer)(void *arg),
+ void *arg, int delay_secs)
+{
+ struct map_symbol *ms = browser->b.priv;
+ struct symbol *sym = ms->sym;
+ struct annotation *notes;
+ struct symbol *target;
+ char *s = strstr(browser->selection->line, "callq ");
+ u64 ip;
+
+ if (s == NULL)
+ return false;
+
+ s = strchr(s, ' ');
+ if (s++ == NULL) {
+ ui_helpline__puts("Invallid callq instruction.");
+ return true;
+ }
+
+ ip = strtoull(s, NULL, 16);
+ ip = ms->map->map_ip(ms->map, ip);
+ target = map__find_symbol(ms->map, ip, NULL);
+ if (target == NULL) {
+ ui_helpline__puts("The called function was not found.");
+ return true;
+ }
+
+ notes = symbol__annotation(target);
+ pthread_mutex_lock(&notes->lock);
+
+ if (notes->src == NULL && symbol__alloc_hist(target) < 0) {
+ pthread_mutex_unlock(&notes->lock);
+ ui__warning("Not enough memory for annotating '%s' symbol!\n",
+ target->name);
+ return true;
+ }
+
+ pthread_mutex_unlock(&notes->lock);
+ symbol__tui_annotate(target, ms->map, evidx, timer, arg, delay_secs);
+ ui_browser__show_title(&browser->b, sym->name);
+ return true;
+}
+
+static struct objdump_line *
+ annotate_browser__find_offset(struct annotate_browser *browser,
+ s64 offset, s64 *idx)
+{
+ struct map_symbol *ms = browser->b.priv;
+ struct symbol *sym = ms->sym;
+ struct annotation *notes = symbol__annotation(sym);
+ struct objdump_line *pos;
+
+ *idx = 0;
+ list_for_each_entry(pos, &notes->src->source, node) {
+ if (pos->offset == offset)
+ return pos;
+ if (!objdump_line__filter(&browser->b, &pos->node))
+ ++*idx;
+ }
+
+ return NULL;
+}
+
+static bool annotate_browser__jump(struct annotate_browser *browser)
+{
+ const char *jumps[] = { "je ", "jne ", "ja ", "jmpq ", "js ", "jmp ", NULL };
+ struct objdump_line *line;
+ s64 idx, offset;
+ char *s = NULL;
+ int i = 0;
+
+ while (jumps[i]) {
+ s = strstr(browser->selection->line, jumps[i++]);
+ if (s)
+ break;
+ }
+
+ if (s == NULL)
+ return false;
+
+ s = strchr(s, '+');
+ if (s++ == NULL) {
+ ui_helpline__puts("Invallid jump instruction.");
+ return true;
+ }
+
+ offset = strtoll(s, NULL, 16);
+ line = annotate_browser__find_offset(browser, offset, &idx);
+ if (line == NULL) {
+ ui_helpline__puts("Invallid jump offset");
+ return true;
+ }
+
+ annotate_browser__set_top(browser, line, idx);
+
+ return true;
+}
+
+static struct objdump_line *
+ annotate_browser__find_string(struct annotate_browser *browser,
+ char *s, s64 *idx)
+{
+ struct map_symbol *ms = browser->b.priv;
+ struct symbol *sym = ms->sym;
+ struct annotation *notes = symbol__annotation(sym);
+ struct objdump_line *pos = browser->selection;
+
+ *idx = browser->b.index;
+ list_for_each_entry_continue(pos, &notes->src->source, node) {
+ if (objdump_line__filter(&browser->b, &pos->node))
+ continue;
+
+ ++*idx;
+
+ if (pos->line && strstr(pos->line, s) != NULL)
+ return pos;
+ }
+
+ return NULL;
+}
+
+static bool __annotate_browser__search(struct annotate_browser *browser)
+{
+ struct objdump_line *line;
+ s64 idx;
+
+ line = annotate_browser__find_string(browser, browser->search_bf, &idx);
+ if (line == NULL) {
+ ui_helpline__puts("String not found!");
+ return false;
+ }
+
+ annotate_browser__set_top(browser, line, idx);
+ browser->searching_backwards = false;
+ return true;
+}
+
+static struct objdump_line *
+ annotate_browser__find_string_reverse(struct annotate_browser *browser,
+ char *s, s64 *idx)
+{
+ struct map_symbol *ms = browser->b.priv;
+ struct symbol *sym = ms->sym;
+ struct annotation *notes = symbol__annotation(sym);
+ struct objdump_line *pos = browser->selection;
+
+ *idx = browser->b.index;
+ list_for_each_entry_continue_reverse(pos, &notes->src->source, node) {
+ if (objdump_line__filter(&browser->b, &pos->node))
+ continue;
+
+ --*idx;
+
+ if (pos->line && strstr(pos->line, s) != NULL)
+ return pos;
+ }
+
+ return NULL;
+}
+
+static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
+{
+ struct objdump_line *line;
+ s64 idx;
+
+ line = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
+ if (line == NULL) {
+ ui_helpline__puts("String not found!");
+ return false;
+ }
+
+ annotate_browser__set_top(browser, line, idx);
+ browser->searching_backwards = true;
+ return true;
+}
+
+static bool annotate_browser__search_window(struct annotate_browser *browser,
+ int delay_secs)
+{
+ if (ui_browser__input_window("Search", "String: ", browser->search_bf,
+ "ENTER: OK, ESC: Cancel",
+ delay_secs * 2) != K_ENTER ||
+ !*browser->search_bf)
+ return false;
+
+ return true;
+}
+
+static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs)
+{
+ if (annotate_browser__search_window(browser, delay_secs))
+ return __annotate_browser__search(browser);
+
+ return false;
+}
+
+static bool annotate_browser__continue_search(struct annotate_browser *browser,
+ int delay_secs)
+{
+ if (!*browser->search_bf)
+ return annotate_browser__search(browser, delay_secs);
+
+ return __annotate_browser__search(browser);
+}
+
+static bool annotate_browser__search_reverse(struct annotate_browser *browser,
+ int delay_secs)
+{
+ if (annotate_browser__search_window(browser, delay_secs))
+ return __annotate_browser__search_reverse(browser);
+
+ return false;
+}
+
+static
+bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
+ int delay_secs)
+{
+ if (!*browser->search_bf)
+ return annotate_browser__search_reverse(browser, delay_secs);
+
+ return __annotate_browser__search_reverse(browser);
+}
+
static int annotate_browser__run(struct annotate_browser *self, int evidx,
void(*timer)(void *arg),
void *arg, int delay_secs)
@@ -230,9 +490,10 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx,
struct rb_node *nd = NULL;
struct map_symbol *ms = self->b.priv;
struct symbol *sym = ms->sym;
- const char *help = "<-, ESC: exit, TAB/shift+TAB: cycle hottest lines, "
- "H: Hottest, -> Line action, S -> Toggle source "
- "code view";
+ const char *help = "<-/ESC: Exit, TAB/shift+TAB: Cycle hot lines, "
+ "H: Go to hottest line, ->/ENTER: Line action, "
+ "O: Toggle offset view, "
+ "S: Toggle source code view";
int key;
if (ui_browser__show(&self->b, sym->name, help) < 0)
@@ -240,8 +501,10 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx,
annotate_browser__calc_percent(self, evidx);
- if (self->curr_hot)
- annotate_browser__set_top(self, self->curr_hot);
+ if (self->curr_hot) {
+ annotate_browser__set_rb_top(self, self->curr_hot);
+ self->b.navkeypressed = false;
+ }
nd = self->curr_hot;
@@ -284,61 +547,43 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx,
nd = self->curr_hot;
break;
case 'H':
+ case 'h':
nd = self->curr_hot;
break;
case 'S':
+ case 's':
if (annotate_browser__toggle_source(self))
ui_helpline__puts(help);
continue;
+ case 'O':
+ case 'o':
+ self->use_offset = !self->use_offset;
+ continue;
+ case '/':
+ if (annotate_browser__search(self, delay_secs)) {
+show_help:
+ ui_helpline__puts(help);
+ }
+ continue;
+ case 'n':
+ if (self->searching_backwards ?
+ annotate_browser__continue_search_reverse(self, delay_secs) :
+ annotate_browser__continue_search(self, delay_secs))
+ goto show_help;
+ continue;
+ case '?':
+ if (annotate_browser__search_reverse(self, delay_secs))
+ goto show_help;
+ continue;
case K_ENTER:
case K_RIGHT:
- if (self->selection == NULL) {
+ if (self->selection == NULL)
ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
- continue;
- }
-
- if (self->selection->offset == -1) {
+ else if (self->selection->offset == -1)
ui_helpline__puts("Actions are only available for assembly lines.");
- continue;
- } else {
- char *s = strstr(self->selection->line, "callq ");
- struct annotation *notes;
- struct symbol *target;
- u64 ip;
-
- if (s == NULL) {
- ui_helpline__puts("Actions are only available for the 'callq' instruction.");
- continue;
- }
-
- s = strchr(s, ' ');
- if (s++ == NULL) {
- ui_helpline__puts("Invallid callq instruction.");
- continue;
- }
-
- ip = strtoull(s, NULL, 16);
- ip = ms->map->map_ip(ms->map, ip);
- target = map__find_symbol(ms->map, ip, NULL);
- if (target == NULL) {
- ui_helpline__puts("The called function was not found.");
- continue;
- }
-
- notes = symbol__annotation(target);
- pthread_mutex_lock(&notes->lock);
-
- if (notes->src == NULL && symbol__alloc_hist(target) < 0) {
- pthread_mutex_unlock(&notes->lock);
- ui__warning("Not enough memory for annotating '%s' symbol!\n",
- target->name);
- continue;
- }
-
- pthread_mutex_unlock(&notes->lock);
- symbol__tui_annotate(target, ms->map, evidx,
- timer, arg, delay_secs);
- }
+ else if (!(annotate_browser__jump(self) ||
+ annotate_browser__callq(self, evidx, timer, arg, delay_secs)))
+ ui_helpline__puts("Actions are only available for the 'callq' and jump instructions.");
continue;
case K_LEFT:
case K_ESC:
@@ -350,7 +595,7 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx,
}
if (nd != NULL)
- annotate_browser__set_top(self, nd);
+ annotate_browser__set_rb_top(self, nd);
}
out:
ui_browser__hide(&self->b);
@@ -400,6 +645,7 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
ui_helpline__push("Press <- or ESC to exit");
notes = symbol__annotation(sym);
+ browser.start = map__rip_2objdump(map, sym->start);
list_for_each_entry(pos, &notes->src->source, node) {
struct objdump_line_rb_node *rbpos;
diff --git a/tools/perf/util/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c
index bfba0490c098..466827e91b87 100644
--- a/tools/perf/util/ui/browsers/hists.c
+++ b/tools/perf/ui/browsers/hists.c
@@ -5,12 +5,12 @@
#include <newt.h>
#include <linux/rbtree.h>
-#include "../../evsel.h"
-#include "../../evlist.h"
-#include "../../hist.h"
-#include "../../pstack.h"
-#include "../../sort.h"
-#include "../../util.h"
+#include "../../util/evsel.h"
+#include "../../util/evlist.h"
+#include "../../util/hist.h"
+#include "../../util/pstack.h"
+#include "../../util/sort.h"
+#include "../../util/util.h"
#include "../browser.h"
#include "../helpline.h"
@@ -125,6 +125,9 @@ static int callchain__count_rows(struct rb_root *chain)
static bool map_symbol__toggle_fold(struct map_symbol *self)
{
+ if (!self)
+ return false;
+
if (!self->has_children)
return false;
@@ -805,8 +808,11 @@ static struct hist_browser *hist_browser__new(struct hists *hists)
self->hists = hists;
self->b.refresh = hist_browser__refresh;
self->b.seek = ui_browser__hists_seek;
- self->b.use_navkeypressed = true,
- self->has_symbols = sort_sym.list.next != NULL;
+ self->b.use_navkeypressed = true;
+ if (sort__branch_mode == 1)
+ self->has_symbols = sort_sym_from.list.next != NULL;
+ else
+ self->has_symbols = sort_sym.list.next != NULL;
}
return self;
@@ -834,25 +840,39 @@ static int hists__browser_title(struct hists *self, char *bf, size_t size,
int printed;
const struct dso *dso = self->dso_filter;
const struct thread *thread = self->thread_filter;
- unsigned long nr_events = self->stats.nr_events[PERF_RECORD_SAMPLE];
+ unsigned long nr_samples = self->stats.nr_events[PERF_RECORD_SAMPLE];
+ u64 nr_events = self->stats.total_period;
+
+ nr_samples = convert_unit(nr_samples, &unit);
+ printed = scnprintf(bf, size,
+ "Samples: %lu%c of event '%s', Event count (approx.): %lu",
+ nr_samples, unit, ev_name, nr_events);
- nr_events = convert_unit(nr_events, &unit);
- printed = snprintf(bf, size, "Events: %lu%c %s", nr_events, unit, ev_name);
if (self->uid_filter_str)
printed += snprintf(bf + printed, size - printed,
", UID: %s", self->uid_filter_str);
if (thread)
- printed += snprintf(bf + printed, size - printed,
+ printed += scnprintf(bf + printed, size - printed,
", Thread: %s(%d)",
(thread->comm_set ? thread->comm : ""),
thread->pid);
if (dso)
- printed += snprintf(bf + printed, size - printed,
+ printed += scnprintf(bf + printed, size - printed,
", DSO: %s", dso->short_name);
return printed;
}
+static inline void free_popup_options(char **options, int n)
+{
+ int i;
+
+ for (i = 0; i < n; ++i) {
+ free(options[i]);
+ options[i] = NULL;
+ }
+}
+
static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
const char *helpline, const char *ev_name,
bool left_exits,
@@ -861,8 +881,12 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
{
struct hists *self = &evsel->hists;
struct hist_browser *browser = hist_browser__new(self);
+ struct branch_info *bi;
struct pstack *fstack;
+ char *options[16];
+ int nr_options = 0;
int key = -1;
+ char buf[64];
if (browser == NULL)
return -1;
@@ -873,13 +897,16 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
ui_helpline__push(helpline);
+ memset(options, 0, sizeof(options));
+
while (1) {
const struct thread *thread = NULL;
const struct dso *dso = NULL;
- char *options[16];
- int nr_options = 0, choice = 0, i,
+ int choice = 0,
annotate = -2, zoom_dso = -2, zoom_thread = -2,
- browse_map = -2;
+ annotate_f = -2, annotate_t = -2, browse_map = -2;
+
+ nr_options = 0;
key = hist_browser__run(browser, ev_name, timer, arg, delay_secs);
@@ -887,7 +914,6 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
thread = hist_browser__selected_thread(browser);
dso = browser->selection->map ? browser->selection->map->dso : NULL;
}
-
switch (key) {
case K_TAB:
case K_UNTAB:
@@ -902,7 +928,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
if (!browser->has_symbols) {
ui_browser__warning(&browser->b, delay_secs * 2,
"Annotation is only available for symbolic views, "
- "include \"sym\" in --sort to use it.");
+ "include \"sym*\" in --sort to use it.");
continue;
}
@@ -915,6 +941,16 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
goto zoom_dso;
case 't':
goto zoom_thread;
+ case 's':
+ if (ui_browser__input_window("Symbol to show",
+ "Please enter the name of symbol you want to see",
+ buf, "ENTER: OK, ESC: Cancel",
+ delay_secs * 2) == K_ENTER) {
+ self->symbol_filter_str = *buf ? buf : NULL;
+ hists__filter_by_symbol(self);
+ hist_browser__reset(browser);
+ }
+ continue;
case K_F1:
case 'h':
case '?':
@@ -932,7 +968,8 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
"C Collapse all callchains\n"
"E Expand all callchains\n"
"d Zoom into current DSO\n"
- "t Zoom into current Thread");
+ "t Zoom into current Thread\n"
+ "s Filter symbol by name");
continue;
case K_ENTER:
case K_RIGHT:
@@ -972,12 +1009,34 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
if (!browser->has_symbols)
goto add_exit_option;
- if (browser->selection != NULL &&
- browser->selection->sym != NULL &&
- !browser->selection->map->dso->annotate_warned &&
- asprintf(&options[nr_options], "Annotate %s",
- browser->selection->sym->name) > 0)
- annotate = nr_options++;
+ if (sort__branch_mode == 1) {
+ bi = browser->he_selection->branch_info;
+ if (browser->selection != NULL &&
+ bi &&
+ bi->from.sym != NULL &&
+ !bi->from.map->dso->annotate_warned &&
+ asprintf(&options[nr_options], "Annotate %s",
+ bi->from.sym->name) > 0)
+ annotate_f = nr_options++;
+
+ if (browser->selection != NULL &&
+ bi &&
+ bi->to.sym != NULL &&
+ !bi->to.map->dso->annotate_warned &&
+ (bi->to.sym != bi->from.sym ||
+ bi->to.map->dso != bi->from.map->dso) &&
+ asprintf(&options[nr_options], "Annotate %s",
+ bi->to.sym->name) > 0)
+ annotate_t = nr_options++;
+ } else {
+
+ if (browser->selection != NULL &&
+ browser->selection->sym != NULL &&
+ !browser->selection->map->dso->annotate_warned &&
+ asprintf(&options[nr_options], "Annotate %s",
+ browser->selection->sym->name) > 0)
+ annotate = nr_options++;
+ }
if (thread != NULL &&
asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
@@ -998,25 +1057,39 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
browse_map = nr_options++;
add_exit_option:
options[nr_options++] = (char *)"Exit";
-
+retry_popup_menu:
choice = ui__popup_menu(nr_options, options);
- for (i = 0; i < nr_options - 1; ++i)
- free(options[i]);
-
if (choice == nr_options - 1)
break;
- if (choice == -1)
+ if (choice == -1) {
+ free_popup_options(options, nr_options - 1);
continue;
+ }
- if (choice == annotate) {
+ if (choice == annotate || choice == annotate_t || choice == annotate_f) {
struct hist_entry *he;
int err;
do_annotate:
he = hist_browser__selected_entry(browser);
if (he == NULL)
continue;
+
+ /*
+ * we stash the branch_info symbol + map into the
+ * the ms so we don't have to rewrite all the annotation
+ * code to use branch_info.
+ * in branch mode, the ms struct is not used
+ */
+ if (choice == annotate_f) {
+ he->ms.sym = he->branch_info->from.sym;
+ he->ms.map = he->branch_info->from.map;
+ } else if (choice == annotate_t) {
+ he->ms.sym = he->branch_info->to.sym;
+ he->ms.map = he->branch_info->to.map;
+ }
+
/*
* Don't let this be freed, say, by hists__decay_entry.
*/
@@ -1024,9 +1097,18 @@ do_annotate:
err = hist_entry__tui_annotate(he, evsel->idx,
timer, arg, delay_secs);
he->used = false;
+ /*
+ * offer option to annotate the other branch source or target
+ * (if they exists) when returning from annotate
+ */
+ if ((err == 'q' || err == CTRL('c'))
+ && annotate_t != -2 && annotate_f != -2)
+ goto retry_popup_menu;
+
ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
if (err)
ui_browser__handle_resize(&browser->b);
+
} else if (choice == browse_map)
map__browse(browser->selection->map);
else if (choice == zoom_dso) {
@@ -1072,6 +1154,7 @@ out_free_stack:
pstack__delete(fstack);
out:
hist_browser__delete(browser);
+ free_popup_options(options, nr_options - 1);
return key;
}
@@ -1098,7 +1181,7 @@ static void perf_evsel_menu__write(struct ui_browser *browser,
HE_COLORSET_NORMAL);
nr_events = convert_unit(nr_events, &unit);
- printed = snprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
+ printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
unit, unit == ' ' ? "" : " ", ev_name);
slsmg_printf("%s", bf);
@@ -1108,8 +1191,8 @@ static void perf_evsel_menu__write(struct ui_browser *browser,
if (!current_entry)
ui_browser__set_color(browser, HE_COLORSET_TOP);
nr_events = convert_unit(nr_events, &unit);
- snprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!", nr_events,
- unit, unit == ' ' ? "" : " ");
+ printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
+ nr_events, unit, unit == ' ' ? "" : " ");
warn = bf;
}
diff --git a/tools/perf/util/ui/browsers/map.c b/tools/perf/ui/browsers/map.c
index eca6575abfd0..98851d55a53e 100644
--- a/tools/perf/util/ui/browsers/map.c
+++ b/tools/perf/ui/browsers/map.c
@@ -5,9 +5,9 @@
#include <sys/ttydefaults.h>
#include <string.h>
#include <linux/bitops.h>
-#include "../../util.h"
-#include "../../debug.h"
-#include "../../symbol.h"
+#include "../../util/util.h"
+#include "../../util/debug.h"
+#include "../../util/symbol.h"
#include "../browser.h"
#include "../helpline.h"
#include "map.h"
diff --git a/tools/perf/util/ui/browsers/map.h b/tools/perf/ui/browsers/map.h
index df8581a43e17..df8581a43e17 100644
--- a/tools/perf/util/ui/browsers/map.h
+++ b/tools/perf/ui/browsers/map.h
diff --git a/tools/perf/ui/gtk/browser.c b/tools/perf/ui/gtk/browser.c
new file mode 100644
index 000000000000..258352a2356c
--- /dev/null
+++ b/tools/perf/ui/gtk/browser.c
@@ -0,0 +1,189 @@
+#include "../evlist.h"
+#include "../cache.h"
+#include "../evsel.h"
+#include "../sort.h"
+#include "../hist.h"
+#include "gtk.h"
+
+#include <signal.h>
+
+#define MAX_COLUMNS 32
+
+void perf_gtk_setup_browser(int argc, const char *argv[],
+ bool fallback_to_pager __used)
+{
+ gtk_init(&argc, (char ***)&argv);
+}
+
+void perf_gtk_exit_browser(bool wait_for_ok __used)
+{
+ gtk_main_quit();
+}
+
+static void perf_gtk_signal(int sig)
+{
+ psignal(sig, "perf");
+ gtk_main_quit();
+}
+
+static void perf_gtk_resize_window(GtkWidget *window)
+{
+ GdkRectangle rect;
+ GdkScreen *screen;
+ int monitor;
+ int height;
+ int width;
+
+ screen = gtk_widget_get_screen(window);
+
+ monitor = gdk_screen_get_monitor_at_window(screen, window->window);
+
+ gdk_screen_get_monitor_geometry(screen, monitor, &rect);
+
+ width = rect.width * 3 / 4;
+ height = rect.height * 3 / 4;
+
+ gtk_window_resize(GTK_WINDOW(window), width, height);
+}
+
+static void perf_gtk_show_hists(GtkWidget *window, struct hists *hists)
+{
+ GType col_types[MAX_COLUMNS];
+ GtkCellRenderer *renderer;
+ struct sort_entry *se;
+ GtkListStore *store;
+ struct rb_node *nd;
+ u64 total_period;
+ GtkWidget *view;
+ int col_idx;
+ int nr_cols;
+
+ nr_cols = 0;
+
+ /* The percentage column */
+ col_types[nr_cols++] = G_TYPE_STRING;
+
+ list_for_each_entry(se, &hist_entry__sort_list, list) {
+ if (se->elide)
+ continue;
+
+ col_types[nr_cols++] = G_TYPE_STRING;
+ }
+
+ store = gtk_list_store_newv(nr_cols, col_types);
+
+ view = gtk_tree_view_new();
+
+ renderer = gtk_cell_renderer_text_new();
+
+ col_idx = 0;
+
+ /* The percentage column */
+ gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
+ -1, "Overhead (%)",
+ renderer, "text",
+ col_idx++, NULL);
+
+ list_for_each_entry(se, &hist_entry__sort_list, list) {
+ if (se->elide)
+ continue;
+
+ gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
+ -1, se->se_header,
+ renderer, "text",
+ col_idx++, NULL);
+ }
+
+ gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store));
+
+ g_object_unref(GTK_TREE_MODEL(store));
+
+ total_period = hists->stats.total_period;
+
+ for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
+ struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
+ GtkTreeIter iter;
+ double percent;
+ char s[512];
+
+ if (h->filtered)
+ continue;
+
+ gtk_list_store_append(store, &iter);
+
+ col_idx = 0;
+
+ percent = (h->period * 100.0) / total_period;
+
+ snprintf(s, ARRAY_SIZE(s), "%.2f", percent);
+
+ gtk_list_store_set(store, &iter, col_idx++, s, -1);
+
+ list_for_each_entry(se, &hist_entry__sort_list, list) {
+ if (se->elide)
+ continue;
+
+ se->se_snprintf(h, s, ARRAY_SIZE(s),
+ hists__col_len(hists, se->se_width_idx));
+
+ gtk_list_store_set(store, &iter, col_idx++, s, -1);
+ }
+ }
+
+ gtk_container_add(GTK_CONTAINER(window), view);
+}
+
+int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist,
+ const char *help __used,
+ void (*timer) (void *arg)__used,
+ void *arg __used, int delay_secs __used)
+{
+ struct perf_evsel *pos;
+ GtkWidget *notebook;
+ GtkWidget *window;
+
+ signal(SIGSEGV, perf_gtk_signal);
+ signal(SIGFPE, perf_gtk_signal);
+ signal(SIGINT, perf_gtk_signal);
+ signal(SIGQUIT, perf_gtk_signal);
+ signal(SIGTERM, perf_gtk_signal);
+
+ window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+
+ gtk_window_set_title(GTK_WINDOW(window), "perf report");
+
+ g_signal_connect(window, "delete_event", gtk_main_quit, NULL);
+
+ notebook = gtk_notebook_new();
+
+ list_for_each_entry(pos, &evlist->entries, node) {
+ struct hists *hists = &pos->hists;
+ const char *evname = event_name(pos);
+ GtkWidget *scrolled_window;
+ GtkWidget *tab_label;
+
+ scrolled_window = gtk_scrolled_window_new(NULL, NULL);
+
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+
+ perf_gtk_show_hists(scrolled_window, hists);
+
+ tab_label = gtk_label_new(evname);
+
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled_window, tab_label);
+ }
+
+ gtk_container_add(GTK_CONTAINER(window), notebook);
+
+ gtk_widget_show_all(window);
+
+ perf_gtk_resize_window(window);
+
+ gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
+
+ gtk_main();
+
+ return 0;
+}
diff --git a/tools/perf/ui/gtk/gtk.h b/tools/perf/ui/gtk/gtk.h
new file mode 100644
index 000000000000..75177ee04032
--- /dev/null
+++ b/tools/perf/ui/gtk/gtk.h
@@ -0,0 +1,8 @@
+#ifndef _PERF_GTK_H_
+#define _PERF_GTK_H_ 1
+
+#pragma GCC diagnostic ignored "-Wstrict-prototypes"
+#include <gtk/gtk.h>
+#pragma GCC diagnostic error "-Wstrict-prototypes"
+
+#endif /* _PERF_GTK_H_ */
diff --git a/tools/perf/util/ui/helpline.c b/tools/perf/ui/helpline.c
index 4f48f5901b30..2f950c2641c8 100644
--- a/tools/perf/util/ui/helpline.c
+++ b/tools/perf/ui/helpline.c
@@ -64,7 +64,7 @@ int ui_helpline__show_help(const char *format, va_list ap)
static int backlog;
pthread_mutex_lock(&ui__lock);
- ret = vsnprintf(ui_helpline__last_msg + backlog,
+ ret = vscnprintf(ui_helpline__last_msg + backlog,
sizeof(ui_helpline__last_msg) - backlog, format, ap);
backlog += ret;
diff --git a/tools/perf/util/ui/helpline.h b/tools/perf/ui/helpline.h
index 7bab6b34e35e..7bab6b34e35e 100644
--- a/tools/perf/util/ui/helpline.h
+++ b/tools/perf/ui/helpline.h
diff --git a/tools/perf/util/ui/keysyms.h b/tools/perf/ui/keysyms.h
index 3458b1985761..809eca5707fa 100644
--- a/tools/perf/util/ui/keysyms.h
+++ b/tools/perf/ui/keysyms.h
@@ -16,6 +16,8 @@
#define K_TAB '\t'
#define K_UNTAB SL_KEY_UNTAB
#define K_UP SL_KEY_UP
+#define K_BKSPC 0x7f
+#define K_DEL SL_KEY_DELETE
/* Not really keys */
#define K_TIMER -1
diff --git a/tools/perf/util/ui/libslang.h b/tools/perf/ui/libslang.h
index 4d54b6450f5b..4d54b6450f5b 100644
--- a/tools/perf/util/ui/libslang.h
+++ b/tools/perf/ui/libslang.h
diff --git a/tools/perf/util/ui/progress.c b/tools/perf/ui/progress.c
index 13aa64e50e11..13aa64e50e11 100644
--- a/tools/perf/util/ui/progress.c
+++ b/tools/perf/ui/progress.c
diff --git a/tools/perf/util/ui/progress.h b/tools/perf/ui/progress.h
index d9c205b59aa1..d9c205b59aa1 100644
--- a/tools/perf/util/ui/progress.h
+++ b/tools/perf/ui/progress.h
diff --git a/tools/perf/util/ui/setup.c b/tools/perf/ui/setup.c
index 85a69faa09aa..85a69faa09aa 100644
--- a/tools/perf/util/ui/setup.c
+++ b/tools/perf/ui/setup.c
diff --git a/tools/perf/util/ui/ui.h b/tools/perf/ui/ui.h
index 7b67045479f6..7b67045479f6 100644
--- a/tools/perf/util/ui/ui.h
+++ b/tools/perf/ui/ui.h
diff --git a/tools/perf/util/ui/util.c b/tools/perf/ui/util.c
index 45daa7c41dad..ad4374a16bb0 100644
--- a/tools/perf/util/ui/util.c
+++ b/tools/perf/ui/util.c
@@ -69,6 +69,88 @@ int ui__popup_menu(int argc, char * const argv[])
return popup_menu__run(&menu);
}
+int ui_browser__input_window(const char *title, const char *text, char *input,
+ const char *exit_msg, int delay_secs)
+{
+ int x, y, len, key;
+ int max_len = 60, nr_lines = 0;
+ static char buf[50];
+ const char *t;
+
+ t = text;
+ while (1) {
+ const char *sep = strchr(t, '\n');
+
+ if (sep == NULL)
+ sep = strchr(t, '\0');
+ len = sep - t;
+ if (max_len < len)
+ max_len = len;
+ ++nr_lines;
+ if (*sep == '\0')
+ break;
+ t = sep + 1;
+ }
+
+ max_len += 2;
+ nr_lines += 8;
+ y = SLtt_Screen_Rows / 2 - nr_lines / 2;
+ x = SLtt_Screen_Cols / 2 - max_len / 2;
+
+ SLsmg_set_color(0);
+ SLsmg_draw_box(y, x++, nr_lines, max_len);
+ if (title) {
+ SLsmg_gotorc(y, x + 1);
+ SLsmg_write_string((char *)title);
+ }
+ SLsmg_gotorc(++y, x);
+ nr_lines -= 7;
+ max_len -= 2;
+ SLsmg_write_wrapped_string((unsigned char *)text, y, x,
+ nr_lines, max_len, 1);
+ y += nr_lines;
+ len = 5;
+ while (len--) {
+ SLsmg_gotorc(y + len - 1, x);
+ SLsmg_write_nstring((char *)" ", max_len);
+ }
+ SLsmg_draw_box(y++, x + 1, 3, max_len - 2);
+
+ SLsmg_gotorc(y + 3, x);
+ SLsmg_write_nstring((char *)exit_msg, max_len);
+ SLsmg_refresh();
+
+ x += 2;
+ len = 0;
+ key = ui__getch(delay_secs);
+ while (key != K_TIMER && key != K_ENTER && key != K_ESC) {
+ if (key == K_BKSPC) {
+ if (len == 0)
+ goto next_key;
+ SLsmg_gotorc(y, x + --len);
+ SLsmg_write_char(' ');
+ } else {
+ buf[len] = key;
+ SLsmg_gotorc(y, x + len++);
+ SLsmg_write_char(key);
+ }
+ SLsmg_refresh();
+
+ /* XXX more graceful overflow handling needed */
+ if (len == sizeof(buf) - 1) {
+ ui_helpline__push("maximum size of symbol name reached!");
+ key = K_ENTER;
+ break;
+ }
+next_key:
+ key = ui__getch(delay_secs);
+ }
+
+ buf[len] = '\0';
+ strncpy(input, buf, len+1);
+ return key;
+}
+
int ui__question_window(const char *title, const char *text,
const char *exit_msg, int delay_secs)
{
diff --git a/tools/perf/util/ui/util.h b/tools/perf/ui/util.h
index 2d1738bd71c8..2d1738bd71c8 100644
--- a/tools/perf/util/ui/util.h
+++ b/tools/perf/ui/util.h
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index 011ed2676604..1e7fd52bd29d 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -28,8 +28,8 @@ int symbol__annotate_init(struct map *map __used, struct symbol *sym)
int symbol__alloc_hist(struct symbol *sym)
{
struct annotation *notes = symbol__annotation(sym);
- size_t sizeof_sym_hist = (sizeof(struct sym_hist) +
- (sym->end - sym->start) * sizeof(u64));
+ const size_t size = sym->end - sym->start + 1;
+ size_t sizeof_sym_hist = (sizeof(struct sym_hist) + size * sizeof(u64));
notes->src = zalloc(sizeof(*notes->src) + symbol_conf.nr_events * sizeof_sym_hist);
if (notes->src == NULL)
@@ -64,8 +64,8 @@ int symbol__inc_addr_samples(struct symbol *sym, struct map *map,
pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, map->unmap_ip(map, addr));
- if (addr >= sym->end)
- return 0;
+ if (addr < sym->start || addr > sym->end)
+ return -ERANGE;
offset = addr - sym->start;
h = annotation__histogram(notes, evidx);
@@ -84,10 +84,15 @@ static struct objdump_line *objdump_line__new(s64 offset, char *line, size_t pri
if (self != NULL) {
self->offset = offset;
- self->line = line;
+ self->line = strdup(line);
+ if (self->line == NULL)
+ goto out_delete;
}
return self;
+out_delete:
+ free(self);
+ return NULL;
}
void objdump_line__free(struct objdump_line *self)
@@ -112,7 +117,7 @@ struct objdump_line *objdump__get_next_ip_line(struct list_head *head,
}
static int objdump_line__print(struct objdump_line *oline, struct symbol *sym,
- int evidx, u64 len, int min_pcnt,
+ u64 start, int evidx, u64 len, int min_pcnt,
int printed, int max_lines,
struct objdump_line *queue)
{
@@ -128,6 +133,7 @@ static int objdump_line__print(struct objdump_line *oline, struct symbol *sym,
struct source_line *src_line = notes->src->lines;
struct sym_hist *h = annotation__histogram(notes, evidx);
s64 offset = oline->offset;
+ const u64 addr = start + offset;
struct objdump_line *next;
next = objdump__get_next_ip_line(&notes->src->source, oline);
@@ -157,7 +163,7 @@ static int objdump_line__print(struct objdump_line *oline, struct symbol *sym,
list_for_each_entry_from(queue, &notes->src->source, node) {
if (queue == oline)
break;
- objdump_line__print(queue, sym, evidx, len,
+ objdump_line__print(queue, sym, start, evidx, len,
0, 0, 1, NULL);
}
}
@@ -180,6 +186,7 @@ static int objdump_line__print(struct objdump_line *oline, struct symbol *sym,
color_fprintf(stdout, color, " %7.2f", percent);
printf(" : ");
+ color_fprintf(stdout, PERF_COLOR_MAGENTA, " %" PRIx64 ":", addr);
color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", oline->line);
} else if (max_lines && printed >= max_lines)
return 1;
@@ -201,7 +208,7 @@ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map,
{
struct annotation *notes = symbol__annotation(sym);
struct objdump_line *objdump_line;
- char *line = NULL, *tmp, *tmp2, *c;
+ char *line = NULL, *parsed_line, *tmp, *tmp2, *c;
size_t line_len;
s64 line_ip, offset = -1;
@@ -219,6 +226,7 @@ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map,
*c = 0;
line_ip = -1;
+ parsed_line = line;
/*
* Strip leading spaces:
@@ -246,13 +254,16 @@ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map,
offset = line_ip - start;
if (offset < 0 || (u64)line_ip > end)
offset = -1;
+ else
+ parsed_line = tmp2 + 1;
}
- objdump_line = objdump_line__new(offset, line, privsize);
- if (objdump_line == NULL) {
- free(line);
+ objdump_line = objdump_line__new(offset, parsed_line, privsize);
+ free(line);
+
+ if (objdump_line == NULL)
return -1;
- }
+
objdump__add_line(&notes->src->source, objdump_line);
return 0;
@@ -315,7 +326,7 @@ fallback:
"Please use:\n\n"
" perf buildid-cache -av vmlinux\n\n"
"or:\n\n"
- " --vmlinux vmlinux",
+ " --vmlinux vmlinux\n",
sym->name, build_id_msg ?: "");
goto out_free_filename;
}
@@ -408,7 +419,7 @@ static int symbol__get_source_line(struct symbol *sym, struct map *map,
if (!notes->src->lines)
return -1;
- start = map->unmap_ip(map, sym->start);
+ start = map__rip_2objdump(map, sym->start);
for (i = 0; i < len; i++) {
char *path = NULL;
@@ -493,6 +504,7 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx,
const char *filename = dso->long_name, *d_filename;
struct annotation *notes = symbol__annotation(sym);
struct objdump_line *pos, *queue = NULL;
+ u64 start = map__rip_2objdump(map, sym->start);
int printed = 2, queue_len = 0;
int more = 0;
u64 len;
@@ -516,8 +528,9 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx,
queue_len = 0;
}
- switch (objdump_line__print(pos, sym, evidx, len, min_pcnt,
- printed, max_lines, queue)) {
+ switch (objdump_line__print(pos, sym, start, evidx, len,
+ min_pcnt, printed, max_lines,
+ queue)) {
case 0:
++printed;
if (context) {
@@ -561,16 +574,12 @@ void symbol__annotate_decay_histogram(struct symbol *sym, int evidx)
{
struct annotation *notes = symbol__annotation(sym);
struct sym_hist *h = annotation__histogram(notes, evidx);
- struct objdump_line *pos;
- int len = sym->end - sym->start;
+ int len = sym->end - sym->start, offset;
h->sum = 0;
-
- list_for_each_entry(pos, &notes->src->source, node) {
- if (pos->offset != -1 && pos->offset < len) {
- h->addr[pos->offset] = h->addr[pos->offset] * 7 / 8;
- h->sum += h->addr[pos->offset];
- }
+ for (offset = 0; offset < len; ++offset) {
+ h->addr[offset] = h->addr[offset] * 7 / 8;
+ h->sum += h->addr[offset];
}
}
diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h
index fc5e5a09d5b9..8dd224df3e54 100644
--- a/tools/perf/util/cache.h
+++ b/tools/perf/util/cache.h
@@ -45,6 +45,18 @@ void setup_browser(bool fallback_to_pager);
void exit_browser(bool wait_for_ok);
#endif
+#ifdef NO_GTK2_SUPPORT
+static inline void perf_gtk_setup_browser(int argc __used, const char *argv[] __used, bool fallback_to_pager)
+{
+ if (fallback_to_pager)
+ setup_pager();
+}
+static inline void perf_gtk_exit_browser(bool wait_for_ok __used) {}
+#else
+void perf_gtk_setup_browser(int argc, const char *argv[], bool fallback_to_pager);
+void perf_gtk_exit_browser(bool wait_for_ok);
+#endif
+
char *alias_lookup(const char *alias);
int split_cmdline(char *cmdline, const char ***argv);
diff --git a/tools/perf/util/color.c b/tools/perf/util/color.c
index 521c38a79190..11e46da17bbb 100644
--- a/tools/perf/util/color.c
+++ b/tools/perf/util/color.c
@@ -1,3 +1,4 @@
+#include <linux/kernel.h>
#include "cache.h"
#include "color.h"
@@ -182,12 +183,12 @@ static int __color_vsnprintf(char *bf, size_t size, const char *color,
}
if (perf_use_color_default && *color)
- r += snprintf(bf, size, "%s", color);
- r += vsnprintf(bf + r, size - r, fmt, args);
+ r += scnprintf(bf, size, "%s", color);
+ r += vscnprintf(bf + r, size - r, fmt, args);
if (perf_use_color_default && *color)
- r += snprintf(bf + r, size - r, "%s", PERF_COLOR_RESET);
+ r += scnprintf(bf + r, size - r, "%s", PERF_COLOR_RESET);
if (trail)
- r += snprintf(bf + r, size - r, "%s", trail);
+ r += scnprintf(bf + r, size - r, "%s", trail);
return r;
}
diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h
index f2ce88d04f54..6bebe7f0a20c 100644
--- a/tools/perf/util/debug.h
+++ b/tools/perf/util/debug.h
@@ -26,7 +26,7 @@ static inline void ui_progress__update(u64 curr __used, u64 total __used,
#else
extern char ui_helpline__last_msg[];
int ui_helpline__show_help(const char *format, va_list ap);
-#include "ui/progress.h"
+#include "../ui/progress.h"
int ui__error(const char *format, ...) __attribute__((format(printf, 1, 2)));
#endif
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
index cbdeaad9c5e5..1b197280c621 100644
--- a/tools/perf/util/event.h
+++ b/tools/perf/util/event.h
@@ -81,6 +81,7 @@ struct perf_sample {
u32 raw_size;
void *raw_data;
struct ip_callchain *callchain;
+ struct branch_stack *branch_stack;
};
#define BUILD_ID_SIZE 20
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c
index f8da9fada002..1986d8051bd1 100644
--- a/tools/perf/util/evlist.c
+++ b/tools/perf/util/evlist.c
@@ -51,13 +51,15 @@ struct perf_evlist *perf_evlist__new(struct cpu_map *cpus,
void perf_evlist__config_attrs(struct perf_evlist *evlist,
struct perf_record_opts *opts)
{
- struct perf_evsel *evsel;
+ struct perf_evsel *evsel, *first;
if (evlist->cpus->map[0] < 0)
opts->no_inherit = true;
+ first = list_entry(evlist->entries.next, struct perf_evsel, node);
+
list_for_each_entry(evsel, &evlist->entries, node) {
- perf_evsel__config(evsel, opts);
+ perf_evsel__config(evsel, opts, first);
if (evlist->nr_entries > 1)
evsel->attr.sample_type |= PERF_SAMPLE_ID;
@@ -765,6 +767,7 @@ out_err:
list_for_each_entry_reverse(evsel, &evlist->entries, node)
perf_evsel__close(evsel, ncpus, nthreads);
+ errno = -err;
return err;
}
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index 302d49a9f985..8c13dbcb84b9 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -34,7 +34,7 @@ int __perf_evsel__sample_size(u64 sample_type)
return size;
}
-static void hists__init(struct hists *hists)
+void hists__init(struct hists *hists)
{
memset(hists, 0, sizeof(*hists));
hists->entries_in_array[0] = hists->entries_in_array[1] = RB_ROOT;
@@ -63,7 +63,8 @@ struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx)
return evsel;
}
-void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts)
+void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts,
+ struct perf_evsel *first)
{
struct perf_event_attr *attr = &evsel->attr;
int track = !evsel->idx; /* only the first counter needs these */
@@ -126,11 +127,16 @@ void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts)
attr->watermark = 0;
attr->wakeup_events = 1;
}
+ if (opts->branch_stack) {
+ attr->sample_type |= PERF_SAMPLE_BRANCH_STACK;
+ attr->branch_sample_type = opts->branch_stack;
+ }
attr->mmap = track;
attr->comm = track;
- if (!opts->target_pid && !opts->target_tid && !opts->system_wide) {
+ if (!opts->target_pid && !opts->target_tid && !opts->system_wide &&
+ (!opts->group || evsel == first)) {
attr->disabled = 1;
attr->enable_on_exec = 1;
}
@@ -574,8 +580,20 @@ int perf_event__parse_sample(const union perf_event *event, u64 type,
return -EFAULT;
data->raw_data = (void *) pdata;
+
+ array = (void *)array + data->raw_size + sizeof(u32);
}
+ if (type & PERF_SAMPLE_BRANCH_STACK) {
+ u64 sz;
+
+ data->branch_stack = (struct branch_stack *)array;
+ array++; /* nr */
+
+ sz = data->branch_stack->nr * sizeof(struct branch_entry);
+ sz /= sizeof(u64);
+ array += sz;
+ }
return 0;
}
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h
index 326b8e4d5035..3d6b3e4cb66b 100644
--- a/tools/perf/util/evsel.h
+++ b/tools/perf/util/evsel.h
@@ -80,7 +80,8 @@ void perf_evsel__exit(struct perf_evsel *evsel);
void perf_evsel__delete(struct perf_evsel *evsel);
void perf_evsel__config(struct perf_evsel *evsel,
- struct perf_record_opts *opts);
+ struct perf_record_opts *opts,
+ struct perf_evsel *first);
int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads);
int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads);
@@ -169,4 +170,6 @@ static inline int perf_evsel__sample_size(struct perf_evsel *evsel)
return __perf_evsel__sample_size(evsel->attr.sample_type);
}
+void hists__init(struct hists *hists);
+
#endif /* __PERF_EVSEL_H */
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 9f867d96c6a5..4c7c2d73251f 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -291,7 +291,7 @@ int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
if (realname == NULL || filename == NULL || linkname == NULL)
goto out_free;
- len = snprintf(filename, size, "%s%s%s",
+ len = scnprintf(filename, size, "%s%s%s",
debugdir, is_kallsyms ? "/" : "", realname);
if (mkdir_p(filename, 0755))
goto out_free;
@@ -306,7 +306,7 @@ int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
goto out_free;
}
- len = snprintf(linkname, size, "%s/.build-id/%.2s",
+ len = scnprintf(linkname, size, "%s/.build-id/%.2s",
debugdir, sbuild_id);
if (access(linkname, X_OK) && mkdir_p(linkname, 0755))
@@ -1023,6 +1023,12 @@ write_it:
return do_write_string(fd, buffer);
}
+static int write_branch_stack(int fd __used, struct perf_header *h __used,
+ struct perf_evlist *evlist __used)
+{
+ return 0;
+}
+
static void print_hostname(struct perf_header *ph, int fd, FILE *fp)
{
char *str = do_read_string(fd, ph);
@@ -1144,8 +1150,9 @@ static void print_event_desc(struct perf_header *ph, int fd, FILE *fp)
uint64_t id;
void *buf = NULL;
char *str;
- u32 nre, sz, nr, i, j, msz;
- int ret;
+ u32 nre, sz, nr, i, j;
+ ssize_t ret;
+ size_t msz;
/* number of events */
ret = read(fd, &nre, sizeof(nre));
@@ -1162,15 +1169,9 @@ static void print_event_desc(struct perf_header *ph, int fd, FILE *fp)
if (ph->needs_swap)
sz = bswap_32(sz);
- /*
- * ensure it is at least to our ABI rev
- */
- if (sz < (u32)sizeof(attr))
- goto error;
-
memset(&attr, 0, sizeof(attr));
- /* read entire region to sync up to next field */
+ /* buffer to hold on file attr struct */
buf = malloc(sz);
if (!buf)
goto error;
@@ -1181,6 +1182,10 @@ static void print_event_desc(struct perf_header *ph, int fd, FILE *fp)
for (i = 0 ; i < nre; i++) {
+ /*
+ * must read entire on-file attr struct to
+ * sync up with layout.
+ */
ret = read(fd, buf, sz);
if (ret != (ssize_t)sz)
goto error;
@@ -1316,6 +1321,12 @@ static void print_cpuid(struct perf_header *ph, int fd, FILE *fp)
free(str);
}
+static void print_branch_stack(struct perf_header *ph __used, int fd __used,
+ FILE *fp)
+{
+ fprintf(fp, "# contains samples with branch stack\n");
+}
+
static int __event_process_build_id(struct build_id_event *bev,
char *filename,
struct perf_session *session)
@@ -1520,6 +1531,7 @@ static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = {
FEAT_OPA(HEADER_CMDLINE, cmdline),
FEAT_OPF(HEADER_CPU_TOPOLOGY, cpu_topology),
FEAT_OPF(HEADER_NUMA_TOPOLOGY, numa_topology),
+ FEAT_OPA(HEADER_BRANCH_STACK, branch_stack),
};
struct header_print_data {
@@ -1804,35 +1816,101 @@ out_free:
return err;
}
-static int check_magic_endian(u64 *magic, struct perf_file_header *header,
- struct perf_header *ph)
+static const int attr_file_abi_sizes[] = {
+ [0] = PERF_ATTR_SIZE_VER0,
+ [1] = PERF_ATTR_SIZE_VER1,
+ 0,
+};
+
+/*
+ * In the legacy file format, the magic number is not used to encode endianness.
+ * hdr_sz was used to encode endianness. But given that hdr_sz can vary based
+ * on ABI revisions, we need to try all combinations for all endianness to
+ * detect the endianness.
+ */
+static int try_all_file_abis(uint64_t hdr_sz, struct perf_header *ph)
{
- int ret;
+ uint64_t ref_size, attr_size;
+ int i;
- /* check for legacy format */
- ret = memcmp(magic, __perf_magic1, sizeof(*magic));
- if (ret == 0) {
- pr_debug("legacy perf.data format\n");
- if (!header)
- return -1;
+ for (i = 0 ; attr_file_abi_sizes[i]; i++) {
+ ref_size = attr_file_abi_sizes[i]
+ + sizeof(struct perf_file_section);
+ if (hdr_sz != ref_size) {
+ attr_size = bswap_64(hdr_sz);
+ if (attr_size != ref_size)
+ continue;
- if (header->attr_size != sizeof(struct perf_file_attr)) {
- u64 attr_size = bswap_64(header->attr_size);
+ ph->needs_swap = true;
+ }
+ pr_debug("ABI%d perf.data file detected, need_swap=%d\n",
+ i,
+ ph->needs_swap);
+ return 0;
+ }
+ /* could not determine endianness */
+ return -1;
+}
- if (attr_size != sizeof(struct perf_file_attr))
- return -1;
+#define PERF_PIPE_HDR_VER0 16
+
+static const size_t attr_pipe_abi_sizes[] = {
+ [0] = PERF_PIPE_HDR_VER0,
+ 0,
+};
+
+/*
+ * In the legacy pipe format, there is an implicit assumption that endiannesss
+ * between host recording the samples, and host parsing the samples is the
+ * same. This is not always the case given that the pipe output may always be
+ * redirected into a file and analyzed on a different machine with possibly a
+ * different endianness and perf_event ABI revsions in the perf tool itself.
+ */
+static int try_all_pipe_abis(uint64_t hdr_sz, struct perf_header *ph)
+{
+ u64 attr_size;
+ int i;
+
+ for (i = 0 ; attr_pipe_abi_sizes[i]; i++) {
+ if (hdr_sz != attr_pipe_abi_sizes[i]) {
+ attr_size = bswap_64(hdr_sz);
+ if (attr_size != hdr_sz)
+ continue;
ph->needs_swap = true;
}
+ pr_debug("Pipe ABI%d perf.data file detected\n", i);
return 0;
}
+ return -1;
+}
+
+static int check_magic_endian(u64 magic, uint64_t hdr_sz,
+ bool is_pipe, struct perf_header *ph)
+{
+ int ret;
+
+ /* check for legacy format */
+ ret = memcmp(&magic, __perf_magic1, sizeof(magic));
+ if (ret == 0) {
+ pr_debug("legacy perf.data format\n");
+ if (is_pipe)
+ return try_all_pipe_abis(hdr_sz, ph);
+
+ return try_all_file_abis(hdr_sz, ph);
+ }
+ /*
+ * the new magic number serves two purposes:
+ * - unique number to identify actual perf.data files
+ * - encode endianness of file
+ */
- /* check magic number with same endianness */
- if (*magic == __perf_magic2)
+ /* check magic number with one endianness */
+ if (magic == __perf_magic2)
return 0;
- /* check magic number but opposite endianness */
- if (*magic != __perf_magic2_sw)
+ /* check magic number with opposite endianness */
+ if (magic != __perf_magic2_sw)
return -1;
ph->needs_swap = true;
@@ -1851,8 +1929,11 @@ int perf_file_header__read(struct perf_file_header *header,
if (ret <= 0)
return -1;
- if (check_magic_endian(&header->magic, header, ph) < 0)
+ if (check_magic_endian(header->magic,
+ header->attr_size, false, ph) < 0) {
+ pr_debug("magic/endian check failed\n");
return -1;
+ }
if (ph->needs_swap) {
mem_bswap_64(header, offsetof(struct perf_file_header,
@@ -1939,21 +2020,17 @@ static int perf_file_header__read_pipe(struct perf_pipe_file_header *header,
if (ret <= 0)
return -1;
- if (check_magic_endian(&header->magic, NULL, ph) < 0)
+ if (check_magic_endian(header->magic, header->size, true, ph) < 0) {
+ pr_debug("endian/magic failed\n");
return -1;
+ }
+
+ if (ph->needs_swap)
+ header->size = bswap_64(header->size);
if (repipe && do_write(STDOUT_FILENO, header, sizeof(*header)) < 0)
return -1;
- if (header->size != sizeof(*header)) {
- u64 size = bswap_64(header->size);
-
- if (size != sizeof(*header))
- return -1;
-
- ph->needs_swap = true;
- }
-
return 0;
}
@@ -1973,6 +2050,52 @@ static int perf_header__read_pipe(struct perf_session *session, int fd)
return 0;
}
+static int read_attr(int fd, struct perf_header *ph,
+ struct perf_file_attr *f_attr)
+{
+ struct perf_event_attr *attr = &f_attr->attr;
+ size_t sz, left;
+ size_t our_sz = sizeof(f_attr->attr);
+ int ret;
+
+ memset(f_attr, 0, sizeof(*f_attr));
+
+ /* read minimal guaranteed structure */
+ ret = readn(fd, attr, PERF_ATTR_SIZE_VER0);
+ if (ret <= 0) {
+ pr_debug("cannot read %d bytes of header attr\n",
+ PERF_ATTR_SIZE_VER0);
+ return -1;
+ }
+
+ /* on file perf_event_attr size */
+ sz = attr->size;
+
+ if (ph->needs_swap)
+ sz = bswap_32(sz);
+
+ if (sz == 0) {
+ /* assume ABI0 */
+ sz = PERF_ATTR_SIZE_VER0;
+ } else if (sz > our_sz) {
+ pr_debug("file uses a more recent and unsupported ABI"
+ " (%zu bytes extra)\n", sz - our_sz);
+ return -1;
+ }
+ /* what we have not yet read and that we know about */
+ left = sz - PERF_ATTR_SIZE_VER0;
+ if (left) {
+ void *ptr = attr;
+ ptr += PERF_ATTR_SIZE_VER0;
+
+ ret = readn(fd, ptr, left);
+ }
+ /* read perf_file_section, ids are read in caller */
+ ret = readn(fd, &f_attr->ids, sizeof(f_attr->ids));
+
+ return ret <= 0 ? -1 : 0;
+}
+
int perf_session__read_header(struct perf_session *session, int fd)
{
struct perf_header *header = &session->header;
@@ -1988,19 +2111,17 @@ int perf_session__read_header(struct perf_session *session, int fd)
if (session->fd_pipe)
return perf_header__read_pipe(session, fd);
- if (perf_file_header__read(&f_header, header, fd) < 0) {
- pr_debug("incompatible file format\n");
+ if (perf_file_header__read(&f_header, header, fd) < 0)
return -EINVAL;
- }
- nr_attrs = f_header.attrs.size / sizeof(f_attr);
+ nr_attrs = f_header.attrs.size / f_header.attr_size;
lseek(fd, f_header.attrs.offset, SEEK_SET);
for (i = 0; i < nr_attrs; i++) {
struct perf_evsel *evsel;
off_t tmp;
- if (readn(fd, &f_attr, sizeof(f_attr)) <= 0)
+ if (read_attr(fd, header, &f_attr) < 0)
goto out_errno;
if (header->needs_swap)
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h
index e68f617d082f..21a6be09c129 100644
--- a/tools/perf/util/header.h
+++ b/tools/perf/util/header.h
@@ -27,7 +27,7 @@ enum {
HEADER_EVENT_DESC,
HEADER_CPU_TOPOLOGY,
HEADER_NUMA_TOPOLOGY,
-
+ HEADER_BRANCH_STACK,
HEADER_LAST_FEATURE,
HEADER_FEAT_BITS = 256,
};
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index 6f505d1abac7..9f6d630d5316 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -10,11 +10,14 @@ static bool hists__filter_entry_by_dso(struct hists *hists,
struct hist_entry *he);
static bool hists__filter_entry_by_thread(struct hists *hists,
struct hist_entry *he);
+static bool hists__filter_entry_by_symbol(struct hists *hists,
+ struct hist_entry *he);
enum hist_filter {
HIST_FILTER__DSO,
HIST_FILTER__THREAD,
HIST_FILTER__PARENT,
+ HIST_FILTER__SYMBOL,
};
struct callchain_param callchain_param = {
@@ -50,21 +53,25 @@ static void hists__reset_col_len(struct hists *hists)
hists__set_col_len(hists, col, 0);
}
+static void hists__set_unres_dso_col_len(struct hists *hists, int dso)
+{
+ const unsigned int unresolved_col_width = BITS_PER_LONG / 4;
+
+ if (hists__col_len(hists, dso) < unresolved_col_width &&
+ !symbol_conf.col_width_list_str && !symbol_conf.field_sep &&
+ !symbol_conf.dso_list)
+ hists__set_col_len(hists, dso, unresolved_col_width);
+}
+
static void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
{
+ const unsigned int unresolved_col_width = BITS_PER_LONG / 4;
u16 len;
if (h->ms.sym)
- hists__new_col_len(hists, HISTC_SYMBOL, h->ms.sym->namelen);
- else {
- const unsigned int unresolved_col_width = BITS_PER_LONG / 4;
-
- if (hists__col_len(hists, HISTC_DSO) < unresolved_col_width &&
- !symbol_conf.col_width_list_str && !symbol_conf.field_sep &&
- !symbol_conf.dso_list)
- hists__set_col_len(hists, HISTC_DSO,
- unresolved_col_width);
- }
+ hists__new_col_len(hists, HISTC_SYMBOL, h->ms.sym->namelen + 4);
+ else
+ hists__set_unres_dso_col_len(hists, HISTC_DSO);
len = thread__comm_len(h->thread);
if (hists__new_col_len(hists, HISTC_COMM, len))
@@ -74,6 +81,37 @@ static void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
len = dso__name_len(h->ms.map->dso);
hists__new_col_len(hists, HISTC_DSO, len);
}
+
+ if (h->branch_info) {
+ int symlen;
+ /*
+ * +4 accounts for '[x] ' priv level info
+ * +2 account of 0x prefix on raw addresses
+ */
+ if (h->branch_info->from.sym) {
+ symlen = (int)h->branch_info->from.sym->namelen + 4;
+ hists__new_col_len(hists, HISTC_SYMBOL_FROM, symlen);
+
+ symlen = dso__name_len(h->branch_info->from.map->dso);
+ hists__new_col_len(hists, HISTC_DSO_FROM, symlen);
+ } else {
+ symlen = unresolved_col_width + 4 + 2;
+ hists__new_col_len(hists, HISTC_SYMBOL_FROM, symlen);
+ hists__set_unres_dso_col_len(hists, HISTC_DSO_FROM);
+ }
+
+ if (h->branch_info->to.sym) {
+ symlen = (int)h->branch_info->to.sym->namelen + 4;
+ hists__new_col_len(hists, HISTC_SYMBOL_TO, symlen);
+
+ symlen = dso__name_len(h->branch_info->to.map->dso);
+ hists__new_col_len(hists, HISTC_DSO_TO, symlen);
+ } else {
+ symlen = unresolved_col_width + 4 + 2;
+ hists__new_col_len(hists, HISTC_SYMBOL_TO, symlen);
+ hists__set_unres_dso_col_len(hists, HISTC_DSO_TO);
+ }
+ }
}
static void hist_entry__add_cpumode_period(struct hist_entry *he,
@@ -195,26 +233,14 @@ static u8 symbol__parent_filter(const struct symbol *parent)
return 0;
}
-struct hist_entry *__hists__add_entry(struct hists *hists,
+static struct hist_entry *add_hist_entry(struct hists *hists,
+ struct hist_entry *entry,
struct addr_location *al,
- struct symbol *sym_parent, u64 period)
+ u64 period)
{
struct rb_node **p;
struct rb_node *parent = NULL;
struct hist_entry *he;
- struct hist_entry entry = {
- .thread = al->thread,
- .ms = {
- .map = al->map,
- .sym = al->sym,
- },
- .cpu = al->cpu,
- .ip = al->addr,
- .level = al->level,
- .period = period,
- .parent = sym_parent,
- .filtered = symbol__parent_filter(sym_parent),
- };
int cmp;
pthread_mutex_lock(&hists->lock);
@@ -225,11 +251,23 @@ struct hist_entry *__hists__add_entry(struct hists *hists,
parent = *p;
he = rb_entry(parent, struct hist_entry, rb_node_in);
- cmp = hist_entry__cmp(&entry, he);
+ cmp = hist_entry__cmp(entry, he);
if (!cmp) {
he->period += period;
++he->nr_events;
+
+ /* If the map of an existing hist_entry has
+ * become out-of-date due to an exec() or
+ * similar, update it. Otherwise we will
+ * mis-adjust symbol addresses when computing
+ * the history counter to increment.
+ */
+ if (he->ms.map != entry->ms.map) {
+ he->ms.map = entry->ms.map;
+ if (he->ms.map)
+ he->ms.map->referenced = true;
+ }
goto out;
}
@@ -239,7 +277,7 @@ struct hist_entry *__hists__add_entry(struct hists *hists,
p = &(*p)->rb_right;
}
- he = hist_entry__new(&entry);
+ he = hist_entry__new(entry);
if (!he)
goto out_unlock;
@@ -252,6 +290,51 @@ out_unlock:
return he;
}
+struct hist_entry *__hists__add_branch_entry(struct hists *self,
+ struct addr_location *al,
+ struct symbol *sym_parent,
+ struct branch_info *bi,
+ u64 period)
+{
+ struct hist_entry entry = {
+ .thread = al->thread,
+ .ms = {
+ .map = bi->to.map,
+ .sym = bi->to.sym,
+ },
+ .cpu = al->cpu,
+ .ip = bi->to.addr,
+ .level = al->level,
+ .period = period,
+ .parent = sym_parent,
+ .filtered = symbol__parent_filter(sym_parent),
+ .branch_info = bi,
+ };
+
+ return add_hist_entry(self, &entry, al, period);
+}
+
+struct hist_entry *__hists__add_entry(struct hists *self,
+ struct addr_location *al,
+ struct symbol *sym_parent, u64 period)
+{
+ struct hist_entry entry = {
+ .thread = al->thread,
+ .ms = {
+ .map = al->map,
+ .sym = al->sym,
+ },
+ .cpu = al->cpu,
+ .ip = al->addr,
+ .level = al->level,
+ .period = period,
+ .parent = sym_parent,
+ .filtered = symbol__parent_filter(sym_parent),
+ };
+
+ return add_hist_entry(self, &entry, al, period);
+}
+
int64_t
hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)
{
@@ -352,6 +435,7 @@ static void hists__apply_filters(struct hists *hists, struct hist_entry *he)
{
hists__filter_entry_by_dso(hists, he);
hists__filter_entry_by_thread(hists, he);
+ hists__filter_entry_by_symbol(hists, he);
}
static void __hists__collapse_resort(struct hists *hists, bool threaded)
@@ -535,7 +619,7 @@ static void init_rem_hits(void)
rem_hits.ms.sym = rem_sq_bracket;
}
-static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
+static size_t __callchain__fprintf_graph(FILE *fp, struct rb_root *root,
u64 total_samples, int depth,
int depth_mask, int left_margin)
{
@@ -543,21 +627,16 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
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;
uint entries_printed = 0;
- if (callchain_param.mode == CHAIN_GRAPH_REL)
- new_total = self->children_hit;
- else
- new_total = total_samples;
+ remaining = total_samples;
- remaining = new_total;
-
- node = rb_first(&self->rb_root);
+ node = rb_first(root);
while (node) {
+ u64 new_total;
u64 cumul;
child = rb_entry(node, struct callchain_node, rb_node);
@@ -585,11 +664,17 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
list_for_each_entry(chain, &child->val, list) {
ret += ipchain__fprintf_graph(fp, chain, depth,
new_depth_mask, i++,
- new_total,
+ total_samples,
cumul,
left_margin);
}
- ret += __callchain__fprintf_graph(fp, child, new_total,
+
+ if (callchain_param.mode == CHAIN_GRAPH_REL)
+ new_total = child->children_hit;
+ else
+ new_total = total_samples;
+
+ ret += __callchain__fprintf_graph(fp, &child->rb_root, new_total,
depth + 1,
new_depth_mask | (1 << depth),
left_margin);
@@ -599,61 +684,75 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
}
if (callchain_param.mode == CHAIN_GRAPH_REL &&
- remaining && remaining != new_total) {
+ remaining && remaining != total_samples) {
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,
+ new_depth_mask, 0, total_samples,
remaining, left_margin);
}
return ret;
}
-static size_t callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
+static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root,
u64 total_samples, int left_margin)
{
+ struct callchain_node *cnode;
struct callchain_list *chain;
+ u32 entries_printed = 0;
bool printed = false;
+ struct rb_node *node;
int i = 0;
- int ret = 0;
- u32 entries_printed = 0;
-
- list_for_each_entry(chain, &self->val, list) {
- if (!i++ && sort__first_dimension == SORT_SYM)
- continue;
-
- if (!printed) {
- ret += callchain__fprintf_left_margin(fp, left_margin);
- ret += fprintf(fp, "|\n");
- ret += callchain__fprintf_left_margin(fp, left_margin);
- ret += fprintf(fp, "---");
-
- left_margin += 3;
- printed = true;
- } else
- ret += callchain__fprintf_left_margin(fp, left_margin);
+ int ret;
- if (chain->ms.sym)
- ret += fprintf(fp, " %s\n", chain->ms.sym->name);
- else
- ret += fprintf(fp, " %p\n", (void *)(long)chain->ip);
+ /*
+ * If have one single callchain root, don't bother printing
+ * its percentage (100 % in fractal mode and the same percentage
+ * than the hist in graph mode). This also avoid one level of column.
+ */
+ node = rb_first(root);
+ if (node && !rb_next(node)) {
+ cnode = rb_entry(node, struct callchain_node, rb_node);
+ list_for_each_entry(chain, &cnode->val, list) {
+ /*
+ * If we sort by symbol, the first entry is the same than
+ * the symbol. No need to print it otherwise it appears as
+ * displayed twice.
+ */
+ if (!i++ && sort__first_dimension == SORT_SYM)
+ continue;
+ if (!printed) {
+ ret += callchain__fprintf_left_margin(fp, left_margin);
+ ret += fprintf(fp, "|\n");
+ ret += callchain__fprintf_left_margin(fp, left_margin);
+ ret += fprintf(fp, "---");
+ left_margin += 3;
+ printed = true;
+ } else
+ ret += callchain__fprintf_left_margin(fp, left_margin);
+
+ if (chain->ms.sym)
+ ret += fprintf(fp, " %s\n", chain->ms.sym->name);
+ else
+ ret += fprintf(fp, " %p\n", (void *)(long)chain->ip);
- if (++entries_printed == callchain_param.print_limit)
- break;
+ if (++entries_printed == callchain_param.print_limit)
+ break;
+ }
+ root = &cnode->rb_root;
}
- ret += __callchain__fprintf_graph(fp, self, total_samples, 1, 1, left_margin);
-
- return ret;
+ return __callchain__fprintf_graph(fp, root, total_samples,
+ 1, 1, left_margin);
}
-static size_t callchain__fprintf_flat(FILE *fp, struct callchain_node *self,
- u64 total_samples)
+static size_t __callchain__fprintf_flat(FILE *fp,
+ struct callchain_node *self,
+ u64 total_samples)
{
struct callchain_list *chain;
size_t ret = 0;
@@ -661,7 +760,7 @@ static size_t callchain__fprintf_flat(FILE *fp, struct callchain_node *self,
if (!self)
return 0;
- ret += callchain__fprintf_flat(fp, self->parent, total_samples);
+ ret += __callchain__fprintf_flat(fp, self->parent, total_samples);
list_for_each_entry(chain, &self->val, list) {
@@ -677,44 +776,58 @@ static size_t callchain__fprintf_flat(FILE *fp, struct callchain_node *self,
return ret;
}
-static size_t hist_entry_callchain__fprintf(struct hist_entry *he,
- u64 total_samples, int left_margin,
- FILE *fp)
+static size_t callchain__fprintf_flat(FILE *fp, struct rb_root *self,
+ u64 total_samples)
{
- struct rb_node *rb_node;
- struct callchain_node *chain;
size_t ret = 0;
u32 entries_printed = 0;
+ struct rb_node *rb_node;
+ struct callchain_node *chain;
- rb_node = rb_first(&he->sorted_chain);
+ rb_node = rb_first(self);
while (rb_node) {
double percent;
chain = rb_entry(rb_node, struct callchain_node, rb_node);
percent = chain->hit * 100.0 / 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,
- left_margin);
- case CHAIN_NONE:
- default:
- break;
- }
+
+ ret = percent_color_fprintf(fp, " %6.2f%%\n", percent);
+ ret += __callchain__fprintf_flat(fp, chain, total_samples);
ret += fprintf(fp, "\n");
if (++entries_printed == callchain_param.print_limit)
break;
+
rb_node = rb_next(rb_node);
}
return ret;
}
+static size_t hist_entry_callchain__fprintf(struct hist_entry *he,
+ u64 total_samples, int left_margin,
+ FILE *fp)
+{
+ switch (callchain_param.mode) {
+ case CHAIN_GRAPH_REL:
+ return callchain__fprintf_graph(fp, &he->sorted_chain, he->period,
+ left_margin);
+ break;
+ case CHAIN_GRAPH_ABS:
+ return callchain__fprintf_graph(fp, &he->sorted_chain, total_samples,
+ left_margin);
+ break;
+ case CHAIN_FLAT:
+ return callchain__fprintf_flat(fp, &he->sorted_chain, total_samples);
+ break;
+ case CHAIN_NONE:
+ break;
+ default:
+ pr_err("Bad callchain mode\n");
+ }
+
+ return 0;
+}
+
void hists__output_recalc_col_len(struct hists *hists, int max_rows)
{
struct rb_node *next = rb_first(&hists->entries);
@@ -768,7 +881,7 @@ static int hist_entry__pcnt_snprintf(struct hist_entry *he, char *s,
sep ? "%.2f" : " %6.2f%%",
(period * 100.0) / total);
else
- ret = snprintf(s, size, sep ? "%.2f" : " %6.2f%%",
+ ret = scnprintf(s, size, sep ? "%.2f" : " %6.2f%%",
(period * 100.0) / total);
if (symbol_conf.show_cpu_utilization) {
ret += percent_color_snprintf(s + ret, size - ret,
@@ -791,20 +904,20 @@ static int hist_entry__pcnt_snprintf(struct hist_entry *he, char *s,
}
}
} else
- ret = snprintf(s, size, sep ? "%" PRIu64 : "%12" PRIu64 " ", period);
+ ret = scnprintf(s, size, sep ? "%" PRIu64 : "%12" PRIu64 " ", period);
if (symbol_conf.show_nr_samples) {
if (sep)
- ret += snprintf(s + ret, size - ret, "%c%" PRIu64, *sep, nr_events);
+ ret += scnprintf(s + ret, size - ret, "%c%" PRIu64, *sep, nr_events);
else
- ret += snprintf(s + ret, size - ret, "%11" PRIu64, nr_events);
+ ret += scnprintf(s + ret, size - ret, "%11" PRIu64, nr_events);
}
if (symbol_conf.show_total_period) {
if (sep)
- ret += snprintf(s + ret, size - ret, "%c%" PRIu64, *sep, period);
+ ret += scnprintf(s + ret, size - ret, "%c%" PRIu64, *sep, period);
else
- ret += snprintf(s + ret, size - ret, " %12" PRIu64, period);
+ ret += scnprintf(s + ret, size - ret, " %12" PRIu64, period);
}
if (pair_hists) {
@@ -819,25 +932,25 @@ static int hist_entry__pcnt_snprintf(struct hist_entry *he, char *s,
diff = new_percent - old_percent;
if (fabs(diff) >= 0.01)
- snprintf(bf, sizeof(bf), "%+4.2F%%", diff);
+ scnprintf(bf, sizeof(bf), "%+4.2F%%", diff);
else
- snprintf(bf, sizeof(bf), " ");
+ scnprintf(bf, sizeof(bf), " ");
if (sep)
- ret += snprintf(s + ret, size - ret, "%c%s", *sep, bf);
+ ret += scnprintf(s + ret, size - ret, "%c%s", *sep, bf);
else
- ret += snprintf(s + ret, size - ret, "%11.11s", bf);
+ ret += scnprintf(s + ret, size - ret, "%11.11s", bf);
if (show_displacement) {
if (displacement)
- snprintf(bf, sizeof(bf), "%+4ld", displacement);
+ scnprintf(bf, sizeof(bf), "%+4ld", displacement);
else
- snprintf(bf, sizeof(bf), " ");
+ scnprintf(bf, sizeof(bf), " ");
if (sep)
- ret += snprintf(s + ret, size - ret, "%c%s", *sep, bf);
+ ret += scnprintf(s + ret, size - ret, "%c%s", *sep, bf);
else
- ret += snprintf(s + ret, size - ret, "%6.6s", bf);
+ ret += scnprintf(s + ret, size - ret, "%6.6s", bf);
}
}
@@ -855,7 +968,7 @@ int hist_entry__snprintf(struct hist_entry *he, char *s, size_t size,
if (se->elide)
continue;
- ret += snprintf(s + ret, size - ret, "%s", sep ?: " ");
+ ret += scnprintf(s + ret, size - ret, "%s", sep ?: " ");
ret += se->se_snprintf(he, s + ret, size - ret,
hists__col_len(hists, se->se_width_idx));
}
@@ -1179,6 +1292,37 @@ void hists__filter_by_thread(struct hists *hists)
}
}
+static bool hists__filter_entry_by_symbol(struct hists *hists,
+ struct hist_entry *he)
+{
+ if (hists->symbol_filter_str != NULL &&
+ (!he->ms.sym || strstr(he->ms.sym->name,
+ hists->symbol_filter_str) == NULL)) {
+ he->filtered |= (1 << HIST_FILTER__SYMBOL);
+ return true;
+ }
+
+ return false;
+}
+
+void hists__filter_by_symbol(struct hists *hists)
+{
+ struct rb_node *nd;
+
+ hists->nr_entries = hists->stats.total_period = 0;
+ hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0;
+ hists__reset_col_len(hists);
+
+ for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
+ struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
+
+ if (hists__filter_entry_by_symbol(hists, h))
+ continue;
+
+ hists__remove_entry_filter(hists, h, HIST_FILTER__SYMBOL);
+ }
+}
+
int hist_entry__inc_addr_samples(struct hist_entry *he, int evidx, u64 ip)
{
return symbol__inc_addr_samples(he->ms.sym, he->ms.map, evidx, ip);
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h
index 48e5acd1e862..cfc64e293f90 100644
--- a/tools/perf/util/hist.h
+++ b/tools/perf/util/hist.h
@@ -42,6 +42,11 @@ enum hist_column {
HISTC_COMM,
HISTC_PARENT,
HISTC_CPU,
+ HISTC_MISPREDICT,
+ HISTC_SYMBOL_FROM,
+ HISTC_SYMBOL_TO,
+ HISTC_DSO_FROM,
+ HISTC_DSO_TO,
HISTC_NR_COLS, /* Last entry */
};
@@ -57,6 +62,7 @@ struct hists {
const struct thread *thread_filter;
const struct dso *dso_filter;
const char *uid_filter_str;
+ const char *symbol_filter_str;
pthread_mutex_t lock;
struct events_stats stats;
u64 event_stream;
@@ -74,6 +80,12 @@ int hist_entry__snprintf(struct hist_entry *self, char *bf, size_t size,
struct hists *hists);
void hist_entry__free(struct hist_entry *);
+struct hist_entry *__hists__add_branch_entry(struct hists *self,
+ struct addr_location *al,
+ struct symbol *sym_parent,
+ struct branch_info *bi,
+ u64 period);
+
void hists__output_resort(struct hists *self);
void hists__output_resort_threaded(struct hists *hists);
void hists__collapse_resort(struct hists *self);
@@ -96,6 +108,7 @@ int hist_entry__annotate(struct hist_entry *self, size_t privsize);
void hists__filter_by_dso(struct hists *hists);
void hists__filter_by_thread(struct hists *hists);
+void hists__filter_by_symbol(struct hists *hists);
u16 hists__col_len(struct hists *self, enum hist_column col);
void hists__set_col_len(struct hists *self, enum hist_column col, u16 len);
@@ -125,7 +138,7 @@ static inline int hist_entry__tui_annotate(struct hist_entry *self __used,
#define K_LEFT -1
#define K_RIGHT -2
#else
-#include "ui/keysyms.h"
+#include "../ui/keysyms.h"
int hist_entry__tui_annotate(struct hist_entry *he, int evidx,
void(*timer)(void *arg), void *arg, int delay_secs);
@@ -134,6 +147,23 @@ int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
int refresh);
#endif
+#ifdef NO_GTK2_SUPPORT
+static inline
+int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist __used,
+ const char *help __used,
+ void(*timer)(void *arg) __used,
+ void *arg __used,
+ int refresh __used)
+{
+ return 0;
+}
+
+#else
+int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, const char *help,
+ void(*timer)(void *arg), void *arg,
+ int refresh);
+#endif
+
unsigned int hists__sort_list_width(struct hists *self);
#endif /* __PERF_HIST_H */
diff --git a/tools/perf/util/include/asm/unistd_32.h b/tools/perf/util/include/asm/unistd_32.h
new file mode 100644
index 000000000000..8b137891791f
--- /dev/null
+++ b/tools/perf/util/include/asm/unistd_32.h
@@ -0,0 +1 @@
+
diff --git a/tools/perf/util/include/asm/unistd_64.h b/tools/perf/util/include/asm/unistd_64.h
new file mode 100644
index 000000000000..8b137891791f
--- /dev/null
+++ b/tools/perf/util/include/asm/unistd_64.h
@@ -0,0 +1 @@
+
diff --git a/tools/perf/util/include/linux/bitops.h b/tools/perf/util/include/linux/bitops.h
index 62cdee78db7b..f1584833bd22 100644
--- a/tools/perf/util/include/linux/bitops.h
+++ b/tools/perf/util/include/linux/bitops.h
@@ -15,7 +15,7 @@
(bit) = find_next_bit((addr), (size), (bit) + 1))
/* same as for_each_set_bit() but use bit as value to start with */
-#define for_each_set_bit_cont(bit, addr, size) \
+#define for_each_set_bit_from(bit, addr, size) \
for ((bit) = find_next_bit((addr), (size), (bit)); \
(bit) < (size); \
(bit) = find_next_bit((addr), (size), (bit) + 1))
diff --git a/tools/perf/util/include/linux/module.h b/tools/perf/util/include/linux/export.h
index b43e2dc21e04..b43e2dc21e04 100644
--- a/tools/perf/util/include/linux/module.h
+++ b/tools/perf/util/include/linux/export.h
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
index dea6d1c1a954..35ae56864e4f 100644
--- a/tools/perf/util/map.c
+++ b/tools/perf/util/map.c
@@ -38,6 +38,7 @@ void map__init(struct map *self, enum map_type type,
RB_CLEAR_NODE(&self->rb_node);
self->groups = NULL;
self->referenced = false;
+ self->erange_warned = false;
}
struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h
index b100c20b7f94..81371bad4ef0 100644
--- a/tools/perf/util/map.h
+++ b/tools/perf/util/map.h
@@ -33,6 +33,7 @@ struct map {
u64 end;
u8 /* enum map_type */ type;
bool referenced;
+ bool erange_warned;
u32 priv;
u64 pgoff;
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index b029296d20d9..5b3a0ef4e232 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -11,6 +11,10 @@
#include "cache.h"
#include "header.h"
#include "debugfs.h"
+#include "parse-events-flex.h"
+#include "pmu.h"
+
+#define MAX_NAME_LEN 100
struct event_symbol {
u8 type;
@@ -19,11 +23,8 @@ struct event_symbol {
const char *alias;
};
-enum event_result {
- EVT_FAILED,
- EVT_HANDLED,
- EVT_HANDLED_ALL
-};
+int parse_events_parse(struct list_head *list, struct list_head *list_tmp,
+ int *idx);
#define CHW(x) .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_##x
#define CSW(x) .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_##x
@@ -165,7 +166,7 @@ struct tracepoint_path *tracepoint_id_to_path(u64 config)
struct tracepoint_path *path = NULL;
DIR *sys_dir, *evt_dir;
struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent;
- char id_buf[4];
+ char id_buf[24];
int fd;
u64 id;
char evt_path[MAXPATHLEN];
@@ -354,7 +355,24 @@ const char *__event_name(int type, u64 config)
return "unknown";
}
-static int parse_aliases(const char **str, const char *names[][MAX_ALIASES], int size)
+static int add_event(struct list_head *list, int *idx,
+ struct perf_event_attr *attr, char *name)
+{
+ struct perf_evsel *evsel;
+
+ event_attr_init(attr);
+
+ evsel = perf_evsel__new(attr, (*idx)++);
+ if (!evsel)
+ return -ENOMEM;
+
+ list_add_tail(&evsel->node, list);
+
+ evsel->name = strdup(name);
+ return 0;
+}
+
+static int parse_aliases(char *str, const char *names[][MAX_ALIASES], int size)
{
int i, j;
int n, longest = -1;
@@ -362,58 +380,57 @@ static int parse_aliases(const char **str, const char *names[][MAX_ALIASES], int
for (i = 0; i < size; 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))
+ if (n > longest && !strncasecmp(str, names[i][j], n))
longest = n;
}
- if (longest > 0) {
- *str += longest;
+ if (longest > 0)
return i;
- }
}
return -1;
}
-static enum event_result
-parse_generic_hw_event(const char **str, struct perf_event_attr *attr)
+int parse_events_add_cache(struct list_head *list, int *idx,
+ char *type, char *op_result1, char *op_result2)
{
- const char *s = *str;
+ struct perf_event_attr attr;
+ char name[MAX_NAME_LEN];
int cache_type = -1, cache_op = -1, cache_result = -1;
+ char *op_result[2] = { op_result1, op_result2 };
+ int i, n;
- 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:
*/
+ cache_type = parse_aliases(type, hw_cache,
+ PERF_COUNT_HW_CACHE_MAX);
if (cache_type == -1)
- return EVT_FAILED;
+ return -EINVAL;
+
+ n = snprintf(name, MAX_NAME_LEN, "%s", type);
+
+ for (i = 0; (i < 2) && (op_result[i]); i++) {
+ char *str = op_result[i];
- while ((cache_op == -1 || cache_result == -1) && *s == '-') {
- ++s;
+ snprintf(name + n, MAX_NAME_LEN - n, "-%s\n", str);
if (cache_op == -1) {
- cache_op = parse_aliases(&s, hw_cache_op,
- PERF_COUNT_HW_CACHE_OP_MAX);
+ cache_op = parse_aliases(str, hw_cache_op,
+ PERF_COUNT_HW_CACHE_OP_MAX);
if (cache_op >= 0) {
if (!is_cache_op_valid(cache_type, cache_op))
- return EVT_FAILED;
+ return -EINVAL;
continue;
}
}
if (cache_result == -1) {
- cache_result = parse_aliases(&s, hw_cache_result,
+ cache_result = parse_aliases(str, 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;
}
/*
@@ -428,20 +445,17 @@ parse_generic_hw_event(const char **str, struct perf_event_attr *attr)
if (cache_result == -1)
cache_result = PERF_COUNT_HW_CACHE_RESULT_ACCESS;
- attr->config = cache_type | (cache_op << 8) | (cache_result << 16);
- attr->type = PERF_TYPE_HW_CACHE;
-
- *str = s;
- return EVT_HANDLED;
+ memset(&attr, 0, sizeof(attr));
+ attr.config = cache_type | (cache_op << 8) | (cache_result << 16);
+ attr.type = PERF_TYPE_HW_CACHE;
+ return add_event(list, idx, &attr, name);
}
-static enum event_result
-parse_single_tracepoint_event(char *sys_name,
- const char *evt_name,
- unsigned int evt_length,
- struct perf_event_attr *attr,
- const char **strp)
+static int add_tracepoint(struct list_head *list, int *idx,
+ char *sys_name, char *evt_name)
{
+ struct perf_event_attr attr;
+ char name[MAX_NAME_LEN];
char evt_path[MAXPATHLEN];
char id_buf[4];
u64 id;
@@ -452,130 +466,80 @@ parse_single_tracepoint_event(char *sys_name,
fd = open(evt_path, O_RDONLY);
if (fd < 0)
- return EVT_FAILED;
+ return -1;
if (read(fd, id_buf, sizeof(id_buf)) < 0) {
close(fd);
- return EVT_FAILED;
+ return -1;
}
close(fd);
id = atoll(id_buf);
- attr->config = id;
- attr->type = PERF_TYPE_TRACEPOINT;
- *strp += strlen(sys_name) + evt_length + 1; /* + 1 for the ':' */
-
- attr->sample_type |= PERF_SAMPLE_RAW;
- attr->sample_type |= PERF_SAMPLE_TIME;
- attr->sample_type |= PERF_SAMPLE_CPU;
- attr->sample_period = 1;
+ memset(&attr, 0, sizeof(attr));
+ attr.config = id;
+ attr.type = PERF_TYPE_TRACEPOINT;
+ attr.sample_type |= PERF_SAMPLE_RAW;
+ attr.sample_type |= PERF_SAMPLE_TIME;
+ attr.sample_type |= PERF_SAMPLE_CPU;
+ attr.sample_period = 1;
-
- return EVT_HANDLED;
+ snprintf(name, MAX_NAME_LEN, "%s:%s", sys_name, evt_name);
+ return add_event(list, idx, &attr, name);
}
-/* sys + ':' + event + ':' + flags*/
-#define MAX_EVOPT_LEN (MAX_EVENT_LENGTH * 2 + 2 + 128)
-static enum event_result
-parse_multiple_tracepoint_event(struct perf_evlist *evlist, char *sys_name,
- const char *evt_exp, char *flags)
+static int add_tracepoint_multi(struct list_head *list, int *idx,
+ char *sys_name, char *evt_name)
{
char evt_path[MAXPATHLEN];
struct dirent *evt_ent;
DIR *evt_dir;
+ int ret = 0;
snprintf(evt_path, MAXPATHLEN, "%s/%s", tracing_events_path, sys_name);
evt_dir = opendir(evt_path);
-
if (!evt_dir) {
perror("Can't open event dir");
- return EVT_FAILED;
+ return -1;
}
- while ((evt_ent = readdir(evt_dir))) {
- char event_opt[MAX_EVOPT_LEN + 1];
- int len;
-
+ while (!ret && (evt_ent = readdir(evt_dir))) {
if (!strcmp(evt_ent->d_name, ".")
|| !strcmp(evt_ent->d_name, "..")
|| !strcmp(evt_ent->d_name, "enable")
|| !strcmp(evt_ent->d_name, "filter"))
continue;
- if (!strglobmatch(evt_ent->d_name, evt_exp))
+ if (!strglobmatch(evt_ent->d_name, evt_name))
continue;
- len = snprintf(event_opt, MAX_EVOPT_LEN, "%s:%s%s%s", sys_name,
- evt_ent->d_name, flags ? ":" : "",
- flags ?: "");
- if (len < 0)
- return EVT_FAILED;
-
- if (parse_events(evlist, event_opt, 0))
- return EVT_FAILED;
+ ret = add_tracepoint(list, idx, sys_name, evt_ent->d_name);
}
- return EVT_HANDLED_ALL;
+ return ret;
}
-static enum event_result
-parse_tracepoint_event(struct perf_evlist *evlist, const char **strp,
- struct perf_event_attr *attr)
+int parse_events_add_tracepoint(struct list_head *list, int *idx,
+ char *sys, char *event)
{
- const char *evt_name;
- char *flags = NULL, *comma_loc;
- char sys_name[MAX_EVENT_LENGTH];
- unsigned int sys_length, evt_length;
-
- if (debugfs_valid_mountpoint(tracing_events_path))
- return 0;
-
- evt_name = strchr(*strp, ':');
- if (!evt_name)
- return EVT_FAILED;
-
- sys_length = evt_name - *strp;
- if (sys_length >= MAX_EVENT_LENGTH)
- return 0;
+ int ret;
- strncpy(sys_name, *strp, sys_length);
- sys_name[sys_length] = '\0';
- evt_name = evt_name + 1;
+ ret = debugfs_valid_mountpoint(tracing_events_path);
+ if (ret)
+ return ret;
- comma_loc = strchr(evt_name, ',');
- if (comma_loc) {
- /* take the event name up to the comma */
- evt_name = strndup(evt_name, comma_loc - evt_name);
- }
- flags = strchr(evt_name, ':');
- if (flags) {
- /* split it out: */
- evt_name = strndup(evt_name, flags - evt_name);
- flags++;
- }
-
- evt_length = strlen(evt_name);
- if (evt_length >= MAX_EVENT_LENGTH)
- return EVT_FAILED;
- if (strpbrk(evt_name, "*?")) {
- *strp += strlen(sys_name) + evt_length + 1; /* 1 == the ':' */
- return parse_multiple_tracepoint_event(evlist, sys_name,
- evt_name, flags);
- } else {
- return parse_single_tracepoint_event(sys_name, evt_name,
- evt_length, attr, strp);
- }
+ return strpbrk(event, "*?") ?
+ add_tracepoint_multi(list, idx, sys, event) :
+ add_tracepoint(list, idx, sys, event);
}
-static enum event_result
-parse_breakpoint_type(const char *type, const char **strp,
- struct perf_event_attr *attr)
+static int
+parse_breakpoint_type(const char *type, struct perf_event_attr *attr)
{
int i;
for (i = 0; i < 3; i++) {
- if (!type[i])
+ if (!type || !type[i])
break;
switch (type[i]) {
@@ -589,164 +553,146 @@ parse_breakpoint_type(const char *type, const char **strp,
attr->bp_type |= HW_BREAKPOINT_X;
break;
default:
- return EVT_FAILED;
+ return -EINVAL;
}
}
+
if (!attr->bp_type) /* Default */
attr->bp_type = HW_BREAKPOINT_R | HW_BREAKPOINT_W;
- *strp = type + i;
-
- return EVT_HANDLED;
+ return 0;
}
-static enum event_result
-parse_breakpoint_event(const char **strp, struct perf_event_attr *attr)
+int parse_events_add_breakpoint(struct list_head *list, int *idx,
+ void *ptr, char *type)
{
- const char *target;
- const char *type;
- char *endaddr;
- u64 addr;
- enum event_result err;
-
- target = strchr(*strp, ':');
- if (!target)
- return EVT_FAILED;
-
- if (strncmp(*strp, "mem", target - *strp) != 0)
- return EVT_FAILED;
-
- target++;
-
- addr = strtoull(target, &endaddr, 0);
- if (target == endaddr)
- return EVT_FAILED;
-
- attr->bp_addr = addr;
- *strp = endaddr;
+ struct perf_event_attr attr;
+ char name[MAX_NAME_LEN];
- type = strchr(target, ':');
+ memset(&attr, 0, sizeof(attr));
+ attr.bp_addr = (unsigned long) ptr;
- /* If no type is defined, just rw as default */
- if (!type) {
- attr->bp_type = HW_BREAKPOINT_R | HW_BREAKPOINT_W;
- } else {
- err = parse_breakpoint_type(++type, strp, attr);
- if (err == EVT_FAILED)
- return EVT_FAILED;
- }
+ if (parse_breakpoint_type(type, &attr))
+ return -EINVAL;
/*
* We should find a nice way to override the access length
* Provide some defaults for now
*/
- if (attr->bp_type == HW_BREAKPOINT_X)
- attr->bp_len = sizeof(long);
+ if (attr.bp_type == HW_BREAKPOINT_X)
+ attr.bp_len = sizeof(long);
else
- attr->bp_len = HW_BREAKPOINT_LEN_4;
+ attr.bp_len = HW_BREAKPOINT_LEN_4;
- attr->type = PERF_TYPE_BREAKPOINT;
+ attr.type = PERF_TYPE_BREAKPOINT;
- return EVT_HANDLED;
+ snprintf(name, MAX_NAME_LEN, "mem:%p:%s", ptr, type ? type : "rw");
+ return add_event(list, idx, &attr, name);
}
-static int check_events(const char *str, unsigned int i)
+static int config_term(struct perf_event_attr *attr,
+ struct parse_events__term *term)
{
- int n;
+ switch (term->type) {
+ case PARSE_EVENTS__TERM_TYPE_CONFIG:
+ attr->config = term->val.num;
+ break;
+ case PARSE_EVENTS__TERM_TYPE_CONFIG1:
+ attr->config1 = term->val.num;
+ break;
+ case PARSE_EVENTS__TERM_TYPE_CONFIG2:
+ attr->config2 = term->val.num;
+ break;
+ case PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD:
+ attr->sample_period = term->val.num;
+ break;
+ case PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE:
+ /*
+ * TODO uncomment when the field is available
+ * attr->branch_sample_type = term->val.num;
+ */
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
- n = strlen(event_symbols[i].symbol);
- if (!strncasecmp(str, event_symbols[i].symbol, n))
- return n;
+static int config_attr(struct perf_event_attr *attr,
+ struct list_head *head, int fail)
+{
+ struct parse_events__term *term;
- n = strlen(event_symbols[i].alias);
- if (n) {
- if (!strncasecmp(str, event_symbols[i].alias, n))
- return n;
- }
+ list_for_each_entry(term, head, list)
+ if (config_term(attr, term) && fail)
+ return -EINVAL;
return 0;
}
-static enum event_result
-parse_symbolic_event(const char **strp, struct perf_event_attr *attr)
+int parse_events_add_numeric(struct list_head *list, int *idx,
+ unsigned long type, unsigned long config,
+ struct list_head *head_config)
{
- const char *str = *strp;
- unsigned int i;
- int n;
-
- 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 EVT_HANDLED;
- }
- }
- return EVT_FAILED;
+ struct perf_event_attr attr;
+
+ memset(&attr, 0, sizeof(attr));
+ attr.type = type;
+ attr.config = config;
+
+ if (head_config &&
+ config_attr(&attr, head_config, 1))
+ return -EINVAL;
+
+ return add_event(list, idx, &attr,
+ (char *) __event_name(type, config));
}
-static enum event_result
-parse_raw_event(const char **strp, struct perf_event_attr *attr)
+int parse_events_add_pmu(struct list_head *list, int *idx,
+ char *name, struct list_head *head_config)
{
- const char *str = *strp;
- u64 config;
- int n;
-
- if (*str != 'r')
- return EVT_FAILED;
- n = hex2u64(str + 1, &config);
- if (n > 0) {
- const char *end = str + n + 1;
- if (*end != '\0' && *end != ',' && *end != ':')
- return EVT_FAILED;
-
- *strp = end;
- attr->type = PERF_TYPE_RAW;
- attr->config = config;
- return EVT_HANDLED;
- }
- return EVT_FAILED;
+ struct perf_event_attr attr;
+ struct perf_pmu *pmu;
+
+ pmu = perf_pmu__find(name);
+ if (!pmu)
+ return -EINVAL;
+
+ memset(&attr, 0, sizeof(attr));
+
+ /*
+ * Configure hardcoded terms first, no need to check
+ * return value when called with fail == 0 ;)
+ */
+ config_attr(&attr, head_config, 0);
+
+ if (perf_pmu__config(pmu, &attr, head_config))
+ return -EINVAL;
+
+ return add_event(list, idx, &attr, (char *) "pmu");
}
-static enum event_result
-parse_numeric_event(const char **strp, struct perf_event_attr *attr)
+void parse_events_update_lists(struct list_head *list_event,
+ struct list_head *list_all)
{
- 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 EVT_HANDLED;
- }
- }
- return EVT_FAILED;
+ /*
+ * Called for single event definition. Update the
+ * 'all event' list, and reinit the 'signle event'
+ * list, for next event definition.
+ */
+ list_splice_tail(list_event, list_all);
+ INIT_LIST_HEAD(list_event);
}
-static int
-parse_event_modifier(const char **strp, struct perf_event_attr *attr)
+int parse_events_modifier(struct list_head *list, char *str)
{
- const char *str = *strp;
+ struct perf_evsel *evsel;
int exclude = 0, exclude_GH = 0;
int eu = 0, ek = 0, eh = 0, eH = 0, eG = 0, precise = 0;
- if (!*str)
+ if (str == NULL)
return 0;
- if (*str == ',')
- return 0;
-
- if (*str++ != ':')
- return -1;
-
while (*str) {
if (*str == 'u') {
if (!exclude)
@@ -775,111 +721,62 @@ parse_event_modifier(const char **strp, struct perf_event_attr *attr)
++str;
}
- if (str < *strp + 2)
- return -1;
- *strp = str;
+ /*
+ * precise ip:
+ *
+ * 0 - SAMPLE_IP can have arbitrary skid
+ * 1 - SAMPLE_IP must have constant skid
+ * 2 - SAMPLE_IP requested to have 0 skid
+ * 3 - SAMPLE_IP must have 0 skid
+ *
+ * See also PERF_RECORD_MISC_EXACT_IP
+ */
+ if (precise > 3)
+ return -EINVAL;
- attr->exclude_user = eu;
- attr->exclude_kernel = ek;
- attr->exclude_hv = eh;
- attr->precise_ip = precise;
- attr->exclude_host = eH;
- attr->exclude_guest = eG;
+ list_for_each_entry(evsel, list, node) {
+ evsel->attr.exclude_user = eu;
+ evsel->attr.exclude_kernel = ek;
+ evsel->attr.exclude_hv = eh;
+ evsel->attr.precise_ip = precise;
+ evsel->attr.exclude_host = eH;
+ evsel->attr.exclude_guest = eG;
+ }
return 0;
}
-/*
- * Each event can have multiple symbolic names.
- * Symbolic names are (almost) exactly matched.
- */
-static enum event_result
-parse_event_symbols(struct perf_evlist *evlist, const char **str,
- struct perf_event_attr *attr)
+int parse_events(struct perf_evlist *evlist, const char *str, int unset __used)
{
- enum event_result ret;
-
- ret = parse_tracepoint_event(evlist, str, attr);
- if (ret != EVT_FAILED)
- goto modifier;
-
- ret = parse_raw_event(str, attr);
- if (ret != EVT_FAILED)
- goto modifier;
+ LIST_HEAD(list);
+ LIST_HEAD(list_tmp);
+ YY_BUFFER_STATE buffer;
+ int ret, idx = evlist->nr_entries;
- ret = parse_numeric_event(str, attr);
- if (ret != EVT_FAILED)
- goto modifier;
+ buffer = parse_events__scan_string(str);
- ret = parse_symbolic_event(str, attr);
- if (ret != EVT_FAILED)
- goto modifier;
+ ret = parse_events_parse(&list, &list_tmp, &idx);
- ret = parse_generic_hw_event(str, attr);
- if (ret != EVT_FAILED)
- goto modifier;
+ parse_events__flush_buffer(buffer);
+ parse_events__delete_buffer(buffer);
- ret = parse_breakpoint_event(str, attr);
- if (ret != EVT_FAILED)
- goto modifier;
-
- fprintf(stderr, "invalid or unsupported event: '%s'\n", *str);
- fprintf(stderr, "Run 'perf list' for a list of valid events\n");
- return EVT_FAILED;
-
-modifier:
- if (parse_event_modifier(str, attr) < 0) {
- fprintf(stderr, "invalid event modifier: '%s'\n", *str);
- fprintf(stderr, "Run 'perf list' for a list of valid events and modifiers\n");
-
- return EVT_FAILED;
+ if (!ret) {
+ int entries = idx - evlist->nr_entries;
+ perf_evlist__splice_list_tail(evlist, &list, entries);
+ return 0;
}
+ /*
+ * There are 2 users - builtin-record and builtin-test objects.
+ * Both call perf_evlist__delete in case of error, so we dont
+ * need to bother.
+ */
+ fprintf(stderr, "invalid or unsupported event: '%s'\n", str);
+ fprintf(stderr, "Run 'perf list' for a list of valid events\n");
return ret;
}
-int parse_events(struct perf_evlist *evlist , const char *str, int unset __used)
-{
- struct perf_event_attr attr;
- enum event_result ret;
- const char *ostr;
-
- for (;;) {
- ostr = str;
- memset(&attr, 0, sizeof(attr));
- event_attr_init(&attr);
- ret = parse_event_symbols(evlist, &str, &attr);
- if (ret == EVT_FAILED)
- return -1;
-
- if (!(*str == 0 || *str == ',' || isspace(*str)))
- return -1;
-
- if (ret != EVT_HANDLED_ALL) {
- struct perf_evsel *evsel;
- evsel = perf_evsel__new(&attr, evlist->nr_entries);
- if (evsel == NULL)
- return -1;
- perf_evlist__add(evlist, evsel);
-
- evsel->name = calloc(str - ostr + 1, 1);
- if (!evsel->name)
- return -1;
- strncpy(evsel->name, ostr, str - ostr);
- }
-
- if (*str == 0)
- break;
- if (*str == ',')
- ++str;
- while (isspace(*str))
- ++str;
- }
-
- return 0;
-}
-
int parse_events_option(const struct option *opt, const char *str,
int unset __used)
{
@@ -1052,8 +949,6 @@ int print_hwcache_events(const char *event_glob)
return printed;
}
-#define MAX_NAME_LEN 100
-
/*
* Print the help text for the event symbols:
*/
@@ -1102,8 +997,12 @@ void print_events(const char *event_glob)
printf("\n");
printf(" %-50s [%s]\n",
- "rNNN (see 'perf list --help' on how to encode it)",
+ "rNNN",
event_type_descriptors[PERF_TYPE_RAW]);
+ printf(" %-50s [%s]\n",
+ "cpu/t1=v1[,t2=v2,t3 ...]/modifier",
+ event_type_descriptors[PERF_TYPE_RAW]);
+ printf(" (see 'perf list --help' on how to encode it)\n");
printf("\n");
printf(" %-50s [%s]\n",
@@ -1113,3 +1012,51 @@ void print_events(const char *event_glob)
print_tracepoint_events(NULL, NULL);
}
+
+int parse_events__is_hardcoded_term(struct parse_events__term *term)
+{
+ return term->type <= PARSE_EVENTS__TERM_TYPE_HARDCODED_MAX;
+}
+
+int parse_events__new_term(struct parse_events__term **_term, int type,
+ char *config, char *str, long num)
+{
+ struct parse_events__term *term;
+
+ term = zalloc(sizeof(*term));
+ if (!term)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&term->list);
+ term->type = type;
+ term->config = config;
+
+ switch (type) {
+ case PARSE_EVENTS__TERM_TYPE_CONFIG:
+ case PARSE_EVENTS__TERM_TYPE_CONFIG1:
+ case PARSE_EVENTS__TERM_TYPE_CONFIG2:
+ case PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD:
+ case PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE:
+ case PARSE_EVENTS__TERM_TYPE_NUM:
+ term->val.num = num;
+ break;
+ case PARSE_EVENTS__TERM_TYPE_STR:
+ term->val.str = str;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ *_term = term;
+ return 0;
+}
+
+void parse_events__free_terms(struct list_head *terms)
+{
+ struct parse_events__term *term, *h;
+
+ list_for_each_entry_safe(term, h, terms, list)
+ free(term);
+
+ free(terms);
+}
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h
index 7e0cbe75d5f1..ca069f893381 100644
--- a/tools/perf/util/parse-events.h
+++ b/tools/perf/util/parse-events.h
@@ -33,6 +33,55 @@ extern int parse_filter(const struct option *opt, const char *str, int unset);
#define EVENTS_HELP_MAX (128*1024)
+enum {
+ PARSE_EVENTS__TERM_TYPE_CONFIG,
+ PARSE_EVENTS__TERM_TYPE_CONFIG1,
+ PARSE_EVENTS__TERM_TYPE_CONFIG2,
+ PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD,
+ PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE,
+ PARSE_EVENTS__TERM_TYPE_NUM,
+ PARSE_EVENTS__TERM_TYPE_STR,
+
+ PARSE_EVENTS__TERM_TYPE_HARDCODED_MAX =
+ PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE,
+};
+
+struct parse_events__term {
+ char *config;
+ union {
+ char *str;
+ long num;
+ } val;
+ int type;
+
+ struct list_head list;
+};
+
+int parse_events__is_hardcoded_term(struct parse_events__term *term);
+int parse_events__new_term(struct parse_events__term **term, int type,
+ char *config, char *str, long num);
+void parse_events__free_terms(struct list_head *terms);
+int parse_events_modifier(struct list_head *list __used, char *str __used);
+int parse_events_add_tracepoint(struct list_head *list, int *idx,
+ char *sys, char *event);
+int parse_events_add_raw(struct perf_evlist *evlist, unsigned long config,
+ unsigned long config1, unsigned long config2,
+ char *mod);
+int parse_events_add_numeric(struct list_head *list, int *idx,
+ unsigned long type, unsigned long config,
+ struct list_head *head_config);
+int parse_events_add_cache(struct list_head *list, int *idx,
+ char *type, char *op_result1, char *op_result2);
+int parse_events_add_breakpoint(struct list_head *list, int *idx,
+ void *ptr, char *type);
+int parse_events_add_pmu(struct list_head *list, int *idx,
+ char *pmu , struct list_head *head_config);
+void parse_events_update_lists(struct list_head *list_event,
+ struct list_head *list_all);
+void parse_events_error(struct list_head *list_all,
+ struct list_head *list_event,
+ int *idx, char const *msg);
+
void print_events(const char *event_glob);
void print_events_type(u8 type);
void print_tracepoint_events(const char *subsys_glob, const char *event_glob);
diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l
new file mode 100644
index 000000000000..05d766e3ecb5
--- /dev/null
+++ b/tools/perf/util/parse-events.l
@@ -0,0 +1,127 @@
+
+%option prefix="parse_events_"
+
+%{
+#include <errno.h>
+#include "../perf.h"
+#include "parse-events-bison.h"
+#include "parse-events.h"
+
+static int __value(char *str, int base, int token)
+{
+ long num;
+
+ errno = 0;
+ num = strtoul(str, NULL, base);
+ if (errno)
+ return PE_ERROR;
+
+ parse_events_lval.num = num;
+ return token;
+}
+
+static int value(int base)
+{
+ return __value(parse_events_text, base, PE_VALUE);
+}
+
+static int raw(void)
+{
+ return __value(parse_events_text + 1, 16, PE_RAW);
+}
+
+static int str(int token)
+{
+ parse_events_lval.str = strdup(parse_events_text);
+ return token;
+}
+
+static int sym(int type, int config)
+{
+ parse_events_lval.num = (type << 16) + config;
+ return PE_VALUE_SYM;
+}
+
+static int term(int type)
+{
+ parse_events_lval.num = type;
+ return PE_TERM;
+}
+
+%}
+
+num_dec [0-9]+
+num_hex 0x[a-fA-F0-9]+
+num_raw_hex [a-fA-F0-9]+
+name [a-zA-Z_*?][a-zA-Z0-9_*?]*
+modifier_event [ukhp]{1,5}
+modifier_bp [rwx]
+
+%%
+cpu-cycles|cycles { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES); }
+stalled-cycles-frontend|idle-cycles-frontend { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_FRONTEND); }
+stalled-cycles-backend|idle-cycles-backend { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_BACKEND); }
+instructions { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_INSTRUCTIONS); }
+cache-references { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_REFERENCES); }
+cache-misses { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_MISSES); }
+branch-instructions|branches { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_BRANCH_INSTRUCTIONS); }
+branch-misses { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_BRANCH_MISSES); }
+bus-cycles { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_BUS_CYCLES); }
+ref-cycles { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_REF_CPU_CYCLES); }
+cpu-clock { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_CLOCK); }
+task-clock { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_TASK_CLOCK); }
+page-faults|faults { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_PAGE_FAULTS); }
+minor-faults { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_PAGE_FAULTS_MIN); }
+major-faults { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_PAGE_FAULTS_MAJ); }
+context-switches|cs { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CONTEXT_SWITCHES); }
+cpu-migrations|migrations { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_MIGRATIONS); }
+alignment-faults { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_ALIGNMENT_FAULTS); }
+emulation-faults { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_EMULATION_FAULTS); }
+
+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 |
+branch|branches|bpu|btb|bpc |
+node { return str(PE_NAME_CACHE_TYPE); }
+
+load|loads|read |
+store|stores|write |
+prefetch|prefetches |
+speculative-read|speculative-load |
+refs|Reference|ops|access |
+misses|miss { return str(PE_NAME_CACHE_OP_RESULT); }
+
+ /*
+ * These are event config hardcoded term names to be specified
+ * within xxx/.../ syntax. So far we dont clash with other names,
+ * so we can put them here directly. In case the we have a conflict
+ * in future, this needs to go into '//' condition block.
+ */
+config { return term(PARSE_EVENTS__TERM_TYPE_CONFIG); }
+config1 { return term(PARSE_EVENTS__TERM_TYPE_CONFIG1); }
+config2 { return term(PARSE_EVENTS__TERM_TYPE_CONFIG2); }
+period { return term(PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD); }
+branch_type { return term(PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE); }
+
+mem: { return PE_PREFIX_MEM; }
+r{num_raw_hex} { return raw(); }
+{num_dec} { return value(10); }
+{num_hex} { return value(16); }
+
+{modifier_event} { return str(PE_MODIFIER_EVENT); }
+{modifier_bp} { return str(PE_MODIFIER_BP); }
+{name} { return str(PE_NAME); }
+"/" { return '/'; }
+- { return '-'; }
+, { return ','; }
+: { return ':'; }
+= { return '='; }
+
+%%
+
+int parse_events_wrap(void)
+{
+ return 1;
+}
diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y
new file mode 100644
index 000000000000..d9637da7333c
--- /dev/null
+++ b/tools/perf/util/parse-events.y
@@ -0,0 +1,229 @@
+
+%name-prefix "parse_events_"
+%parse-param {struct list_head *list_all}
+%parse-param {struct list_head *list_event}
+%parse-param {int *idx}
+
+%{
+
+#define YYDEBUG 1
+
+#include <linux/compiler.h>
+#include <linux/list.h>
+#include "types.h"
+#include "util.h"
+#include "parse-events.h"
+
+extern int parse_events_lex (void);
+
+#define ABORT_ON(val) \
+do { \
+ if (val) \
+ YYABORT; \
+} while (0)
+
+%}
+
+%token PE_VALUE PE_VALUE_SYM PE_RAW PE_TERM
+%token PE_NAME
+%token PE_MODIFIER_EVENT PE_MODIFIER_BP
+%token PE_NAME_CACHE_TYPE PE_NAME_CACHE_OP_RESULT
+%token PE_PREFIX_MEM PE_PREFIX_RAW
+%token PE_ERROR
+%type <num> PE_VALUE
+%type <num> PE_VALUE_SYM
+%type <num> PE_RAW
+%type <num> PE_TERM
+%type <str> PE_NAME
+%type <str> PE_NAME_CACHE_TYPE
+%type <str> PE_NAME_CACHE_OP_RESULT
+%type <str> PE_MODIFIER_EVENT
+%type <str> PE_MODIFIER_BP
+%type <head> event_config
+%type <term> event_term
+
+%union
+{
+ char *str;
+ unsigned long num;
+ struct list_head *head;
+ struct parse_events__term *term;
+}
+%%
+
+events:
+events ',' event | event
+
+event:
+event_def PE_MODIFIER_EVENT
+{
+ /*
+ * Apply modifier on all events added by single event definition
+ * (there could be more events added for multiple tracepoint
+ * definitions via '*?'.
+ */
+ ABORT_ON(parse_events_modifier(list_event, $2));
+ parse_events_update_lists(list_event, list_all);
+}
+|
+event_def
+{
+ parse_events_update_lists(list_event, list_all);
+}
+
+event_def: event_pmu |
+ event_legacy_symbol |
+ event_legacy_cache sep_dc |
+ event_legacy_mem |
+ event_legacy_tracepoint sep_dc |
+ event_legacy_numeric sep_dc |
+ event_legacy_raw sep_dc
+
+event_pmu:
+PE_NAME '/' event_config '/'
+{
+ ABORT_ON(parse_events_add_pmu(list_event, idx, $1, $3));
+ parse_events__free_terms($3);
+}
+
+event_legacy_symbol:
+PE_VALUE_SYM '/' event_config '/'
+{
+ int type = $1 >> 16;
+ int config = $1 & 255;
+
+ ABORT_ON(parse_events_add_numeric(list_event, idx, type, config, $3));
+ parse_events__free_terms($3);
+}
+|
+PE_VALUE_SYM sep_slash_dc
+{
+ int type = $1 >> 16;
+ int config = $1 & 255;
+
+ ABORT_ON(parse_events_add_numeric(list_event, idx, type, config, NULL));
+}
+
+event_legacy_cache:
+PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT '-' PE_NAME_CACHE_OP_RESULT
+{
+ ABORT_ON(parse_events_add_cache(list_event, idx, $1, $3, $5));
+}
+|
+PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT
+{
+ ABORT_ON(parse_events_add_cache(list_event, idx, $1, $3, NULL));
+}
+|
+PE_NAME_CACHE_TYPE
+{
+ ABORT_ON(parse_events_add_cache(list_event, idx, $1, NULL, NULL));
+}
+
+event_legacy_mem:
+PE_PREFIX_MEM PE_VALUE ':' PE_MODIFIER_BP sep_dc
+{
+ ABORT_ON(parse_events_add_breakpoint(list_event, idx, (void *) $2, $4));
+}
+|
+PE_PREFIX_MEM PE_VALUE sep_dc
+{
+ ABORT_ON(parse_events_add_breakpoint(list_event, idx, (void *) $2, NULL));
+}
+
+event_legacy_tracepoint:
+PE_NAME ':' PE_NAME
+{
+ ABORT_ON(parse_events_add_tracepoint(list_event, idx, $1, $3));
+}
+
+event_legacy_numeric:
+PE_VALUE ':' PE_VALUE
+{
+ ABORT_ON(parse_events_add_numeric(list_event, idx, $1, $3, NULL));
+}
+
+event_legacy_raw:
+PE_RAW
+{
+ ABORT_ON(parse_events_add_numeric(list_event, idx, PERF_TYPE_RAW, $1, NULL));
+}
+
+event_config:
+event_config ',' event_term
+{
+ struct list_head *head = $1;
+ struct parse_events__term *term = $3;
+
+ ABORT_ON(!head);
+ list_add_tail(&term->list, head);
+ $$ = $1;
+}
+|
+event_term
+{
+ struct list_head *head = malloc(sizeof(*head));
+ struct parse_events__term *term = $1;
+
+ ABORT_ON(!head);
+ INIT_LIST_HEAD(head);
+ list_add_tail(&term->list, head);
+ $$ = head;
+}
+
+event_term:
+PE_NAME '=' PE_NAME
+{
+ struct parse_events__term *term;
+
+ ABORT_ON(parse_events__new_term(&term, PARSE_EVENTS__TERM_TYPE_STR,
+ $1, $3, 0));
+ $$ = term;
+}
+|
+PE_NAME '=' PE_VALUE
+{
+ struct parse_events__term *term;
+
+ ABORT_ON(parse_events__new_term(&term, PARSE_EVENTS__TERM_TYPE_NUM,
+ $1, NULL, $3));
+ $$ = term;
+}
+|
+PE_NAME
+{
+ struct parse_events__term *term;
+
+ ABORT_ON(parse_events__new_term(&term, PARSE_EVENTS__TERM_TYPE_NUM,
+ $1, NULL, 1));
+ $$ = term;
+}
+|
+PE_TERM '=' PE_VALUE
+{
+ struct parse_events__term *term;
+
+ ABORT_ON(parse_events__new_term(&term, $1, NULL, NULL, $3));
+ $$ = term;
+}
+|
+PE_TERM
+{
+ struct parse_events__term *term;
+
+ ABORT_ON(parse_events__new_term(&term, $1, NULL, NULL, 1));
+ $$ = term;
+}
+
+sep_dc: ':' |
+
+sep_slash_dc: '/' | ':' |
+
+%%
+
+void parse_events_error(struct list_head *list_all __used,
+ struct list_head *list_event __used,
+ int *idx __used,
+ char const *msg __used)
+{
+}
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
new file mode 100644
index 000000000000..cb08a118e811
--- /dev/null
+++ b/tools/perf/util/pmu.c
@@ -0,0 +1,469 @@
+
+#include <linux/list.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <dirent.h>
+#include "sysfs.h"
+#include "util.h"
+#include "pmu.h"
+#include "parse-events.h"
+
+int perf_pmu_parse(struct list_head *list, char *name);
+extern FILE *perf_pmu_in;
+
+static LIST_HEAD(pmus);
+
+/*
+ * Parse & process all the sysfs attributes located under
+ * the directory specified in 'dir' parameter.
+ */
+static int pmu_format_parse(char *dir, struct list_head *head)
+{
+ struct dirent *evt_ent;
+ DIR *format_dir;
+ int ret = 0;
+
+ format_dir = opendir(dir);
+ if (!format_dir)
+ return -EINVAL;
+
+ while (!ret && (evt_ent = readdir(format_dir))) {
+ char path[PATH_MAX];
+ char *name = evt_ent->d_name;
+ FILE *file;
+
+ if (!strcmp(name, ".") || !strcmp(name, ".."))
+ continue;
+
+ snprintf(path, PATH_MAX, "%s/%s", dir, name);
+
+ ret = -EINVAL;
+ file = fopen(path, "r");
+ if (!file)
+ break;
+
+ perf_pmu_in = file;
+ ret = perf_pmu_parse(head, name);
+ fclose(file);
+ }
+
+ closedir(format_dir);
+ return ret;
+}
+
+/*
+ * Reading/parsing the default pmu format definition, which should be
+ * located at:
+ * /sys/bus/event_source/devices/<dev>/format as sysfs group attributes.
+ */
+static int pmu_format(char *name, struct list_head *format)
+{
+ struct stat st;
+ char path[PATH_MAX];
+ const char *sysfs;
+
+ sysfs = sysfs_find_mountpoint();
+ if (!sysfs)
+ return -1;
+
+ snprintf(path, PATH_MAX,
+ "%s/bus/event_source/devices/%s/format", sysfs, name);
+
+ if (stat(path, &st) < 0)
+ return -1;
+
+ if (pmu_format_parse(path, format))
+ return -1;
+
+ return 0;
+}
+
+/*
+ * Reading/parsing the default pmu type value, which should be
+ * located at:
+ * /sys/bus/event_source/devices/<dev>/type as sysfs attribute.
+ */
+static int pmu_type(char *name, __u32 *type)
+{
+ struct stat st;
+ char path[PATH_MAX];
+ const char *sysfs;
+ FILE *file;
+ int ret = 0;
+
+ sysfs = sysfs_find_mountpoint();
+ if (!sysfs)
+ return -1;
+
+ snprintf(path, PATH_MAX,
+ "%s/bus/event_source/devices/%s/type", sysfs, name);
+
+ if (stat(path, &st) < 0)
+ return -1;
+
+ file = fopen(path, "r");
+ if (!file)
+ return -EINVAL;
+
+ if (1 != fscanf(file, "%u", type))
+ ret = -1;
+
+ fclose(file);
+ return ret;
+}
+
+static struct perf_pmu *pmu_lookup(char *name)
+{
+ struct perf_pmu *pmu;
+ LIST_HEAD(format);
+ __u32 type;
+
+ /*
+ * The pmu data we store & need consists of the pmu
+ * type value and format definitions. Load both right
+ * now.
+ */
+ if (pmu_format(name, &format))
+ return NULL;
+
+ if (pmu_type(name, &type))
+ return NULL;
+
+ pmu = zalloc(sizeof(*pmu));
+ if (!pmu)
+ return NULL;
+
+ INIT_LIST_HEAD(&pmu->format);
+ list_splice(&format, &pmu->format);
+ pmu->name = strdup(name);
+ pmu->type = type;
+ return pmu;
+}
+
+static struct perf_pmu *pmu_find(char *name)
+{
+ struct perf_pmu *pmu;
+
+ list_for_each_entry(pmu, &pmus, list)
+ if (!strcmp(pmu->name, name))
+ return pmu;
+
+ return NULL;
+}
+
+struct perf_pmu *perf_pmu__find(char *name)
+{
+ struct perf_pmu *pmu;
+
+ /*
+ * Once PMU is loaded it stays in the list,
+ * so we keep us from multiple reading/parsing
+ * the pmu format definitions.
+ */
+ pmu = pmu_find(name);
+ if (pmu)
+ return pmu;
+
+ return pmu_lookup(name);
+}
+
+static struct perf_pmu__format*
+pmu_find_format(struct list_head *formats, char *name)
+{
+ struct perf_pmu__format *format;
+
+ list_for_each_entry(format, formats, list)
+ if (!strcmp(format->name, name))
+ return format;
+
+ return NULL;
+}
+
+/*
+ * Returns value based on the format definition (format parameter)
+ * and unformated value (value parameter).
+ *
+ * TODO maybe optimize a little ;)
+ */
+static __u64 pmu_format_value(unsigned long *format, __u64 value)
+{
+ unsigned long fbit, vbit;
+ __u64 v = 0;
+
+ for (fbit = 0, vbit = 0; fbit < PERF_PMU_FORMAT_BITS; fbit++) {
+
+ if (!test_bit(fbit, format))
+ continue;
+
+ if (!(value & (1llu << vbit++)))
+ continue;
+
+ v |= (1llu << fbit);
+ }
+
+ return v;
+}
+
+/*
+ * Setup one of config[12] attr members based on the
+ * user input data - temr parameter.
+ */
+static int pmu_config_term(struct list_head *formats,
+ struct perf_event_attr *attr,
+ struct parse_events__term *term)
+{
+ struct perf_pmu__format *format;
+ __u64 *vp;
+
+ /*
+ * Support only for hardcoded and numnerial terms.
+ * Hardcoded terms should be already in, so nothing
+ * to be done for them.
+ */
+ if (parse_events__is_hardcoded_term(term))
+ return 0;
+
+ if (term->type != PARSE_EVENTS__TERM_TYPE_NUM)
+ return -EINVAL;
+
+ format = pmu_find_format(formats, term->config);
+ if (!format)
+ return -EINVAL;
+
+ switch (format->value) {
+ case PERF_PMU_FORMAT_VALUE_CONFIG:
+ vp = &attr->config;
+ break;
+ case PERF_PMU_FORMAT_VALUE_CONFIG1:
+ vp = &attr->config1;
+ break;
+ case PERF_PMU_FORMAT_VALUE_CONFIG2:
+ vp = &attr->config2;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ *vp |= pmu_format_value(format->bits, term->val.num);
+ return 0;
+}
+
+static int pmu_config(struct list_head *formats, struct perf_event_attr *attr,
+ struct list_head *head_terms)
+{
+ struct parse_events__term *term, *h;
+
+ list_for_each_entry_safe(term, h, head_terms, list)
+ if (pmu_config_term(formats, attr, term))
+ return -EINVAL;
+
+ return 0;
+}
+
+/*
+ * Configures event's 'attr' parameter based on the:
+ * 1) users input - specified in terms parameter
+ * 2) pmu format definitions - specified by pmu parameter
+ */
+int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
+ struct list_head *head_terms)
+{
+ attr->type = pmu->type;
+ return pmu_config(&pmu->format, attr, head_terms);
+}
+
+int perf_pmu__new_format(struct list_head *list, char *name,
+ int config, unsigned long *bits)
+{
+ struct perf_pmu__format *format;
+
+ format = zalloc(sizeof(*format));
+ if (!format)
+ return -ENOMEM;
+
+ format->name = strdup(name);
+ format->value = config;
+ memcpy(format->bits, bits, sizeof(format->bits));
+
+ list_add_tail(&format->list, list);
+ return 0;
+}
+
+void perf_pmu__set_format(unsigned long *bits, long from, long to)
+{
+ long b;
+
+ if (!to)
+ to = from;
+
+ memset(bits, 0, BITS_TO_LONGS(PERF_PMU_FORMAT_BITS));
+ for (b = from; b <= to; b++)
+ set_bit(b, bits);
+}
+
+/* Simulated format definitions. */
+static struct test_format {
+ const char *name;
+ const char *value;
+} test_formats[] = {
+ { "krava01", "config:0-1,62-63\n", },
+ { "krava02", "config:10-17\n", },
+ { "krava03", "config:5\n", },
+ { "krava11", "config1:0,2,4,6,8,20-28\n", },
+ { "krava12", "config1:63\n", },
+ { "krava13", "config1:45-47\n", },
+ { "krava21", "config2:0-3,10-13,20-23,30-33,40-43,50-53,60-63\n", },
+ { "krava22", "config2:8,18,48,58\n", },
+ { "krava23", "config2:28-29,38\n", },
+};
+
+#define TEST_FORMATS_CNT (sizeof(test_formats) / sizeof(struct test_format))
+
+/* Simulated users input. */
+static struct parse_events__term test_terms[] = {
+ {
+ .config = (char *) "krava01",
+ .val.num = 15,
+ .type = PARSE_EVENTS__TERM_TYPE_NUM,
+ },
+ {
+ .config = (char *) "krava02",
+ .val.num = 170,
+ .type = PARSE_EVENTS__TERM_TYPE_NUM,
+ },
+ {
+ .config = (char *) "krava03",
+ .val.num = 1,
+ .type = PARSE_EVENTS__TERM_TYPE_NUM,
+ },
+ {
+ .config = (char *) "krava11",
+ .val.num = 27,
+ .type = PARSE_EVENTS__TERM_TYPE_NUM,
+ },
+ {
+ .config = (char *) "krava12",
+ .val.num = 1,
+ .type = PARSE_EVENTS__TERM_TYPE_NUM,
+ },
+ {
+ .config = (char *) "krava13",
+ .val.num = 2,
+ .type = PARSE_EVENTS__TERM_TYPE_NUM,
+ },
+ {
+ .config = (char *) "krava21",
+ .val.num = 119,
+ .type = PARSE_EVENTS__TERM_TYPE_NUM,
+ },
+ {
+ .config = (char *) "krava22",
+ .val.num = 11,
+ .type = PARSE_EVENTS__TERM_TYPE_NUM,
+ },
+ {
+ .config = (char *) "krava23",
+ .val.num = 2,
+ .type = PARSE_EVENTS__TERM_TYPE_NUM,
+ },
+};
+#define TERMS_CNT (sizeof(test_terms) / sizeof(struct parse_events__term))
+
+/*
+ * Prepare format directory data, exported by kernel
+ * at /sys/bus/event_source/devices/<dev>/format.
+ */
+static char *test_format_dir_get(void)
+{
+ static char dir[PATH_MAX];
+ unsigned int i;
+
+ snprintf(dir, PATH_MAX, "/tmp/perf-pmu-test-format-XXXXXX");
+ if (!mkdtemp(dir))
+ return NULL;
+
+ for (i = 0; i < TEST_FORMATS_CNT; i++) {
+ static char name[PATH_MAX];
+ struct test_format *format = &test_formats[i];
+ FILE *file;
+
+ snprintf(name, PATH_MAX, "%s/%s", dir, format->name);
+
+ file = fopen(name, "w");
+ if (!file)
+ return NULL;
+
+ if (1 != fwrite(format->value, strlen(format->value), 1, file))
+ break;
+
+ fclose(file);
+ }
+
+ return dir;
+}
+
+/* Cleanup format directory. */
+static int test_format_dir_put(char *dir)
+{
+ char buf[PATH_MAX];
+ snprintf(buf, PATH_MAX, "rm -f %s/*\n", dir);
+ if (system(buf))
+ return -1;
+
+ snprintf(buf, PATH_MAX, "rmdir %s\n", dir);
+ return system(buf);
+}
+
+static struct list_head *test_terms_list(void)
+{
+ static LIST_HEAD(terms);
+ unsigned int i;
+
+ for (i = 0; i < TERMS_CNT; i++)
+ list_add_tail(&test_terms[i].list, &terms);
+
+ return &terms;
+}
+
+#undef TERMS_CNT
+
+int perf_pmu__test(void)
+{
+ char *format = test_format_dir_get();
+ LIST_HEAD(formats);
+ struct list_head *terms = test_terms_list();
+ int ret;
+
+ if (!format)
+ return -EINVAL;
+
+ do {
+ struct perf_event_attr attr;
+
+ memset(&attr, 0, sizeof(attr));
+
+ ret = pmu_format_parse(format, &formats);
+ if (ret)
+ break;
+
+ ret = pmu_config(&formats, &attr, terms);
+ if (ret)
+ break;
+
+ ret = -EINVAL;
+
+ if (attr.config != 0xc00000000002a823)
+ break;
+ if (attr.config1 != 0x8000400000000145)
+ break;
+ if (attr.config2 != 0x0400000020041d07)
+ break;
+
+ ret = 0;
+ } while (0);
+
+ test_format_dir_put(format);
+ return ret;
+}
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
new file mode 100644
index 000000000000..68c0db965e1f
--- /dev/null
+++ b/tools/perf/util/pmu.h
@@ -0,0 +1,41 @@
+#ifndef __PMU_H
+#define __PMU_H
+
+#include <linux/bitops.h>
+#include "../../../include/linux/perf_event.h"
+
+enum {
+ PERF_PMU_FORMAT_VALUE_CONFIG,
+ PERF_PMU_FORMAT_VALUE_CONFIG1,
+ PERF_PMU_FORMAT_VALUE_CONFIG2,
+};
+
+#define PERF_PMU_FORMAT_BITS 64
+
+struct perf_pmu__format {
+ char *name;
+ int value;
+ DECLARE_BITMAP(bits, PERF_PMU_FORMAT_BITS);
+ struct list_head list;
+};
+
+struct perf_pmu {
+ char *name;
+ __u32 type;
+ struct list_head format;
+ struct list_head list;
+};
+
+struct perf_pmu *perf_pmu__find(char *name);
+int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
+ struct list_head *head_terms);
+
+int perf_pmu_wrap(void);
+void perf_pmu_error(struct list_head *list, char *name, char const *msg);
+
+int perf_pmu__new_format(struct list_head *list, char *name,
+ int config, unsigned long *bits);
+void perf_pmu__set_format(unsigned long *bits, long from, long to);
+
+int perf_pmu__test(void);
+#endif /* __PMU_H */
diff --git a/tools/perf/util/pmu.l b/tools/perf/util/pmu.l
new file mode 100644
index 000000000000..a15d9fbd7c0e
--- /dev/null
+++ b/tools/perf/util/pmu.l
@@ -0,0 +1,43 @@
+%option prefix="perf_pmu_"
+
+%{
+#include <stdlib.h>
+#include <linux/bitops.h>
+#include "pmu.h"
+#include "pmu-bison.h"
+
+static int value(int base)
+{
+ long num;
+
+ errno = 0;
+ num = strtoul(perf_pmu_text, NULL, base);
+ if (errno)
+ return PP_ERROR;
+
+ perf_pmu_lval.num = num;
+ return PP_VALUE;
+}
+
+%}
+
+num_dec [0-9]+
+
+%%
+
+{num_dec} { return value(10); }
+config { return PP_CONFIG; }
+config1 { return PP_CONFIG1; }
+config2 { return PP_CONFIG2; }
+- { return '-'; }
+: { return ':'; }
+, { return ','; }
+. { ; }
+\n { ; }
+
+%%
+
+int perf_pmu_wrap(void)
+{
+ return 1;
+}
diff --git a/tools/perf/util/pmu.y b/tools/perf/util/pmu.y
new file mode 100644
index 000000000000..20ea77e93169
--- /dev/null
+++ b/tools/perf/util/pmu.y
@@ -0,0 +1,93 @@
+
+%name-prefix "perf_pmu_"
+%parse-param {struct list_head *format}
+%parse-param {char *name}
+
+%{
+
+#include <linux/compiler.h>
+#include <linux/list.h>
+#include <linux/bitmap.h>
+#include <string.h>
+#include "pmu.h"
+
+extern int perf_pmu_lex (void);
+
+#define ABORT_ON(val) \
+do { \
+ if (val) \
+ YYABORT; \
+} while (0)
+
+%}
+
+%token PP_CONFIG PP_CONFIG1 PP_CONFIG2
+%token PP_VALUE PP_ERROR
+%type <num> PP_VALUE
+%type <bits> bit_term
+%type <bits> bits
+
+%union
+{
+ unsigned long num;
+ DECLARE_BITMAP(bits, PERF_PMU_FORMAT_BITS);
+}
+
+%%
+
+format:
+format format_term
+|
+format_term
+
+format_term:
+PP_CONFIG ':' bits
+{
+ ABORT_ON(perf_pmu__new_format(format, name,
+ PERF_PMU_FORMAT_VALUE_CONFIG,
+ $3));
+}
+|
+PP_CONFIG1 ':' bits
+{
+ ABORT_ON(perf_pmu__new_format(format, name,
+ PERF_PMU_FORMAT_VALUE_CONFIG1,
+ $3));
+}
+|
+PP_CONFIG2 ':' bits
+{
+ ABORT_ON(perf_pmu__new_format(format, name,
+ PERF_PMU_FORMAT_VALUE_CONFIG2,
+ $3));
+}
+
+bits:
+bits ',' bit_term
+{
+ bitmap_or($$, $1, $3, 64);
+}
+|
+bit_term
+{
+ memcpy($$, $1, sizeof($1));
+}
+
+bit_term:
+PP_VALUE '-' PP_VALUE
+{
+ perf_pmu__set_format($$, $1, $3);
+}
+|
+PP_VALUE
+{
+ perf_pmu__set_format($$, $1, 0);
+}
+
+%%
+
+void perf_pmu_error(struct list_head *list __used,
+ char *name __used,
+ char const *msg __used)
+{
+}
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 15f9bb1b5f0a..8a8ee64e72d1 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -34,7 +34,6 @@
#include "util.h"
#include "event.h"
-#include "string.h"
#include "strlist.h"
#include "debug.h"
#include "cache.h"
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index 2cc162d3b78c..d448984ed789 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -972,10 +972,12 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data)
struct dwarf_callback_param *param = data;
struct probe_finder *pf = param->data;
struct perf_probe_point *pp = &pf->pev->point;
+ Dwarf_Attribute attr;
/* Check tag and diename */
if (dwarf_tag(sp_die) != DW_TAG_subprogram ||
- !die_compare_name(sp_die, pp->function))
+ !die_compare_name(sp_die, pp->function) ||
+ dwarf_attr(sp_die, DW_AT_declaration, &attr))
return DWARF_CB_OK;
/* Check declared file */
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index 9f833cf9c6a9..9412e3b05f68 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -24,7 +24,7 @@ static int perf_session__open(struct perf_session *self, bool force)
self->fd = STDIN_FILENO;
if (perf_session__read_header(self, self->fd) < 0)
- pr_err("incompatible file format");
+ pr_err("incompatible file format (rerun with -v to learn more)");
return 0;
}
@@ -56,7 +56,7 @@ static int perf_session__open(struct perf_session *self, bool force)
}
if (perf_session__read_header(self, self->fd) < 0) {
- pr_err("incompatible file format");
+ pr_err("incompatible file format (rerun with -v to learn more)");
goto out_close;
}
@@ -140,6 +140,7 @@ struct perf_session *perf_session__new(const char *filename, int mode,
INIT_LIST_HEAD(&self->ordered_samples.sample_cache);
INIT_LIST_HEAD(&self->ordered_samples.to_free);
machine__init(&self->host_machine, "", HOST_KERNEL_ID);
+ hists__init(&self->hists);
if (mode == O_RDONLY) {
if (perf_session__open(self, force) < 0)
@@ -229,6 +230,64 @@ static bool symbol__match_parent_regex(struct symbol *sym)
return 0;
}
+static const u8 cpumodes[] = {
+ PERF_RECORD_MISC_USER,
+ PERF_RECORD_MISC_KERNEL,
+ PERF_RECORD_MISC_GUEST_USER,
+ PERF_RECORD_MISC_GUEST_KERNEL
+};
+#define NCPUMODES (sizeof(cpumodes)/sizeof(u8))
+
+static void ip__resolve_ams(struct machine *self, struct thread *thread,
+ struct addr_map_symbol *ams,
+ u64 ip)
+{
+ struct addr_location al;
+ size_t i;
+ u8 m;
+
+ memset(&al, 0, sizeof(al));
+
+ for (i = 0; i < NCPUMODES; i++) {
+ m = cpumodes[i];
+ /*
+ * We cannot use the header.misc hint to determine whether a
+ * branch stack address is user, kernel, guest, hypervisor.
+ * Branches may straddle the kernel/user/hypervisor boundaries.
+ * Thus, we have to try consecutively until we find a match
+ * or else, the symbol is unknown
+ */
+ thread__find_addr_location(thread, self, m, MAP__FUNCTION,
+ ip, &al, NULL);
+ if (al.sym)
+ goto found;
+ }
+found:
+ ams->addr = ip;
+ ams->al_addr = al.addr;
+ ams->sym = al.sym;
+ ams->map = al.map;
+}
+
+struct branch_info *machine__resolve_bstack(struct machine *self,
+ struct thread *thr,
+ struct branch_stack *bs)
+{
+ struct branch_info *bi;
+ unsigned int i;
+
+ bi = calloc(bs->nr, sizeof(struct branch_info));
+ if (!bi)
+ return NULL;
+
+ for (i = 0; i < bs->nr; i++) {
+ ip__resolve_ams(self, thr, &bi[i].to, bs->entries[i].to);
+ ip__resolve_ams(self, thr, &bi[i].from, bs->entries[i].from);
+ bi[i].flags = bs->entries[i].flags;
+ }
+ return bi;
+}
+
int machine__resolve_callchain(struct machine *self, struct perf_evsel *evsel,
struct thread *thread,
struct ip_callchain *chain,
@@ -697,6 +756,18 @@ static void callchain__printf(struct perf_sample *sample)
i, sample->callchain->ips[i]);
}
+static void branch_stack__printf(struct perf_sample *sample)
+{
+ uint64_t i;
+
+ printf("... branch stack: nr:%" PRIu64 "\n", sample->branch_stack->nr);
+
+ for (i = 0; i < sample->branch_stack->nr; i++)
+ printf("..... %2"PRIu64": %016" PRIx64 " -> %016" PRIx64 "\n",
+ i, sample->branch_stack->entries[i].from,
+ sample->branch_stack->entries[i].to);
+}
+
static void perf_session__print_tstamp(struct perf_session *session,
union perf_event *event,
struct perf_sample *sample)
@@ -744,6 +815,9 @@ static void dump_sample(struct perf_session *session, union perf_event *event,
if (session->sample_type & PERF_SAMPLE_CALLCHAIN)
callchain__printf(sample);
+
+ if (session->sample_type & PERF_SAMPLE_BRANCH_STACK)
+ branch_stack__printf(sample);
}
static struct machine *
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
index c8d90178e7de..7a5434c00565 100644
--- a/tools/perf/util/session.h
+++ b/tools/perf/util/session.h
@@ -73,6 +73,10 @@ int perf_session__resolve_callchain(struct perf_session *self, struct perf_evsel
struct ip_callchain *chain,
struct symbol **parent);
+struct branch_info *machine__resolve_bstack(struct machine *self,
+ struct thread *thread,
+ struct branch_stack *bs);
+
bool perf_session__has_traces(struct perf_session *self, const char *msg);
void mem_bswap_64(void *src, int byte_size);
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c
index 16da30d8d765..a27237430c5f 100644
--- a/tools/perf/util/sort.c
+++ b/tools/perf/util/sort.c
@@ -8,6 +8,7 @@ const char default_sort_order[] = "comm,dso,symbol";
const char *sort_order = default_sort_order;
int sort__need_collapse = 0;
int sort__has_parent = 0;
+int sort__branch_mode = -1; /* -1 = means not set */
enum sort_type sort__first_dimension;
@@ -33,6 +34,9 @@ static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...)
}
}
va_end(ap);
+
+ if (n >= (int)size)
+ return size - 1;
return n;
}
@@ -94,6 +98,26 @@ static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf,
return repsep_snprintf(bf, size, "%*s", width, self->thread->comm);
}
+static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r)
+{
+ struct dso *dso_l = map_l ? map_l->dso : NULL;
+ struct dso *dso_r = map_r ? map_r->dso : NULL;
+ const char *dso_name_l, *dso_name_r;
+
+ if (!dso_l || !dso_r)
+ return cmp_null(dso_l, dso_r);
+
+ if (verbose) {
+ dso_name_l = dso_l->long_name;
+ dso_name_r = dso_r->long_name;
+ } else {
+ dso_name_l = dso_l->short_name;
+ dso_name_r = dso_r->short_name;
+ }
+
+ return strcmp(dso_name_l, dso_name_r);
+}
+
struct sort_entry sort_comm = {
.se_header = "Command",
.se_cmp = sort__comm_cmp,
@@ -107,36 +131,74 @@ struct sort_entry sort_comm = {
static int64_t
sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
{
- struct dso *dso_l = left->ms.map ? left->ms.map->dso : NULL;
- struct dso *dso_r = right->ms.map ? right->ms.map->dso : NULL;
- const char *dso_name_l, *dso_name_r;
+ return _sort__dso_cmp(left->ms.map, right->ms.map);
+}
- if (!dso_l || !dso_r)
- return cmp_null(dso_l, dso_r);
- if (verbose) {
- dso_name_l = dso_l->long_name;
- dso_name_r = dso_r->long_name;
- } else {
- dso_name_l = dso_l->short_name;
- dso_name_r = dso_r->short_name;
+static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r,
+ u64 ip_l, u64 ip_r)
+{
+ if (!sym_l || !sym_r)
+ return cmp_null(sym_l, sym_r);
+
+ if (sym_l == sym_r)
+ return 0;
+
+ if (sym_l)
+ ip_l = sym_l->start;
+ if (sym_r)
+ ip_r = sym_r->start;
+
+ return (int64_t)(ip_r - ip_l);
+}
+
+static int _hist_entry__dso_snprintf(struct map *map, char *bf,
+ size_t size, unsigned int width)
+{
+ if (map && map->dso) {
+ const char *dso_name = !verbose ? map->dso->short_name :
+ map->dso->long_name;
+ return repsep_snprintf(bf, size, "%-*s", width, dso_name);
}
- return strcmp(dso_name_l, dso_name_r);
+ return repsep_snprintf(bf, size, "%-*s", width, "[unknown]");
}
static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf,
size_t size, unsigned int width)
{
- if (self->ms.map && self->ms.map->dso) {
- const char *dso_name = !verbose ? self->ms.map->dso->short_name :
- self->ms.map->dso->long_name;
- return repsep_snprintf(bf, size, "%-*s", width, dso_name);
+ return _hist_entry__dso_snprintf(self->ms.map, bf, size, width);
+}
+
+static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym,
+ u64 ip, char level, char *bf, size_t size,
+ unsigned int width __used)
+{
+ size_t ret = 0;
+
+ if (verbose) {
+ char o = map ? dso__symtab_origin(map->dso) : '!';
+ ret += repsep_snprintf(bf, size, "%-#*llx %c ",
+ BITS_PER_LONG / 4, ip, o);
}
- return repsep_snprintf(bf, size, "%-*s", width, "[unknown]");
+ ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level);
+ if (sym)
+ ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
+ width - ret,
+ sym->name);
+ else {
+ size_t len = BITS_PER_LONG / 4;
+ ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx",
+ len, ip);
+ ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
+ width - ret, "");
+ }
+
+ return ret;
}
+
struct sort_entry sort_dso = {
.se_header = "Shared Object",
.se_cmp = sort__dso_cmp,
@@ -144,8 +206,14 @@ struct sort_entry sort_dso = {
.se_width_idx = HISTC_DSO,
};
-/* --sort symbol */
+static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf,
+ size_t size, unsigned int width __used)
+{
+ return _hist_entry__sym_snprintf(self->ms.map, self->ms.sym, self->ip,
+ self->level, bf, size, width);
+}
+/* --sort symbol */
static int64_t
sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
{
@@ -163,31 +231,7 @@ sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
ip_l = left->ms.sym->start;
ip_r = right->ms.sym->start;
- return (int64_t)(ip_r - ip_l);
-}
-
-static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf,
- size_t size, unsigned int width __used)
-{
- size_t ret = 0;
-
- if (verbose) {
- char o = self->ms.map ? dso__symtab_origin(self->ms.map->dso) : '!';
- ret += repsep_snprintf(bf, size, "%-#*llx %c ",
- BITS_PER_LONG / 4, self->ip, o);
- }
-
- if (!sort_dso.elide)
- ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", self->level);
-
- if (self->ms.sym)
- ret += repsep_snprintf(bf + ret, size - ret, "%s",
- self->ms.sym->name);
- else
- ret += repsep_snprintf(bf + ret, size - ret, "%-#*llx",
- BITS_PER_LONG / 4, self->ip);
-
- return ret;
+ return _sort__sym_cmp(left->ms.sym, right->ms.sym, ip_l, ip_r);
}
struct sort_entry sort_sym = {
@@ -246,19 +290,155 @@ struct sort_entry sort_cpu = {
.se_width_idx = HISTC_CPU,
};
+static int64_t
+sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right)
+{
+ return _sort__dso_cmp(left->branch_info->from.map,
+ right->branch_info->from.map);
+}
+
+static int hist_entry__dso_from_snprintf(struct hist_entry *self, char *bf,
+ size_t size, unsigned int width)
+{
+ return _hist_entry__dso_snprintf(self->branch_info->from.map,
+ bf, size, width);
+}
+
+struct sort_entry sort_dso_from = {
+ .se_header = "Source Shared Object",
+ .se_cmp = sort__dso_from_cmp,
+ .se_snprintf = hist_entry__dso_from_snprintf,
+ .se_width_idx = HISTC_DSO_FROM,
+};
+
+static int64_t
+sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right)
+{
+ return _sort__dso_cmp(left->branch_info->to.map,
+ right->branch_info->to.map);
+}
+
+static int hist_entry__dso_to_snprintf(struct hist_entry *self, char *bf,
+ size_t size, unsigned int width)
+{
+ return _hist_entry__dso_snprintf(self->branch_info->to.map,
+ bf, size, width);
+}
+
+static int64_t
+sort__sym_from_cmp(struct hist_entry *left, struct hist_entry *right)
+{
+ struct addr_map_symbol *from_l = &left->branch_info->from;
+ struct addr_map_symbol *from_r = &right->branch_info->from;
+
+ if (!from_l->sym && !from_r->sym)
+ return right->level - left->level;
+
+ return _sort__sym_cmp(from_l->sym, from_r->sym, from_l->addr,
+ from_r->addr);
+}
+
+static int64_t
+sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right)
+{
+ struct addr_map_symbol *to_l = &left->branch_info->to;
+ struct addr_map_symbol *to_r = &right->branch_info->to;
+
+ if (!to_l->sym && !to_r->sym)
+ return right->level - left->level;
+
+ return _sort__sym_cmp(to_l->sym, to_r->sym, to_l->addr, to_r->addr);
+}
+
+static int hist_entry__sym_from_snprintf(struct hist_entry *self, char *bf,
+ size_t size, unsigned int width __used)
+{
+ struct addr_map_symbol *from = &self->branch_info->from;
+ return _hist_entry__sym_snprintf(from->map, from->sym, from->addr,
+ self->level, bf, size, width);
+
+}
+
+static int hist_entry__sym_to_snprintf(struct hist_entry *self, char *bf,
+ size_t size, unsigned int width __used)
+{
+ struct addr_map_symbol *to = &self->branch_info->to;
+ return _hist_entry__sym_snprintf(to->map, to->sym, to->addr,
+ self->level, bf, size, width);
+
+}
+
+struct sort_entry sort_dso_to = {
+ .se_header = "Target Shared Object",
+ .se_cmp = sort__dso_to_cmp,
+ .se_snprintf = hist_entry__dso_to_snprintf,
+ .se_width_idx = HISTC_DSO_TO,
+};
+
+struct sort_entry sort_sym_from = {
+ .se_header = "Source Symbol",
+ .se_cmp = sort__sym_from_cmp,
+ .se_snprintf = hist_entry__sym_from_snprintf,
+ .se_width_idx = HISTC_SYMBOL_FROM,
+};
+
+struct sort_entry sort_sym_to = {
+ .se_header = "Target Symbol",
+ .se_cmp = sort__sym_to_cmp,
+ .se_snprintf = hist_entry__sym_to_snprintf,
+ .se_width_idx = HISTC_SYMBOL_TO,
+};
+
+static int64_t
+sort__mispredict_cmp(struct hist_entry *left, struct hist_entry *right)
+{
+ const unsigned char mp = left->branch_info->flags.mispred !=
+ right->branch_info->flags.mispred;
+ const unsigned char p = left->branch_info->flags.predicted !=
+ right->branch_info->flags.predicted;
+
+ return mp || p;
+}
+
+static int hist_entry__mispredict_snprintf(struct hist_entry *self, char *bf,
+ size_t size, unsigned int width){
+ static const char *out = "N/A";
+
+ if (self->branch_info->flags.predicted)
+ out = "N";
+ else if (self->branch_info->flags.mispred)
+ out = "Y";
+
+ return repsep_snprintf(bf, size, "%-*s", width, out);
+}
+
+struct sort_entry sort_mispredict = {
+ .se_header = "Branch Mispredicted",
+ .se_cmp = sort__mispredict_cmp,
+ .se_snprintf = hist_entry__mispredict_snprintf,
+ .se_width_idx = HISTC_MISPREDICT,
+};
+
struct sort_dimension {
const char *name;
struct sort_entry *entry;
int taken;
};
+#define DIM(d, n, func) [d] = { .name = n, .entry = &(func) }
+
static struct sort_dimension sort_dimensions[] = {
- { .name = "pid", .entry = &sort_thread, },
- { .name = "comm", .entry = &sort_comm, },
- { .name = "dso", .entry = &sort_dso, },
- { .name = "symbol", .entry = &sort_sym, },
- { .name = "parent", .entry = &sort_parent, },
- { .name = "cpu", .entry = &sort_cpu, },
+ DIM(SORT_PID, "pid", sort_thread),
+ DIM(SORT_COMM, "comm", sort_comm),
+ DIM(SORT_DSO, "dso", sort_dso),
+ DIM(SORT_DSO_FROM, "dso_from", sort_dso_from),
+ DIM(SORT_DSO_TO, "dso_to", sort_dso_to),
+ DIM(SORT_SYM, "symbol", sort_sym),
+ DIM(SORT_SYM_TO, "symbol_from", sort_sym_from),
+ DIM(SORT_SYM_FROM, "symbol_to", sort_sym_to),
+ DIM(SORT_PARENT, "parent", sort_parent),
+ DIM(SORT_CPU, "cpu", sort_cpu),
+ DIM(SORT_MISPREDICT, "mispredict", sort_mispredict),
};
int sort_dimension__add(const char *tok)
@@ -270,7 +450,6 @@ int sort_dimension__add(const char *tok)
if (strncasecmp(tok, sd->name, strlen(tok)))
continue;
-
if (sd->entry == &sort_parent) {
int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED);
if (ret) {
@@ -302,6 +481,16 @@ int sort_dimension__add(const char *tok)
sort__first_dimension = SORT_PARENT;
else if (!strcmp(sd->name, "cpu"))
sort__first_dimension = SORT_CPU;
+ else if (!strcmp(sd->name, "symbol_from"))
+ sort__first_dimension = SORT_SYM_FROM;
+ else if (!strcmp(sd->name, "symbol_to"))
+ sort__first_dimension = SORT_SYM_TO;
+ else if (!strcmp(sd->name, "dso_from"))
+ sort__first_dimension = SORT_DSO_FROM;
+ else if (!strcmp(sd->name, "dso_to"))
+ sort__first_dimension = SORT_DSO_TO;
+ else if (!strcmp(sd->name, "mispredict"))
+ sort__first_dimension = SORT_MISPREDICT;
}
list_add_tail(&sd->entry->list, &hist_entry__sort_list);
@@ -309,7 +498,6 @@ int sort_dimension__add(const char *tok)
return 0;
}
-
return -ESRCH;
}
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h
index 3f67ae395752..472aa5a63a58 100644
--- a/tools/perf/util/sort.h
+++ b/tools/perf/util/sort.h
@@ -31,11 +31,16 @@ extern const char *parent_pattern;
extern const char default_sort_order[];
extern int sort__need_collapse;
extern int sort__has_parent;
+extern int sort__branch_mode;
extern char *field_sep;
extern struct sort_entry sort_comm;
extern struct sort_entry sort_dso;
extern struct sort_entry sort_sym;
extern struct sort_entry sort_parent;
+extern struct sort_entry sort_dso_from;
+extern struct sort_entry sort_dso_to;
+extern struct sort_entry sort_sym_from;
+extern struct sort_entry sort_sym_to;
extern enum sort_type sort__first_dimension;
/**
@@ -72,6 +77,7 @@ struct hist_entry {
struct hist_entry *pair;
struct rb_root sorted_chain;
};
+ struct branch_info *branch_info;
struct callchain_root callchain[0];
};
@@ -82,6 +88,11 @@ enum sort_type {
SORT_SYM,
SORT_PARENT,
SORT_CPU,
+ SORT_DSO_FROM,
+ SORT_DSO_TO,
+ SORT_SYM_FROM,
+ SORT_SYM_TO,
+ SORT_MISPREDICT,
};
/*
diff --git a/tools/perf/util/strbuf.c b/tools/perf/util/strbuf.c
index 92e068517c1a..2eeb51baf077 100644
--- a/tools/perf/util/strbuf.c
+++ b/tools/perf/util/strbuf.c
@@ -1,4 +1,5 @@
#include "cache.h"
+#include <linux/kernel.h>
int prefixcmp(const char *str, const char *prefix)
{
@@ -89,14 +90,14 @@ void strbuf_addf(struct strbuf *sb, const char *fmt, ...)
if (!strbuf_avail(sb))
strbuf_grow(sb, 64);
va_start(ap, fmt);
- len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap);
+ len = vscnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap);
va_end(ap);
if (len < 0)
- die("your vsnprintf is broken");
+ die("your vscnprintf is broken");
if (len > strbuf_avail(sb)) {
strbuf_grow(sb, len);
va_start(ap, fmt);
- len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap);
+ len = vscnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap);
va_end(ap);
if (len > strbuf_avail(sb)) {
die("this should not happen, your snprintf is broken");
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 5dd83c3e2c0c..c0a028c3ebaf 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -1,6 +1,5 @@
#include <dirent.h>
#include <errno.h>
-#include <libgen.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
@@ -51,6 +50,8 @@ struct symbol_conf symbol_conf = {
int dso__name_len(const struct dso *dso)
{
+ if (!dso)
+ return strlen("[unknown]");
if (verbose)
return dso->long_name_len;
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 2a683d4fc918..ac49ef208a5f 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -5,6 +5,7 @@
#include <stdbool.h>
#include <stdint.h>
#include "map.h"
+#include "../perf.h"
#include <linux/list.h>
#include <linux/rbtree.h>
#include <stdio.h>
@@ -96,7 +97,11 @@ struct symbol_conf {
*col_width_list_str;
struct strlist *dso_list,
*comm_list,
- *sym_list;
+ *sym_list,
+ *dso_from_list,
+ *dso_to_list,
+ *sym_from_list,
+ *sym_to_list;
const char *symfs;
};
@@ -120,6 +125,19 @@ struct map_symbol {
bool has_children;
};
+struct addr_map_symbol {
+ struct map *map;
+ struct symbol *sym;
+ u64 addr;
+ u64 al_addr;
+};
+
+struct branch_info {
+ struct addr_map_symbol from;
+ struct addr_map_symbol to;
+ struct branch_flags flags;
+};
+
struct addr_location {
struct thread *thread;
struct map *map;
diff --git a/tools/perf/util/thread_map.c b/tools/perf/util/thread_map.c
index e15983cf077d..84d9bd782004 100644
--- a/tools/perf/util/thread_map.c
+++ b/tools/perf/util/thread_map.c
@@ -229,7 +229,7 @@ static struct thread_map *thread_map__new_by_tid_str(const char *tid_str)
if (!tid_str) {
threads = malloc(sizeof(*threads) + sizeof(pid_t));
if (threads != NULL) {
- threads->map[1] = -1;
+ threads->map[0] = -1;
threads->nr = 1;
}
return threads;
diff --git a/tools/perf/util/thread_map.h b/tools/perf/util/thread_map.h
index 7da80f14418b..f718df8a3c59 100644
--- a/tools/perf/util/thread_map.h
+++ b/tools/perf/util/thread_map.h
@@ -6,7 +6,7 @@
struct thread_map {
int nr;
- int map[];
+ pid_t map[];
};
struct thread_map *thread_map__new_by_pid(pid_t pid);
diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c
index e0a4f652f289..dfd1bd8371a4 100644
--- a/tools/perf/util/trace-event-parse.c
+++ b/tools/perf/util/trace-event-parse.c
@@ -722,7 +722,7 @@ static char *event_read_name(void)
static int event_read_id(void)
{
char *token;
- int id;
+ int id = -1;
if (read_expected_item(EVENT_ITEM, "ID") < 0)
return -1;
@@ -731,15 +731,13 @@ static int event_read_id(void)
return -1;
if (read_expect_type(EVENT_ITEM, &token) < 0)
- goto fail;
+ goto free;
id = strtoul(token, NULL, 0);
- free_token(token);
- return id;
- fail:
+ free:
free_token(token);
- return -1;
+ return id;
}
static int field_is_string(struct format_field *field)
@@ -1423,6 +1421,11 @@ static long long arg_num_eval(struct print_arg *arg)
die("unknown op '%s'", arg->op.op);
}
break;
+ case '+':
+ left = arg_num_eval(arg->op.left);
+ right = arg_num_eval(arg->op.right);
+ val = left + right;
+ break;
default:
die("unknown op '%s'", arg->op.op);
}
@@ -1483,6 +1486,13 @@ process_fields(struct event *event, struct print_flag_sym **list, char **tok)
free_token(token);
type = process_arg(event, arg, &token);
+
+ if (type == EVENT_OP)
+ type = process_op(event, arg, &token);
+
+ if (type == EVENT_ERROR)
+ goto out_free;
+
if (test_type_token(type, token, EVENT_DELIM, ","))
goto out_free;