mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2025-01-17 13:58:46 +00:00
perf/core improvements and fixes
. Handle PERF_RECORD_EXIT events in sched and annotate. . struct machine refactorings to help in top and trace. . Add on_exit implementation for systems without one, e.g. Android, from Bernhard Rosenkraenzer. . Only process events for vcpus of interest, helps handling large number of events, from David Ahern. . Cross compiling fixes for Android, from Irina Tirdea. . Add documentation on compiling for Android, from Irina Tirdea. . perf diff improvements from Jiri Olsa. . Target (task/user/cpu/syswide) handling improvements, from Namhyung Kim. . Add support in 'trace' for tracing workload given by command line, from Namhyung Kim. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.14 (GNU/Linux) iQIcBAABAgAGBQJQc0PxAAoJENZQFvNTUqpAEQMP/3HlgQZ0IASO/ti7ts4CMHUs YcwuStn+JdD3KGf9Spu07YhhkbXlabbEjxZ+RwG/PjnXk4U3H6rYYD329MhKEbK4 JVICtyFWX6AMdUfh4VRNqq6YZsiWiMNURbWU18rk+WtPpCuyW57hwLDNeU0T3yNA 7gReqFXP7pK4m5vOUh8wjCUKvJJKZGKg9sL3l35wb0q9rHUDqLwYhwzsIAW5c7Pe GsBT2v2GBWYizyW1/qVD/qEJCgRZFFPk6QRrd3KWHbREr817iQLsDGbgQDPjmzUX 9OabB3KXOBo8v4cXCeGoZxxsdP54oRW13t0tAhuCdh1phacZd3kDTlXDrzJ2NEz8 wriyi88udwoucJcBiWI6pbnQoxQ0yiX1MGFUP3JiQxdOTNCbKfF2TeSjrma4D4SM cCOHDo4c1sKvANI3cNdeaEGXRzinZZy2i5GGy858YcNkZ6L/SA5UdzUl1/k/uAk2 ZdhGXb7AlIIhVhJVutmLetUgi1i2BsekeHxR36F20ReK3fBzy+8mZGWiKxBdwL/J oTq2S46SY/DdNOfjA3EXPDlnyPkQ01dOWgZ9AeoVBl92WCZ07oU4V+OGEtIB0ust jHRIhX/37k7XLt8ExuhhtfCJ7scRNnvvfPiLIMstO0jTOb31wJjO3kqMGXgLgiDj 7cYyhVRqwNbb67qogfxM =7tdm -----END PGP SIGNATURE----- Merge tag 'perf-core-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo: * Handle PERF_RECORD_EXIT events in sched and annotate. * struct machine refactorings to help in top and trace. * Add on_exit implementation for systems without one, e.g. Android, from Bernhard Rosenkraenzer. * Only process events for vcpus of interest, helps handling large number of events, from David Ahern. * Cross compilation fixes for Android, from Irina Tirdea. * Add documentation on compiling for Android, from Irina Tirdea. * perf diff improvements from Jiri Olsa. * Target (task/user/cpu/syswide) handling improvements, from Namhyung Kim. * Add support in 'trace' for tracing workload given by command line, from Namhyung Kim. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
commit
6fcdb1ed2e
75
tools/perf/Documentation/android.txt
Normal file
75
tools/perf/Documentation/android.txt
Normal file
@ -0,0 +1,75 @@
|
||||
How to compile perf for Android
|
||||
=========================================
|
||||
|
||||
I. Set the Android NDK environment
|
||||
------------------------------------------------
|
||||
|
||||
(a). Use the Android NDK
|
||||
------------------------------------------------
|
||||
1. You need to download and install the Android Native Development Kit (NDK).
|
||||
Set the NDK variable to point to the path where you installed the NDK:
|
||||
export NDK=/path/to/android-ndk
|
||||
|
||||
2. Set cross-compiling environment variables for NDK toolchain and sysroot.
|
||||
For arm:
|
||||
export NDK_TOOLCHAIN=${NDK}/toolchains/arm-linux-androideabi-4.6/prebuilt/linux-x86/bin/arm-linux-androideabi-
|
||||
export NDK_SYSROOT=${NDK}/platforms/android-9/arch-arm
|
||||
For x86:
|
||||
export NDK_TOOLCHAIN=${NDK}/toolchains/x86-4.6/prebuilt/linux-x86/bin/i686-linux-android-
|
||||
export NDK_SYSROOT=${NDK}/platforms/android-9/arch-x86
|
||||
|
||||
This method is not working for Android NDK versions up to Revision 8b.
|
||||
perf uses some bionic enhancements that are not included in these NDK versions.
|
||||
You can use method (b) described below instead.
|
||||
|
||||
(b). Use the Android source tree
|
||||
-----------------------------------------------
|
||||
1. Download the master branch of the Android source tree.
|
||||
Set the environment for the target you want using:
|
||||
source build/envsetup.sh
|
||||
lunch
|
||||
|
||||
2. Build your own NDK sysroot to contain latest bionic changes and set the
|
||||
NDK sysroot environment variable.
|
||||
cd ${ANDROID_BUILD_TOP}/ndk
|
||||
For arm:
|
||||
./build/tools/build-ndk-sysroot.sh --abi=arm
|
||||
export NDK_SYSROOT=${ANDROID_BUILD_TOP}/ndk/build/platforms/android-3/arch-arm
|
||||
For x86:
|
||||
./build/tools/build-ndk-sysroot.sh --abi=x86
|
||||
export NDK_SYSROOT=${ANDROID_BUILD_TOP}/ndk/build/platforms/android-3/arch-x86
|
||||
|
||||
3. Set the NDK toolchain environment variable.
|
||||
For arm:
|
||||
export NDK_TOOLCHAIN=${ANDROID_TOOLCHAIN}/arm-linux-androideabi-
|
||||
For x86:
|
||||
export NDK_TOOLCHAIN=${ANDROID_TOOLCHAIN}/i686-linux-android-
|
||||
|
||||
II. Compile perf for Android
|
||||
------------------------------------------------
|
||||
You need to run make with the NDK toolchain and sysroot defined above:
|
||||
make CROSS_COMPILE=${NDK_TOOLCHAIN} CFLAGS="--sysroot=${NDK_SYSROOT}"
|
||||
|
||||
III. Install perf
|
||||
-----------------------------------------------
|
||||
You need to connect to your Android device/emulator using adb.
|
||||
Install perf using:
|
||||
adb push perf /data/perf
|
||||
|
||||
If you also want to use perf-archive you need busybox tools for Android.
|
||||
For installing perf-archive, you first need to replace #!/bin/bash with #!/system/bin/sh:
|
||||
sed 's/#!\/bin\/bash/#!\/system\/bin\/sh/g' perf-archive >> /tmp/perf-archive
|
||||
chmod +x /tmp/perf-archive
|
||||
adb push /tmp/perf-archive /data/perf-archive
|
||||
|
||||
IV. Environment settings for running perf
|
||||
------------------------------------------------
|
||||
Some perf features need environment variables to run properly.
|
||||
You need to set these before running perf on the target:
|
||||
adb shell
|
||||
# PERF_PAGER=cat
|
||||
|
||||
IV. Run perf
|
||||
------------------------------------------------
|
||||
Run perf on your device/emulator to which you previously connected using adb:
|
||||
# ./data/perf
|
@ -72,6 +72,66 @@ OPTIONS
|
||||
--symfs=<directory>::
|
||||
Look for files with symbols relative to this directory.
|
||||
|
||||
-b::
|
||||
--baseline-only::
|
||||
Show only items with match in baseline.
|
||||
|
||||
-c::
|
||||
--compute::
|
||||
Differential computation selection - delta,ratio,wdiff (default is delta).
|
||||
If '+' is specified as a first character, the output is sorted based
|
||||
on the computation results.
|
||||
See COMPARISON METHODS section for more info.
|
||||
|
||||
-p::
|
||||
--period::
|
||||
Show period values for both compared hist entries.
|
||||
|
||||
-F::
|
||||
--formula::
|
||||
Show formula for given computation.
|
||||
|
||||
COMPARISON METHODS
|
||||
------------------
|
||||
delta
|
||||
~~~~~
|
||||
If specified the 'Delta' column is displayed with value 'd' computed as:
|
||||
|
||||
d = A->period_percent - B->period_percent
|
||||
|
||||
with:
|
||||
- A/B being matching hist entry from first/second file specified
|
||||
(or perf.data/perf.data.old) respectively.
|
||||
|
||||
- period_percent being the % of the hist entry period value within
|
||||
single data file
|
||||
|
||||
ratio
|
||||
~~~~~
|
||||
If specified the 'Ratio' column is displayed with value 'r' computed as:
|
||||
|
||||
r = A->period / B->period
|
||||
|
||||
with:
|
||||
- A/B being matching hist entry from first/second file specified
|
||||
(or perf.data/perf.data.old) respectively.
|
||||
|
||||
- period being the hist entry period value
|
||||
|
||||
wdiff
|
||||
~~~~~
|
||||
If specified the 'Weighted diff' column is displayed with value 'd' computed as:
|
||||
|
||||
d = B->period * WEIGHT-A - A->period * WEIGHT-B
|
||||
|
||||
- A/B being matching hist entry from first/second file specified
|
||||
(or perf.data/perf.data.old) respectively.
|
||||
|
||||
- period being the hist entry period value
|
||||
|
||||
- WEIGHT-A/WEIGHT-B being user suplied weights in the the '-c' option
|
||||
behind ':' separator like '-c wdiff:1,2'.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-record[1]
|
||||
|
@ -45,6 +45,8 @@ include config/utilities.mak
|
||||
#
|
||||
# Define NO_LIBUNWIND if you do not want libunwind dependency for dwarf
|
||||
# backtrace post unwind.
|
||||
#
|
||||
# Define NO_BACKTRACE if you do not want stack backtrace debug feature
|
||||
|
||||
$(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE
|
||||
@$(SHELL_PATH) util/PERF-VERSION-GEN $(OUTPUT)
|
||||
@ -153,15 +155,15 @@ SPARSE_FLAGS = -D__BIG_ENDIAN__ -D__powerpc__
|
||||
|
||||
-include config/feature-tests.mak
|
||||
|
||||
ifeq ($(call try-cc,$(SOURCE_HELLO),-Werror -fstack-protector-all),y)
|
||||
ifeq ($(call try-cc,$(SOURCE_HELLO),$(CFLAGS) -Werror -fstack-protector-all),y)
|
||||
CFLAGS := $(CFLAGS) -fstack-protector-all
|
||||
endif
|
||||
|
||||
ifeq ($(call try-cc,$(SOURCE_HELLO),-Werror -Wstack-protector),y)
|
||||
ifeq ($(call try-cc,$(SOURCE_HELLO),$(CFLAGS) -Werror -Wstack-protector),y)
|
||||
CFLAGS := $(CFLAGS) -Wstack-protector
|
||||
endif
|
||||
|
||||
ifeq ($(call try-cc,$(SOURCE_HELLO),-Werror -Wvolatile-register-var),y)
|
||||
ifeq ($(call try-cc,$(SOURCE_HELLO),$(CFLAGS) -Werror -Wvolatile-register-var),y)
|
||||
CFLAGS := $(CFLAGS) -Wvolatile-register-var
|
||||
endif
|
||||
|
||||
@ -170,6 +172,13 @@ endif
|
||||
BASIC_CFLAGS = -Iutil/include -Iarch/$(ARCH)/include -I$(OUTPUT)util -I$(TRACE_EVENT_DIR) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE
|
||||
BASIC_LDFLAGS =
|
||||
|
||||
ifeq ($(call try-cc,$(SOURCE_BIONIC),$(CFLAGS)),y)
|
||||
BIONIC := 1
|
||||
EXTLIBS := $(filter-out -lrt,$(EXTLIBS))
|
||||
EXTLIBS := $(filter-out -lpthread,$(EXTLIBS))
|
||||
BASIC_CFLAGS += -I.
|
||||
endif
|
||||
|
||||
# Guard against environment variables
|
||||
BUILTIN_OBJS =
|
||||
LIB_H =
|
||||
@ -185,7 +194,7 @@ strip-libs = $(filter-out -l%,$(1))
|
||||
PYTHON_EXT_SRCS := $(shell grep -v ^\# util/python-ext-sources)
|
||||
PYTHON_EXT_DEPS := util/python-ext-sources util/setup.py
|
||||
|
||||
$(OUTPUT)python/perf.so: $(PYRF_OBJS) $(PYTHON_EXT_SRCS) $(PYTHON_EXT_DEPS)
|
||||
$(OUTPUT)python/perf.so: $(PYTHON_EXT_SRCS) $(PYTHON_EXT_DEPS)
|
||||
$(QUIET_GEN)CFLAGS='$(BASIC_CFLAGS)' $(PYTHON_WORD) util/setup.py \
|
||||
--quiet build_ext; \
|
||||
mkdir -p $(OUTPUT)python && \
|
||||
@ -298,6 +307,7 @@ LIB_H += util/evlist.h
|
||||
LIB_H += util/exec_cmd.h
|
||||
LIB_H += util/types.h
|
||||
LIB_H += util/levenshtein.h
|
||||
LIB_H += util/machine.h
|
||||
LIB_H += util/map.h
|
||||
LIB_H += util/parse-options.h
|
||||
LIB_H += util/parse-events.h
|
||||
@ -381,6 +391,7 @@ LIB_OBJS += $(OUTPUT)util/header.o
|
||||
LIB_OBJS += $(OUTPUT)util/callchain.o
|
||||
LIB_OBJS += $(OUTPUT)util/values.o
|
||||
LIB_OBJS += $(OUTPUT)util/debug.o
|
||||
LIB_OBJS += $(OUTPUT)util/machine.o
|
||||
LIB_OBJS += $(OUTPUT)util/map.o
|
||||
LIB_OBJS += $(OUTPUT)util/pstack.o
|
||||
LIB_OBJS += $(OUTPUT)util/session.o
|
||||
@ -446,20 +457,6 @@ BUILTIN_OBJS += $(OUTPUT)builtin-inject.o
|
||||
|
||||
PERFLIBS = $(LIB_FILE) $(LIBTRACEEVENT)
|
||||
|
||||
# Files needed for the python binding, perf.so
|
||||
# pyrf is just an internal name needed for all those wrappers.
|
||||
# This has to be in sync with what is in the 'sources' variable in
|
||||
# tools/perf/util/setup.py
|
||||
|
||||
PYRF_OBJS += $(OUTPUT)util/cpumap.o
|
||||
PYRF_OBJS += $(OUTPUT)util/ctype.o
|
||||
PYRF_OBJS += $(OUTPUT)util/evlist.o
|
||||
PYRF_OBJS += $(OUTPUT)util/evsel.o
|
||||
PYRF_OBJS += $(OUTPUT)util/python.o
|
||||
PYRF_OBJS += $(OUTPUT)util/thread_map.o
|
||||
PYRF_OBJS += $(OUTPUT)util/util.o
|
||||
PYRF_OBJS += $(OUTPUT)util/xyarray.o
|
||||
|
||||
#
|
||||
# Platform specific tweaks
|
||||
#
|
||||
@ -479,14 +476,26 @@ else
|
||||
FLAGS_LIBELF=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS)
|
||||
ifneq ($(call try-cc,$(SOURCE_LIBELF),$(FLAGS_LIBELF)),y)
|
||||
FLAGS_GLIBC=$(ALL_CFLAGS) $(ALL_LDFLAGS)
|
||||
ifneq ($(call try-cc,$(SOURCE_GLIBC),$(FLAGS_GLIBC)),y)
|
||||
msg := $(error No gnu/libc-version.h found, please install glibc-dev[el]/glibc-static);
|
||||
else
|
||||
ifeq ($(call try-cc,$(SOURCE_GLIBC),$(FLAGS_GLIBC)),y)
|
||||
LIBC_SUPPORT := 1
|
||||
endif
|
||||
ifeq ($(BIONIC),1)
|
||||
LIBC_SUPPORT := 1
|
||||
endif
|
||||
ifeq ($(LIBC_SUPPORT),1)
|
||||
NO_LIBELF := 1
|
||||
NO_DWARF := 1
|
||||
NO_DEMANGLE := 1
|
||||
else
|
||||
msg := $(error No gnu/libc-version.h found, please install glibc-dev[el]/glibc-static);
|
||||
endif
|
||||
endif
|
||||
else
|
||||
FLAGS_DWARF=$(ALL_CFLAGS) -ldw -lelf $(ALL_LDFLAGS) $(EXTLIBS)
|
||||
ifneq ($(call try-cc,$(SOURCE_DWARF),$(FLAGS_DWARF)),y)
|
||||
msg := $(warning No libdw.h found or old libdw.h found or elfutils is older than 0.138, disables dwarf support. Please install new elfutils-devel/libdw-dev);
|
||||
NO_DWARF := 1
|
||||
endif # Dwarf support
|
||||
endif # SOURCE_LIBELF
|
||||
endif # NO_LIBELF
|
||||
|
||||
ifndef NO_LIBUNWIND
|
||||
@ -511,8 +520,6 @@ ifneq ($(OUTPUT),)
|
||||
endif
|
||||
|
||||
ifdef NO_LIBELF
|
||||
BASIC_CFLAGS += -DNO_LIBELF_SUPPORT
|
||||
|
||||
EXTLIBS := $(filter-out -lelf,$(EXTLIBS))
|
||||
|
||||
# Remove ELF/DWARF dependent codes
|
||||
@ -527,17 +534,12 @@ BUILTIN_OBJS := $(filter-out $(OUTPUT)builtin-probe.o,$(BUILTIN_OBJS))
|
||||
LIB_OBJS += $(OUTPUT)util/symbol-minimal.o
|
||||
|
||||
else # NO_LIBELF
|
||||
BASIC_CFLAGS += -DLIBELF_SUPPORT
|
||||
|
||||
ifneq ($(call try-cc,$(SOURCE_ELF_MMAP),$(FLAGS_COMMON)),y)
|
||||
BASIC_CFLAGS += -DLIBELF_NO_MMAP
|
||||
ifeq ($(call try-cc,$(SOURCE_ELF_MMAP),$(FLAGS_COMMON)),y)
|
||||
BASIC_CFLAGS += -DLIBELF_MMAP
|
||||
endif
|
||||
|
||||
FLAGS_DWARF=$(ALL_CFLAGS) -ldw -lelf $(ALL_LDFLAGS) $(EXTLIBS)
|
||||
ifneq ($(call try-cc,$(SOURCE_DWARF),$(FLAGS_DWARF)),y)
|
||||
msg := $(warning No libdw.h found or old libdw.h found or elfutils is older than 0.138, disables dwarf support. Please install new elfutils-devel/libdw-dev);
|
||||
NO_DWARF := 1
|
||||
endif # Dwarf support
|
||||
|
||||
ifndef NO_DWARF
|
||||
ifeq ($(origin PERF_HAVE_DWARF_REGS), undefined)
|
||||
msg := $(warning DWARF register mappings have not been defined for architecture $(ARCH), DWARF support disabled);
|
||||
@ -550,38 +552,33 @@ endif # PERF_HAVE_DWARF_REGS
|
||||
endif # NO_DWARF
|
||||
endif # NO_LIBELF
|
||||
|
||||
ifdef NO_LIBUNWIND
|
||||
BASIC_CFLAGS += -DNO_LIBUNWIND_SUPPORT
|
||||
else
|
||||
ifndef NO_LIBUNWIND
|
||||
BASIC_CFLAGS += -DLIBUNWIND_SUPPORT
|
||||
EXTLIBS += $(LIBUNWIND_LIBS)
|
||||
BASIC_CFLAGS := $(LIBUNWIND_CFLAGS) $(BASIC_CFLAGS)
|
||||
BASIC_LDFLAGS := $(LIBUNWIND_LDFLAGS) $(BASIC_LDFLAGS)
|
||||
LIB_OBJS += $(OUTPUT)util/unwind.o
|
||||
endif
|
||||
|
||||
ifdef NO_LIBAUDIT
|
||||
BASIC_CFLAGS += -DNO_LIBAUDIT_SUPPORT
|
||||
else
|
||||
ifndef NO_LIBAUDIT
|
||||
FLAGS_LIBAUDIT = $(ALL_CFLAGS) $(ALL_LDFLAGS) -laudit
|
||||
ifneq ($(call try-cc,$(SOURCE_LIBAUDIT),$(FLAGS_LIBAUDIT)),y)
|
||||
msg := $(warning No libaudit.h found, disables 'trace' tool, please install audit-libs-devel or libaudit-dev);
|
||||
BASIC_CFLAGS += -DNO_LIBAUDIT_SUPPORT
|
||||
else
|
||||
BASIC_CFLAGS += -DLIBAUDIT_SUPPORT
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-trace.o
|
||||
EXTLIBS += -laudit
|
||||
endif
|
||||
endif
|
||||
|
||||
ifdef NO_NEWT
|
||||
BASIC_CFLAGS += -DNO_NEWT_SUPPORT
|
||||
else
|
||||
ifndef NO_NEWT
|
||||
FLAGS_NEWT=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -lnewt
|
||||
ifneq ($(call try-cc,$(SOURCE_NEWT),$(FLAGS_NEWT)),y)
|
||||
msg := $(warning newt not found, disables TUI support. Please install newt-devel or libnewt-dev);
|
||||
BASIC_CFLAGS += -DNO_NEWT_SUPPORT
|
||||
else
|
||||
# Fedora has /usr/include/slang/slang.h, but ubuntu /usr/include/slang.h
|
||||
BASIC_CFLAGS += -I/usr/include/slang
|
||||
BASIC_CFLAGS += -DNEWT_SUPPORT
|
||||
EXTLIBS += -lnewt -lslang
|
||||
LIB_OBJS += $(OUTPUT)ui/setup.o
|
||||
LIB_OBJS += $(OUTPUT)ui/browser.o
|
||||
@ -603,17 +600,15 @@ else
|
||||
endif
|
||||
endif
|
||||
|
||||
ifdef NO_GTK2
|
||||
BASIC_CFLAGS += -DNO_GTK2_SUPPORT
|
||||
else
|
||||
ifndef NO_GTK2
|
||||
FLAGS_GTK2=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) $(shell pkg-config --libs --cflags gtk+-2.0 2>/dev/null)
|
||||
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
|
||||
ifeq ($(call try-cc,$(SOURCE_GTK2_INFOBAR),$(FLAGS_GTK2)),y)
|
||||
BASIC_CFLAGS += -DHAVE_GTK_INFO_BAR
|
||||
endif
|
||||
BASIC_CFLAGS += -DGTK2_SUPPORT
|
||||
BASIC_CFLAGS += $(shell pkg-config --cflags gtk+-2.0 2>/dev/null)
|
||||
EXTLIBS += $(shell pkg-config --libs gtk+-2.0 2>/dev/null)
|
||||
LIB_OBJS += $(OUTPUT)ui/gtk/browser.o
|
||||
@ -621,7 +616,7 @@ else
|
||||
LIB_OBJS += $(OUTPUT)ui/gtk/util.o
|
||||
LIB_OBJS += $(OUTPUT)ui/gtk/helpline.o
|
||||
# Make sure that it'd be included only once.
|
||||
ifneq ($(findstring -DNO_NEWT_SUPPORT,$(BASIC_CFLAGS)),)
|
||||
ifeq ($(findstring -DNEWT_SUPPORT,$(BASIC_CFLAGS)),)
|
||||
LIB_OBJS += $(OUTPUT)ui/setup.o
|
||||
LIB_OBJS += $(OUTPUT)ui/util.o
|
||||
endif
|
||||
@ -762,23 +757,24 @@ ifeq ($(NO_PERF_REGS),0)
|
||||
ifeq ($(ARCH),x86)
|
||||
LIB_H += arch/x86/include/perf_regs.h
|
||||
endif
|
||||
else
|
||||
BASIC_CFLAGS += -DNO_PERF_REGS
|
||||
BASIC_CFLAGS += -DHAVE_PERF_REGS
|
||||
endif
|
||||
|
||||
ifdef NO_STRLCPY
|
||||
BASIC_CFLAGS += -DNO_STRLCPY
|
||||
else
|
||||
ifneq ($(call try-cc,$(SOURCE_STRLCPY),),y)
|
||||
BASIC_CFLAGS += -DNO_STRLCPY
|
||||
ifndef NO_STRLCPY
|
||||
ifeq ($(call try-cc,$(SOURCE_STRLCPY),),y)
|
||||
BASIC_CFLAGS += -DHAVE_STRLCPY
|
||||
endif
|
||||
endif
|
||||
|
||||
ifdef NO_BACKTRACE
|
||||
BASIC_CFLAGS += -DNO_BACKTRACE
|
||||
else
|
||||
ifneq ($(call try-cc,$(SOURCE_BACKTRACE),),y)
|
||||
BASIC_CFLAGS += -DNO_BACKTRACE
|
||||
ifndef NO_ON_EXIT
|
||||
ifeq ($(call try-cc,$(SOURCE_ON_EXIT),),y)
|
||||
BASIC_CFLAGS += -DHAVE_ON_EXIT
|
||||
endif
|
||||
endif
|
||||
|
||||
ifndef NO_BACKTRACE
|
||||
ifeq ($(call try-cc,$(SOURCE_BACKTRACE),),y)
|
||||
BASIC_CFLAGS += -DBACKTRACE_SUPPORT
|
||||
endif
|
||||
endif
|
||||
|
||||
|
@ -1,23 +1,59 @@
|
||||
# perf completion
|
||||
|
||||
function_exists()
|
||||
{
|
||||
declare -F $1 > /dev/null
|
||||
return $?
|
||||
}
|
||||
|
||||
function_exists __ltrim_colon_completions ||
|
||||
__ltrim_colon_completions()
|
||||
{
|
||||
if [[ "$1" == *:* && "$COMP_WORDBREAKS" == *:* ]]; then
|
||||
# Remove colon-word prefix from COMPREPLY items
|
||||
local colon_word=${1%${1##*:}}
|
||||
local i=${#COMPREPLY[*]}
|
||||
while [[ $((--i)) -ge 0 ]]; do
|
||||
COMPREPLY[$i]=${COMPREPLY[$i]#"$colon_word"}
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
have perf &&
|
||||
_perf()
|
||||
{
|
||||
local cur cmd
|
||||
local cur prev cmd
|
||||
|
||||
COMPREPLY=()
|
||||
_get_comp_words_by_ref cur prev
|
||||
if function_exists _get_comp_words_by_ref; then
|
||||
_get_comp_words_by_ref -n : cur prev
|
||||
else
|
||||
cur=$(_get_cword :)
|
||||
prev=${COMP_WORDS[COMP_CWORD-1]}
|
||||
fi
|
||||
|
||||
cmd=${COMP_WORDS[0]}
|
||||
|
||||
# List perf subcommands
|
||||
# List perf subcommands or long options
|
||||
if [ $COMP_CWORD -eq 1 ]; then
|
||||
cmds=$($cmd --list-cmds)
|
||||
COMPREPLY=( $( compgen -W '$cmds' -- "$cur" ) )
|
||||
if [[ $cur == --* ]]; then
|
||||
COMPREPLY=( $( compgen -W '--help --version \
|
||||
--exec-path --html-path --paginate --no-pager \
|
||||
--perf-dir --work-tree --debugfs-dir' -- "$cur" ) )
|
||||
else
|
||||
cmds=$($cmd --list-cmds)
|
||||
COMPREPLY=( $( compgen -W '$cmds' -- "$cur" ) )
|
||||
fi
|
||||
# List possible events for -e option
|
||||
elif [[ $prev == "-e" && "${COMP_WORDS[1]}" == @(record|stat|top) ]]; then
|
||||
cmds=$($cmd list --raw-dump)
|
||||
COMPREPLY=( $( compgen -W '$cmds' -- "$cur" ) )
|
||||
evts=$($cmd list --raw-dump)
|
||||
COMPREPLY=( $( compgen -W '$evts' -- "$cur" ) )
|
||||
__ltrim_colon_completions $cur
|
||||
# List long option names
|
||||
elif [[ $cur == --* ]]; then
|
||||
subcmd=${COMP_WORDS[1]}
|
||||
opts=$($cmd $subcmd --list-opts)
|
||||
COMPREPLY=( $( compgen -W '$opts' -- "$cur" ) )
|
||||
# Fall down to list regular files
|
||||
else
|
||||
_filedir
|
||||
|
@ -246,7 +246,8 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
.sample = process_sample_event,
|
||||
.mmap = perf_event__process_mmap,
|
||||
.comm = perf_event__process_comm,
|
||||
.fork = perf_event__process_task,
|
||||
.exit = perf_event__process_exit,
|
||||
.fork = perf_event__process_fork,
|
||||
.ordered_samples = true,
|
||||
.ordering_requires_timestamps = true,
|
||||
},
|
||||
|
@ -15,22 +15,6 @@
|
||||
#include "util/strlist.h"
|
||||
#include "util/symbol.h"
|
||||
|
||||
static char const *add_name_list_str, *remove_name_list_str;
|
||||
|
||||
static const char * const buildid_cache_usage[] = {
|
||||
"perf buildid-cache [<options>]",
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct option buildid_cache_options[] = {
|
||||
OPT_STRING('a', "add", &add_name_list_str,
|
||||
"file list", "file(s) to add"),
|
||||
OPT_STRING('r', "remove", &remove_name_list_str, "file list",
|
||||
"file(s) to remove"),
|
||||
OPT_INCR('v', "verbose", &verbose, "be more verbose"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
static int build_id_cache__add_file(const char *filename, const char *debugdir)
|
||||
{
|
||||
char sbuild_id[BUILD_ID_SIZE * 2 + 1];
|
||||
@ -51,8 +35,8 @@ static int build_id_cache__add_file(const char *filename, const char *debugdir)
|
||||
return err;
|
||||
}
|
||||
|
||||
static int build_id_cache__remove_file(const char *filename __maybe_unused,
|
||||
const char *debugdir __maybe_unused)
|
||||
static int build_id_cache__remove_file(const char *filename,
|
||||
const char *debugdir)
|
||||
{
|
||||
u8 build_id[BUILD_ID_SIZE];
|
||||
char sbuild_id[BUILD_ID_SIZE * 2 + 1];
|
||||
@ -73,11 +57,34 @@ static int build_id_cache__remove_file(const char *filename __maybe_unused,
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __cmd_buildid_cache(void)
|
||||
int cmd_buildid_cache(int argc, const char **argv,
|
||||
const char *prefix __maybe_unused)
|
||||
{
|
||||
struct strlist *list;
|
||||
struct str_node *pos;
|
||||
char debugdir[PATH_MAX];
|
||||
char const *add_name_list_str = NULL,
|
||||
*remove_name_list_str = NULL;
|
||||
const struct option buildid_cache_options[] = {
|
||||
OPT_STRING('a', "add", &add_name_list_str,
|
||||
"file list", "file(s) to add"),
|
||||
OPT_STRING('r', "remove", &remove_name_list_str, "file list",
|
||||
"file(s) to remove"),
|
||||
OPT_INCR('v', "verbose", &verbose, "be more verbose"),
|
||||
OPT_END()
|
||||
};
|
||||
const char * const buildid_cache_usage[] = {
|
||||
"perf buildid-cache [<options>]",
|
||||
NULL
|
||||
};
|
||||
|
||||
argc = parse_options(argc, argv, buildid_cache_options,
|
||||
buildid_cache_usage, 0);
|
||||
|
||||
if (symbol__init() < 0)
|
||||
return -1;
|
||||
|
||||
setup_pager();
|
||||
|
||||
snprintf(debugdir, sizeof(debugdir), "%s", buildid_dir);
|
||||
|
||||
@ -119,16 +126,3 @@ static int __cmd_buildid_cache(void)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmd_buildid_cache(int argc, const char **argv,
|
||||
const char *prefix __maybe_unused)
|
||||
{
|
||||
argc = parse_options(argc, argv, buildid_cache_options,
|
||||
buildid_cache_usage, 0);
|
||||
|
||||
if (symbol__init() < 0)
|
||||
return -1;
|
||||
|
||||
setup_pager();
|
||||
return __cmd_buildid_cache();
|
||||
}
|
||||
|
@ -16,27 +16,6 @@
|
||||
#include "util/session.h"
|
||||
#include "util/symbol.h"
|
||||
|
||||
static const char *input_name;
|
||||
static bool force;
|
||||
static bool show_kernel;
|
||||
static bool with_hits;
|
||||
|
||||
static const char * const buildid_list_usage[] = {
|
||||
"perf buildid-list [<options>]",
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
OPT_BOOLEAN('H', "with-hits", &with_hits, "Show only DSOs with hits"),
|
||||
OPT_STRING('i', "input", &input_name, "file",
|
||||
"input file name"),
|
||||
OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
|
||||
OPT_BOOLEAN('k', "kernel", &show_kernel, "Show current kernel build id"),
|
||||
OPT_INCR('v', "verbose", &verbose,
|
||||
"be more verbose"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
static int sysfs__fprintf_build_id(FILE *fp)
|
||||
{
|
||||
u8 kallsyms_build_id[BUILD_ID_SIZE];
|
||||
@ -65,7 +44,8 @@ static int filename__fprintf_build_id(const char *name, FILE *fp)
|
||||
return fprintf(fp, "%s\n", sbuild_id);
|
||||
}
|
||||
|
||||
static int perf_session__list_build_ids(void)
|
||||
static int perf_session__list_build_ids(const char *input_name,
|
||||
bool force, bool with_hits)
|
||||
{
|
||||
struct perf_session *session;
|
||||
|
||||
@ -95,18 +75,31 @@ out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __cmd_buildid_list(void)
|
||||
{
|
||||
if (show_kernel)
|
||||
return sysfs__fprintf_build_id(stdout);
|
||||
|
||||
return perf_session__list_build_ids();
|
||||
}
|
||||
|
||||
int cmd_buildid_list(int argc, const char **argv,
|
||||
const char *prefix __maybe_unused)
|
||||
{
|
||||
bool show_kernel = false;
|
||||
bool with_hits = false;
|
||||
bool force = false;
|
||||
const char *input_name = NULL;
|
||||
const struct option options[] = {
|
||||
OPT_BOOLEAN('H', "with-hits", &with_hits, "Show only DSOs with hits"),
|
||||
OPT_STRING('i', "input", &input_name, "file", "input file name"),
|
||||
OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
|
||||
OPT_BOOLEAN('k', "kernel", &show_kernel, "Show current kernel build id"),
|
||||
OPT_INCR('v', "verbose", &verbose, "be more verbose"),
|
||||
OPT_END()
|
||||
};
|
||||
const char * const buildid_list_usage[] = {
|
||||
"perf buildid-list [<options>]",
|
||||
NULL
|
||||
};
|
||||
|
||||
argc = parse_options(argc, argv, options, buildid_list_usage, 0);
|
||||
setup_pager();
|
||||
return __cmd_buildid_list();
|
||||
|
||||
if (show_kernel)
|
||||
return sysfs__fprintf_build_id(stdout);
|
||||
|
||||
return perf_session__list_build_ids(input_name, force, with_hits);
|
||||
}
|
||||
|
@ -24,6 +24,228 @@ static char const *input_old = "perf.data.old",
|
||||
static char diff__default_sort_order[] = "dso,symbol";
|
||||
static bool force;
|
||||
static bool show_displacement;
|
||||
static bool show_period;
|
||||
static bool show_formula;
|
||||
static bool show_baseline_only;
|
||||
static bool sort_compute;
|
||||
|
||||
static s64 compute_wdiff_w1;
|
||||
static s64 compute_wdiff_w2;
|
||||
|
||||
enum {
|
||||
COMPUTE_DELTA,
|
||||
COMPUTE_RATIO,
|
||||
COMPUTE_WEIGHTED_DIFF,
|
||||
COMPUTE_MAX,
|
||||
};
|
||||
|
||||
const char *compute_names[COMPUTE_MAX] = {
|
||||
[COMPUTE_DELTA] = "delta",
|
||||
[COMPUTE_RATIO] = "ratio",
|
||||
[COMPUTE_WEIGHTED_DIFF] = "wdiff",
|
||||
};
|
||||
|
||||
static int compute;
|
||||
|
||||
static int setup_compute_opt_wdiff(char *opt)
|
||||
{
|
||||
char *w1_str = opt;
|
||||
char *w2_str;
|
||||
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (!opt)
|
||||
goto out;
|
||||
|
||||
w2_str = strchr(opt, ',');
|
||||
if (!w2_str)
|
||||
goto out;
|
||||
|
||||
*w2_str++ = 0x0;
|
||||
if (!*w2_str)
|
||||
goto out;
|
||||
|
||||
compute_wdiff_w1 = strtol(w1_str, NULL, 10);
|
||||
compute_wdiff_w2 = strtol(w2_str, NULL, 10);
|
||||
|
||||
if (!compute_wdiff_w1 || !compute_wdiff_w2)
|
||||
goto out;
|
||||
|
||||
pr_debug("compute wdiff w1(%" PRId64 ") w2(%" PRId64 ")\n",
|
||||
compute_wdiff_w1, compute_wdiff_w2);
|
||||
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
if (ret)
|
||||
pr_err("Failed: wrong weight data, use 'wdiff:w1,w2'\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int setup_compute_opt(char *opt)
|
||||
{
|
||||
if (compute == COMPUTE_WEIGHTED_DIFF)
|
||||
return setup_compute_opt_wdiff(opt);
|
||||
|
||||
if (opt) {
|
||||
pr_err("Failed: extra option specified '%s'", opt);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int setup_compute(const struct option *opt, const char *str,
|
||||
int unset __maybe_unused)
|
||||
{
|
||||
int *cp = (int *) opt->value;
|
||||
char *cstr = (char *) str;
|
||||
char buf[50];
|
||||
unsigned i;
|
||||
char *option;
|
||||
|
||||
if (!str) {
|
||||
*cp = COMPUTE_DELTA;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (*str == '+') {
|
||||
sort_compute = true;
|
||||
cstr = (char *) ++str;
|
||||
if (!*str)
|
||||
return 0;
|
||||
}
|
||||
|
||||
option = strchr(str, ':');
|
||||
if (option) {
|
||||
unsigned len = option++ - str;
|
||||
|
||||
/*
|
||||
* The str data are not writeable, so we need
|
||||
* to use another buffer.
|
||||
*/
|
||||
|
||||
/* No option value is longer. */
|
||||
if (len >= sizeof(buf))
|
||||
return -EINVAL;
|
||||
|
||||
strncpy(buf, str, len);
|
||||
buf[len] = 0x0;
|
||||
cstr = buf;
|
||||
}
|
||||
|
||||
for (i = 0; i < COMPUTE_MAX; i++)
|
||||
if (!strcmp(cstr, compute_names[i])) {
|
||||
*cp = i;
|
||||
return setup_compute_opt(option);
|
||||
}
|
||||
|
||||
pr_err("Failed: '%s' is not computation method "
|
||||
"(use 'delta','ratio' or 'wdiff')\n", str);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static double get_period_percent(struct hist_entry *he, u64 period)
|
||||
{
|
||||
u64 total = he->hists->stats.total_period;
|
||||
return (period * 100.0) / total;
|
||||
}
|
||||
|
||||
double perf_diff__compute_delta(struct hist_entry *he)
|
||||
{
|
||||
struct hist_entry *pair = he->pair;
|
||||
double new_percent = get_period_percent(he, he->stat.period);
|
||||
double old_percent = pair ? get_period_percent(pair, pair->stat.period) : 0.0;
|
||||
|
||||
he->diff.period_ratio_delta = new_percent - old_percent;
|
||||
he->diff.computed = true;
|
||||
return he->diff.period_ratio_delta;
|
||||
}
|
||||
|
||||
double perf_diff__compute_ratio(struct hist_entry *he)
|
||||
{
|
||||
struct hist_entry *pair = he->pair;
|
||||
double new_period = he->stat.period;
|
||||
double old_period = pair ? pair->stat.period : 0;
|
||||
|
||||
he->diff.computed = true;
|
||||
he->diff.period_ratio = pair ? (new_period / old_period) : 0;
|
||||
return he->diff.period_ratio;
|
||||
}
|
||||
|
||||
s64 perf_diff__compute_wdiff(struct hist_entry *he)
|
||||
{
|
||||
struct hist_entry *pair = he->pair;
|
||||
u64 new_period = he->stat.period;
|
||||
u64 old_period = pair ? pair->stat.period : 0;
|
||||
|
||||
he->diff.computed = true;
|
||||
|
||||
if (!pair)
|
||||
he->diff.wdiff = 0;
|
||||
else
|
||||
he->diff.wdiff = new_period * compute_wdiff_w2 -
|
||||
old_period * compute_wdiff_w1;
|
||||
|
||||
return he->diff.wdiff;
|
||||
}
|
||||
|
||||
static int formula_delta(struct hist_entry *he, char *buf, size_t size)
|
||||
{
|
||||
struct hist_entry *pair = he->pair;
|
||||
|
||||
if (!pair)
|
||||
return -1;
|
||||
|
||||
return scnprintf(buf, size,
|
||||
"(%" PRIu64 " * 100 / %" PRIu64 ") - "
|
||||
"(%" PRIu64 " * 100 / %" PRIu64 ")",
|
||||
he->stat.period, he->hists->stats.total_period,
|
||||
pair->stat.period, pair->hists->stats.total_period);
|
||||
}
|
||||
|
||||
static int formula_ratio(struct hist_entry *he, char *buf, size_t size)
|
||||
{
|
||||
struct hist_entry *pair = he->pair;
|
||||
double new_period = he->stat.period;
|
||||
double old_period = pair ? pair->stat.period : 0;
|
||||
|
||||
if (!pair)
|
||||
return -1;
|
||||
|
||||
return scnprintf(buf, size, "%.0F / %.0F", new_period, old_period);
|
||||
}
|
||||
|
||||
static int formula_wdiff(struct hist_entry *he, char *buf, size_t size)
|
||||
{
|
||||
struct hist_entry *pair = he->pair;
|
||||
u64 new_period = he->stat.period;
|
||||
u64 old_period = pair ? pair->stat.period : 0;
|
||||
|
||||
if (!pair)
|
||||
return -1;
|
||||
|
||||
return scnprintf(buf, size,
|
||||
"(%" PRIu64 " * " "%" PRId64 ") - (%" PRIu64 " * " "%" PRId64 ")",
|
||||
new_period, compute_wdiff_w2, old_period, compute_wdiff_w1);
|
||||
}
|
||||
|
||||
int perf_diff__formula(char *buf, size_t size, struct hist_entry *he)
|
||||
{
|
||||
switch (compute) {
|
||||
case COMPUTE_DELTA:
|
||||
return formula_delta(he, buf, size);
|
||||
case COMPUTE_RATIO:
|
||||
return formula_ratio(he, buf, size);
|
||||
case COMPUTE_WEIGHTED_DIFF:
|
||||
return formula_wdiff(he, buf, size);
|
||||
default:
|
||||
BUG_ON(1);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int hists__add_entry(struct hists *self,
|
||||
struct addr_location *al, u64 period)
|
||||
@ -47,7 +269,7 @@ static int diff__process_sample_event(struct perf_tool *tool __maybe_unused,
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (al.filtered || al.sym == NULL)
|
||||
if (al.filtered)
|
||||
return 0;
|
||||
|
||||
if (hists__add_entry(&evsel->hists, &al, sample->period)) {
|
||||
@ -63,15 +285,15 @@ static struct perf_tool 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,
|
||||
.exit = perf_event__process_exit,
|
||||
.fork = perf_event__process_fork,
|
||||
.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,
|
||||
struct hist_entry *he)
|
||||
static void insert_hist_entry_by_name(struct rb_root *root,
|
||||
struct hist_entry *he)
|
||||
{
|
||||
struct rb_node **p = &root->rb_node;
|
||||
struct rb_node *parent = NULL;
|
||||
@ -90,7 +312,7 @@ static void perf_session__insert_hist_entry_by_name(struct rb_root *root,
|
||||
rb_insert_color(&he->rb_node, root);
|
||||
}
|
||||
|
||||
static void hists__resort_entries(struct hists *self)
|
||||
static void hists__name_resort(struct hists *self, bool sort)
|
||||
{
|
||||
unsigned long position = 1;
|
||||
struct rb_root tmp = RB_ROOT;
|
||||
@ -100,12 +322,16 @@ static void hists__resort_entries(struct hists *self)
|
||||
struct hist_entry *n = rb_entry(next, struct hist_entry, rb_node);
|
||||
|
||||
next = rb_next(&n->rb_node);
|
||||
rb_erase(&n->rb_node, &self->entries);
|
||||
n->position = position++;
|
||||
perf_session__insert_hist_entry_by_name(&tmp, n);
|
||||
|
||||
if (sort) {
|
||||
rb_erase(&n->rb_node, &self->entries);
|
||||
insert_hist_entry_by_name(&tmp, n);
|
||||
}
|
||||
}
|
||||
|
||||
self->entries = tmp;
|
||||
if (sort)
|
||||
self->entries = tmp;
|
||||
}
|
||||
|
||||
static struct hist_entry *hists__find_entry(struct hists *self,
|
||||
@ -121,7 +347,7 @@ static struct hist_entry *hists__find_entry(struct hists *self,
|
||||
n = n->rb_left;
|
||||
else if (cmp > 0)
|
||||
n = n->rb_right;
|
||||
else
|
||||
else
|
||||
return iter;
|
||||
}
|
||||
|
||||
@ -150,6 +376,160 @@ static struct perf_evsel *evsel_match(struct perf_evsel *evsel,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void perf_evlist__resort_hists(struct perf_evlist *evlist, bool name)
|
||||
{
|
||||
struct perf_evsel *evsel;
|
||||
|
||||
list_for_each_entry(evsel, &evlist->entries, node) {
|
||||
struct hists *hists = &evsel->hists;
|
||||
|
||||
hists__output_resort(hists);
|
||||
|
||||
/*
|
||||
* The hists__name_resort only sets possition
|
||||
* if name is false.
|
||||
*/
|
||||
if (name || ((!name) && show_displacement))
|
||||
hists__name_resort(hists, name);
|
||||
}
|
||||
}
|
||||
|
||||
static void hists__baseline_only(struct hists *hists)
|
||||
{
|
||||
struct rb_node *next = rb_first(&hists->entries);
|
||||
|
||||
while (next != NULL) {
|
||||
struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node);
|
||||
|
||||
next = rb_next(&he->rb_node);
|
||||
if (!he->pair) {
|
||||
rb_erase(&he->rb_node, &hists->entries);
|
||||
hist_entry__free(he);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void hists__precompute(struct hists *hists)
|
||||
{
|
||||
struct rb_node *next = rb_first(&hists->entries);
|
||||
|
||||
while (next != NULL) {
|
||||
struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node);
|
||||
|
||||
next = rb_next(&he->rb_node);
|
||||
|
||||
switch (compute) {
|
||||
case COMPUTE_DELTA:
|
||||
perf_diff__compute_delta(he);
|
||||
break;
|
||||
case COMPUTE_RATIO:
|
||||
perf_diff__compute_ratio(he);
|
||||
break;
|
||||
case COMPUTE_WEIGHTED_DIFF:
|
||||
perf_diff__compute_wdiff(he);
|
||||
break;
|
||||
default:
|
||||
BUG_ON(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int64_t cmp_doubles(double l, double r)
|
||||
{
|
||||
if (l > r)
|
||||
return -1;
|
||||
else if (l < r)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int64_t
|
||||
hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right,
|
||||
int c)
|
||||
{
|
||||
switch (c) {
|
||||
case COMPUTE_DELTA:
|
||||
{
|
||||
double l = left->diff.period_ratio_delta;
|
||||
double r = right->diff.period_ratio_delta;
|
||||
|
||||
return cmp_doubles(l, r);
|
||||
}
|
||||
case COMPUTE_RATIO:
|
||||
{
|
||||
double l = left->diff.period_ratio;
|
||||
double r = right->diff.period_ratio;
|
||||
|
||||
return cmp_doubles(l, r);
|
||||
}
|
||||
case COMPUTE_WEIGHTED_DIFF:
|
||||
{
|
||||
s64 l = left->diff.wdiff;
|
||||
s64 r = right->diff.wdiff;
|
||||
|
||||
return r - l;
|
||||
}
|
||||
default:
|
||||
BUG_ON(1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void insert_hist_entry_by_compute(struct rb_root *root,
|
||||
struct hist_entry *he,
|
||||
int c)
|
||||
{
|
||||
struct rb_node **p = &root->rb_node;
|
||||
struct rb_node *parent = NULL;
|
||||
struct hist_entry *iter;
|
||||
|
||||
while (*p != NULL) {
|
||||
parent = *p;
|
||||
iter = rb_entry(parent, struct hist_entry, rb_node);
|
||||
if (hist_entry__cmp_compute(he, iter, c) < 0)
|
||||
p = &(*p)->rb_left;
|
||||
else
|
||||
p = &(*p)->rb_right;
|
||||
}
|
||||
|
||||
rb_link_node(&he->rb_node, parent, p);
|
||||
rb_insert_color(&he->rb_node, root);
|
||||
}
|
||||
|
||||
static void hists__compute_resort(struct hists *hists)
|
||||
{
|
||||
struct rb_root tmp = RB_ROOT;
|
||||
struct rb_node *next = rb_first(&hists->entries);
|
||||
|
||||
while (next != NULL) {
|
||||
struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node);
|
||||
|
||||
next = rb_next(&he->rb_node);
|
||||
|
||||
rb_erase(&he->rb_node, &hists->entries);
|
||||
insert_hist_entry_by_compute(&tmp, he, compute);
|
||||
}
|
||||
|
||||
hists->entries = tmp;
|
||||
}
|
||||
|
||||
static void hists__process(struct hists *old, struct hists *new)
|
||||
{
|
||||
hists__match(old, new);
|
||||
|
||||
if (show_baseline_only)
|
||||
hists__baseline_only(new);
|
||||
|
||||
if (sort_compute) {
|
||||
hists__precompute(new);
|
||||
hists__compute_resort(new);
|
||||
}
|
||||
|
||||
hists__fprintf(new, true, 0, 0, stdout);
|
||||
}
|
||||
|
||||
static int __cmd_diff(void)
|
||||
{
|
||||
int ret, i;
|
||||
@ -176,15 +556,8 @@ static int __cmd_diff(void)
|
||||
evlist_old = older->evlist;
|
||||
evlist_new = newer->evlist;
|
||||
|
||||
list_for_each_entry(evsel, &evlist_new->entries, node)
|
||||
hists__output_resort(&evsel->hists);
|
||||
|
||||
list_for_each_entry(evsel, &evlist_old->entries, node) {
|
||||
hists__output_resort(&evsel->hists);
|
||||
|
||||
if (show_displacement)
|
||||
hists__resort_entries(&evsel->hists);
|
||||
}
|
||||
perf_evlist__resort_hists(evlist_old, true);
|
||||
perf_evlist__resort_hists(evlist_new, false);
|
||||
|
||||
list_for_each_entry(evsel, &evlist_new->entries, node) {
|
||||
struct perf_evsel *evsel_old;
|
||||
@ -198,9 +571,7 @@ static int __cmd_diff(void)
|
||||
|
||||
first = false;
|
||||
|
||||
hists__match(&evsel_old->hists, &evsel->hists);
|
||||
hists__fprintf(&evsel->hists, &evsel_old->hists,
|
||||
show_displacement, true, 0, 0, stdout);
|
||||
hists__process(&evsel_old->hists, &evsel->hists);
|
||||
}
|
||||
|
||||
out_delete:
|
||||
@ -221,6 +592,16 @@ static const struct option options[] = {
|
||||
"be more verbose (show symbol address, etc)"),
|
||||
OPT_BOOLEAN('M', "displacement", &show_displacement,
|
||||
"Show position displacement relative to baseline"),
|
||||
OPT_BOOLEAN('b', "baseline-only", &show_baseline_only,
|
||||
"Show only items with match in baseline"),
|
||||
OPT_CALLBACK('c', "compute", &compute,
|
||||
"delta,ratio,wdiff:w1,w2 (default delta)",
|
||||
"Entries differential computation selection",
|
||||
setup_compute),
|
||||
OPT_BOOLEAN('p', "period", &show_period,
|
||||
"Show period values."),
|
||||
OPT_BOOLEAN('F', "formula", &show_formula,
|
||||
"Show formula."),
|
||||
OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
|
||||
"dump raw trace in ASCII"),
|
||||
OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
|
||||
@ -242,6 +623,45 @@ static const struct option options[] = {
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
static void ui_init(void)
|
||||
{
|
||||
perf_hpp__init();
|
||||
|
||||
/* No overhead column. */
|
||||
perf_hpp__column_enable(PERF_HPP__OVERHEAD, false);
|
||||
|
||||
/*
|
||||
* Display baseline/delta/ratio/displacement/
|
||||
* formula/periods columns.
|
||||
*/
|
||||
perf_hpp__column_enable(PERF_HPP__BASELINE, true);
|
||||
|
||||
switch (compute) {
|
||||
case COMPUTE_DELTA:
|
||||
perf_hpp__column_enable(PERF_HPP__DELTA, true);
|
||||
break;
|
||||
case COMPUTE_RATIO:
|
||||
perf_hpp__column_enable(PERF_HPP__RATIO, true);
|
||||
break;
|
||||
case COMPUTE_WEIGHTED_DIFF:
|
||||
perf_hpp__column_enable(PERF_HPP__WEIGHTED_DIFF, true);
|
||||
break;
|
||||
default:
|
||||
BUG_ON(1);
|
||||
};
|
||||
|
||||
if (show_displacement)
|
||||
perf_hpp__column_enable(PERF_HPP__DISPL, true);
|
||||
|
||||
if (show_formula)
|
||||
perf_hpp__column_enable(PERF_HPP__FORMULA, true);
|
||||
|
||||
if (show_period) {
|
||||
perf_hpp__column_enable(PERF_HPP__PERIOD, true);
|
||||
perf_hpp__column_enable(PERF_HPP__PERIOD_BASELINE, true);
|
||||
}
|
||||
}
|
||||
|
||||
int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
{
|
||||
sort_order = diff__default_sort_order;
|
||||
@ -264,7 +684,8 @@ int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
if (symbol__init() < 0)
|
||||
return -1;
|
||||
|
||||
perf_hpp__init(true, show_displacement);
|
||||
ui_init();
|
||||
|
||||
setup_sorting(diff_usage, options);
|
||||
setup_pager();
|
||||
|
||||
|
@ -108,23 +108,20 @@ static int __cmd_evlist(const char *input_name, struct perf_attr_details *detail
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char * const evlist_usage[] = {
|
||||
"perf evlist [<options>]",
|
||||
NULL
|
||||
};
|
||||
|
||||
int cmd_evlist(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
{
|
||||
struct perf_attr_details details = { .verbose = false, };
|
||||
const char *input_name = NULL;
|
||||
const struct option options[] = {
|
||||
OPT_STRING('i', "input", &input_name, "file",
|
||||
"Input file name"),
|
||||
OPT_BOOLEAN('F', "freq", &details.freq,
|
||||
"Show the sample frequency"),
|
||||
OPT_BOOLEAN('v', "verbose", &details.verbose,
|
||||
"Show all event attr details"),
|
||||
OPT_END()
|
||||
OPT_STRING('i', "input", &input_name, "file", "Input file name"),
|
||||
OPT_BOOLEAN('F', "freq", &details.freq, "Show the sample frequency"),
|
||||
OPT_BOOLEAN('v', "verbose", &details.verbose,
|
||||
"Show all event attr details"),
|
||||
OPT_END()
|
||||
};
|
||||
const char * const evlist_usage[] = {
|
||||
"perf evlist [<options>]",
|
||||
NULL
|
||||
};
|
||||
|
||||
argc = parse_options(argc, argv, options, evlist_usage, 0);
|
||||
|
@ -30,23 +30,6 @@ enum help_format {
|
||||
HELP_FORMAT_WEB,
|
||||
};
|
||||
|
||||
static bool show_all = false;
|
||||
static enum help_format help_format = HELP_FORMAT_NONE;
|
||||
static struct option builtin_help_options[] = {
|
||||
OPT_BOOLEAN('a', "all", &show_all, "print all available commands"),
|
||||
OPT_SET_UINT('m', "man", &help_format, "show man page", HELP_FORMAT_MAN),
|
||||
OPT_SET_UINT('w', "web", &help_format, "show manual in web browser",
|
||||
HELP_FORMAT_WEB),
|
||||
OPT_SET_UINT('i', "info", &help_format, "show info page",
|
||||
HELP_FORMAT_INFO),
|
||||
OPT_END(),
|
||||
};
|
||||
|
||||
static const char * const builtin_help_usage[] = {
|
||||
"perf help [--all] [--man|--web|--info] [command]",
|
||||
NULL
|
||||
};
|
||||
|
||||
static enum help_format parse_help_format(const char *format)
|
||||
{
|
||||
if (!strcmp(format, "man"))
|
||||
@ -258,11 +241,13 @@ static int add_man_viewer_info(const char *var, const char *value)
|
||||
|
||||
static int perf_help_config(const char *var, const char *value, void *cb)
|
||||
{
|
||||
enum help_format *help_formatp = cb;
|
||||
|
||||
if (!strcmp(var, "help.format")) {
|
||||
if (!value)
|
||||
return config_error_nonbool(var);
|
||||
help_format = parse_help_format(value);
|
||||
if (help_format == HELP_FORMAT_NONE)
|
||||
*help_formatp = parse_help_format(value);
|
||||
if (*help_formatp == HELP_FORMAT_NONE)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
@ -428,12 +413,27 @@ static int show_html_page(const char *perf_cmd)
|
||||
|
||||
int cmd_help(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
{
|
||||
bool show_all = false;
|
||||
enum help_format help_format = HELP_FORMAT_NONE;
|
||||
struct option builtin_help_options[] = {
|
||||
OPT_BOOLEAN('a', "all", &show_all, "print all available commands"),
|
||||
OPT_SET_UINT('m', "man", &help_format, "show man page", HELP_FORMAT_MAN),
|
||||
OPT_SET_UINT('w', "web", &help_format, "show manual in web browser",
|
||||
HELP_FORMAT_WEB),
|
||||
OPT_SET_UINT('i', "info", &help_format, "show info page",
|
||||
HELP_FORMAT_INFO),
|
||||
OPT_END(),
|
||||
};
|
||||
const char * const builtin_help_usage[] = {
|
||||
"perf help [--all] [--man|--web|--info] [command]",
|
||||
NULL
|
||||
};
|
||||
const char *alias;
|
||||
int rc = 0;
|
||||
|
||||
load_command_list("perf-", &main_cmds, &other_cmds);
|
||||
|
||||
perf_config(perf_help_config, NULL);
|
||||
perf_config(perf_help_config, &help_format);
|
||||
|
||||
argc = parse_options(argc, argv, builtin_help_options,
|
||||
builtin_help_usage, 0);
|
||||
|
@ -14,8 +14,10 @@
|
||||
|
||||
#include "util/parse-options.h"
|
||||
|
||||
static char const *input_name = "-";
|
||||
static bool inject_build_ids;
|
||||
struct perf_inject {
|
||||
struct perf_tool tool;
|
||||
bool build_ids;
|
||||
};
|
||||
|
||||
static int perf_event__repipe_synth(struct perf_tool *tool __maybe_unused,
|
||||
union perf_event *event,
|
||||
@ -100,14 +102,14 @@ static int perf_event__repipe_mmap(struct perf_tool *tool,
|
||||
return err;
|
||||
}
|
||||
|
||||
static int perf_event__repipe_task(struct perf_tool *tool,
|
||||
static int perf_event__repipe_fork(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = perf_event__process_task(tool, event, sample, machine);
|
||||
err = perf_event__process_fork(tool, event, sample, machine);
|
||||
perf_event__repipe(tool, event, sample, machine);
|
||||
|
||||
return err;
|
||||
@ -194,7 +196,7 @@ static int perf_event__inject_buildid(struct perf_tool *tool,
|
||||
* account this as unresolved.
|
||||
*/
|
||||
} else {
|
||||
#ifndef NO_LIBELF_SUPPORT
|
||||
#ifdef LIBELF_SUPPORT
|
||||
pr_warning("no symbols found in %s, maybe "
|
||||
"install a debug package?\n",
|
||||
al.map->dso->long_name);
|
||||
@ -208,22 +210,6 @@ repipe:
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct perf_tool perf_inject = {
|
||||
.sample = perf_event__repipe_sample,
|
||||
.mmap = perf_event__repipe,
|
||||
.comm = perf_event__repipe,
|
||||
.fork = perf_event__repipe,
|
||||
.exit = perf_event__repipe,
|
||||
.lost = perf_event__repipe,
|
||||
.read = perf_event__repipe_sample,
|
||||
.throttle = perf_event__repipe,
|
||||
.unthrottle = perf_event__repipe,
|
||||
.attr = perf_event__repipe_attr,
|
||||
.event_type = perf_event__repipe_event_type_synth,
|
||||
.tracing_data = perf_event__repipe_tracing_data_synth,
|
||||
.build_id = perf_event__repipe_op2_synth,
|
||||
};
|
||||
|
||||
extern volatile int session_done;
|
||||
|
||||
static void sig_handler(int sig __maybe_unused)
|
||||
@ -231,56 +217,72 @@ static void sig_handler(int sig __maybe_unused)
|
||||
session_done = 1;
|
||||
}
|
||||
|
||||
static int __cmd_inject(void)
|
||||
static int __cmd_inject(struct perf_inject *inject)
|
||||
{
|
||||
struct perf_session *session;
|
||||
int ret = -EINVAL;
|
||||
|
||||
signal(SIGINT, sig_handler);
|
||||
|
||||
if (inject_build_ids) {
|
||||
perf_inject.sample = perf_event__inject_buildid;
|
||||
perf_inject.mmap = perf_event__repipe_mmap;
|
||||
perf_inject.fork = perf_event__repipe_task;
|
||||
perf_inject.tracing_data = perf_event__repipe_tracing_data;
|
||||
if (inject->build_ids) {
|
||||
inject->tool.sample = perf_event__inject_buildid;
|
||||
inject->tool.mmap = perf_event__repipe_mmap;
|
||||
inject->tool.fork = perf_event__repipe_fork;
|
||||
inject->tool.tracing_data = perf_event__repipe_tracing_data;
|
||||
}
|
||||
|
||||
session = perf_session__new(input_name, O_RDONLY, false, true, &perf_inject);
|
||||
session = perf_session__new("-", O_RDONLY, false, true, &inject->tool);
|
||||
if (session == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = perf_session__process_events(session, &perf_inject);
|
||||
ret = perf_session__process_events(session, &inject->tool);
|
||||
|
||||
perf_session__delete(session);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const char * const report_usage[] = {
|
||||
"perf inject [<options>]",
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
OPT_BOOLEAN('b', "build-ids", &inject_build_ids,
|
||||
"Inject build-ids into the output stream"),
|
||||
OPT_INCR('v', "verbose", &verbose,
|
||||
"be more verbose (show build ids, etc)"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
{
|
||||
argc = parse_options(argc, argv, options, report_usage, 0);
|
||||
struct perf_inject inject = {
|
||||
.tool = {
|
||||
.sample = perf_event__repipe_sample,
|
||||
.mmap = perf_event__repipe,
|
||||
.comm = perf_event__repipe,
|
||||
.fork = perf_event__repipe,
|
||||
.exit = perf_event__repipe,
|
||||
.lost = perf_event__repipe,
|
||||
.read = perf_event__repipe_sample,
|
||||
.throttle = perf_event__repipe,
|
||||
.unthrottle = perf_event__repipe,
|
||||
.attr = perf_event__repipe_attr,
|
||||
.event_type = perf_event__repipe_event_type_synth,
|
||||
.tracing_data = perf_event__repipe_tracing_data_synth,
|
||||
.build_id = perf_event__repipe_op2_synth,
|
||||
},
|
||||
};
|
||||
const struct option options[] = {
|
||||
OPT_BOOLEAN('b', "build-ids", &inject.build_ids,
|
||||
"Inject build-ids into the output stream"),
|
||||
OPT_INCR('v', "verbose", &verbose,
|
||||
"be more verbose (show build ids, etc)"),
|
||||
OPT_END()
|
||||
};
|
||||
const char * const inject_usage[] = {
|
||||
"perf inject [<options>]",
|
||||
NULL
|
||||
};
|
||||
|
||||
argc = parse_options(argc, argv, options, inject_usage, 0);
|
||||
|
||||
/*
|
||||
* Any (unrecognized) arguments left?
|
||||
*/
|
||||
if (argc)
|
||||
usage_with_options(report_usage, options);
|
||||
usage_with_options(inject_usage, options);
|
||||
|
||||
if (symbol__init() < 0)
|
||||
return -1;
|
||||
|
||||
return __cmd_inject();
|
||||
return __cmd_inject(&inject);
|
||||
}
|
||||
|
@ -21,8 +21,6 @@
|
||||
struct alloc_stat;
|
||||
typedef int (*sort_fn_t)(struct alloc_stat *, struct alloc_stat *);
|
||||
|
||||
static const char *input_name;
|
||||
|
||||
static int alloc_flag;
|
||||
static int caller_flag;
|
||||
|
||||
@ -31,8 +29,6 @@ static int caller_lines = -1;
|
||||
|
||||
static bool raw_ip;
|
||||
|
||||
static char default_sort_order[] = "frag,hit,bytes";
|
||||
|
||||
static int *cpunode_map;
|
||||
static int max_cpu_num;
|
||||
|
||||
@ -481,7 +477,7 @@ static void sort_result(void)
|
||||
__sort_result(&root_caller_stat, &root_caller_sorted, &caller_sort);
|
||||
}
|
||||
|
||||
static int __cmd_kmem(void)
|
||||
static int __cmd_kmem(const char *input_name)
|
||||
{
|
||||
int err = -EINVAL;
|
||||
struct perf_session *session;
|
||||
@ -520,11 +516,6 @@ out_delete:
|
||||
return err;
|
||||
}
|
||||
|
||||
static const char * const kmem_usage[] = {
|
||||
"perf kmem [<options>] {record|stat}",
|
||||
NULL
|
||||
};
|
||||
|
||||
static int ptr_cmp(struct alloc_stat *l, struct alloc_stat *r)
|
||||
{
|
||||
if (l->ptr < r->ptr)
|
||||
@ -720,41 +711,17 @@ static int parse_line_opt(const struct option *opt __maybe_unused,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct option kmem_options[] = {
|
||||
OPT_STRING('i', "input", &input_name, "file",
|
||||
"input file name"),
|
||||
OPT_CALLBACK_NOOPT(0, "caller", NULL, NULL,
|
||||
"show per-callsite statistics",
|
||||
parse_caller_opt),
|
||||
OPT_CALLBACK_NOOPT(0, "alloc", NULL, NULL,
|
||||
"show per-allocation statistics",
|
||||
parse_alloc_opt),
|
||||
OPT_CALLBACK('s', "sort", NULL, "key[,key2...]",
|
||||
"sort by keys: ptr, call_site, bytes, hit, pingpong, frag",
|
||||
parse_sort_opt),
|
||||
OPT_CALLBACK('l', "line", NULL, "num",
|
||||
"show n lines",
|
||||
parse_line_opt),
|
||||
OPT_BOOLEAN(0, "raw-ip", &raw_ip, "show raw ip instead of symbol"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
static const char *record_args[] = {
|
||||
"record",
|
||||
"-a",
|
||||
"-R",
|
||||
"-f",
|
||||
"-c", "1",
|
||||
static int __cmd_record(int argc, const char **argv)
|
||||
{
|
||||
const char * const record_args[] = {
|
||||
"record", "-a", "-R", "-f", "-c", "1",
|
||||
"-e", "kmem:kmalloc",
|
||||
"-e", "kmem:kmalloc_node",
|
||||
"-e", "kmem:kfree",
|
||||
"-e", "kmem:kmem_cache_alloc",
|
||||
"-e", "kmem:kmem_cache_alloc_node",
|
||||
"-e", "kmem:kmem_cache_free",
|
||||
};
|
||||
|
||||
static int __cmd_record(int argc, const char **argv)
|
||||
{
|
||||
};
|
||||
unsigned int rec_argc, i, j;
|
||||
const char **rec_argv;
|
||||
|
||||
@ -775,6 +742,25 @@ static int __cmd_record(int argc, const char **argv)
|
||||
|
||||
int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
{
|
||||
const char * const default_sort_order = "frag,hit,bytes";
|
||||
const char *input_name = NULL;
|
||||
const struct option kmem_options[] = {
|
||||
OPT_STRING('i', "input", &input_name, "file", "input file name"),
|
||||
OPT_CALLBACK_NOOPT(0, "caller", NULL, NULL,
|
||||
"show per-callsite statistics", parse_caller_opt),
|
||||
OPT_CALLBACK_NOOPT(0, "alloc", NULL, NULL,
|
||||
"show per-allocation statistics", parse_alloc_opt),
|
||||
OPT_CALLBACK('s', "sort", NULL, "key[,key2...]",
|
||||
"sort by keys: ptr, call_site, bytes, hit, pingpong, frag",
|
||||
parse_sort_opt),
|
||||
OPT_CALLBACK('l', "line", NULL, "num", "show n lines", parse_line_opt),
|
||||
OPT_BOOLEAN(0, "raw-ip", &raw_ip, "show raw ip instead of symbol"),
|
||||
OPT_END()
|
||||
};
|
||||
const char * const kmem_usage[] = {
|
||||
"perf kmem [<options>] {record|stat}",
|
||||
NULL
|
||||
};
|
||||
argc = parse_options(argc, argv, kmem_options, kmem_usage, 0);
|
||||
|
||||
if (!argc)
|
||||
@ -793,7 +779,7 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
if (list_empty(&alloc_sort))
|
||||
setup_sorting(&alloc_sort, default_sort_order);
|
||||
|
||||
return __cmd_kmem();
|
||||
return __cmd_kmem(input_name);
|
||||
} else
|
||||
usage_with_options(kmem_usage, kmem_options);
|
||||
|
||||
|
@ -32,16 +32,76 @@ struct event_key {
|
||||
int info;
|
||||
};
|
||||
|
||||
struct kvm_event_stats {
|
||||
u64 time;
|
||||
struct stats stats;
|
||||
};
|
||||
|
||||
struct kvm_event {
|
||||
struct list_head hash_entry;
|
||||
struct rb_node rb;
|
||||
|
||||
struct event_key key;
|
||||
|
||||
struct kvm_event_stats total;
|
||||
|
||||
#define DEFAULT_VCPU_NUM 8
|
||||
int max_vcpu;
|
||||
struct kvm_event_stats *vcpu;
|
||||
};
|
||||
|
||||
typedef int (*key_cmp_fun)(struct kvm_event*, struct kvm_event*, int);
|
||||
|
||||
struct kvm_event_key {
|
||||
const char *name;
|
||||
key_cmp_fun key;
|
||||
};
|
||||
|
||||
|
||||
struct perf_kvm;
|
||||
|
||||
struct kvm_events_ops {
|
||||
bool (*is_begin_event)(struct perf_evsel *evsel,
|
||||
struct perf_sample *sample,
|
||||
struct event_key *key);
|
||||
bool (*is_end_event)(struct perf_evsel *evsel,
|
||||
struct perf_sample *sample, struct event_key *key);
|
||||
void (*decode_key)(struct event_key *key, char decode[20]);
|
||||
void (*decode_key)(struct perf_kvm *kvm, struct event_key *key,
|
||||
char decode[20]);
|
||||
const char *name;
|
||||
};
|
||||
|
||||
struct exit_reasons_table {
|
||||
unsigned long exit_code;
|
||||
const char *reason;
|
||||
};
|
||||
|
||||
#define EVENTS_BITS 12
|
||||
#define EVENTS_CACHE_SIZE (1UL << EVENTS_BITS)
|
||||
|
||||
struct perf_kvm {
|
||||
struct perf_tool tool;
|
||||
struct perf_session *session;
|
||||
|
||||
const char *file_name;
|
||||
const char *report_event;
|
||||
const char *sort_key;
|
||||
int trace_vcpu;
|
||||
|
||||
struct exit_reasons_table *exit_reasons;
|
||||
int exit_reasons_size;
|
||||
const char *exit_reasons_isa;
|
||||
|
||||
struct kvm_events_ops *events_ops;
|
||||
key_cmp_fun compare;
|
||||
struct list_head kvm_events_cache[EVENTS_CACHE_SIZE];
|
||||
u64 total_time;
|
||||
u64 total_count;
|
||||
|
||||
struct rb_root result;
|
||||
};
|
||||
|
||||
|
||||
static void exit_event_get_key(struct perf_evsel *evsel,
|
||||
struct perf_sample *sample,
|
||||
struct event_key *key)
|
||||
@ -78,45 +138,35 @@ static bool exit_event_end(struct perf_evsel *evsel,
|
||||
return kvm_entry_event(evsel);
|
||||
}
|
||||
|
||||
struct exit_reasons_table {
|
||||
unsigned long exit_code;
|
||||
const char *reason;
|
||||
};
|
||||
|
||||
struct exit_reasons_table vmx_exit_reasons[] = {
|
||||
static struct exit_reasons_table vmx_exit_reasons[] = {
|
||||
VMX_EXIT_REASONS
|
||||
};
|
||||
|
||||
struct exit_reasons_table svm_exit_reasons[] = {
|
||||
static struct exit_reasons_table svm_exit_reasons[] = {
|
||||
SVM_EXIT_REASONS
|
||||
};
|
||||
|
||||
static int cpu_isa;
|
||||
|
||||
static const char *get_exit_reason(u64 exit_code)
|
||||
static const char *get_exit_reason(struct perf_kvm *kvm, u64 exit_code)
|
||||
{
|
||||
int table_size = ARRAY_SIZE(svm_exit_reasons);
|
||||
struct exit_reasons_table *table = svm_exit_reasons;
|
||||
int i = kvm->exit_reasons_size;
|
||||
struct exit_reasons_table *tbl = kvm->exit_reasons;
|
||||
|
||||
if (cpu_isa == 1) {
|
||||
table = vmx_exit_reasons;
|
||||
table_size = ARRAY_SIZE(vmx_exit_reasons);
|
||||
}
|
||||
|
||||
while (table_size--) {
|
||||
if (table->exit_code == exit_code)
|
||||
return table->reason;
|
||||
table++;
|
||||
while (i--) {
|
||||
if (tbl->exit_code == exit_code)
|
||||
return tbl->reason;
|
||||
tbl++;
|
||||
}
|
||||
|
||||
pr_err("unknown kvm exit code:%lld on %s\n",
|
||||
(unsigned long long)exit_code, cpu_isa ? "VMX" : "SVM");
|
||||
(unsigned long long)exit_code, kvm->exit_reasons_isa);
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
static void exit_event_decode_key(struct event_key *key, char decode[20])
|
||||
static void exit_event_decode_key(struct perf_kvm *kvm,
|
||||
struct event_key *key,
|
||||
char decode[20])
|
||||
{
|
||||
const char *exit_reason = get_exit_reason(key->key);
|
||||
const char *exit_reason = get_exit_reason(kvm, key->key);
|
||||
|
||||
scnprintf(decode, 20, "%s", exit_reason);
|
||||
}
|
||||
@ -128,11 +178,11 @@ static struct kvm_events_ops exit_events = {
|
||||
.name = "VM-EXIT"
|
||||
};
|
||||
|
||||
/*
|
||||
* For the mmio events, we treat:
|
||||
* the time of MMIO write: kvm_mmio(KVM_TRACE_MMIO_WRITE...) -> kvm_entry
|
||||
* the time of MMIO read: kvm_exit -> kvm_mmio(KVM_TRACE_MMIO_READ...).
|
||||
*/
|
||||
/*
|
||||
* For the mmio events, we treat:
|
||||
* the time of MMIO write: kvm_mmio(KVM_TRACE_MMIO_WRITE...) -> kvm_entry
|
||||
* the time of MMIO read: kvm_exit -> kvm_mmio(KVM_TRACE_MMIO_READ...).
|
||||
*/
|
||||
static void mmio_event_get_key(struct perf_evsel *evsel, struct perf_sample *sample,
|
||||
struct event_key *key)
|
||||
{
|
||||
@ -178,7 +228,9 @@ static bool mmio_event_end(struct perf_evsel *evsel, struct perf_sample *sample,
|
||||
return false;
|
||||
}
|
||||
|
||||
static void mmio_event_decode_key(struct event_key *key, char decode[20])
|
||||
static void mmio_event_decode_key(struct perf_kvm *kvm __maybe_unused,
|
||||
struct event_key *key,
|
||||
char decode[20])
|
||||
{
|
||||
scnprintf(decode, 20, "%#lx:%s", (unsigned long)key->key,
|
||||
key->info == KVM_TRACE_MMIO_WRITE ? "W" : "R");
|
||||
@ -219,7 +271,9 @@ static bool ioport_event_end(struct perf_evsel *evsel,
|
||||
return kvm_entry_event(evsel);
|
||||
}
|
||||
|
||||
static void ioport_event_decode_key(struct event_key *key, char decode[20])
|
||||
static void ioport_event_decode_key(struct perf_kvm *kvm __maybe_unused,
|
||||
struct event_key *key,
|
||||
char decode[20])
|
||||
{
|
||||
scnprintf(decode, 20, "%#llx:%s", (unsigned long long)key->key,
|
||||
key->info ? "POUT" : "PIN");
|
||||
@ -232,64 +286,37 @@ static struct kvm_events_ops ioport_events = {
|
||||
.name = "IO Port Access"
|
||||
};
|
||||
|
||||
static const char *report_event = "vmexit";
|
||||
struct kvm_events_ops *events_ops;
|
||||
|
||||
static bool register_kvm_events_ops(void)
|
||||
static bool register_kvm_events_ops(struct perf_kvm *kvm)
|
||||
{
|
||||
bool ret = true;
|
||||
|
||||
if (!strcmp(report_event, "vmexit"))
|
||||
events_ops = &exit_events;
|
||||
else if (!strcmp(report_event, "mmio"))
|
||||
events_ops = &mmio_events;
|
||||
else if (!strcmp(report_event, "ioport"))
|
||||
events_ops = &ioport_events;
|
||||
if (!strcmp(kvm->report_event, "vmexit"))
|
||||
kvm->events_ops = &exit_events;
|
||||
else if (!strcmp(kvm->report_event, "mmio"))
|
||||
kvm->events_ops = &mmio_events;
|
||||
else if (!strcmp(kvm->report_event, "ioport"))
|
||||
kvm->events_ops = &ioport_events;
|
||||
else {
|
||||
pr_err("Unknown report event:%s\n", report_event);
|
||||
pr_err("Unknown report event:%s\n", kvm->report_event);
|
||||
ret = false;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct kvm_event_stats {
|
||||
u64 time;
|
||||
struct stats stats;
|
||||
};
|
||||
|
||||
struct kvm_event {
|
||||
struct list_head hash_entry;
|
||||
struct rb_node rb;
|
||||
|
||||
struct event_key key;
|
||||
|
||||
struct kvm_event_stats total;
|
||||
|
||||
#define DEFAULT_VCPU_NUM 8
|
||||
int max_vcpu;
|
||||
struct kvm_event_stats *vcpu;
|
||||
};
|
||||
|
||||
struct vcpu_event_record {
|
||||
int vcpu_id;
|
||||
u64 start_time;
|
||||
struct kvm_event *last_event;
|
||||
};
|
||||
|
||||
#define EVENTS_BITS 12
|
||||
#define EVENTS_CACHE_SIZE (1UL << EVENTS_BITS)
|
||||
|
||||
static u64 total_time;
|
||||
static u64 total_count;
|
||||
static struct list_head kvm_events_cache[EVENTS_CACHE_SIZE];
|
||||
|
||||
static void init_kvm_event_record(void)
|
||||
static void init_kvm_event_record(struct perf_kvm *kvm)
|
||||
{
|
||||
int i;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < (int)EVENTS_CACHE_SIZE; i++)
|
||||
INIT_LIST_HEAD(&kvm_events_cache[i]);
|
||||
for (i = 0; i < EVENTS_CACHE_SIZE; i++)
|
||||
INIT_LIST_HEAD(&kvm->kvm_events_cache[i]);
|
||||
}
|
||||
|
||||
static int kvm_events_hash_fn(u64 key)
|
||||
@ -333,17 +360,19 @@ static struct kvm_event *kvm_alloc_init_event(struct event_key *key)
|
||||
return event;
|
||||
}
|
||||
|
||||
static struct kvm_event *find_create_kvm_event(struct event_key *key)
|
||||
static struct kvm_event *find_create_kvm_event(struct perf_kvm *kvm,
|
||||
struct event_key *key)
|
||||
{
|
||||
struct kvm_event *event;
|
||||
struct list_head *head;
|
||||
|
||||
BUG_ON(key->key == INVALID_KEY);
|
||||
|
||||
head = &kvm_events_cache[kvm_events_hash_fn(key->key)];
|
||||
list_for_each_entry(event, head, hash_entry)
|
||||
head = &kvm->kvm_events_cache[kvm_events_hash_fn(key->key)];
|
||||
list_for_each_entry(event, head, hash_entry) {
|
||||
if (event->key.key == key->key && event->key.info == key->info)
|
||||
return event;
|
||||
}
|
||||
|
||||
event = kvm_alloc_init_event(key);
|
||||
if (!event)
|
||||
@ -353,13 +382,14 @@ static struct kvm_event *find_create_kvm_event(struct event_key *key)
|
||||
return event;
|
||||
}
|
||||
|
||||
static bool handle_begin_event(struct vcpu_event_record *vcpu_record,
|
||||
static bool handle_begin_event(struct perf_kvm *kvm,
|
||||
struct vcpu_event_record *vcpu_record,
|
||||
struct event_key *key, u64 timestamp)
|
||||
{
|
||||
struct kvm_event *event = NULL;
|
||||
|
||||
if (key->key != INVALID_KEY)
|
||||
event = find_create_kvm_event(key);
|
||||
event = find_create_kvm_event(kvm, key);
|
||||
|
||||
vcpu_record->last_event = event;
|
||||
vcpu_record->start_time = timestamp;
|
||||
@ -387,7 +417,10 @@ static double kvm_event_rel_stddev(int vcpu_id, struct kvm_event *event)
|
||||
static bool update_kvm_event(struct kvm_event *event, int vcpu_id,
|
||||
u64 time_diff)
|
||||
{
|
||||
kvm_update_event_stats(&event->total, time_diff);
|
||||
if (vcpu_id == -1) {
|
||||
kvm_update_event_stats(&event->total, time_diff);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!kvm_event_expand(event, vcpu_id))
|
||||
return false;
|
||||
@ -396,11 +429,19 @@ static bool update_kvm_event(struct kvm_event *event, int vcpu_id,
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool handle_end_event(struct vcpu_event_record *vcpu_record,
|
||||
struct event_key *key, u64 timestamp)
|
||||
static bool handle_end_event(struct perf_kvm *kvm,
|
||||
struct vcpu_event_record *vcpu_record,
|
||||
struct event_key *key,
|
||||
u64 timestamp)
|
||||
{
|
||||
struct kvm_event *event;
|
||||
u64 time_begin, time_diff;
|
||||
int vcpu;
|
||||
|
||||
if (kvm->trace_vcpu == -1)
|
||||
vcpu = -1;
|
||||
else
|
||||
vcpu = vcpu_record->vcpu_id;
|
||||
|
||||
event = vcpu_record->last_event;
|
||||
time_begin = vcpu_record->start_time;
|
||||
@ -419,7 +460,7 @@ static bool handle_end_event(struct vcpu_event_record *vcpu_record,
|
||||
return true;
|
||||
|
||||
if (!event)
|
||||
event = find_create_kvm_event(key);
|
||||
event = find_create_kvm_event(kvm, key);
|
||||
|
||||
if (!event)
|
||||
return false;
|
||||
@ -430,7 +471,7 @@ static bool handle_end_event(struct vcpu_event_record *vcpu_record,
|
||||
BUG_ON(timestamp < time_begin);
|
||||
|
||||
time_diff = timestamp - time_begin;
|
||||
return update_kvm_event(event, vcpu_record->vcpu_id, time_diff);
|
||||
return update_kvm_event(event, vcpu, time_diff);
|
||||
}
|
||||
|
||||
static
|
||||
@ -455,7 +496,9 @@ struct vcpu_event_record *per_vcpu_record(struct thread *thread,
|
||||
return thread->priv;
|
||||
}
|
||||
|
||||
static bool handle_kvm_event(struct thread *thread, struct perf_evsel *evsel,
|
||||
static bool handle_kvm_event(struct perf_kvm *kvm,
|
||||
struct thread *thread,
|
||||
struct perf_evsel *evsel,
|
||||
struct perf_sample *sample)
|
||||
{
|
||||
struct vcpu_event_record *vcpu_record;
|
||||
@ -465,22 +508,20 @@ static bool handle_kvm_event(struct thread *thread, struct perf_evsel *evsel,
|
||||
if (!vcpu_record)
|
||||
return true;
|
||||
|
||||
if (events_ops->is_begin_event(evsel, sample, &key))
|
||||
return handle_begin_event(vcpu_record, &key, sample->time);
|
||||
/* only process events for vcpus user cares about */
|
||||
if ((kvm->trace_vcpu != -1) &&
|
||||
(kvm->trace_vcpu != vcpu_record->vcpu_id))
|
||||
return true;
|
||||
|
||||
if (events_ops->is_end_event(evsel, sample, &key))
|
||||
return handle_end_event(vcpu_record, &key, sample->time);
|
||||
if (kvm->events_ops->is_begin_event(evsel, sample, &key))
|
||||
return handle_begin_event(kvm, vcpu_record, &key, sample->time);
|
||||
|
||||
if (kvm->events_ops->is_end_event(evsel, sample, &key))
|
||||
return handle_end_event(kvm, vcpu_record, &key, sample->time);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef int (*key_cmp_fun)(struct kvm_event*, struct kvm_event*, int);
|
||||
struct kvm_event_key {
|
||||
const char *name;
|
||||
key_cmp_fun key;
|
||||
};
|
||||
|
||||
static int trace_vcpu = -1;
|
||||
#define GET_EVENT_KEY(func, field) \
|
||||
static u64 get_event_ ##func(struct kvm_event *event, int vcpu) \
|
||||
{ \
|
||||
@ -515,29 +556,25 @@ static struct kvm_event_key keys[] = {
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static const char *sort_key = "sample";
|
||||
static key_cmp_fun compare;
|
||||
|
||||
static bool select_key(void)
|
||||
static bool select_key(struct perf_kvm *kvm)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; keys[i].name; i++) {
|
||||
if (!strcmp(keys[i].name, sort_key)) {
|
||||
compare = keys[i].key;
|
||||
if (!strcmp(keys[i].name, kvm->sort_key)) {
|
||||
kvm->compare = keys[i].key;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
pr_err("Unknown compare key:%s\n", sort_key);
|
||||
pr_err("Unknown compare key:%s\n", kvm->sort_key);
|
||||
return false;
|
||||
}
|
||||
|
||||
static struct rb_root result;
|
||||
static void insert_to_result(struct kvm_event *event, key_cmp_fun bigger,
|
||||
int vcpu)
|
||||
static void insert_to_result(struct rb_root *result, struct kvm_event *event,
|
||||
key_cmp_fun bigger, int vcpu)
|
||||
{
|
||||
struct rb_node **rb = &result.rb_node;
|
||||
struct rb_node **rb = &result->rb_node;
|
||||
struct rb_node *parent = NULL;
|
||||
struct kvm_event *p;
|
||||
|
||||
@ -552,13 +589,15 @@ static void insert_to_result(struct kvm_event *event, key_cmp_fun bigger,
|
||||
}
|
||||
|
||||
rb_link_node(&event->rb, parent, rb);
|
||||
rb_insert_color(&event->rb, &result);
|
||||
rb_insert_color(&event->rb, result);
|
||||
}
|
||||
|
||||
static void update_total_count(struct kvm_event *event, int vcpu)
|
||||
static void update_total_count(struct perf_kvm *kvm, struct kvm_event *event)
|
||||
{
|
||||
total_count += get_event_count(event, vcpu);
|
||||
total_time += get_event_time(event, vcpu);
|
||||
int vcpu = kvm->trace_vcpu;
|
||||
|
||||
kvm->total_count += get_event_count(event, vcpu);
|
||||
kvm->total_time += get_event_time(event, vcpu);
|
||||
}
|
||||
|
||||
static bool event_is_valid(struct kvm_event *event, int vcpu)
|
||||
@ -566,28 +605,32 @@ static bool event_is_valid(struct kvm_event *event, int vcpu)
|
||||
return !!get_event_count(event, vcpu);
|
||||
}
|
||||
|
||||
static void sort_result(int vcpu)
|
||||
static void sort_result(struct perf_kvm *kvm)
|
||||
{
|
||||
unsigned int i;
|
||||
int vcpu = kvm->trace_vcpu;
|
||||
struct kvm_event *event;
|
||||
|
||||
for (i = 0; i < EVENTS_CACHE_SIZE; i++)
|
||||
list_for_each_entry(event, &kvm_events_cache[i], hash_entry)
|
||||
for (i = 0; i < EVENTS_CACHE_SIZE; i++) {
|
||||
list_for_each_entry(event, &kvm->kvm_events_cache[i], hash_entry) {
|
||||
if (event_is_valid(event, vcpu)) {
|
||||
update_total_count(event, vcpu);
|
||||
insert_to_result(event, compare, vcpu);
|
||||
update_total_count(kvm, event);
|
||||
insert_to_result(&kvm->result, event,
|
||||
kvm->compare, vcpu);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* returns left most element of result, and erase it */
|
||||
static struct kvm_event *pop_from_result(void)
|
||||
static struct kvm_event *pop_from_result(struct rb_root *result)
|
||||
{
|
||||
struct rb_node *node = rb_first(&result);
|
||||
struct rb_node *node = rb_first(result);
|
||||
|
||||
if (!node)
|
||||
return NULL;
|
||||
|
||||
rb_erase(node, &result);
|
||||
rb_erase(node, result);
|
||||
return container_of(node, struct kvm_event, rb);
|
||||
}
|
||||
|
||||
@ -601,14 +644,15 @@ static void print_vcpu_info(int vcpu)
|
||||
pr_info("VCPU %d:\n\n", vcpu);
|
||||
}
|
||||
|
||||
static void print_result(int vcpu)
|
||||
static void print_result(struct perf_kvm *kvm)
|
||||
{
|
||||
char decode[20];
|
||||
struct kvm_event *event;
|
||||
int vcpu = kvm->trace_vcpu;
|
||||
|
||||
pr_info("\n\n");
|
||||
print_vcpu_info(vcpu);
|
||||
pr_info("%20s ", events_ops->name);
|
||||
pr_info("%20s ", kvm->events_ops->name);
|
||||
pr_info("%10s ", "Samples");
|
||||
pr_info("%9s ", "Samples%");
|
||||
|
||||
@ -616,33 +660,34 @@ static void print_result(int vcpu)
|
||||
pr_info("%16s ", "Avg time");
|
||||
pr_info("\n\n");
|
||||
|
||||
while ((event = pop_from_result())) {
|
||||
while ((event = pop_from_result(&kvm->result))) {
|
||||
u64 ecount, etime;
|
||||
|
||||
ecount = get_event_count(event, vcpu);
|
||||
etime = get_event_time(event, vcpu);
|
||||
|
||||
events_ops->decode_key(&event->key, decode);
|
||||
kvm->events_ops->decode_key(kvm, &event->key, decode);
|
||||
pr_info("%20s ", decode);
|
||||
pr_info("%10llu ", (unsigned long long)ecount);
|
||||
pr_info("%8.2f%% ", (double)ecount / total_count * 100);
|
||||
pr_info("%8.2f%% ", (double)etime / total_time * 100);
|
||||
pr_info("%8.2f%% ", (double)ecount / kvm->total_count * 100);
|
||||
pr_info("%8.2f%% ", (double)etime / kvm->total_time * 100);
|
||||
pr_info("%9.2fus ( +-%7.2f%% )", (double)etime / ecount/1e3,
|
||||
kvm_event_rel_stddev(vcpu, event));
|
||||
pr_info("\n");
|
||||
}
|
||||
|
||||
pr_info("\nTotal Samples:%lld, Total events handled time:%.2fus.\n\n",
|
||||
(unsigned long long)total_count, total_time / 1e3);
|
||||
pr_info("\nTotal Samples:%" PRIu64 ", Total events handled time:%.2fus.\n\n",
|
||||
kvm->total_count, kvm->total_time / 1e3);
|
||||
}
|
||||
|
||||
static int process_sample_event(struct perf_tool *tool __maybe_unused,
|
||||
static int process_sample_event(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct perf_evsel *evsel,
|
||||
struct machine *machine)
|
||||
{
|
||||
struct thread *thread = machine__findnew_thread(machine, sample->tid);
|
||||
struct perf_kvm *kvm = container_of(tool, struct perf_kvm, tool);
|
||||
|
||||
if (thread == NULL) {
|
||||
pr_debug("problem processing %d event, skipping it.\n",
|
||||
@ -650,18 +695,12 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused,
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!handle_kvm_event(thread, evsel, sample))
|
||||
if (!handle_kvm_event(kvm, thread, evsel, sample))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct perf_tool eops = {
|
||||
.sample = process_sample_event,
|
||||
.comm = perf_event__process_comm,
|
||||
.ordered_samples = true,
|
||||
};
|
||||
|
||||
static int get_cpu_isa(struct perf_session *session)
|
||||
{
|
||||
char *cpuid = session->header.env.cpuid;
|
||||
@ -679,34 +718,43 @@ static int get_cpu_isa(struct perf_session *session)
|
||||
return isa;
|
||||
}
|
||||
|
||||
static const char *file_name;
|
||||
|
||||
static int read_events(void)
|
||||
static int read_events(struct perf_kvm *kvm)
|
||||
{
|
||||
struct perf_session *kvm_session;
|
||||
int ret;
|
||||
|
||||
kvm_session = perf_session__new(file_name, O_RDONLY, 0, false, &eops);
|
||||
if (!kvm_session) {
|
||||
struct perf_tool eops = {
|
||||
.sample = process_sample_event,
|
||||
.comm = perf_event__process_comm,
|
||||
.ordered_samples = true,
|
||||
};
|
||||
|
||||
kvm->tool = eops;
|
||||
kvm->session = perf_session__new(kvm->file_name, O_RDONLY, 0, false,
|
||||
&kvm->tool);
|
||||
if (!kvm->session) {
|
||||
pr_err("Initializing perf session failed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!perf_session__has_traces(kvm_session, "kvm record"))
|
||||
if (!perf_session__has_traces(kvm->session, "kvm record"))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Do not use 'isa' recorded in kvm_exit tracepoint since it is not
|
||||
* traced in the old kernel.
|
||||
*/
|
||||
ret = get_cpu_isa(kvm_session);
|
||||
ret = get_cpu_isa(kvm->session);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
cpu_isa = ret;
|
||||
if (ret == 1) {
|
||||
kvm->exit_reasons = vmx_exit_reasons;
|
||||
kvm->exit_reasons_size = ARRAY_SIZE(vmx_exit_reasons);
|
||||
kvm->exit_reasons_isa = "VMX";
|
||||
}
|
||||
|
||||
return perf_session__process_events(kvm_session, &eops);
|
||||
return perf_session__process_events(kvm->session, &kvm->tool);
|
||||
}
|
||||
|
||||
static bool verify_vcpu(int vcpu)
|
||||
@ -719,28 +767,30 @@ static bool verify_vcpu(int vcpu)
|
||||
return true;
|
||||
}
|
||||
|
||||
static int kvm_events_report_vcpu(int vcpu)
|
||||
static int kvm_events_report_vcpu(struct perf_kvm *kvm)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
int vcpu = kvm->trace_vcpu;
|
||||
|
||||
if (!verify_vcpu(vcpu))
|
||||
goto exit;
|
||||
|
||||
if (!select_key())
|
||||
if (!select_key(kvm))
|
||||
goto exit;
|
||||
|
||||
if (!register_kvm_events_ops())
|
||||
if (!register_kvm_events_ops(kvm))
|
||||
goto exit;
|
||||
|
||||
init_kvm_event_record();
|
||||
init_kvm_event_record(kvm);
|
||||
setup_pager();
|
||||
|
||||
ret = read_events();
|
||||
ret = read_events(kvm);
|
||||
if (ret)
|
||||
goto exit;
|
||||
|
||||
sort_result(vcpu);
|
||||
print_result(vcpu);
|
||||
sort_result(kvm);
|
||||
print_result(kvm);
|
||||
|
||||
exit:
|
||||
return ret;
|
||||
}
|
||||
@ -765,7 +815,7 @@ static const char * const record_args[] = {
|
||||
_p; \
|
||||
})
|
||||
|
||||
static int kvm_events_record(int argc, const char **argv)
|
||||
static int kvm_events_record(struct perf_kvm *kvm, int argc, const char **argv)
|
||||
{
|
||||
unsigned int rec_argc, i, j;
|
||||
const char **rec_argv;
|
||||
@ -780,7 +830,7 @@ static int kvm_events_record(int argc, const char **argv)
|
||||
rec_argv[i] = STRDUP_FAIL_EXIT(record_args[i]);
|
||||
|
||||
rec_argv[i++] = STRDUP_FAIL_EXIT("-o");
|
||||
rec_argv[i++] = STRDUP_FAIL_EXIT(file_name);
|
||||
rec_argv[i++] = STRDUP_FAIL_EXIT(kvm->file_name);
|
||||
|
||||
for (j = 1; j < (unsigned int)argc; j++, i++)
|
||||
rec_argv[i] = argv[j];
|
||||
@ -788,24 +838,24 @@ static int kvm_events_record(int argc, const char **argv)
|
||||
return cmd_record(i, rec_argv, NULL);
|
||||
}
|
||||
|
||||
static const char * const kvm_events_report_usage[] = {
|
||||
"perf kvm stat report [<options>]",
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct option kvm_events_report_options[] = {
|
||||
OPT_STRING(0, "event", &report_event, "report event",
|
||||
"event for reporting: vmexit, mmio, ioport"),
|
||||
OPT_INTEGER(0, "vcpu", &trace_vcpu,
|
||||
"vcpu id to report"),
|
||||
OPT_STRING('k', "key", &sort_key, "sort-key",
|
||||
"key for sorting: sample(sort by samples number)"
|
||||
" time (sort by avg time)"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
static int kvm_events_report(int argc, const char **argv)
|
||||
static int kvm_events_report(struct perf_kvm *kvm, int argc, const char **argv)
|
||||
{
|
||||
const struct option kvm_events_report_options[] = {
|
||||
OPT_STRING(0, "event", &kvm->report_event, "report event",
|
||||
"event for reporting: vmexit, mmio, ioport"),
|
||||
OPT_INTEGER(0, "vcpu", &kvm->trace_vcpu,
|
||||
"vcpu id to report"),
|
||||
OPT_STRING('k', "key", &kvm->sort_key, "sort-key",
|
||||
"key for sorting: sample(sort by samples number)"
|
||||
" time (sort by avg time)"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
const char * const kvm_events_report_usage[] = {
|
||||
"perf kvm stat report [<options>]",
|
||||
NULL
|
||||
};
|
||||
|
||||
symbol__init();
|
||||
|
||||
if (argc) {
|
||||
@ -817,7 +867,7 @@ static int kvm_events_report(int argc, const char **argv)
|
||||
kvm_events_report_options);
|
||||
}
|
||||
|
||||
return kvm_events_report_vcpu(trace_vcpu);
|
||||
return kvm_events_report_vcpu(kvm);
|
||||
}
|
||||
|
||||
static void print_kvm_stat_usage(void)
|
||||
@ -831,7 +881,7 @@ static void print_kvm_stat_usage(void)
|
||||
printf("\nOtherwise, it is the alias of 'perf stat':\n");
|
||||
}
|
||||
|
||||
static int kvm_cmd_stat(int argc, const char **argv)
|
||||
static int kvm_cmd_stat(struct perf_kvm *kvm, int argc, const char **argv)
|
||||
{
|
||||
if (argc == 1) {
|
||||
print_kvm_stat_usage();
|
||||
@ -839,44 +889,16 @@ static int kvm_cmd_stat(int argc, const char **argv)
|
||||
}
|
||||
|
||||
if (!strncmp(argv[1], "rec", 3))
|
||||
return kvm_events_record(argc - 1, argv + 1);
|
||||
return kvm_events_record(kvm, argc - 1, argv + 1);
|
||||
|
||||
if (!strncmp(argv[1], "rep", 3))
|
||||
return kvm_events_report(argc - 1 , argv + 1);
|
||||
return kvm_events_report(kvm, argc - 1 , argv + 1);
|
||||
|
||||
perf_stat:
|
||||
return cmd_stat(argc, argv, NULL);
|
||||
}
|
||||
|
||||
static char name_buffer[256];
|
||||
|
||||
static const char * const kvm_usage[] = {
|
||||
"perf kvm [<options>] {top|record|report|diff|buildid-list|stat}",
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct option kvm_options[] = {
|
||||
OPT_STRING('i', "input", &file_name, "file",
|
||||
"Input file name"),
|
||||
OPT_STRING('o', "output", &file_name, "file",
|
||||
"Output file name"),
|
||||
OPT_BOOLEAN(0, "guest", &perf_guest,
|
||||
"Collect guest os data"),
|
||||
OPT_BOOLEAN(0, "host", &perf_host,
|
||||
"Collect host os data"),
|
||||
OPT_STRING(0, "guestmount", &symbol_conf.guestmount, "directory",
|
||||
"guest mount directory under which every guest os"
|
||||
" instance has a subdir"),
|
||||
OPT_STRING(0, "guestvmlinux", &symbol_conf.default_guest_vmlinux_name,
|
||||
"file", "file saving guest os vmlinux"),
|
||||
OPT_STRING(0, "guestkallsyms", &symbol_conf.default_guest_kallsyms,
|
||||
"file", "file saving guest os /proc/kallsyms"),
|
||||
OPT_STRING(0, "guestmodules", &symbol_conf.default_guest_modules,
|
||||
"file", "file saving guest os /proc/modules"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
static int __cmd_record(int argc, const char **argv)
|
||||
static int __cmd_record(struct perf_kvm *kvm, int argc, const char **argv)
|
||||
{
|
||||
int rec_argc, i = 0, j;
|
||||
const char **rec_argv;
|
||||
@ -885,7 +907,7 @@ static int __cmd_record(int argc, const char **argv)
|
||||
rec_argv = calloc(rec_argc + 1, sizeof(char *));
|
||||
rec_argv[i++] = strdup("record");
|
||||
rec_argv[i++] = strdup("-o");
|
||||
rec_argv[i++] = strdup(file_name);
|
||||
rec_argv[i++] = strdup(kvm->file_name);
|
||||
for (j = 1; j < argc; j++, i++)
|
||||
rec_argv[i] = argv[j];
|
||||
|
||||
@ -894,7 +916,7 @@ static int __cmd_record(int argc, const char **argv)
|
||||
return cmd_record(i, rec_argv, NULL);
|
||||
}
|
||||
|
||||
static int __cmd_report(int argc, const char **argv)
|
||||
static int __cmd_report(struct perf_kvm *kvm, int argc, const char **argv)
|
||||
{
|
||||
int rec_argc, i = 0, j;
|
||||
const char **rec_argv;
|
||||
@ -903,7 +925,7 @@ static int __cmd_report(int argc, const char **argv)
|
||||
rec_argv = calloc(rec_argc + 1, sizeof(char *));
|
||||
rec_argv[i++] = strdup("report");
|
||||
rec_argv[i++] = strdup("-i");
|
||||
rec_argv[i++] = strdup(file_name);
|
||||
rec_argv[i++] = strdup(kvm->file_name);
|
||||
for (j = 1; j < argc; j++, i++)
|
||||
rec_argv[i] = argv[j];
|
||||
|
||||
@ -912,7 +934,7 @@ static int __cmd_report(int argc, const char **argv)
|
||||
return cmd_report(i, rec_argv, NULL);
|
||||
}
|
||||
|
||||
static int __cmd_buildid_list(int argc, const char **argv)
|
||||
static int __cmd_buildid_list(struct perf_kvm *kvm, int argc, const char **argv)
|
||||
{
|
||||
int rec_argc, i = 0, j;
|
||||
const char **rec_argv;
|
||||
@ -921,7 +943,7 @@ static int __cmd_buildid_list(int argc, const char **argv)
|
||||
rec_argv = calloc(rec_argc + 1, sizeof(char *));
|
||||
rec_argv[i++] = strdup("buildid-list");
|
||||
rec_argv[i++] = strdup("-i");
|
||||
rec_argv[i++] = strdup(file_name);
|
||||
rec_argv[i++] = strdup(kvm->file_name);
|
||||
for (j = 1; j < argc; j++, i++)
|
||||
rec_argv[i] = argv[j];
|
||||
|
||||
@ -932,6 +954,43 @@ static int __cmd_buildid_list(int argc, const char **argv)
|
||||
|
||||
int cmd_kvm(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
{
|
||||
struct perf_kvm kvm = {
|
||||
.trace_vcpu = -1,
|
||||
.report_event = "vmexit",
|
||||
.sort_key = "sample",
|
||||
|
||||
.exit_reasons = svm_exit_reasons,
|
||||
.exit_reasons_size = ARRAY_SIZE(svm_exit_reasons),
|
||||
.exit_reasons_isa = "SVM",
|
||||
};
|
||||
|
||||
const struct option kvm_options[] = {
|
||||
OPT_STRING('i', "input", &kvm.file_name, "file",
|
||||
"Input file name"),
|
||||
OPT_STRING('o', "output", &kvm.file_name, "file",
|
||||
"Output file name"),
|
||||
OPT_BOOLEAN(0, "guest", &perf_guest,
|
||||
"Collect guest os data"),
|
||||
OPT_BOOLEAN(0, "host", &perf_host,
|
||||
"Collect host os data"),
|
||||
OPT_STRING(0, "guestmount", &symbol_conf.guestmount, "directory",
|
||||
"guest mount directory under which every guest os"
|
||||
" instance has a subdir"),
|
||||
OPT_STRING(0, "guestvmlinux", &symbol_conf.default_guest_vmlinux_name,
|
||||
"file", "file saving guest os vmlinux"),
|
||||
OPT_STRING(0, "guestkallsyms", &symbol_conf.default_guest_kallsyms,
|
||||
"file", "file saving guest os /proc/kallsyms"),
|
||||
OPT_STRING(0, "guestmodules", &symbol_conf.default_guest_modules,
|
||||
"file", "file saving guest os /proc/modules"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
|
||||
const char * const kvm_usage[] = {
|
||||
"perf kvm [<options>] {top|record|report|diff|buildid-list|stat}",
|
||||
NULL
|
||||
};
|
||||
|
||||
perf_host = 0;
|
||||
perf_guest = 1;
|
||||
|
||||
@ -943,28 +1002,32 @@ int cmd_kvm(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
if (!perf_host)
|
||||
perf_guest = 1;
|
||||
|
||||
if (!file_name) {
|
||||
if (!kvm.file_name) {
|
||||
if (perf_host && !perf_guest)
|
||||
sprintf(name_buffer, "perf.data.host");
|
||||
kvm.file_name = strdup("perf.data.host");
|
||||
else if (!perf_host && perf_guest)
|
||||
sprintf(name_buffer, "perf.data.guest");
|
||||
kvm.file_name = strdup("perf.data.guest");
|
||||
else
|
||||
sprintf(name_buffer, "perf.data.kvm");
|
||||
file_name = name_buffer;
|
||||
kvm.file_name = strdup("perf.data.kvm");
|
||||
|
||||
if (!kvm.file_name) {
|
||||
pr_err("Failed to allocate memory for filename\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
if (!strncmp(argv[0], "rec", 3))
|
||||
return __cmd_record(argc, argv);
|
||||
return __cmd_record(&kvm, argc, argv);
|
||||
else if (!strncmp(argv[0], "rep", 3))
|
||||
return __cmd_report(argc, argv);
|
||||
return __cmd_report(&kvm, argc, argv);
|
||||
else if (!strncmp(argv[0], "diff", 4))
|
||||
return cmd_diff(argc, argv, NULL);
|
||||
else if (!strncmp(argv[0], "top", 3))
|
||||
return cmd_top(argc, argv, NULL);
|
||||
else if (!strncmp(argv[0], "buildid-list", 12))
|
||||
return __cmd_buildid_list(argc, argv);
|
||||
return __cmd_buildid_list(&kvm, argc, argv);
|
||||
else if (!strncmp(argv[0], "stat", 4))
|
||||
return kvm_cmd_stat(argc, argv);
|
||||
return kvm_cmd_stat(&kvm, argc, argv);
|
||||
else
|
||||
usage_with_options(kvm_usage, kvm_options);
|
||||
|
||||
|
@ -823,12 +823,6 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct perf_tool eops = {
|
||||
.sample = process_sample_event,
|
||||
.comm = perf_event__process_comm,
|
||||
.ordered_samples = true,
|
||||
};
|
||||
|
||||
static const struct perf_evsel_str_handler lock_tracepoints[] = {
|
||||
{ "lock:lock_acquire", perf_evsel__process_lock_acquire, }, /* CONFIG_LOCKDEP */
|
||||
{ "lock:lock_acquired", perf_evsel__process_lock_acquired, }, /* CONFIG_LOCKDEP, CONFIG_LOCK_STAT */
|
||||
@ -838,6 +832,11 @@ static const struct perf_evsel_str_handler lock_tracepoints[] = {
|
||||
|
||||
static int read_events(void)
|
||||
{
|
||||
struct perf_tool eops = {
|
||||
.sample = process_sample_event,
|
||||
.comm = perf_event__process_comm,
|
||||
.ordered_samples = true,
|
||||
};
|
||||
session = perf_session__new(input_name, O_RDONLY, 0, false, &eops);
|
||||
if (!session) {
|
||||
pr_err("Initializing perf session failed\n");
|
||||
@ -878,53 +877,11 @@ static int __cmd_report(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char * const report_usage[] = {
|
||||
"perf lock report [<options>]",
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct option report_options[] = {
|
||||
OPT_STRING('k', "key", &sort_key, "acquired",
|
||||
"key for sorting (acquired / contended / wait_total / wait_max / wait_min)"),
|
||||
/* TODO: type */
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
static const char * const info_usage[] = {
|
||||
"perf lock info [<options>]",
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct option info_options[] = {
|
||||
OPT_BOOLEAN('t', "threads", &info_threads,
|
||||
"dump thread list in perf.data"),
|
||||
OPT_BOOLEAN('m', "map", &info_map,
|
||||
"map of lock instances (address:name table)"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
static const char * const lock_usage[] = {
|
||||
"perf lock [<options>] {record|report|script|info}",
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct option lock_options[] = {
|
||||
OPT_STRING('i', "input", &input_name, "file", "input file name"),
|
||||
OPT_INCR('v', "verbose", &verbose, "be more verbose (show symbol address, etc)"),
|
||||
OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, "dump raw trace in ASCII"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
static const char *record_args[] = {
|
||||
"record",
|
||||
"-R",
|
||||
"-f",
|
||||
"-m", "1024",
|
||||
"-c", "1",
|
||||
};
|
||||
|
||||
static int __cmd_record(int argc, const char **argv)
|
||||
{
|
||||
const char *record_args[] = {
|
||||
"record", "-R", "-f", "-m", "1024", "-c", "1",
|
||||
};
|
||||
unsigned int rec_argc, i, j;
|
||||
const char **rec_argv;
|
||||
|
||||
@ -963,6 +920,37 @@ static int __cmd_record(int argc, const char **argv)
|
||||
|
||||
int cmd_lock(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
{
|
||||
const struct option info_options[] = {
|
||||
OPT_BOOLEAN('t', "threads", &info_threads,
|
||||
"dump thread list in perf.data"),
|
||||
OPT_BOOLEAN('m', "map", &info_map,
|
||||
"map of lock instances (address:name table)"),
|
||||
OPT_END()
|
||||
};
|
||||
const struct option lock_options[] = {
|
||||
OPT_STRING('i', "input", &input_name, "file", "input file name"),
|
||||
OPT_INCR('v', "verbose", &verbose, "be more verbose (show symbol address, etc)"),
|
||||
OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, "dump raw trace in ASCII"),
|
||||
OPT_END()
|
||||
};
|
||||
const struct option report_options[] = {
|
||||
OPT_STRING('k', "key", &sort_key, "acquired",
|
||||
"key for sorting (acquired / contended / wait_total / wait_max / wait_min)"),
|
||||
/* TODO: type */
|
||||
OPT_END()
|
||||
};
|
||||
const char * const info_usage[] = {
|
||||
"perf lock info [<options>]",
|
||||
NULL
|
||||
};
|
||||
const char * const lock_usage[] = {
|
||||
"perf lock [<options>] {record|report|script|info}",
|
||||
NULL
|
||||
};
|
||||
const char * const report_usage[] = {
|
||||
"perf lock report [<options>]",
|
||||
NULL
|
||||
};
|
||||
unsigned int i;
|
||||
int rc = 0;
|
||||
|
||||
|
@ -250,19 +250,20 @@ static int opt_set_filter(const struct option *opt __maybe_unused,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char * const probe_usage[] = {
|
||||
"perf probe [<options>] 'PROBEDEF' ['PROBEDEF' ...]",
|
||||
"perf probe [<options>] --add 'PROBEDEF' [--add 'PROBEDEF' ...]",
|
||||
"perf probe [<options>] --del '[GROUP:]EVENT' ...",
|
||||
"perf probe --list",
|
||||
int cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
{
|
||||
const char * const probe_usage[] = {
|
||||
"perf probe [<options>] 'PROBEDEF' ['PROBEDEF' ...]",
|
||||
"perf probe [<options>] --add 'PROBEDEF' [--add 'PROBEDEF' ...]",
|
||||
"perf probe [<options>] --del '[GROUP:]EVENT' ...",
|
||||
"perf probe --list",
|
||||
#ifdef DWARF_SUPPORT
|
||||
"perf probe [<options>] --line 'LINEDESC'",
|
||||
"perf probe [<options>] --vars 'PROBEPOINT'",
|
||||
"perf probe [<options>] --line 'LINEDESC'",
|
||||
"perf probe [<options>] --vars 'PROBEPOINT'",
|
||||
#endif
|
||||
NULL
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
const struct option options[] = {
|
||||
OPT_INCR('v', "verbose", &verbose,
|
||||
"be more verbose (show parsed arguments, etc)"),
|
||||
OPT_BOOLEAN('l', "list", ¶ms.list_events,
|
||||
@ -325,10 +326,7 @@ static const struct option options[] = {
|
||||
OPT_CALLBACK('x', "exec", NULL, "executable|path",
|
||||
"target executable name or path", opt_set_target),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
int cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
{
|
||||
};
|
||||
int ret;
|
||||
|
||||
argc = parse_options(argc, argv, options, probe_usage,
|
||||
|
@ -31,13 +31,36 @@
|
||||
#include <sched.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#define CALLCHAIN_HELP "do call-graph (stack chain/backtrace) recording: "
|
||||
#ifndef HAVE_ON_EXIT
|
||||
#ifndef ATEXIT_MAX
|
||||
#define ATEXIT_MAX 32
|
||||
#endif
|
||||
static int __on_exit_count = 0;
|
||||
typedef void (*on_exit_func_t) (int, void *);
|
||||
static on_exit_func_t __on_exit_funcs[ATEXIT_MAX];
|
||||
static void *__on_exit_args[ATEXIT_MAX];
|
||||
static int __exitcode = 0;
|
||||
static void __handle_on_exit_funcs(void);
|
||||
static int on_exit(on_exit_func_t function, void *arg);
|
||||
#define exit(x) (exit)(__exitcode = (x))
|
||||
|
||||
#ifdef NO_LIBUNWIND_SUPPORT
|
||||
static char callchain_help[] = CALLCHAIN_HELP "[fp]";
|
||||
#else
|
||||
static unsigned long default_stack_dump_size = 8192;
|
||||
static char callchain_help[] = CALLCHAIN_HELP "[fp] dwarf";
|
||||
static int on_exit(on_exit_func_t function, void *arg)
|
||||
{
|
||||
if (__on_exit_count == ATEXIT_MAX)
|
||||
return -ENOMEM;
|
||||
else if (__on_exit_count == 0)
|
||||
atexit(__handle_on_exit_funcs);
|
||||
__on_exit_funcs[__on_exit_count] = function;
|
||||
__on_exit_args[__on_exit_count++] = arg;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __handle_on_exit_funcs(void)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < __on_exit_count; i++)
|
||||
__on_exit_funcs[i] (__exitcode, __on_exit_args[i]);
|
||||
}
|
||||
#endif
|
||||
|
||||
enum write_mode_t {
|
||||
@ -800,7 +823,7 @@ error:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifndef NO_LIBUNWIND_SUPPORT
|
||||
#ifdef LIBUNWIND_SUPPORT
|
||||
static int get_stack_size(char *str, unsigned long *_size)
|
||||
{
|
||||
char *endptr;
|
||||
@ -826,7 +849,7 @@ static int get_stack_size(char *str, unsigned long *_size)
|
||||
max_size, str);
|
||||
return -1;
|
||||
}
|
||||
#endif /* !NO_LIBUNWIND_SUPPORT */
|
||||
#endif /* LIBUNWIND_SUPPORT */
|
||||
|
||||
static int
|
||||
parse_callchain_opt(const struct option *opt __maybe_unused, const char *arg,
|
||||
@ -865,9 +888,11 @@ parse_callchain_opt(const struct option *opt __maybe_unused, const char *arg,
|
||||
"needed for -g fp\n");
|
||||
break;
|
||||
|
||||
#ifndef NO_LIBUNWIND_SUPPORT
|
||||
#ifdef LIBUNWIND_SUPPORT
|
||||
/* Dwarf style */
|
||||
} else if (!strncmp(name, "dwarf", sizeof("dwarf"))) {
|
||||
const unsigned long default_stack_dump_size = 8192;
|
||||
|
||||
ret = 0;
|
||||
rec->opts.call_graph = CALLCHAIN_DWARF;
|
||||
rec->opts.stack_dump_size = default_stack_dump_size;
|
||||
@ -883,7 +908,7 @@ parse_callchain_opt(const struct option *opt __maybe_unused, const char *arg,
|
||||
if (!ret)
|
||||
pr_debug("callchain: stack dump size %d\n",
|
||||
rec->opts.stack_dump_size);
|
||||
#endif /* !NO_LIBUNWIND_SUPPORT */
|
||||
#endif /* LIBUNWIND_SUPPORT */
|
||||
} else {
|
||||
pr_err("callchain: Unknown -g option "
|
||||
"value: %s\n", arg);
|
||||
@ -930,6 +955,14 @@ static struct perf_record record = {
|
||||
.file_new = true,
|
||||
};
|
||||
|
||||
#define CALLCHAIN_HELP "do call-graph (stack chain/backtrace) recording: "
|
||||
|
||||
#ifdef LIBUNWIND_SUPPORT
|
||||
static const char callchain_help[] = CALLCHAIN_HELP "[fp] dwarf";
|
||||
#else
|
||||
static const char callchain_help[] = CALLCHAIN_HELP "[fp]";
|
||||
#endif
|
||||
|
||||
/*
|
||||
* XXX Will stay a global variable till we fix builtin-script.c to stop messing
|
||||
* with it and switch to use the library functions in perf_evlist that came
|
||||
|
@ -320,7 +320,7 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist,
|
||||
const char *evname = perf_evsel__name(pos);
|
||||
|
||||
hists__fprintf_nr_sample_events(hists, evname, stdout);
|
||||
hists__fprintf(hists, NULL, false, true, 0, 0, stdout);
|
||||
hists__fprintf(hists, true, 0, 0, stdout);
|
||||
fprintf(stdout, "\n\n");
|
||||
}
|
||||
|
||||
@ -556,8 +556,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
.sample = process_sample_event,
|
||||
.mmap = perf_event__process_mmap,
|
||||
.comm = perf_event__process_comm,
|
||||
.exit = perf_event__process_task,
|
||||
.fork = perf_event__process_task,
|
||||
.exit = perf_event__process_exit,
|
||||
.fork = perf_event__process_fork,
|
||||
.lost = perf_event__process_lost,
|
||||
.read = process_read_event,
|
||||
.attr = perf_event__process_attr,
|
||||
@ -691,7 +691,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
setup_browser(true);
|
||||
else {
|
||||
use_browser = 0;
|
||||
perf_hpp__init(false, false);
|
||||
perf_hpp__init();
|
||||
}
|
||||
|
||||
setup_sorting(report_usage, options);
|
||||
|
@ -1426,7 +1426,7 @@ static int perf_sched__process_tracepoint_sample(struct perf_tool *tool __maybe_
|
||||
struct perf_evsel *evsel,
|
||||
struct machine *machine)
|
||||
{
|
||||
struct thread *thread = machine__findnew_thread(machine, sample->pid);
|
||||
struct thread *thread = machine__findnew_thread(machine, sample->tid);
|
||||
int err = 0;
|
||||
|
||||
if (thread == NULL) {
|
||||
@ -1672,7 +1672,8 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
.sample = perf_sched__process_tracepoint_sample,
|
||||
.comm = perf_event__process_comm,
|
||||
.lost = perf_event__process_lost,
|
||||
.fork = perf_event__process_task,
|
||||
.exit = perf_event__process_exit,
|
||||
.fork = perf_event__process_fork,
|
||||
.ordered_samples = true,
|
||||
},
|
||||
.cmp_pid = LIST_HEAD_INIT(sched.cmp_pid),
|
||||
|
@ -24,7 +24,6 @@ static u64 last_timestamp;
|
||||
static u64 nr_unordered;
|
||||
extern const struct option record_options[];
|
||||
static bool no_callchain;
|
||||
static bool show_full_info;
|
||||
static bool system_wide;
|
||||
static const char *cpu_list;
|
||||
static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
|
||||
@ -473,8 +472,6 @@ static int cleanup_scripting(void)
|
||||
return scripting_ops->stop_script();
|
||||
}
|
||||
|
||||
static const char *input_name;
|
||||
|
||||
static int process_sample_event(struct perf_tool *tool __maybe_unused,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
@ -523,8 +520,8 @@ static struct perf_tool perf_script = {
|
||||
.sample = process_sample_event,
|
||||
.mmap = perf_event__process_mmap,
|
||||
.comm = perf_event__process_comm,
|
||||
.exit = perf_event__process_task,
|
||||
.fork = perf_event__process_task,
|
||||
.exit = perf_event__process_exit,
|
||||
.fork = perf_event__process_fork,
|
||||
.attr = perf_event__process_attr,
|
||||
.event_type = perf_event__process_event_type,
|
||||
.tracing_data = perf_event__process_tracing_data,
|
||||
@ -1156,62 +1153,6 @@ out:
|
||||
return n_args;
|
||||
}
|
||||
|
||||
static const char * const script_usage[] = {
|
||||
"perf script [<options>]",
|
||||
"perf script [<options>] record <script> [<record-options>] <command>",
|
||||
"perf script [<options>] report <script> [script-args]",
|
||||
"perf script [<options>] <script> [<record-options>] <command>",
|
||||
"perf script [<options>] <top-script> [script-args]",
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
|
||||
"dump raw trace in ASCII"),
|
||||
OPT_INCR('v', "verbose", &verbose,
|
||||
"be more verbose (show symbol address, etc)"),
|
||||
OPT_BOOLEAN('L', "Latency", &latency_format,
|
||||
"show latency attributes (irqs/preemption disabled, etc)"),
|
||||
OPT_CALLBACK_NOOPT('l', "list", NULL, NULL, "list available scripts",
|
||||
list_available_scripts),
|
||||
OPT_CALLBACK('s', "script", NULL, "name",
|
||||
"script file name (lang:script name, script name, or *)",
|
||||
parse_scriptname),
|
||||
OPT_STRING('g', "gen-script", &generate_script_lang, "lang",
|
||||
"generate perf-script.xx script in specified language"),
|
||||
OPT_STRING('i', "input", &input_name, "file",
|
||||
"input file name"),
|
||||
OPT_BOOLEAN('d', "debug-mode", &debug_mode,
|
||||
"do various checks like samples ordering and lost events"),
|
||||
OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
|
||||
"file", "vmlinux pathname"),
|
||||
OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name,
|
||||
"file", "kallsyms pathname"),
|
||||
OPT_BOOLEAN('G', "hide-call-graph", &no_callchain,
|
||||
"When printing symbols do not display call chain"),
|
||||
OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
|
||||
"Look for files with symbols relative to this directory"),
|
||||
OPT_CALLBACK('f', "fields", NULL, "str",
|
||||
"comma separated output fields prepend with 'type:'. "
|
||||
"Valid types: hw,sw,trace,raw. "
|
||||
"Fields: comm,tid,pid,time,cpu,event,trace,ip,sym,dso,"
|
||||
"addr,symoff",
|
||||
parse_output_fields),
|
||||
OPT_BOOLEAN('a', "all-cpus", &system_wide,
|
||||
"system-wide collection from all CPUs"),
|
||||
OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]",
|
||||
"only consider these symbols"),
|
||||
OPT_STRING('C', "cpu", &cpu_list, "cpu", "list of cpus to profile"),
|
||||
OPT_STRING('c', "comms", &symbol_conf.comm_list_str, "comm[,comm...]",
|
||||
"only display events for these comms"),
|
||||
OPT_BOOLEAN('I', "show-info", &show_full_info,
|
||||
"display extended information from perf.data file"),
|
||||
OPT_BOOLEAN('\0', "show-kernel-path", &symbol_conf.show_kernel_path,
|
||||
"Show the path of [kernel.kallsyms]"),
|
||||
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
static int have_cmd(int argc, const char **argv)
|
||||
{
|
||||
char **__argv = malloc(sizeof(const char *) * argc);
|
||||
@ -1233,12 +1174,65 @@ static int have_cmd(int argc, const char **argv)
|
||||
|
||||
int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
{
|
||||
bool show_full_info = false;
|
||||
const char *input_name = NULL;
|
||||
char *rec_script_path = NULL;
|
||||
char *rep_script_path = NULL;
|
||||
struct perf_session *session;
|
||||
char *script_path = NULL;
|
||||
const char **__argv;
|
||||
int i, j, err;
|
||||
const struct option options[] = {
|
||||
OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
|
||||
"dump raw trace in ASCII"),
|
||||
OPT_INCR('v', "verbose", &verbose,
|
||||
"be more verbose (show symbol address, etc)"),
|
||||
OPT_BOOLEAN('L', "Latency", &latency_format,
|
||||
"show latency attributes (irqs/preemption disabled, etc)"),
|
||||
OPT_CALLBACK_NOOPT('l', "list", NULL, NULL, "list available scripts",
|
||||
list_available_scripts),
|
||||
OPT_CALLBACK('s', "script", NULL, "name",
|
||||
"script file name (lang:script name, script name, or *)",
|
||||
parse_scriptname),
|
||||
OPT_STRING('g', "gen-script", &generate_script_lang, "lang",
|
||||
"generate perf-script.xx script in specified language"),
|
||||
OPT_STRING('i', "input", &input_name, "file", "input file name"),
|
||||
OPT_BOOLEAN('d', "debug-mode", &debug_mode,
|
||||
"do various checks like samples ordering and lost events"),
|
||||
OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
|
||||
"file", "vmlinux pathname"),
|
||||
OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name,
|
||||
"file", "kallsyms pathname"),
|
||||
OPT_BOOLEAN('G', "hide-call-graph", &no_callchain,
|
||||
"When printing symbols do not display call chain"),
|
||||
OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
|
||||
"Look for files with symbols relative to this directory"),
|
||||
OPT_CALLBACK('f', "fields", NULL, "str",
|
||||
"comma separated output fields prepend with 'type:'. "
|
||||
"Valid types: hw,sw,trace,raw. "
|
||||
"Fields: comm,tid,pid,time,cpu,event,trace,ip,sym,dso,"
|
||||
"addr,symoff", parse_output_fields),
|
||||
OPT_BOOLEAN('a', "all-cpus", &system_wide,
|
||||
"system-wide collection from all CPUs"),
|
||||
OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]",
|
||||
"only consider these symbols"),
|
||||
OPT_STRING('C', "cpu", &cpu_list, "cpu", "list of cpus to profile"),
|
||||
OPT_STRING('c', "comms", &symbol_conf.comm_list_str, "comm[,comm...]",
|
||||
"only display events for these comms"),
|
||||
OPT_BOOLEAN('I', "show-info", &show_full_info,
|
||||
"display extended information from perf.data file"),
|
||||
OPT_BOOLEAN('\0', "show-kernel-path", &symbol_conf.show_kernel_path,
|
||||
"Show the path of [kernel.kallsyms]"),
|
||||
OPT_END()
|
||||
};
|
||||
const char * const script_usage[] = {
|
||||
"perf script [<options>]",
|
||||
"perf script [<options>] record <script> [<record-options>] <command>",
|
||||
"perf script [<options>] report <script> [script-args]",
|
||||
"perf script [<options>] <script> [<record-options>] <command>",
|
||||
"perf script [<options>] <top-script> [script-args]",
|
||||
NULL
|
||||
};
|
||||
|
||||
setup_scripting();
|
||||
|
||||
|
@ -64,122 +64,12 @@
|
||||
#define CNTR_NOT_SUPPORTED "<not supported>"
|
||||
#define CNTR_NOT_COUNTED "<not counted>"
|
||||
|
||||
static struct perf_event_attr default_attrs[] = {
|
||||
|
||||
{ .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_TASK_CLOCK },
|
||||
{ .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_CONTEXT_SWITCHES },
|
||||
{ .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_CPU_MIGRATIONS },
|
||||
{ .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_PAGE_FAULTS },
|
||||
|
||||
{ .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_CPU_CYCLES },
|
||||
{ .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_STALLED_CYCLES_FRONTEND },
|
||||
{ .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_STALLED_CYCLES_BACKEND },
|
||||
{ .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_INSTRUCTIONS },
|
||||
{ .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS },
|
||||
{ .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_BRANCH_MISSES },
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
* Detailed stats (-d), covering the L1 and last level data caches:
|
||||
*/
|
||||
static struct perf_event_attr detailed_attrs[] = {
|
||||
|
||||
{ .type = PERF_TYPE_HW_CACHE,
|
||||
.config =
|
||||
PERF_COUNT_HW_CACHE_L1D << 0 |
|
||||
(PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
(PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) },
|
||||
|
||||
{ .type = PERF_TYPE_HW_CACHE,
|
||||
.config =
|
||||
PERF_COUNT_HW_CACHE_L1D << 0 |
|
||||
(PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
(PERF_COUNT_HW_CACHE_RESULT_MISS << 16) },
|
||||
|
||||
{ .type = PERF_TYPE_HW_CACHE,
|
||||
.config =
|
||||
PERF_COUNT_HW_CACHE_LL << 0 |
|
||||
(PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
(PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) },
|
||||
|
||||
{ .type = PERF_TYPE_HW_CACHE,
|
||||
.config =
|
||||
PERF_COUNT_HW_CACHE_LL << 0 |
|
||||
(PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
(PERF_COUNT_HW_CACHE_RESULT_MISS << 16) },
|
||||
};
|
||||
|
||||
/*
|
||||
* Very detailed stats (-d -d), covering the instruction cache and the TLB caches:
|
||||
*/
|
||||
static struct perf_event_attr very_detailed_attrs[] = {
|
||||
|
||||
{ .type = PERF_TYPE_HW_CACHE,
|
||||
.config =
|
||||
PERF_COUNT_HW_CACHE_L1I << 0 |
|
||||
(PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
(PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) },
|
||||
|
||||
{ .type = PERF_TYPE_HW_CACHE,
|
||||
.config =
|
||||
PERF_COUNT_HW_CACHE_L1I << 0 |
|
||||
(PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
(PERF_COUNT_HW_CACHE_RESULT_MISS << 16) },
|
||||
|
||||
{ .type = PERF_TYPE_HW_CACHE,
|
||||
.config =
|
||||
PERF_COUNT_HW_CACHE_DTLB << 0 |
|
||||
(PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
(PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) },
|
||||
|
||||
{ .type = PERF_TYPE_HW_CACHE,
|
||||
.config =
|
||||
PERF_COUNT_HW_CACHE_DTLB << 0 |
|
||||
(PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
(PERF_COUNT_HW_CACHE_RESULT_MISS << 16) },
|
||||
|
||||
{ .type = PERF_TYPE_HW_CACHE,
|
||||
.config =
|
||||
PERF_COUNT_HW_CACHE_ITLB << 0 |
|
||||
(PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
(PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) },
|
||||
|
||||
{ .type = PERF_TYPE_HW_CACHE,
|
||||
.config =
|
||||
PERF_COUNT_HW_CACHE_ITLB << 0 |
|
||||
(PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
(PERF_COUNT_HW_CACHE_RESULT_MISS << 16) },
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
* Very, very detailed stats (-d -d -d), adding prefetch events:
|
||||
*/
|
||||
static struct perf_event_attr very_very_detailed_attrs[] = {
|
||||
|
||||
{ .type = PERF_TYPE_HW_CACHE,
|
||||
.config =
|
||||
PERF_COUNT_HW_CACHE_L1D << 0 |
|
||||
(PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) |
|
||||
(PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) },
|
||||
|
||||
{ .type = PERF_TYPE_HW_CACHE,
|
||||
.config =
|
||||
PERF_COUNT_HW_CACHE_L1D << 0 |
|
||||
(PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) |
|
||||
(PERF_COUNT_HW_CACHE_RESULT_MISS << 16) },
|
||||
};
|
||||
|
||||
|
||||
|
||||
static struct perf_evlist *evsel_list;
|
||||
|
||||
static struct perf_target target = {
|
||||
.uid = UINT_MAX,
|
||||
};
|
||||
|
||||
static int run_idx = 0;
|
||||
static int run_count = 1;
|
||||
static bool no_inherit = false;
|
||||
static bool scale = true;
|
||||
@ -187,15 +77,12 @@ static bool no_aggr = false;
|
||||
static pid_t child_pid = -1;
|
||||
static bool null_run = false;
|
||||
static int detailed_run = 0;
|
||||
static bool sync_run = false;
|
||||
static bool big_num = true;
|
||||
static int big_num_opt = -1;
|
||||
static const char *csv_sep = NULL;
|
||||
static bool csv_output = false;
|
||||
static bool group = false;
|
||||
static const char *output_name = NULL;
|
||||
static FILE *output = NULL;
|
||||
static int output_fd;
|
||||
|
||||
static volatile int done = 0;
|
||||
|
||||
@ -1028,11 +915,6 @@ static void sig_atexit(void)
|
||||
kill(getpid(), signr);
|
||||
}
|
||||
|
||||
static const char * const stat_usage[] = {
|
||||
"perf stat [<options>] [<command>]",
|
||||
NULL
|
||||
};
|
||||
|
||||
static int stat__set_big_num(const struct option *opt __maybe_unused,
|
||||
const char *s __maybe_unused, int unset)
|
||||
{
|
||||
@ -1040,62 +922,119 @@ static int stat__set_big_num(const struct option *opt __maybe_unused,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool append_file;
|
||||
|
||||
static const struct option options[] = {
|
||||
OPT_CALLBACK('e', "event", &evsel_list, "event",
|
||||
"event selector. use 'perf list' to list available events",
|
||||
parse_events_option),
|
||||
OPT_CALLBACK(0, "filter", &evsel_list, "filter",
|
||||
"event filter", parse_filter),
|
||||
OPT_BOOLEAN('i', "no-inherit", &no_inherit,
|
||||
"child tasks do not inherit counters"),
|
||||
OPT_STRING('p', "pid", &target.pid, "pid",
|
||||
"stat events on existing process id"),
|
||||
OPT_STRING('t', "tid", &target.tid, "tid",
|
||||
"stat events on existing thread id"),
|
||||
OPT_BOOLEAN('a', "all-cpus", &target.system_wide,
|
||||
"system-wide collection from all CPUs"),
|
||||
OPT_BOOLEAN('g', "group", &group,
|
||||
"put the counters into a counter group"),
|
||||
OPT_BOOLEAN('c', "scale", &scale,
|
||||
"scale/normalize counters"),
|
||||
OPT_INCR('v', "verbose", &verbose,
|
||||
"be more verbose (show counter open errors, etc)"),
|
||||
OPT_INTEGER('r', "repeat", &run_count,
|
||||
"repeat command and print average + stddev (max: 100)"),
|
||||
OPT_BOOLEAN('n', "null", &null_run,
|
||||
"null run - dont start any counters"),
|
||||
OPT_INCR('d', "detailed", &detailed_run,
|
||||
"detailed run - start a lot of events"),
|
||||
OPT_BOOLEAN('S', "sync", &sync_run,
|
||||
"call sync() before starting a run"),
|
||||
OPT_CALLBACK_NOOPT('B', "big-num", NULL, NULL,
|
||||
"print large numbers with thousands\' separators",
|
||||
stat__set_big_num),
|
||||
OPT_STRING('C', "cpu", &target.cpu_list, "cpu",
|
||||
"list of cpus to monitor in system-wide"),
|
||||
OPT_BOOLEAN('A', "no-aggr", &no_aggr,
|
||||
"disable CPU count aggregation"),
|
||||
OPT_STRING('x', "field-separator", &csv_sep, "separator",
|
||||
"print counts with custom separator"),
|
||||
OPT_CALLBACK('G', "cgroup", &evsel_list, "name",
|
||||
"monitor event in cgroup name only",
|
||||
parse_cgroups),
|
||||
OPT_STRING('o', "output", &output_name, "file",
|
||||
"output file name"),
|
||||
OPT_BOOLEAN(0, "append", &append_file, "append to the output file"),
|
||||
OPT_INTEGER(0, "log-fd", &output_fd,
|
||||
"log output to fd, instead of stderr"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
/*
|
||||
* Add default attributes, if there were no attributes specified or
|
||||
* if -d/--detailed, -d -d or -d -d -d is used:
|
||||
*/
|
||||
static int add_default_attributes(void)
|
||||
{
|
||||
struct perf_event_attr default_attrs[] = {
|
||||
|
||||
{ .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_TASK_CLOCK },
|
||||
{ .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_CONTEXT_SWITCHES },
|
||||
{ .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_CPU_MIGRATIONS },
|
||||
{ .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_PAGE_FAULTS },
|
||||
|
||||
{ .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_CPU_CYCLES },
|
||||
{ .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_STALLED_CYCLES_FRONTEND },
|
||||
{ .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_STALLED_CYCLES_BACKEND },
|
||||
{ .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_INSTRUCTIONS },
|
||||
{ .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS },
|
||||
{ .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_BRANCH_MISSES },
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
* Detailed stats (-d), covering the L1 and last level data caches:
|
||||
*/
|
||||
struct perf_event_attr detailed_attrs[] = {
|
||||
|
||||
{ .type = PERF_TYPE_HW_CACHE,
|
||||
.config =
|
||||
PERF_COUNT_HW_CACHE_L1D << 0 |
|
||||
(PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
(PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) },
|
||||
|
||||
{ .type = PERF_TYPE_HW_CACHE,
|
||||
.config =
|
||||
PERF_COUNT_HW_CACHE_L1D << 0 |
|
||||
(PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
(PERF_COUNT_HW_CACHE_RESULT_MISS << 16) },
|
||||
|
||||
{ .type = PERF_TYPE_HW_CACHE,
|
||||
.config =
|
||||
PERF_COUNT_HW_CACHE_LL << 0 |
|
||||
(PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
(PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) },
|
||||
|
||||
{ .type = PERF_TYPE_HW_CACHE,
|
||||
.config =
|
||||
PERF_COUNT_HW_CACHE_LL << 0 |
|
||||
(PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
(PERF_COUNT_HW_CACHE_RESULT_MISS << 16) },
|
||||
};
|
||||
|
||||
/*
|
||||
* Very detailed stats (-d -d), covering the instruction cache and the TLB caches:
|
||||
*/
|
||||
struct perf_event_attr very_detailed_attrs[] = {
|
||||
|
||||
{ .type = PERF_TYPE_HW_CACHE,
|
||||
.config =
|
||||
PERF_COUNT_HW_CACHE_L1I << 0 |
|
||||
(PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
(PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) },
|
||||
|
||||
{ .type = PERF_TYPE_HW_CACHE,
|
||||
.config =
|
||||
PERF_COUNT_HW_CACHE_L1I << 0 |
|
||||
(PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
(PERF_COUNT_HW_CACHE_RESULT_MISS << 16) },
|
||||
|
||||
{ .type = PERF_TYPE_HW_CACHE,
|
||||
.config =
|
||||
PERF_COUNT_HW_CACHE_DTLB << 0 |
|
||||
(PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
(PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) },
|
||||
|
||||
{ .type = PERF_TYPE_HW_CACHE,
|
||||
.config =
|
||||
PERF_COUNT_HW_CACHE_DTLB << 0 |
|
||||
(PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
(PERF_COUNT_HW_CACHE_RESULT_MISS << 16) },
|
||||
|
||||
{ .type = PERF_TYPE_HW_CACHE,
|
||||
.config =
|
||||
PERF_COUNT_HW_CACHE_ITLB << 0 |
|
||||
(PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
(PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) },
|
||||
|
||||
{ .type = PERF_TYPE_HW_CACHE,
|
||||
.config =
|
||||
PERF_COUNT_HW_CACHE_ITLB << 0 |
|
||||
(PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
(PERF_COUNT_HW_CACHE_RESULT_MISS << 16) },
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
* Very, very detailed stats (-d -d -d), adding prefetch events:
|
||||
*/
|
||||
struct perf_event_attr very_very_detailed_attrs[] = {
|
||||
|
||||
{ .type = PERF_TYPE_HW_CACHE,
|
||||
.config =
|
||||
PERF_COUNT_HW_CACHE_L1D << 0 |
|
||||
(PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) |
|
||||
(PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) },
|
||||
|
||||
{ .type = PERF_TYPE_HW_CACHE,
|
||||
.config =
|
||||
PERF_COUNT_HW_CACHE_L1D << 0 |
|
||||
(PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) |
|
||||
(PERF_COUNT_HW_CACHE_RESULT_MISS << 16) },
|
||||
};
|
||||
|
||||
/* Set attrs if no event is selected and !null_run: */
|
||||
if (null_run)
|
||||
return 0;
|
||||
@ -1130,8 +1069,59 @@ static int add_default_attributes(void)
|
||||
|
||||
int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
{
|
||||
bool append_file = false,
|
||||
sync_run = false;
|
||||
int output_fd = 0;
|
||||
const char *output_name = NULL;
|
||||
const struct option options[] = {
|
||||
OPT_CALLBACK('e', "event", &evsel_list, "event",
|
||||
"event selector. use 'perf list' to list available events",
|
||||
parse_events_option),
|
||||
OPT_CALLBACK(0, "filter", &evsel_list, "filter",
|
||||
"event filter", parse_filter),
|
||||
OPT_BOOLEAN('i', "no-inherit", &no_inherit,
|
||||
"child tasks do not inherit counters"),
|
||||
OPT_STRING('p', "pid", &target.pid, "pid",
|
||||
"stat events on existing process id"),
|
||||
OPT_STRING('t', "tid", &target.tid, "tid",
|
||||
"stat events on existing thread id"),
|
||||
OPT_BOOLEAN('a', "all-cpus", &target.system_wide,
|
||||
"system-wide collection from all CPUs"),
|
||||
OPT_BOOLEAN('g', "group", &group,
|
||||
"put the counters into a counter group"),
|
||||
OPT_BOOLEAN('c', "scale", &scale, "scale/normalize counters"),
|
||||
OPT_INCR('v', "verbose", &verbose,
|
||||
"be more verbose (show counter open errors, etc)"),
|
||||
OPT_INTEGER('r', "repeat", &run_count,
|
||||
"repeat command and print average + stddev (max: 100)"),
|
||||
OPT_BOOLEAN('n', "null", &null_run,
|
||||
"null run - dont start any counters"),
|
||||
OPT_INCR('d', "detailed", &detailed_run,
|
||||
"detailed run - start a lot of events"),
|
||||
OPT_BOOLEAN('S', "sync", &sync_run,
|
||||
"call sync() before starting a run"),
|
||||
OPT_CALLBACK_NOOPT('B', "big-num", NULL, NULL,
|
||||
"print large numbers with thousands\' separators",
|
||||
stat__set_big_num),
|
||||
OPT_STRING('C', "cpu", &target.cpu_list, "cpu",
|
||||
"list of cpus to monitor in system-wide"),
|
||||
OPT_BOOLEAN('A', "no-aggr", &no_aggr, "disable CPU count aggregation"),
|
||||
OPT_STRING('x', "field-separator", &csv_sep, "separator",
|
||||
"print counts with custom separator"),
|
||||
OPT_CALLBACK('G', "cgroup", &evsel_list, "name",
|
||||
"monitor event in cgroup name only", parse_cgroups),
|
||||
OPT_STRING('o', "output", &output_name, "file", "output file name"),
|
||||
OPT_BOOLEAN(0, "append", &append_file, "append to the output file"),
|
||||
OPT_INTEGER(0, "log-fd", &output_fd,
|
||||
"log output to fd, instead of stderr"),
|
||||
OPT_END()
|
||||
};
|
||||
const char * const stat_usage[] = {
|
||||
"perf stat [<options>] [<command>]",
|
||||
NULL
|
||||
};
|
||||
struct perf_evsel *pos;
|
||||
int status = -ENOMEM;
|
||||
int status = -ENOMEM, run_idx;
|
||||
const char *mode;
|
||||
|
||||
setlocale(LC_ALL, "");
|
||||
|
@ -35,7 +35,6 @@ static int test__vmlinux_matches_kallsyms(void)
|
||||
struct map *kallsyms_map, *vmlinux_map;
|
||||
struct machine kallsyms, vmlinux;
|
||||
enum map_type type = MAP__FUNCTION;
|
||||
long page_size = sysconf(_SC_PAGE_SIZE);
|
||||
struct ref_reloc_sym ref_reloc_sym = { .name = "_stext", };
|
||||
|
||||
/*
|
||||
@ -1007,7 +1006,6 @@ static void segfault_handler(int sig __maybe_unused,
|
||||
|
||||
static int __test__rdpmc(void)
|
||||
{
|
||||
long page_size = sysconf(_SC_PAGE_SIZE);
|
||||
volatile int tmp = 0;
|
||||
u64 i, loops = 1000;
|
||||
int n;
|
||||
|
@ -38,9 +38,6 @@
|
||||
#define PWR_EVENT_EXIT -1
|
||||
|
||||
|
||||
static const char *input_name;
|
||||
static const char *output_name = "output.svg";
|
||||
|
||||
static unsigned int numcpus;
|
||||
static u64 min_freq; /* Lowest CPU frequency seen */
|
||||
static u64 max_freq; /* Highest CPU frequency seen */
|
||||
@ -968,16 +965,15 @@ static void write_svg_file(const char *filename)
|
||||
svg_close();
|
||||
}
|
||||
|
||||
static struct perf_tool perf_timechart = {
|
||||
.comm = process_comm_event,
|
||||
.fork = process_fork_event,
|
||||
.exit = process_exit_event,
|
||||
.sample = process_sample_event,
|
||||
.ordered_samples = true,
|
||||
};
|
||||
|
||||
static int __cmd_timechart(void)
|
||||
static int __cmd_timechart(const char *input_name, const char *output_name)
|
||||
{
|
||||
struct perf_tool perf_timechart = {
|
||||
.comm = process_comm_event,
|
||||
.fork = process_fork_event,
|
||||
.exit = process_exit_event,
|
||||
.sample = process_sample_event,
|
||||
.ordered_samples = true,
|
||||
};
|
||||
struct perf_session *session = perf_session__new(input_name, O_RDONLY,
|
||||
0, false, &perf_timechart);
|
||||
int ret = -EINVAL;
|
||||
@ -1005,40 +1001,25 @@ out_delete:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const char * const timechart_usage[] = {
|
||||
"perf timechart [<options>] {record}",
|
||||
NULL
|
||||
};
|
||||
|
||||
#ifdef SUPPORT_OLD_POWER_EVENTS
|
||||
static const char * const record_old_args[] = {
|
||||
"record",
|
||||
"-a",
|
||||
"-R",
|
||||
"-f",
|
||||
"-c", "1",
|
||||
"-e", "power:power_start",
|
||||
"-e", "power:power_end",
|
||||
"-e", "power:power_frequency",
|
||||
"-e", "sched:sched_wakeup",
|
||||
"-e", "sched:sched_switch",
|
||||
};
|
||||
#endif
|
||||
|
||||
static const char * const record_new_args[] = {
|
||||
"record",
|
||||
"-a",
|
||||
"-R",
|
||||
"-f",
|
||||
"-c", "1",
|
||||
"-e", "power:cpu_frequency",
|
||||
"-e", "power:cpu_idle",
|
||||
"-e", "sched:sched_wakeup",
|
||||
"-e", "sched:sched_switch",
|
||||
};
|
||||
|
||||
static int __cmd_record(int argc, const char **argv)
|
||||
{
|
||||
#ifdef SUPPORT_OLD_POWER_EVENTS
|
||||
const char * const record_old_args[] = {
|
||||
"record", "-a", "-R", "-f", "-c", "1",
|
||||
"-e", "power:power_start",
|
||||
"-e", "power:power_end",
|
||||
"-e", "power:power_frequency",
|
||||
"-e", "sched:sched_wakeup",
|
||||
"-e", "sched:sched_switch",
|
||||
};
|
||||
#endif
|
||||
const char * const record_new_args[] = {
|
||||
"record", "-a", "-R", "-f", "-c", "1",
|
||||
"-e", "power:cpu_frequency",
|
||||
"-e", "power:cpu_idle",
|
||||
"-e", "sched:sched_wakeup",
|
||||
"-e", "sched:sched_switch",
|
||||
};
|
||||
unsigned int rec_argc, i, j;
|
||||
const char **rec_argv;
|
||||
const char * const *record_args = record_new_args;
|
||||
@ -1077,27 +1058,28 @@ parse_process(const struct option *opt __maybe_unused, const char *arg,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct option options[] = {
|
||||
OPT_STRING('i', "input", &input_name, "file",
|
||||
"input file name"),
|
||||
OPT_STRING('o', "output", &output_name, "file",
|
||||
"output file name"),
|
||||
OPT_INTEGER('w', "width", &svg_page_width,
|
||||
"page width"),
|
||||
OPT_BOOLEAN('P', "power-only", &power_only,
|
||||
"output power data only"),
|
||||
int cmd_timechart(int argc, const char **argv,
|
||||
const char *prefix __maybe_unused)
|
||||
{
|
||||
const char *input_name;
|
||||
const char *output_name = "output.svg";
|
||||
const struct option options[] = {
|
||||
OPT_STRING('i', "input", &input_name, "file", "input file name"),
|
||||
OPT_STRING('o', "output", &output_name, "file", "output file name"),
|
||||
OPT_INTEGER('w', "width", &svg_page_width, "page width"),
|
||||
OPT_BOOLEAN('P', "power-only", &power_only, "output power data only"),
|
||||
OPT_CALLBACK('p', "process", NULL, "process",
|
||||
"process selector. Pass a pid or process name.",
|
||||
parse_process),
|
||||
OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
|
||||
"Look for files with symbols relative to this directory"),
|
||||
OPT_END()
|
||||
};
|
||||
};
|
||||
const char * const timechart_usage[] = {
|
||||
"perf timechart [<options>] {record}",
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
int cmd_timechart(int argc, const char **argv,
|
||||
const char *prefix __maybe_unused)
|
||||
{
|
||||
argc = parse_options(argc, argv, options, timechart_usage,
|
||||
PARSE_OPT_STOP_AT_NON_OPTION);
|
||||
|
||||
@ -1110,5 +1092,5 @@ int cmd_timechart(int argc, const char **argv,
|
||||
|
||||
setup_pager();
|
||||
|
||||
return __cmd_timechart();
|
||||
return __cmd_timechart(input_name, output_name);
|
||||
}
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "util/color.h"
|
||||
#include "util/evlist.h"
|
||||
#include "util/evsel.h"
|
||||
#include "util/machine.h"
|
||||
#include "util/session.h"
|
||||
#include "util/symbol.h"
|
||||
#include "util/thread.h"
|
||||
@ -316,7 +317,7 @@ static void perf_top__print_sym_table(struct perf_top *top)
|
||||
hists__output_recalc_col_len(&top->sym_evsel->hists,
|
||||
top->winsize.ws_row - 3);
|
||||
putchar('\n');
|
||||
hists__fprintf(&top->sym_evsel->hists, NULL, false, false,
|
||||
hists__fprintf(&top->sym_evsel->hists, false,
|
||||
top->winsize.ws_row - 4 - printed, win_width, stdout);
|
||||
}
|
||||
|
||||
@ -871,7 +872,7 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx)
|
||||
&sample, machine);
|
||||
} else if (event->header.type < PERF_RECORD_MAX) {
|
||||
hists__inc_nr_events(&evsel->hists, event->header.type);
|
||||
perf_event__process(&top->tool, event, &sample, machine);
|
||||
machine__process_event(machine, event);
|
||||
} else
|
||||
++session->hists.stats.nr_unknown_events;
|
||||
}
|
||||
@ -1159,11 +1160,6 @@ setup:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char * const top_usage[] = {
|
||||
"perf top [<options>]",
|
||||
NULL
|
||||
};
|
||||
|
||||
int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
{
|
||||
struct perf_evsel *pos;
|
||||
@ -1250,6 +1246,10 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
OPT_STRING('u', "uid", &top.target.uid_str, "user", "user to profile"),
|
||||
OPT_END()
|
||||
};
|
||||
const char * const top_usage[] = {
|
||||
"perf top [<options>]",
|
||||
NULL
|
||||
};
|
||||
|
||||
top.evlist = perf_evlist__new(NULL, NULL);
|
||||
if (top.evlist == NULL)
|
||||
|
@ -52,6 +52,13 @@ struct trace {
|
||||
struct perf_record_opts opts;
|
||||
};
|
||||
|
||||
static bool done = false;
|
||||
|
||||
static void sig_handler(int sig __maybe_unused)
|
||||
{
|
||||
done = true;
|
||||
}
|
||||
|
||||
static int trace__read_syscall_info(struct trace *trace, int id)
|
||||
{
|
||||
char tp_name[128];
|
||||
@ -114,33 +121,99 @@ static size_t syscall__fprintf_args(struct syscall *sc, unsigned long *args, FIL
|
||||
return printed;
|
||||
}
|
||||
|
||||
static int trace__run(struct trace *trace)
|
||||
typedef int (*tracepoint_handler)(struct trace *trace, struct perf_evsel *evsel,
|
||||
struct perf_sample *sample);
|
||||
|
||||
static struct syscall *trace__syscall_info(struct trace *trace,
|
||||
struct perf_evsel *evsel,
|
||||
struct perf_sample *sample)
|
||||
{
|
||||
int id = perf_evsel__intval(evsel, sample, "id");
|
||||
|
||||
if (id < 0) {
|
||||
printf("Invalid syscall %d id, skipping...\n", id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((id > trace->syscalls.max || trace->syscalls.table[id].name == NULL) &&
|
||||
trace__read_syscall_info(trace, id))
|
||||
goto out_cant_read;
|
||||
|
||||
if ((id > trace->syscalls.max || trace->syscalls.table[id].name == NULL))
|
||||
goto out_cant_read;
|
||||
|
||||
return &trace->syscalls.table[id];
|
||||
|
||||
out_cant_read:
|
||||
printf("Problems reading syscall %d information\n", id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel,
|
||||
struct perf_sample *sample)
|
||||
{
|
||||
void *args;
|
||||
struct syscall *sc = trace__syscall_info(trace, evsel, sample);
|
||||
|
||||
if (sc == NULL)
|
||||
return -1;
|
||||
|
||||
args = perf_evsel__rawptr(evsel, sample, "args");
|
||||
if (args == NULL) {
|
||||
printf("Problems reading syscall arguments\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("%s(", sc->name);
|
||||
syscall__fprintf_args(sc, args, stdout);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel,
|
||||
struct perf_sample *sample)
|
||||
{
|
||||
int ret;
|
||||
struct syscall *sc = trace__syscall_info(trace, evsel, sample);
|
||||
|
||||
if (sc == NULL)
|
||||
return -1;
|
||||
|
||||
ret = perf_evsel__intval(evsel, sample, "ret");
|
||||
|
||||
if (ret < 0 && sc->fmt && sc->fmt->errmsg) {
|
||||
char bf[256];
|
||||
const char *emsg = strerror_r(-ret, bf, sizeof(bf)),
|
||||
*e = audit_errno_to_name(-ret);
|
||||
|
||||
printf(") = -1 %s %s", e, emsg);
|
||||
} else if (ret == 0 && sc->fmt && sc->fmt->timeout)
|
||||
printf(") = 0 Timeout");
|
||||
else
|
||||
printf(") = %d", ret);
|
||||
|
||||
putchar('\n');
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int trace__run(struct trace *trace, int argc, const char **argv)
|
||||
{
|
||||
struct perf_evlist *evlist = perf_evlist__new(NULL, NULL);
|
||||
struct perf_evsel *evsel, *evsel_enter, *evsel_exit;
|
||||
struct perf_evsel *evsel;
|
||||
int err = -1, i, nr_events = 0, before;
|
||||
const bool forks = argc > 0;
|
||||
|
||||
if (evlist == NULL) {
|
||||
printf("Not enough memory to run!\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
evsel_enter = perf_evsel__newtp("raw_syscalls", "sys_enter", 0);
|
||||
if (evsel_enter == NULL) {
|
||||
printf("Couldn't read the raw_syscalls:sys_enter tracepoint information!\n");
|
||||
if (perf_evlist__add_newtp(evlist, "raw_syscalls", "sys_enter", trace__sys_enter) ||
|
||||
perf_evlist__add_newtp(evlist, "raw_syscalls", "sys_exit", trace__sys_exit)) {
|
||||
printf("Couldn't read the raw_syscalls tracepoints information!\n");
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
perf_evlist__add(evlist, evsel_enter);
|
||||
|
||||
evsel_exit = perf_evsel__newtp("raw_syscalls", "sys_exit", 1);
|
||||
if (evsel_exit == NULL) {
|
||||
printf("Couldn't read the raw_syscalls:sys_exit tracepoint information!\n");
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
perf_evlist__add(evlist, evsel_exit);
|
||||
|
||||
err = perf_evlist__create_maps(evlist, &trace->opts.target);
|
||||
if (err < 0) {
|
||||
printf("Problems parsing the target to trace, check your options!\n");
|
||||
@ -149,6 +222,17 @@ static int trace__run(struct trace *trace)
|
||||
|
||||
perf_evlist__config_attrs(evlist, &trace->opts);
|
||||
|
||||
signal(SIGCHLD, sig_handler);
|
||||
signal(SIGINT, sig_handler);
|
||||
|
||||
if (forks) {
|
||||
err = perf_evlist__prepare_workload(evlist, &trace->opts, argv);
|
||||
if (err < 0) {
|
||||
printf("Couldn't run the workload!\n");
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
}
|
||||
|
||||
err = perf_evlist__open(evlist);
|
||||
if (err < 0) {
|
||||
printf("Couldn't create the events: %s\n", strerror(errno));
|
||||
@ -162,6 +246,10 @@ static int trace__run(struct trace *trace)
|
||||
}
|
||||
|
||||
perf_evlist__enable(evlist);
|
||||
|
||||
if (forks)
|
||||
perf_evlist__start_workload(evlist);
|
||||
|
||||
again:
|
||||
before = nr_events;
|
||||
|
||||
@ -170,9 +258,8 @@ again:
|
||||
|
||||
while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) {
|
||||
const u32 type = event->header.type;
|
||||
struct syscall *sc;
|
||||
tracepoint_handler handler;
|
||||
struct perf_sample sample;
|
||||
int id;
|
||||
|
||||
++nr_events;
|
||||
|
||||
@ -200,50 +287,23 @@ again:
|
||||
continue;
|
||||
}
|
||||
|
||||
id = perf_evsel__intval(evsel, &sample, "id");
|
||||
if (id < 0) {
|
||||
printf("Invalid syscall %d id, skipping...\n", id);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((id > trace->syscalls.max || trace->syscalls.table[id].name == NULL) &&
|
||||
trace__read_syscall_info(trace, id))
|
||||
continue;
|
||||
|
||||
if ((id > trace->syscalls.max || trace->syscalls.table[id].name == NULL))
|
||||
continue;
|
||||
|
||||
sc = &trace->syscalls.table[id];
|
||||
|
||||
if (evlist->threads->map[0] == -1 || evlist->threads->nr > 1)
|
||||
printf("%d ", sample.tid);
|
||||
|
||||
if (evsel == evsel_enter) {
|
||||
void *args = perf_evsel__rawptr(evsel, &sample, "args");
|
||||
|
||||
printf("%s(", sc->name);
|
||||
syscall__fprintf_args(sc, args, stdout);
|
||||
} else if (evsel == evsel_exit) {
|
||||
int ret = perf_evsel__intval(evsel, &sample, "ret");
|
||||
|
||||
if (ret < 0 && sc->fmt && sc->fmt->errmsg) {
|
||||
char bf[256];
|
||||
const char *emsg = strerror_r(-ret, bf, sizeof(bf)),
|
||||
*e = audit_errno_to_name(-ret);
|
||||
|
||||
printf(") = -1 %s %s", e, emsg);
|
||||
} else if (ret == 0 && sc->fmt && sc->fmt->timeout)
|
||||
printf(") = 0 Timeout");
|
||||
else
|
||||
printf(") = %d", ret);
|
||||
|
||||
putchar('\n');
|
||||
}
|
||||
handler = evsel->handler.func;
|
||||
handler(trace, evsel, &sample);
|
||||
}
|
||||
}
|
||||
|
||||
if (nr_events == before)
|
||||
if (nr_events == before) {
|
||||
if (done)
|
||||
goto out_delete_evlist;
|
||||
|
||||
poll(evlist->pollfd, evlist->nr_fds, -1);
|
||||
}
|
||||
|
||||
if (done)
|
||||
perf_evlist__disable(evlist);
|
||||
|
||||
goto again;
|
||||
|
||||
@ -256,7 +316,8 @@ out:
|
||||
int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
{
|
||||
const char * const trace_usage[] = {
|
||||
"perf trace [<options>]",
|
||||
"perf trace [<options>] [<command>]",
|
||||
"perf trace [<options>] -- <command> [<options>]",
|
||||
NULL
|
||||
};
|
||||
struct trace trace = {
|
||||
@ -293,18 +354,26 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
OPT_END()
|
||||
};
|
||||
int err;
|
||||
char bf[BUFSIZ];
|
||||
|
||||
argc = parse_options(argc, argv, trace_options, trace_usage, 0);
|
||||
if (argc)
|
||||
usage_with_options(trace_usage, trace_options);
|
||||
|
||||
err = perf_target__parse_uid(&trace.opts.target);
|
||||
err = perf_target__validate(&trace.opts.target);
|
||||
if (err) {
|
||||
char bf[BUFSIZ];
|
||||
perf_target__strerror(&trace.opts.target, err, bf, sizeof(bf));
|
||||
printf("%s", bf);
|
||||
return err;
|
||||
}
|
||||
|
||||
return trace__run(&trace);
|
||||
err = perf_target__parse_uid(&trace.opts.target);
|
||||
if (err) {
|
||||
perf_target__strerror(&trace.opts.target, err, bf, sizeof(bf));
|
||||
printf("%s", bf);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (!argc && perf_target__none(&trace.opts.target))
|
||||
trace.opts.target.system_wide = true;
|
||||
|
||||
return trace__run(&trace, argc, argv);
|
||||
}
|
||||
|
@ -43,6 +43,15 @@ int main(void)
|
||||
}
|
||||
endef
|
||||
|
||||
define SOURCE_BIONIC
|
||||
#include <android/api-level.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
return __ANDROID_API__;
|
||||
}
|
||||
endef
|
||||
|
||||
define SOURCE_ELF_MMAP
|
||||
#include <libelf.h>
|
||||
int main(void)
|
||||
@ -203,4 +212,13 @@ int main(void)
|
||||
return audit_open();
|
||||
}
|
||||
endef
|
||||
endif
|
||||
endif
|
||||
|
||||
define SOURCE_ON_EXIT
|
||||
#include <stdio.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
return on_exit(NULL, NULL);
|
||||
}
|
||||
endef
|
||||
|
@ -48,14 +48,14 @@ static struct cmd_struct commands[] = {
|
||||
{ "version", cmd_version, 0 },
|
||||
{ "script", cmd_script, 0 },
|
||||
{ "sched", cmd_sched, 0 },
|
||||
#ifndef NO_LIBELF_SUPPORT
|
||||
#ifdef LIBELF_SUPPORT
|
||||
{ "probe", cmd_probe, 0 },
|
||||
#endif
|
||||
{ "kmem", cmd_kmem, 0 },
|
||||
{ "lock", cmd_lock, 0 },
|
||||
{ "kvm", cmd_kvm, 0 },
|
||||
{ "test", cmd_test, 0 },
|
||||
#ifndef NO_LIBAUDIT_SUPPORT
|
||||
#ifdef LIBAUDIT_SUPPORT
|
||||
{ "trace", cmd_trace, 0 },
|
||||
#endif
|
||||
{ "inject", cmd_inject, 0 },
|
||||
@ -440,6 +440,8 @@ int main(int argc, const char **argv)
|
||||
{
|
||||
const char *cmd;
|
||||
|
||||
page_size = sysconf(_SC_PAGE_SIZE);
|
||||
|
||||
cmd = perf_extract_argv0_path(argv[0]);
|
||||
if (!cmd)
|
||||
cmd = "perf-help";
|
||||
|
@ -569,7 +569,8 @@ static int hist_browser__show_callchain(struct hist_browser *browser,
|
||||
static int hist_browser__hpp_color_ ## _name(struct perf_hpp *hpp, \
|
||||
struct hist_entry *he) \
|
||||
{ \
|
||||
double percent = 100.0 * he->_field / hpp->total_period; \
|
||||
struct hists *hists = he->hists; \
|
||||
double percent = 100.0 * he->stat._field / hists->stats.total_period; \
|
||||
*(double *)hpp->ptr = percent; \
|
||||
return scnprintf(hpp->buf, hpp->size, "%6.2f%%", percent); \
|
||||
}
|
||||
@ -584,7 +585,7 @@ HPP__COLOR_FN(overhead_guest_us, period_guest_us)
|
||||
|
||||
void hist_browser__init_hpp(void)
|
||||
{
|
||||
perf_hpp__init(false, false);
|
||||
perf_hpp__init();
|
||||
|
||||
perf_hpp__format[PERF_HPP__OVERHEAD].color =
|
||||
hist_browser__hpp_color_overhead;
|
||||
@ -624,7 +625,6 @@ static int hist_browser__show_entry(struct hist_browser *browser,
|
||||
struct perf_hpp hpp = {
|
||||
.buf = s,
|
||||
.size = sizeof(s),
|
||||
.total_period = browser->hists->stats.total_period,
|
||||
};
|
||||
|
||||
ui_browser__gotorc(&browser->b, row, 0);
|
||||
@ -982,7 +982,7 @@ static int hist_browser__fprintf_entry(struct hist_browser *browser,
|
||||
folded_sign = hist_entry__folded(he);
|
||||
|
||||
hist_entry__sort_snprintf(he, s, sizeof(s), browser->hists);
|
||||
percent = (he->period * 100.0) / browser->hists->stats.total_period;
|
||||
percent = (he->stat.period * 100.0) / browser->hists->stats.total_period;
|
||||
|
||||
if (symbol_conf.use_callchain)
|
||||
printed += fprintf(fp, "%c ", folded_sign);
|
||||
@ -990,10 +990,10 @@ static int hist_browser__fprintf_entry(struct hist_browser *browser,
|
||||
printed += fprintf(fp, " %5.2f%%", percent);
|
||||
|
||||
if (symbol_conf.show_nr_samples)
|
||||
printed += fprintf(fp, " %11u", he->nr_events);
|
||||
printed += fprintf(fp, " %11u", he->stat.nr_events);
|
||||
|
||||
if (symbol_conf.show_total_period)
|
||||
printed += fprintf(fp, " %12" PRIu64, he->period);
|
||||
printed += fprintf(fp, " %12" PRIu64, he->stat.period);
|
||||
|
||||
printed += fprintf(fp, "%s\n", rtrim(s));
|
||||
|
||||
|
@ -49,7 +49,8 @@ static const char *perf_gtk__get_percent_color(double percent)
|
||||
static int perf_gtk__hpp_color_ ## _name(struct perf_hpp *hpp, \
|
||||
struct hist_entry *he) \
|
||||
{ \
|
||||
double percent = 100.0 * he->_field / hpp->total_period; \
|
||||
struct hists *hists = he->hists; \
|
||||
double percent = 100.0 * he->stat._field / hists->stats.total_period; \
|
||||
const char *markup; \
|
||||
int ret = 0; \
|
||||
\
|
||||
@ -73,7 +74,7 @@ HPP__COLOR_FN(overhead_guest_us, period_guest_us)
|
||||
|
||||
void perf_gtk__init_hpp(void)
|
||||
{
|
||||
perf_hpp__init(false, false);
|
||||
perf_hpp__init();
|
||||
|
||||
perf_hpp__format[PERF_HPP__OVERHEAD].color =
|
||||
perf_gtk__hpp_color_overhead;
|
||||
@ -102,7 +103,6 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists)
|
||||
struct perf_hpp hpp = {
|
||||
.buf = s,
|
||||
.size = sizeof(s),
|
||||
.total_period = hists->stats.total_period,
|
||||
};
|
||||
|
||||
nr_cols = 0;
|
||||
|
@ -116,7 +116,7 @@ struct perf_error_ops perf_gtk_eops = {
|
||||
* FIXME: Functions below should be implemented properly.
|
||||
* For now, just add stubs for NO_NEWT=1 build.
|
||||
*/
|
||||
#ifdef NO_NEWT_SUPPORT
|
||||
#ifndef NEWT_SUPPORT
|
||||
void ui_progress__update(u64 curr __maybe_unused, u64 total __maybe_unused,
|
||||
const char *title __maybe_unused)
|
||||
{
|
||||
|
@ -23,25 +23,25 @@ void ui_helpline__puts(const char *msg);
|
||||
|
||||
extern char ui_helpline__current[512];
|
||||
|
||||
#ifdef NO_NEWT_SUPPORT
|
||||
#ifdef NEWT_SUPPORT
|
||||
extern char ui_helpline__last_msg[];
|
||||
int ui_helpline__show_help(const char *format, va_list ap);
|
||||
#else
|
||||
static inline int ui_helpline__show_help(const char *format __maybe_unused,
|
||||
va_list ap __maybe_unused)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
extern char ui_helpline__last_msg[];
|
||||
int ui_helpline__show_help(const char *format, va_list ap);
|
||||
#endif /* NO_NEWT_SUPPORT */
|
||||
#endif /* NEWT_SUPPORT */
|
||||
|
||||
#ifdef NO_GTK2_SUPPORT
|
||||
#ifdef GTK2_SUPPORT
|
||||
int perf_gtk__show_helpline(const char *format, va_list ap);
|
||||
#else
|
||||
static inline int perf_gtk__show_helpline(const char *format __maybe_unused,
|
||||
va_list ap __maybe_unused)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
int perf_gtk__show_helpline(const char *format, va_list ap);
|
||||
#endif /* NO_GTK2_SUPPORT */
|
||||
#endif /* GTK2_SUPPORT */
|
||||
|
||||
#endif /* _PERF_UI_HELPLINE_H_ */
|
||||
|
@ -8,9 +8,7 @@
|
||||
/* hist period print (hpp) functions */
|
||||
static int hpp__header_overhead(struct perf_hpp *hpp)
|
||||
{
|
||||
const char *fmt = hpp->ptr ? "Baseline" : "Overhead";
|
||||
|
||||
return scnprintf(hpp->buf, hpp->size, fmt);
|
||||
return scnprintf(hpp->buf, hpp->size, "Overhead");
|
||||
}
|
||||
|
||||
static int hpp__width_overhead(struct perf_hpp *hpp __maybe_unused)
|
||||
@ -20,38 +18,18 @@ static int hpp__width_overhead(struct perf_hpp *hpp __maybe_unused)
|
||||
|
||||
static int hpp__color_overhead(struct perf_hpp *hpp, struct hist_entry *he)
|
||||
{
|
||||
double percent = 100.0 * he->period / hpp->total_period;
|
||||
|
||||
if (hpp->ptr) {
|
||||
struct hists *old_hists = hpp->ptr;
|
||||
u64 total_period = old_hists->stats.total_period;
|
||||
u64 base_period = he->pair ? he->pair->period : 0;
|
||||
|
||||
if (total_period)
|
||||
percent = 100.0 * base_period / total_period;
|
||||
else
|
||||
percent = 0.0;
|
||||
}
|
||||
struct hists *hists = he->hists;
|
||||
double percent = 100.0 * he->stat.period / hists->stats.total_period;
|
||||
|
||||
return percent_color_snprintf(hpp->buf, hpp->size, " %6.2f%%", percent);
|
||||
}
|
||||
|
||||
static int hpp__entry_overhead(struct perf_hpp *hpp, struct hist_entry *he)
|
||||
{
|
||||
double percent = 100.0 * he->period / hpp->total_period;
|
||||
struct hists *hists = he->hists;
|
||||
double percent = 100.0 * he->stat.period / hists->stats.total_period;
|
||||
const char *fmt = symbol_conf.field_sep ? "%.2f" : " %6.2f%%";
|
||||
|
||||
if (hpp->ptr) {
|
||||
struct hists *old_hists = hpp->ptr;
|
||||
u64 total_period = old_hists->stats.total_period;
|
||||
u64 base_period = he->pair ? he->pair->period : 0;
|
||||
|
||||
if (total_period)
|
||||
percent = 100.0 * base_period / total_period;
|
||||
else
|
||||
percent = 0.0;
|
||||
}
|
||||
|
||||
return scnprintf(hpp->buf, hpp->size, fmt, percent);
|
||||
}
|
||||
|
||||
@ -69,13 +47,16 @@ static int hpp__width_overhead_sys(struct perf_hpp *hpp __maybe_unused)
|
||||
|
||||
static int hpp__color_overhead_sys(struct perf_hpp *hpp, struct hist_entry *he)
|
||||
{
|
||||
double percent = 100.0 * he->period_sys / hpp->total_period;
|
||||
struct hists *hists = he->hists;
|
||||
double percent = 100.0 * he->stat.period_sys / hists->stats.total_period;
|
||||
|
||||
return percent_color_snprintf(hpp->buf, hpp->size, "%6.2f%%", percent);
|
||||
}
|
||||
|
||||
static int hpp__entry_overhead_sys(struct perf_hpp *hpp, struct hist_entry *he)
|
||||
{
|
||||
double percent = 100.0 * he->period_sys / hpp->total_period;
|
||||
struct hists *hists = he->hists;
|
||||
double percent = 100.0 * he->stat.period_sys / hists->stats.total_period;
|
||||
const char *fmt = symbol_conf.field_sep ? "%.2f" : "%6.2f%%";
|
||||
|
||||
return scnprintf(hpp->buf, hpp->size, fmt, percent);
|
||||
@ -95,13 +76,16 @@ static int hpp__width_overhead_us(struct perf_hpp *hpp __maybe_unused)
|
||||
|
||||
static int hpp__color_overhead_us(struct perf_hpp *hpp, struct hist_entry *he)
|
||||
{
|
||||
double percent = 100.0 * he->period_us / hpp->total_period;
|
||||
struct hists *hists = he->hists;
|
||||
double percent = 100.0 * he->stat.period_us / hists->stats.total_period;
|
||||
|
||||
return percent_color_snprintf(hpp->buf, hpp->size, "%6.2f%%", percent);
|
||||
}
|
||||
|
||||
static int hpp__entry_overhead_us(struct perf_hpp *hpp, struct hist_entry *he)
|
||||
{
|
||||
double percent = 100.0 * he->period_us / hpp->total_period;
|
||||
struct hists *hists = he->hists;
|
||||
double percent = 100.0 * he->stat.period_us / hists->stats.total_period;
|
||||
const char *fmt = symbol_conf.field_sep ? "%.2f" : "%6.2f%%";
|
||||
|
||||
return scnprintf(hpp->buf, hpp->size, fmt, percent);
|
||||
@ -120,14 +104,17 @@ static int hpp__width_overhead_guest_sys(struct perf_hpp *hpp __maybe_unused)
|
||||
static int hpp__color_overhead_guest_sys(struct perf_hpp *hpp,
|
||||
struct hist_entry *he)
|
||||
{
|
||||
double percent = 100.0 * he->period_guest_sys / hpp->total_period;
|
||||
struct hists *hists = he->hists;
|
||||
double percent = 100.0 * he->stat.period_guest_sys / hists->stats.total_period;
|
||||
|
||||
return percent_color_snprintf(hpp->buf, hpp->size, " %6.2f%% ", percent);
|
||||
}
|
||||
|
||||
static int hpp__entry_overhead_guest_sys(struct perf_hpp *hpp,
|
||||
struct hist_entry *he)
|
||||
{
|
||||
double percent = 100.0 * he->period_guest_sys / hpp->total_period;
|
||||
struct hists *hists = he->hists;
|
||||
double percent = 100.0 * he->stat.period_guest_sys / hists->stats.total_period;
|
||||
const char *fmt = symbol_conf.field_sep ? "%.2f" : " %6.2f%% ";
|
||||
|
||||
return scnprintf(hpp->buf, hpp->size, fmt, percent);
|
||||
@ -146,19 +133,69 @@ static int hpp__width_overhead_guest_us(struct perf_hpp *hpp __maybe_unused)
|
||||
static int hpp__color_overhead_guest_us(struct perf_hpp *hpp,
|
||||
struct hist_entry *he)
|
||||
{
|
||||
double percent = 100.0 * he->period_guest_us / hpp->total_period;
|
||||
struct hists *hists = he->hists;
|
||||
double percent = 100.0 * he->stat.period_guest_us / hists->stats.total_period;
|
||||
|
||||
return percent_color_snprintf(hpp->buf, hpp->size, " %6.2f%% ", percent);
|
||||
}
|
||||
|
||||
static int hpp__entry_overhead_guest_us(struct perf_hpp *hpp,
|
||||
struct hist_entry *he)
|
||||
{
|
||||
double percent = 100.0 * he->period_guest_us / hpp->total_period;
|
||||
struct hists *hists = he->hists;
|
||||
double percent = 100.0 * he->stat.period_guest_us / hists->stats.total_period;
|
||||
const char *fmt = symbol_conf.field_sep ? "%.2f" : " %6.2f%% ";
|
||||
|
||||
return scnprintf(hpp->buf, hpp->size, fmt, percent);
|
||||
}
|
||||
|
||||
static int hpp__header_baseline(struct perf_hpp *hpp)
|
||||
{
|
||||
return scnprintf(hpp->buf, hpp->size, "Baseline");
|
||||
}
|
||||
|
||||
static int hpp__width_baseline(struct perf_hpp *hpp __maybe_unused)
|
||||
{
|
||||
return 8;
|
||||
}
|
||||
|
||||
static double baseline_percent(struct hist_entry *he)
|
||||
{
|
||||
struct hist_entry *pair = he->pair;
|
||||
struct hists *pair_hists = pair ? pair->hists : NULL;
|
||||
double percent = 0.0;
|
||||
|
||||
if (pair) {
|
||||
u64 total_period = pair_hists->stats.total_period;
|
||||
u64 base_period = pair->stat.period;
|
||||
|
||||
percent = 100.0 * base_period / total_period;
|
||||
}
|
||||
|
||||
return percent;
|
||||
}
|
||||
|
||||
static int hpp__color_baseline(struct perf_hpp *hpp, struct hist_entry *he)
|
||||
{
|
||||
double percent = baseline_percent(he);
|
||||
|
||||
if (he->pair)
|
||||
return percent_color_snprintf(hpp->buf, hpp->size, " %6.2f%%", percent);
|
||||
else
|
||||
return scnprintf(hpp->buf, hpp->size, " ");
|
||||
}
|
||||
|
||||
static int hpp__entry_baseline(struct perf_hpp *hpp, struct hist_entry *he)
|
||||
{
|
||||
double percent = baseline_percent(he);
|
||||
const char *fmt = symbol_conf.field_sep ? "%.2f" : " %6.2f%%";
|
||||
|
||||
if (he->pair || symbol_conf.field_sep)
|
||||
return scnprintf(hpp->buf, hpp->size, fmt, percent);
|
||||
else
|
||||
return scnprintf(hpp->buf, hpp->size, " ");
|
||||
}
|
||||
|
||||
static int hpp__header_samples(struct perf_hpp *hpp)
|
||||
{
|
||||
const char *fmt = symbol_conf.field_sep ? "%s" : "%11s";
|
||||
@ -175,7 +212,7 @@ static int hpp__entry_samples(struct perf_hpp *hpp, struct hist_entry *he)
|
||||
{
|
||||
const char *fmt = symbol_conf.field_sep ? "%" PRIu64 : "%11" PRIu64;
|
||||
|
||||
return scnprintf(hpp->buf, hpp->size, fmt, he->nr_events);
|
||||
return scnprintf(hpp->buf, hpp->size, fmt, he->stat.nr_events);
|
||||
}
|
||||
|
||||
static int hpp__header_period(struct perf_hpp *hpp)
|
||||
@ -194,9 +231,29 @@ static int hpp__entry_period(struct perf_hpp *hpp, struct hist_entry *he)
|
||||
{
|
||||
const char *fmt = symbol_conf.field_sep ? "%" PRIu64 : "%12" PRIu64;
|
||||
|
||||
return scnprintf(hpp->buf, hpp->size, fmt, he->period);
|
||||
return scnprintf(hpp->buf, hpp->size, fmt, he->stat.period);
|
||||
}
|
||||
|
||||
static int hpp__header_period_baseline(struct perf_hpp *hpp)
|
||||
{
|
||||
const char *fmt = symbol_conf.field_sep ? "%s" : "%12s";
|
||||
|
||||
return scnprintf(hpp->buf, hpp->size, fmt, "Period Base");
|
||||
}
|
||||
|
||||
static int hpp__width_period_baseline(struct perf_hpp *hpp __maybe_unused)
|
||||
{
|
||||
return 12;
|
||||
}
|
||||
|
||||
static int hpp__entry_period_baseline(struct perf_hpp *hpp, struct hist_entry *he)
|
||||
{
|
||||
struct hist_entry *pair = he->pair;
|
||||
u64 period = pair ? pair->stat.period : 0;
|
||||
const char *fmt = symbol_conf.field_sep ? "%" PRIu64 : "%12" PRIu64;
|
||||
|
||||
return scnprintf(hpp->buf, hpp->size, fmt, period);
|
||||
}
|
||||
static int hpp__header_delta(struct perf_hpp *hpp)
|
||||
{
|
||||
const char *fmt = symbol_conf.field_sep ? "%s" : "%7s";
|
||||
@ -211,28 +268,79 @@ static int hpp__width_delta(struct perf_hpp *hpp __maybe_unused)
|
||||
|
||||
static int hpp__entry_delta(struct perf_hpp *hpp, struct hist_entry *he)
|
||||
{
|
||||
struct hists *pair_hists = hpp->ptr;
|
||||
u64 old_total, new_total;
|
||||
double old_percent = 0, new_percent = 0;
|
||||
double diff;
|
||||
const char *fmt = symbol_conf.field_sep ? "%s" : "%7.7s";
|
||||
char buf[32] = " ";
|
||||
double diff;
|
||||
|
||||
old_total = pair_hists->stats.total_period;
|
||||
if (old_total > 0 && he->pair)
|
||||
old_percent = 100.0 * he->pair->period / old_total;
|
||||
if (he->diff.computed)
|
||||
diff = he->diff.period_ratio_delta;
|
||||
else
|
||||
diff = perf_diff__compute_delta(he);
|
||||
|
||||
new_total = hpp->total_period;
|
||||
if (new_total > 0)
|
||||
new_percent = 100.0 * he->period / new_total;
|
||||
|
||||
diff = new_percent - old_percent;
|
||||
if (fabs(diff) >= 0.01)
|
||||
scnprintf(buf, sizeof(buf), "%+4.2F%%", diff);
|
||||
|
||||
return scnprintf(hpp->buf, hpp->size, fmt, buf);
|
||||
}
|
||||
|
||||
static int hpp__header_ratio(struct perf_hpp *hpp)
|
||||
{
|
||||
const char *fmt = symbol_conf.field_sep ? "%s" : "%14s";
|
||||
|
||||
return scnprintf(hpp->buf, hpp->size, fmt, "Ratio");
|
||||
}
|
||||
|
||||
static int hpp__width_ratio(struct perf_hpp *hpp __maybe_unused)
|
||||
{
|
||||
return 14;
|
||||
}
|
||||
|
||||
static int hpp__entry_ratio(struct perf_hpp *hpp, struct hist_entry *he)
|
||||
{
|
||||
const char *fmt = symbol_conf.field_sep ? "%s" : "%14s";
|
||||
char buf[32] = " ";
|
||||
double ratio;
|
||||
|
||||
if (he->diff.computed)
|
||||
ratio = he->diff.period_ratio;
|
||||
else
|
||||
ratio = perf_diff__compute_ratio(he);
|
||||
|
||||
if (ratio > 0.0)
|
||||
scnprintf(buf, sizeof(buf), "%+14.6F", ratio);
|
||||
|
||||
return scnprintf(hpp->buf, hpp->size, fmt, buf);
|
||||
}
|
||||
|
||||
static int hpp__header_wdiff(struct perf_hpp *hpp)
|
||||
{
|
||||
const char *fmt = symbol_conf.field_sep ? "%s" : "%14s";
|
||||
|
||||
return scnprintf(hpp->buf, hpp->size, fmt, "Weighted diff");
|
||||
}
|
||||
|
||||
static int hpp__width_wdiff(struct perf_hpp *hpp __maybe_unused)
|
||||
{
|
||||
return 14;
|
||||
}
|
||||
|
||||
static int hpp__entry_wdiff(struct perf_hpp *hpp, struct hist_entry *he)
|
||||
{
|
||||
const char *fmt = symbol_conf.field_sep ? "%s" : "%14s";
|
||||
char buf[32] = " ";
|
||||
s64 wdiff;
|
||||
|
||||
if (he->diff.computed)
|
||||
wdiff = he->diff.wdiff;
|
||||
else
|
||||
wdiff = perf_diff__compute_wdiff(he);
|
||||
|
||||
if (wdiff != 0)
|
||||
scnprintf(buf, sizeof(buf), "%14ld", wdiff);
|
||||
|
||||
return scnprintf(hpp->buf, hpp->size, fmt, buf);
|
||||
}
|
||||
|
||||
static int hpp__header_displ(struct perf_hpp *hpp)
|
||||
{
|
||||
return scnprintf(hpp->buf, hpp->size, "Displ.");
|
||||
@ -244,17 +352,40 @@ static int hpp__width_displ(struct perf_hpp *hpp __maybe_unused)
|
||||
}
|
||||
|
||||
static int hpp__entry_displ(struct perf_hpp *hpp,
|
||||
struct hist_entry *he __maybe_unused)
|
||||
struct hist_entry *he)
|
||||
{
|
||||
struct hist_entry *pair = he->pair;
|
||||
long displacement = pair ? pair->position - he->position : 0;
|
||||
const char *fmt = symbol_conf.field_sep ? "%s" : "%6.6s";
|
||||
char buf[32] = " ";
|
||||
|
||||
if (hpp->displacement)
|
||||
scnprintf(buf, sizeof(buf), "%+4ld", hpp->displacement);
|
||||
if (displacement)
|
||||
scnprintf(buf, sizeof(buf), "%+4ld", displacement);
|
||||
|
||||
return scnprintf(hpp->buf, hpp->size, fmt, buf);
|
||||
}
|
||||
|
||||
static int hpp__header_formula(struct perf_hpp *hpp)
|
||||
{
|
||||
const char *fmt = symbol_conf.field_sep ? "%s" : "%70s";
|
||||
|
||||
return scnprintf(hpp->buf, hpp->size, fmt, "Formula");
|
||||
}
|
||||
|
||||
static int hpp__width_formula(struct perf_hpp *hpp __maybe_unused)
|
||||
{
|
||||
return 70;
|
||||
}
|
||||
|
||||
static int hpp__entry_formula(struct perf_hpp *hpp, struct hist_entry *he)
|
||||
{
|
||||
const char *fmt = symbol_conf.field_sep ? "%s" : "%-70s";
|
||||
char buf[96] = " ";
|
||||
|
||||
perf_diff__formula(buf, sizeof(buf), he);
|
||||
return scnprintf(hpp->buf, hpp->size, fmt, buf);
|
||||
}
|
||||
|
||||
#define HPP__COLOR_PRINT_FNS(_name) \
|
||||
.header = hpp__header_ ## _name, \
|
||||
.width = hpp__width_ ## _name, \
|
||||
@ -267,6 +398,7 @@ static int hpp__entry_displ(struct perf_hpp *hpp,
|
||||
.entry = hpp__entry_ ## _name
|
||||
|
||||
struct perf_hpp_fmt perf_hpp__format[] = {
|
||||
{ .cond = false, HPP__COLOR_PRINT_FNS(baseline) },
|
||||
{ .cond = true, HPP__COLOR_PRINT_FNS(overhead) },
|
||||
{ .cond = false, HPP__COLOR_PRINT_FNS(overhead_sys) },
|
||||
{ .cond = false, HPP__COLOR_PRINT_FNS(overhead_us) },
|
||||
@ -274,14 +406,18 @@ struct perf_hpp_fmt perf_hpp__format[] = {
|
||||
{ .cond = false, HPP__COLOR_PRINT_FNS(overhead_guest_us) },
|
||||
{ .cond = false, HPP__PRINT_FNS(samples) },
|
||||
{ .cond = false, HPP__PRINT_FNS(period) },
|
||||
{ .cond = false, HPP__PRINT_FNS(period_baseline) },
|
||||
{ .cond = false, HPP__PRINT_FNS(delta) },
|
||||
{ .cond = false, HPP__PRINT_FNS(displ) }
|
||||
{ .cond = false, HPP__PRINT_FNS(ratio) },
|
||||
{ .cond = false, HPP__PRINT_FNS(wdiff) },
|
||||
{ .cond = false, HPP__PRINT_FNS(displ) },
|
||||
{ .cond = false, HPP__PRINT_FNS(formula) }
|
||||
};
|
||||
|
||||
#undef HPP__COLOR_PRINT_FNS
|
||||
#undef HPP__PRINT_FNS
|
||||
|
||||
void perf_hpp__init(bool need_pair, bool show_displacement)
|
||||
void perf_hpp__init(void)
|
||||
{
|
||||
if (symbol_conf.show_cpu_utilization) {
|
||||
perf_hpp__format[PERF_HPP__OVERHEAD_SYS].cond = true;
|
||||
@ -298,13 +434,12 @@ void perf_hpp__init(bool need_pair, bool show_displacement)
|
||||
|
||||
if (symbol_conf.show_total_period)
|
||||
perf_hpp__format[PERF_HPP__PERIOD].cond = true;
|
||||
}
|
||||
|
||||
if (need_pair) {
|
||||
perf_hpp__format[PERF_HPP__DELTA].cond = true;
|
||||
|
||||
if (show_displacement)
|
||||
perf_hpp__format[PERF_HPP__DISPL].cond = true;
|
||||
}
|
||||
void perf_hpp__column_enable(unsigned col, bool enable)
|
||||
{
|
||||
BUG_ON(col >= PERF_HPP__MAX_INDEX);
|
||||
perf_hpp__format[col].cond = enable;
|
||||
}
|
||||
|
||||
static inline void advance_hpp(struct perf_hpp *hpp, int inc)
|
||||
@ -319,6 +454,7 @@ int hist_entry__period_snprintf(struct perf_hpp *hpp, struct hist_entry *he,
|
||||
const char *sep = symbol_conf.field_sep;
|
||||
char *start = hpp->buf;
|
||||
int i, ret;
|
||||
bool first = true;
|
||||
|
||||
if (symbol_conf.exclude_other && !he->parent)
|
||||
return 0;
|
||||
@ -327,9 +463,10 @@ int hist_entry__period_snprintf(struct perf_hpp *hpp, struct hist_entry *he,
|
||||
if (!perf_hpp__format[i].cond)
|
||||
continue;
|
||||
|
||||
if (!sep || i > 0) {
|
||||
if (!sep || !first) {
|
||||
ret = scnprintf(hpp->buf, hpp->size, "%s", sep ?: " ");
|
||||
advance_hpp(hpp, ret);
|
||||
first = false;
|
||||
}
|
||||
|
||||
if (color && perf_hpp__format[i].color)
|
||||
|
@ -30,7 +30,7 @@ void setup_browser(bool fallback_to_pager)
|
||||
if (fallback_to_pager)
|
||||
setup_pager();
|
||||
|
||||
perf_hpp__init(false, false);
|
||||
perf_hpp__init();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -271,7 +271,7 @@ static size_t hist_entry_callchain__fprintf(struct hist_entry *he,
|
||||
{
|
||||
switch (callchain_param.mode) {
|
||||
case CHAIN_GRAPH_REL:
|
||||
return callchain__fprintf_graph(fp, &he->sorted_chain, he->period,
|
||||
return callchain__fprintf_graph(fp, &he->sorted_chain, he->stat.period,
|
||||
left_margin);
|
||||
break;
|
||||
case CHAIN_GRAPH_ABS:
|
||||
@ -292,9 +292,10 @@ static size_t hist_entry_callchain__fprintf(struct hist_entry *he,
|
||||
|
||||
static size_t hist_entry__callchain_fprintf(struct hist_entry *he,
|
||||
struct hists *hists,
|
||||
u64 total_period, FILE *fp)
|
||||
FILE *fp)
|
||||
{
|
||||
int left_margin = 0;
|
||||
u64 total_period = hists->stats.total_period;
|
||||
|
||||
if (sort__first_dimension == SORT_COMM) {
|
||||
struct sort_entry *se = list_first_entry(&hist_entry__sort_list,
|
||||
@ -307,17 +308,13 @@ static size_t hist_entry__callchain_fprintf(struct hist_entry *he,
|
||||
}
|
||||
|
||||
static int hist_entry__fprintf(struct hist_entry *he, size_t size,
|
||||
struct hists *hists, struct hists *pair_hists,
|
||||
long displacement, u64 total_period, FILE *fp)
|
||||
struct hists *hists, FILE *fp)
|
||||
{
|
||||
char bf[512];
|
||||
int ret;
|
||||
struct perf_hpp hpp = {
|
||||
.buf = bf,
|
||||
.size = size,
|
||||
.total_period = total_period,
|
||||
.displacement = displacement,
|
||||
.ptr = pair_hists,
|
||||
};
|
||||
bool color = !symbol_conf.field_sep;
|
||||
|
||||
@ -330,32 +327,27 @@ static int hist_entry__fprintf(struct hist_entry *he, size_t size,
|
||||
ret = fprintf(fp, "%s\n", bf);
|
||||
|
||||
if (symbol_conf.use_callchain)
|
||||
ret += hist_entry__callchain_fprintf(he, hists,
|
||||
total_period, fp);
|
||||
ret += hist_entry__callchain_fprintf(he, hists, fp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t hists__fprintf(struct hists *hists, struct hists *pair,
|
||||
bool show_displacement, bool show_header, int max_rows,
|
||||
size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
|
||||
int max_cols, FILE *fp)
|
||||
{
|
||||
struct sort_entry *se;
|
||||
struct rb_node *nd;
|
||||
size_t ret = 0;
|
||||
u64 total_period;
|
||||
unsigned long position = 1;
|
||||
long displacement = 0;
|
||||
unsigned int width;
|
||||
const char *sep = symbol_conf.field_sep;
|
||||
const char *col_width = symbol_conf.col_width_list_str;
|
||||
int idx, nr_rows = 0;
|
||||
char bf[64];
|
||||
char bf[96];
|
||||
struct perf_hpp dummy_hpp = {
|
||||
.buf = bf,
|
||||
.size = sizeof(bf),
|
||||
.ptr = pair,
|
||||
};
|
||||
bool first = true;
|
||||
|
||||
init_rem_hits();
|
||||
|
||||
@ -367,8 +359,10 @@ size_t hists__fprintf(struct hists *hists, struct hists *pair,
|
||||
if (!perf_hpp__format[idx].cond)
|
||||
continue;
|
||||
|
||||
if (idx)
|
||||
if (!first)
|
||||
fprintf(fp, "%s", sep ?: " ");
|
||||
else
|
||||
first = false;
|
||||
|
||||
perf_hpp__format[idx].header(&dummy_hpp);
|
||||
fprintf(fp, "%s", bf);
|
||||
@ -403,6 +397,8 @@ size_t hists__fprintf(struct hists *hists, struct hists *pair,
|
||||
if (sep)
|
||||
goto print_entries;
|
||||
|
||||
first = true;
|
||||
|
||||
fprintf(fp, "# ");
|
||||
for (idx = 0; idx < PERF_HPP__MAX_INDEX; idx++) {
|
||||
unsigned int i;
|
||||
@ -410,8 +406,10 @@ size_t hists__fprintf(struct hists *hists, struct hists *pair,
|
||||
if (!perf_hpp__format[idx].cond)
|
||||
continue;
|
||||
|
||||
if (idx)
|
||||
if (!first)
|
||||
fprintf(fp, "%s", sep ?: " ");
|
||||
else
|
||||
first = false;
|
||||
|
||||
width = perf_hpp__format[idx].width(&dummy_hpp);
|
||||
for (i = 0; i < width; i++)
|
||||
@ -441,24 +439,13 @@ size_t hists__fprintf(struct hists *hists, struct hists *pair,
|
||||
goto out;
|
||||
|
||||
print_entries:
|
||||
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);
|
||||
|
||||
if (h->filtered)
|
||||
continue;
|
||||
|
||||
if (show_displacement) {
|
||||
if (h->pair != NULL)
|
||||
displacement = ((long)h->pair->position -
|
||||
(long)position);
|
||||
else
|
||||
displacement = 0;
|
||||
++position;
|
||||
}
|
||||
ret += hist_entry__fprintf(h, max_cols, hists, pair, displacement,
|
||||
total_period, fp);
|
||||
ret += hist_entry__fprintf(h, max_cols, hists, fp);
|
||||
|
||||
if (max_rows && ++nr_rows >= max_rows)
|
||||
goto out;
|
||||
|
@ -138,7 +138,10 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx,
|
||||
bool print_lines, bool full_paths, int min_pcnt,
|
||||
int max_lines);
|
||||
|
||||
#ifdef NO_NEWT_SUPPORT
|
||||
#ifdef NEWT_SUPPORT
|
||||
int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
|
||||
void(*timer)(void *arg), void *arg, int delay_secs);
|
||||
#else
|
||||
static inline int symbol__tui_annotate(struct symbol *sym __maybe_unused,
|
||||
struct map *map __maybe_unused,
|
||||
int evidx __maybe_unused,
|
||||
@ -148,9 +151,6 @@ static inline int symbol__tui_annotate(struct symbol *sym __maybe_unused,
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
|
||||
void(*timer)(void *arg), void *arg, int delay_secs);
|
||||
#endif
|
||||
|
||||
extern const char *disassembler_style;
|
||||
|
@ -64,7 +64,7 @@ static int perf_event__exit_del_thread(struct perf_tool *tool __maybe_unused,
|
||||
struct perf_tool build_id__mark_dso_hit_ops = {
|
||||
.sample = build_id__mark_dso_hit,
|
||||
.mmap = perf_event__process_mmap,
|
||||
.fork = perf_event__process_task,
|
||||
.fork = perf_event__process_fork,
|
||||
.exit = perf_event__exit_del_thread,
|
||||
.attr = perf_event__process_attr,
|
||||
.build_id = perf_event__process_build_id,
|
||||
|
@ -33,39 +33,41 @@ extern int pager_use_color;
|
||||
|
||||
extern int use_browser;
|
||||
|
||||
#if defined(NO_NEWT_SUPPORT) && defined(NO_GTK2_SUPPORT)
|
||||
#if defined(NEWT_SUPPORT) || defined(GTK2_SUPPORT)
|
||||
void setup_browser(bool fallback_to_pager);
|
||||
void exit_browser(bool wait_for_ok);
|
||||
|
||||
#ifdef NEWT_SUPPORT
|
||||
int ui__init(void);
|
||||
void ui__exit(bool wait_for_ok);
|
||||
#else
|
||||
static inline int ui__init(void)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
static inline void ui__exit(bool wait_for_ok __maybe_unused) {}
|
||||
#endif
|
||||
|
||||
#ifdef GTK2_SUPPORT
|
||||
int perf_gtk__init(void);
|
||||
void perf_gtk__exit(bool wait_for_ok);
|
||||
#else
|
||||
static inline int perf_gtk__init(void)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
static inline void perf_gtk__exit(bool wait_for_ok __maybe_unused) {}
|
||||
#endif
|
||||
|
||||
#else /* NEWT_SUPPORT || GTK2_SUPPORT */
|
||||
|
||||
static inline void setup_browser(bool fallback_to_pager)
|
||||
{
|
||||
if (fallback_to_pager)
|
||||
setup_pager();
|
||||
}
|
||||
static inline void exit_browser(bool wait_for_ok __maybe_unused) {}
|
||||
#else
|
||||
void setup_browser(bool fallback_to_pager);
|
||||
void exit_browser(bool wait_for_ok);
|
||||
|
||||
#ifdef NO_NEWT_SUPPORT
|
||||
static inline int ui__init(void)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
static inline void ui__exit(bool wait_for_ok __maybe_unused) {}
|
||||
#else
|
||||
int ui__init(void);
|
||||
void ui__exit(bool wait_for_ok);
|
||||
#endif
|
||||
|
||||
#ifdef NO_GTK2_SUPPORT
|
||||
static inline int perf_gtk__init(void)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
static inline void perf_gtk__exit(bool wait_for_ok __maybe_unused) {}
|
||||
#else
|
||||
int perf_gtk__init(void);
|
||||
void perf_gtk__exit(bool wait_for_ok);
|
||||
#endif
|
||||
#endif /* NO_NEWT_SUPPORT && NO_GTK2_SUPPORT */
|
||||
#endif /* NEWT_SUPPORT || GTK2_SUPPORT */
|
||||
|
||||
char *alias_lookup(const char *alias);
|
||||
int split_cmdline(char *cmdline, const char ***argv);
|
||||
@ -105,7 +107,7 @@ extern char *perf_path(const char *fmt, ...) __attribute__((format (printf, 1, 2
|
||||
extern char *perf_pathdup(const char *fmt, ...)
|
||||
__attribute__((format (printf, 1, 2)));
|
||||
|
||||
#ifdef NO_STRLCPY
|
||||
#ifndef HAVE_STRLCPY
|
||||
extern size_t strlcpy(char *dest, const char *src, size_t size);
|
||||
#endif
|
||||
|
||||
|
@ -49,7 +49,7 @@ int dump_printf(const char *fmt, ...)
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if defined(NO_NEWT_SUPPORT) && defined(NO_GTK2_SUPPORT)
|
||||
#if !defined(NEWT_SUPPORT) && !defined(GTK2_SUPPORT)
|
||||
int ui__warning(const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
@ -15,7 +15,14 @@ void trace_event(union perf_event *event);
|
||||
struct ui_progress;
|
||||
struct perf_error_ops;
|
||||
|
||||
#if defined(NO_NEWT_SUPPORT) && defined(NO_GTK2_SUPPORT)
|
||||
#if defined(NEWT_SUPPORT) || defined(GTK2_SUPPORT)
|
||||
|
||||
#include "../ui/progress.h"
|
||||
int ui__error(const char *format, ...) __attribute__((format(printf, 1, 2)));
|
||||
#include "../ui/util.h"
|
||||
|
||||
#else
|
||||
|
||||
static inline void ui_progress__update(u64 curr __maybe_unused,
|
||||
u64 total __maybe_unused,
|
||||
const char *title __maybe_unused) {}
|
||||
@ -34,13 +41,7 @@ perf_error__unregister(struct perf_error_ops *eops __maybe_unused)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else /* NO_NEWT_SUPPORT && NO_GTK2_SUPPORT */
|
||||
|
||||
#include "../ui/progress.h"
|
||||
int ui__error(const char *format, ...) __attribute__((format(printf, 1, 2)));
|
||||
#include "../ui/util.h"
|
||||
|
||||
#endif /* NO_NEWT_SUPPORT && NO_GTK2_SUPPORT */
|
||||
#endif /* NEWT_SUPPORT || GTK2_SUPPORT */
|
||||
|
||||
int ui__warning(const char *format, ...) __attribute__((format(printf, 1, 2)));
|
||||
int ui__error_paranoid(void);
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include <linux/types.h>
|
||||
#include "event.h"
|
||||
#include "debug.h"
|
||||
#include "machine.h"
|
||||
#include "sort.h"
|
||||
#include "string.h"
|
||||
#include "strlist.h"
|
||||
@ -519,134 +520,15 @@ int perf_event__process_comm(struct perf_tool *tool __maybe_unused,
|
||||
struct perf_sample *sample __maybe_unused,
|
||||
struct machine *machine)
|
||||
{
|
||||
struct thread *thread = machine__findnew_thread(machine, event->comm.tid);
|
||||
|
||||
if (dump_trace)
|
||||
perf_event__fprintf_comm(event, stdout);
|
||||
|
||||
if (thread == NULL || thread__set_comm(thread, event->comm.comm)) {
|
||||
dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return machine__process_comm_event(machine, event);
|
||||
}
|
||||
|
||||
int perf_event__process_lost(struct perf_tool *tool __maybe_unused,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample __maybe_unused,
|
||||
struct machine *machine __maybe_unused)
|
||||
struct machine *machine)
|
||||
{
|
||||
dump_printf(": id:%" PRIu64 ": lost:%" PRIu64 "\n",
|
||||
event->lost.id, event->lost.lost);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void perf_event__set_kernel_mmap_len(union perf_event *event,
|
||||
struct map **maps)
|
||||
{
|
||||
maps[MAP__FUNCTION]->start = event->mmap.start;
|
||||
maps[MAP__FUNCTION]->end = event->mmap.start + event->mmap.len;
|
||||
/*
|
||||
* Be a bit paranoid here, some perf.data file came with
|
||||
* a zero sized synthesized MMAP event for the kernel.
|
||||
*/
|
||||
if (maps[MAP__FUNCTION]->end == 0)
|
||||
maps[MAP__FUNCTION]->end = ~0ULL;
|
||||
}
|
||||
|
||||
static int perf_event__process_kernel_mmap(struct perf_tool *tool
|
||||
__maybe_unused,
|
||||
union perf_event *event,
|
||||
struct machine *machine)
|
||||
{
|
||||
struct map *map;
|
||||
char kmmap_prefix[PATH_MAX];
|
||||
enum dso_kernel_type kernel_type;
|
||||
bool is_kernel_mmap;
|
||||
|
||||
machine__mmap_name(machine, kmmap_prefix, sizeof(kmmap_prefix));
|
||||
if (machine__is_host(machine))
|
||||
kernel_type = DSO_TYPE_KERNEL;
|
||||
else
|
||||
kernel_type = DSO_TYPE_GUEST_KERNEL;
|
||||
|
||||
is_kernel_mmap = memcmp(event->mmap.filename,
|
||||
kmmap_prefix,
|
||||
strlen(kmmap_prefix) - 1) == 0;
|
||||
if (event->mmap.filename[0] == '/' ||
|
||||
(!is_kernel_mmap && event->mmap.filename[0] == '[')) {
|
||||
|
||||
char short_module_name[1024];
|
||||
char *name, *dot;
|
||||
|
||||
if (event->mmap.filename[0] == '/') {
|
||||
name = strrchr(event->mmap.filename, '/');
|
||||
if (name == NULL)
|
||||
goto out_problem;
|
||||
|
||||
++name; /* skip / */
|
||||
dot = strrchr(name, '.');
|
||||
if (dot == NULL)
|
||||
goto out_problem;
|
||||
snprintf(short_module_name, sizeof(short_module_name),
|
||||
"[%.*s]", (int)(dot - name), name);
|
||||
strxfrchar(short_module_name, '-', '_');
|
||||
} else
|
||||
strcpy(short_module_name, event->mmap.filename);
|
||||
|
||||
map = machine__new_module(machine, event->mmap.start,
|
||||
event->mmap.filename);
|
||||
if (map == NULL)
|
||||
goto out_problem;
|
||||
|
||||
name = strdup(short_module_name);
|
||||
if (name == NULL)
|
||||
goto out_problem;
|
||||
|
||||
map->dso->short_name = name;
|
||||
map->dso->sname_alloc = 1;
|
||||
map->end = map->start + event->mmap.len;
|
||||
} else if (is_kernel_mmap) {
|
||||
const char *symbol_name = (event->mmap.filename +
|
||||
strlen(kmmap_prefix));
|
||||
/*
|
||||
* Should be there already, from the build-id table in
|
||||
* the header.
|
||||
*/
|
||||
struct dso *kernel = __dsos__findnew(&machine->kernel_dsos,
|
||||
kmmap_prefix);
|
||||
if (kernel == NULL)
|
||||
goto out_problem;
|
||||
|
||||
kernel->kernel = kernel_type;
|
||||
if (__machine__create_kernel_maps(machine, kernel) < 0)
|
||||
goto out_problem;
|
||||
|
||||
perf_event__set_kernel_mmap_len(event, machine->vmlinux_maps);
|
||||
|
||||
/*
|
||||
* Avoid using a zero address (kptr_restrict) for the ref reloc
|
||||
* symbol. Effectively having zero here means that at record
|
||||
* time /proc/sys/kernel/kptr_restrict was non zero.
|
||||
*/
|
||||
if (event->mmap.pgoff != 0) {
|
||||
maps__set_kallsyms_ref_reloc_sym(machine->vmlinux_maps,
|
||||
symbol_name,
|
||||
event->mmap.pgoff);
|
||||
}
|
||||
|
||||
if (machine__is_default_guest(machine)) {
|
||||
/*
|
||||
* preload dso of guest kernel and modules
|
||||
*/
|
||||
dso__load(kernel, machine->vmlinux_maps[MAP__FUNCTION],
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
out_problem:
|
||||
return -1;
|
||||
return machine__process_lost_event(machine, event);
|
||||
}
|
||||
|
||||
size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp)
|
||||
@ -656,43 +538,12 @@ size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp)
|
||||
event->mmap.len, event->mmap.pgoff, event->mmap.filename);
|
||||
}
|
||||
|
||||
int perf_event__process_mmap(struct perf_tool *tool,
|
||||
int perf_event__process_mmap(struct perf_tool *tool __maybe_unused,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample __maybe_unused,
|
||||
struct machine *machine)
|
||||
{
|
||||
struct thread *thread;
|
||||
struct map *map;
|
||||
u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
|
||||
int ret = 0;
|
||||
|
||||
if (dump_trace)
|
||||
perf_event__fprintf_mmap(event, stdout);
|
||||
|
||||
if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL ||
|
||||
cpumode == PERF_RECORD_MISC_KERNEL) {
|
||||
ret = perf_event__process_kernel_mmap(tool, event, machine);
|
||||
if (ret < 0)
|
||||
goto out_problem;
|
||||
return 0;
|
||||
}
|
||||
|
||||
thread = machine__findnew_thread(machine, event->mmap.pid);
|
||||
if (thread == NULL)
|
||||
goto out_problem;
|
||||
map = map__new(&machine->user_dsos, event->mmap.start,
|
||||
event->mmap.len, event->mmap.pgoff,
|
||||
event->mmap.pid, event->mmap.filename,
|
||||
MAP__FUNCTION);
|
||||
if (map == NULL)
|
||||
goto out_problem;
|
||||
|
||||
thread__insert_map(thread, map);
|
||||
return 0;
|
||||
|
||||
out_problem:
|
||||
dump_printf("problem processing PERF_RECORD_MMAP, skipping event.\n");
|
||||
return 0;
|
||||
return machine__process_mmap_event(machine, event);
|
||||
}
|
||||
|
||||
size_t perf_event__fprintf_task(union perf_event *event, FILE *fp)
|
||||
@ -702,29 +553,20 @@ size_t perf_event__fprintf_task(union perf_event *event, FILE *fp)
|
||||
event->fork.ppid, event->fork.ptid);
|
||||
}
|
||||
|
||||
int perf_event__process_task(struct perf_tool *tool __maybe_unused,
|
||||
int perf_event__process_fork(struct perf_tool *tool __maybe_unused,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample __maybe_unused,
|
||||
struct machine *machine)
|
||||
struct machine *machine)
|
||||
{
|
||||
struct thread *thread = machine__findnew_thread(machine, event->fork.tid);
|
||||
struct thread *parent = machine__findnew_thread(machine, event->fork.ptid);
|
||||
return machine__process_fork_event(machine, event);
|
||||
}
|
||||
|
||||
if (dump_trace)
|
||||
perf_event__fprintf_task(event, stdout);
|
||||
|
||||
if (event->header.type == PERF_RECORD_EXIT) {
|
||||
machine__remove_thread(machine, thread);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (thread == NULL || parent == NULL ||
|
||||
thread__fork(thread, parent) < 0) {
|
||||
dump_printf("problem processing PERF_RECORD_FORK, skipping event.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
int perf_event__process_exit(struct perf_tool *tool __maybe_unused,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample __maybe_unused,
|
||||
struct machine *machine)
|
||||
{
|
||||
return machine__process_exit_event(machine, event);
|
||||
}
|
||||
|
||||
size_t perf_event__fprintf(union perf_event *event, FILE *fp)
|
||||
@ -750,27 +592,12 @@ size_t perf_event__fprintf(union perf_event *event, FILE *fp)
|
||||
return ret;
|
||||
}
|
||||
|
||||
int perf_event__process(struct perf_tool *tool, union perf_event *event,
|
||||
struct perf_sample *sample, struct machine *machine)
|
||||
int perf_event__process(struct perf_tool *tool __maybe_unused,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample __maybe_unused,
|
||||
struct machine *machine)
|
||||
{
|
||||
switch (event->header.type) {
|
||||
case PERF_RECORD_COMM:
|
||||
perf_event__process_comm(tool, event, sample, machine);
|
||||
break;
|
||||
case PERF_RECORD_MMAP:
|
||||
perf_event__process_mmap(tool, event, sample, machine);
|
||||
break;
|
||||
case PERF_RECORD_FORK:
|
||||
case PERF_RECORD_EXIT:
|
||||
perf_event__process_task(tool, event, sample, machine);
|
||||
break;
|
||||
case PERF_RECORD_LOST:
|
||||
perf_event__process_lost(tool, event, sample, machine);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return machine__process_event(machine, event);
|
||||
}
|
||||
|
||||
void thread__find_addr_map(struct thread *self,
|
||||
|
@ -191,7 +191,11 @@ int perf_event__process_mmap(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine);
|
||||
int perf_event__process_task(struct perf_tool *tool,
|
||||
int perf_event__process_fork(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine);
|
||||
int perf_event__process_exit(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine);
|
||||
|
@ -154,8 +154,8 @@ error:
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
int perf_evlist__add_attrs(struct perf_evlist *evlist,
|
||||
struct perf_event_attr *attrs, size_t nr_attrs)
|
||||
static int perf_evlist__add_attrs(struct perf_evlist *evlist,
|
||||
struct perf_event_attr *attrs, size_t nr_attrs)
|
||||
{
|
||||
struct perf_evsel *evsel, *n;
|
||||
LIST_HEAD(head);
|
||||
@ -189,60 +189,6 @@ int __perf_evlist__add_default_attrs(struct perf_evlist *evlist,
|
||||
return perf_evlist__add_attrs(evlist, attrs, nr_attrs);
|
||||
}
|
||||
|
||||
static int trace_event__id(const char *evname)
|
||||
{
|
||||
char *filename, *colon;
|
||||
int err = -1, fd;
|
||||
|
||||
if (asprintf(&filename, "%s/%s/id", tracing_events_path, evname) < 0)
|
||||
return -1;
|
||||
|
||||
colon = strrchr(filename, ':');
|
||||
if (colon != NULL)
|
||||
*colon = '/';
|
||||
|
||||
fd = open(filename, O_RDONLY);
|
||||
if (fd >= 0) {
|
||||
char id[16];
|
||||
if (read(fd, id, sizeof(id)) > 0)
|
||||
err = atoi(id);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
free(filename);
|
||||
return err;
|
||||
}
|
||||
|
||||
int perf_evlist__add_tracepoints(struct perf_evlist *evlist,
|
||||
const char *tracepoints[],
|
||||
size_t nr_tracepoints)
|
||||
{
|
||||
int err;
|
||||
size_t i;
|
||||
struct perf_event_attr *attrs = zalloc(nr_tracepoints * sizeof(*attrs));
|
||||
|
||||
if (attrs == NULL)
|
||||
return -1;
|
||||
|
||||
for (i = 0; i < nr_tracepoints; i++) {
|
||||
err = trace_event__id(tracepoints[i]);
|
||||
|
||||
if (err < 0)
|
||||
goto out_free_attrs;
|
||||
|
||||
attrs[i].type = PERF_TYPE_TRACEPOINT;
|
||||
attrs[i].config = err;
|
||||
attrs[i].sample_type = (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME |
|
||||
PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD);
|
||||
attrs[i].sample_period = 1;
|
||||
}
|
||||
|
||||
err = perf_evlist__add_attrs(evlist, attrs, nr_tracepoints);
|
||||
out_free_attrs:
|
||||
free(attrs);
|
||||
return err;
|
||||
}
|
||||
|
||||
struct perf_evsel *
|
||||
perf_evlist__find_tracepoint_by_id(struct perf_evlist *evlist, int id)
|
||||
{
|
||||
@ -257,32 +203,18 @@ perf_evlist__find_tracepoint_by_id(struct perf_evlist *evlist, int id)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int perf_evlist__set_tracepoints_handlers(struct perf_evlist *evlist,
|
||||
const struct perf_evsel_str_handler *assocs,
|
||||
size_t nr_assocs)
|
||||
int perf_evlist__add_newtp(struct perf_evlist *evlist,
|
||||
const char *sys, const char *name, void *handler)
|
||||
{
|
||||
struct perf_evsel *evsel;
|
||||
int err;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < nr_assocs; i++) {
|
||||
err = trace_event__id(assocs[i].name);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
evsel = perf_evsel__newtp(sys, name, evlist->nr_entries);
|
||||
if (evsel == NULL)
|
||||
return -1;
|
||||
|
||||
evsel = perf_evlist__find_tracepoint_by_id(evlist, err);
|
||||
if (evsel == NULL)
|
||||
continue;
|
||||
|
||||
err = -EEXIST;
|
||||
if (evsel->handler.func != NULL)
|
||||
goto out;
|
||||
evsel->handler.func = assocs[i].handler;
|
||||
}
|
||||
|
||||
err = 0;
|
||||
out:
|
||||
return err;
|
||||
evsel->handler.func = handler;
|
||||
perf_evlist__add(evlist, evsel);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void perf_evlist__disable(struct perf_evlist *evlist)
|
||||
@ -393,8 +325,6 @@ struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id)
|
||||
|
||||
union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx)
|
||||
{
|
||||
/* XXX Move this to perf.c, making it generally available */
|
||||
unsigned int page_size = sysconf(_SC_PAGE_SIZE);
|
||||
struct perf_mmap *md = &evlist->mmap[idx];
|
||||
unsigned int head = perf_mmap__read_head(md);
|
||||
unsigned int old = md->prev;
|
||||
@ -596,7 +526,6 @@ out_unmap:
|
||||
int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages,
|
||||
bool overwrite)
|
||||
{
|
||||
unsigned int page_size = sysconf(_SC_PAGE_SIZE);
|
||||
struct perf_evsel *evsel;
|
||||
const struct cpu_map *cpus = evlist->cpus;
|
||||
const struct thread_map *threads = evlist->threads;
|
||||
|
@ -51,26 +51,14 @@ void perf_evlist__delete(struct perf_evlist *evlist);
|
||||
|
||||
void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry);
|
||||
int perf_evlist__add_default(struct perf_evlist *evlist);
|
||||
int perf_evlist__add_attrs(struct perf_evlist *evlist,
|
||||
struct perf_event_attr *attrs, size_t nr_attrs);
|
||||
int __perf_evlist__add_default_attrs(struct perf_evlist *evlist,
|
||||
struct perf_event_attr *attrs, size_t nr_attrs);
|
||||
int perf_evlist__add_tracepoints(struct perf_evlist *evlist,
|
||||
const char *tracepoints[], size_t nr_tracepoints);
|
||||
int perf_evlist__set_tracepoints_handlers(struct perf_evlist *evlist,
|
||||
const struct perf_evsel_str_handler *assocs,
|
||||
size_t nr_assocs);
|
||||
|
||||
#define perf_evlist__add_attrs_array(evlist, array) \
|
||||
perf_evlist__add_attrs(evlist, array, ARRAY_SIZE(array))
|
||||
#define perf_evlist__add_default_attrs(evlist, array) \
|
||||
__perf_evlist__add_default_attrs(evlist, array, ARRAY_SIZE(array))
|
||||
|
||||
#define perf_evlist__add_tracepoints_array(evlist, array) \
|
||||
perf_evlist__add_tracepoints(evlist, array, ARRAY_SIZE(array))
|
||||
|
||||
#define perf_evlist__set_tracepoints_handlers_array(evlist, array) \
|
||||
perf_evlist__set_tracepoints_handlers(evlist, array, ARRAY_SIZE(array))
|
||||
int perf_evlist__add_newtp(struct perf_evlist *evlist,
|
||||
const char *sys, const char *name, void *handler);
|
||||
|
||||
int perf_evlist__set_filter(struct perf_evlist *evlist, const char *filter);
|
||||
|
||||
|
@ -22,7 +22,7 @@ do
|
||||
}' "Documentation/perf-$cmd.txt"
|
||||
done
|
||||
|
||||
echo "#ifndef NO_LIBELF_SUPPORT"
|
||||
echo "#ifdef LIBELF_SUPPORT"
|
||||
sed -n -e 's/^perf-\([^ ]*\)[ ].* full.*/\1/p' command-list.txt |
|
||||
sort |
|
||||
while read cmd
|
||||
@ -35,5 +35,5 @@ do
|
||||
p
|
||||
}' "Documentation/perf-$cmd.txt"
|
||||
done
|
||||
echo "#endif /* NO_LIBELF_SUPPORT */"
|
||||
echo "#endif /* LIBELF_SUPPORT */"
|
||||
echo "};"
|
||||
|
@ -135,31 +135,47 @@ static void hist_entry__add_cpumode_period(struct hist_entry *he,
|
||||
{
|
||||
switch (cpumode) {
|
||||
case PERF_RECORD_MISC_KERNEL:
|
||||
he->period_sys += period;
|
||||
he->stat.period_sys += period;
|
||||
break;
|
||||
case PERF_RECORD_MISC_USER:
|
||||
he->period_us += period;
|
||||
he->stat.period_us += period;
|
||||
break;
|
||||
case PERF_RECORD_MISC_GUEST_KERNEL:
|
||||
he->period_guest_sys += period;
|
||||
he->stat.period_guest_sys += period;
|
||||
break;
|
||||
case PERF_RECORD_MISC_GUEST_USER:
|
||||
he->period_guest_us += period;
|
||||
he->stat.period_guest_us += period;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void he_stat__add_period(struct he_stat *he_stat, u64 period)
|
||||
{
|
||||
he_stat->period += period;
|
||||
he_stat->nr_events += 1;
|
||||
}
|
||||
|
||||
static void he_stat__add_stat(struct he_stat *dest, struct he_stat *src)
|
||||
{
|
||||
dest->period += src->period;
|
||||
dest->period_sys += src->period_sys;
|
||||
dest->period_us += src->period_us;
|
||||
dest->period_guest_sys += src->period_guest_sys;
|
||||
dest->period_guest_us += src->period_guest_us;
|
||||
dest->nr_events += src->nr_events;
|
||||
}
|
||||
|
||||
static void hist_entry__decay(struct hist_entry *he)
|
||||
{
|
||||
he->period = (he->period * 7) / 8;
|
||||
he->nr_events = (he->nr_events * 7) / 8;
|
||||
he->stat.period = (he->stat.period * 7) / 8;
|
||||
he->stat.nr_events = (he->stat.nr_events * 7) / 8;
|
||||
}
|
||||
|
||||
static bool hists__decay_entry(struct hists *hists, struct hist_entry *he)
|
||||
{
|
||||
u64 prev_period = he->period;
|
||||
u64 prev_period = he->stat.period;
|
||||
|
||||
if (prev_period == 0)
|
||||
return true;
|
||||
@ -167,9 +183,9 @@ static bool hists__decay_entry(struct hists *hists, struct hist_entry *he)
|
||||
hist_entry__decay(he);
|
||||
|
||||
if (!he->filtered)
|
||||
hists->stats.total_period -= prev_period - he->period;
|
||||
hists->stats.total_period -= prev_period - he->stat.period;
|
||||
|
||||
return he->period == 0;
|
||||
return he->stat.period == 0;
|
||||
}
|
||||
|
||||
static void __hists__decay_entries(struct hists *hists, bool zap_user,
|
||||
@ -223,7 +239,7 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template)
|
||||
|
||||
if (he != NULL) {
|
||||
*he = *template;
|
||||
he->nr_events = 1;
|
||||
|
||||
if (he->ms.map)
|
||||
he->ms.map->referenced = true;
|
||||
if (symbol_conf.use_callchain)
|
||||
@ -238,7 +254,7 @@ static void hists__inc_nr_entries(struct hists *hists, struct hist_entry *h)
|
||||
if (!h->filtered) {
|
||||
hists__calc_col_len(hists, h);
|
||||
++hists->nr_entries;
|
||||
hists->stats.total_period += h->period;
|
||||
hists->stats.total_period += h->stat.period;
|
||||
}
|
||||
}
|
||||
|
||||
@ -270,8 +286,7 @@ static struct hist_entry *add_hist_entry(struct hists *hists,
|
||||
cmp = hist_entry__cmp(entry, he);
|
||||
|
||||
if (!cmp) {
|
||||
he->period += period;
|
||||
++he->nr_events;
|
||||
he_stat__add_period(&he->stat, period);
|
||||
|
||||
/* If the map of an existing hist_entry has
|
||||
* become out-of-date due to an exec() or
|
||||
@ -321,10 +336,14 @@ struct hist_entry *__hists__add_branch_entry(struct hists *self,
|
||||
.cpu = al->cpu,
|
||||
.ip = bi->to.addr,
|
||||
.level = al->level,
|
||||
.period = period,
|
||||
.stat = {
|
||||
.period = period,
|
||||
.nr_events = 1,
|
||||
},
|
||||
.parent = sym_parent,
|
||||
.filtered = symbol__parent_filter(sym_parent),
|
||||
.branch_info = bi,
|
||||
.hists = self,
|
||||
};
|
||||
|
||||
return add_hist_entry(self, &entry, al, period);
|
||||
@ -343,9 +362,13 @@ struct hist_entry *__hists__add_entry(struct hists *self,
|
||||
.cpu = al->cpu,
|
||||
.ip = al->addr,
|
||||
.level = al->level,
|
||||
.period = period,
|
||||
.stat = {
|
||||
.period = period,
|
||||
.nr_events = 1,
|
||||
},
|
||||
.parent = sym_parent,
|
||||
.filtered = symbol__parent_filter(sym_parent),
|
||||
.hists = self,
|
||||
};
|
||||
|
||||
return add_hist_entry(self, &entry, al, period);
|
||||
@ -410,12 +433,7 @@ static bool hists__collapse_insert_entry(struct hists *hists __maybe_unused,
|
||||
cmp = hist_entry__collapse(iter, he);
|
||||
|
||||
if (!cmp) {
|
||||
iter->period += he->period;
|
||||
iter->period_sys += he->period_sys;
|
||||
iter->period_us += he->period_us;
|
||||
iter->period_guest_sys += he->period_guest_sys;
|
||||
iter->period_guest_us += he->period_guest_us;
|
||||
iter->nr_events += he->nr_events;
|
||||
he_stat__add_stat(&iter->stat, &he->stat);
|
||||
|
||||
if (symbol_conf.use_callchain) {
|
||||
callchain_cursor_reset(&callchain_cursor);
|
||||
@ -518,7 +536,7 @@ static void __hists__insert_output_entry(struct rb_root *entries,
|
||||
parent = *p;
|
||||
iter = rb_entry(parent, struct hist_entry, rb_node);
|
||||
|
||||
if (he->period > iter->period)
|
||||
if (he->stat.period > iter->stat.period)
|
||||
p = &(*p)->rb_left;
|
||||
else
|
||||
p = &(*p)->rb_right;
|
||||
@ -579,8 +597,8 @@ static void hists__remove_entry_filter(struct hists *hists, struct hist_entry *h
|
||||
if (h->ms.unfolded)
|
||||
hists->nr_entries += h->nr_rows;
|
||||
h->row_offset = 0;
|
||||
hists->stats.total_period += h->period;
|
||||
hists->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events;
|
||||
hists->stats.total_period += h->stat.period;
|
||||
hists->stats.nr_events[PERF_RECORD_SAMPLE] += h->stat.nr_events;
|
||||
|
||||
hists__calc_col_len(hists, h);
|
||||
}
|
||||
|
@ -98,9 +98,8 @@ void hists__output_recalc_col_len(struct hists *hists, int max_rows);
|
||||
void hists__inc_nr_events(struct hists *self, u32 type);
|
||||
size_t hists__fprintf_nr_events(struct hists *self, FILE *fp);
|
||||
|
||||
size_t hists__fprintf(struct hists *self, struct hists *pair,
|
||||
bool show_displacement, bool show_header,
|
||||
int max_rows, int max_cols, FILE *fp);
|
||||
size_t hists__fprintf(struct hists *self, bool show_header, int max_rows,
|
||||
int max_cols, FILE *fp);
|
||||
|
||||
int hist_entry__inc_addr_samples(struct hist_entry *self, int evidx, u64 addr);
|
||||
int hist_entry__annotate(struct hist_entry *self, size_t privsize);
|
||||
@ -118,9 +117,7 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *he);
|
||||
struct perf_hpp {
|
||||
char *buf;
|
||||
size_t size;
|
||||
u64 total_period;
|
||||
const char *sep;
|
||||
long displacement;
|
||||
void *ptr;
|
||||
};
|
||||
|
||||
@ -135,6 +132,7 @@ struct perf_hpp_fmt {
|
||||
extern struct perf_hpp_fmt perf_hpp__format[];
|
||||
|
||||
enum {
|
||||
PERF_HPP__BASELINE,
|
||||
PERF_HPP__OVERHEAD,
|
||||
PERF_HPP__OVERHEAD_SYS,
|
||||
PERF_HPP__OVERHEAD_US,
|
||||
@ -142,19 +140,32 @@ enum {
|
||||
PERF_HPP__OVERHEAD_GUEST_US,
|
||||
PERF_HPP__SAMPLES,
|
||||
PERF_HPP__PERIOD,
|
||||
PERF_HPP__PERIOD_BASELINE,
|
||||
PERF_HPP__DELTA,
|
||||
PERF_HPP__RATIO,
|
||||
PERF_HPP__WEIGHTED_DIFF,
|
||||
PERF_HPP__DISPL,
|
||||
PERF_HPP__FORMULA,
|
||||
|
||||
PERF_HPP__MAX_INDEX
|
||||
};
|
||||
|
||||
void perf_hpp__init(bool need_pair, bool show_displacement);
|
||||
void perf_hpp__init(void);
|
||||
void perf_hpp__column_enable(unsigned col, bool enable);
|
||||
int hist_entry__period_snprintf(struct perf_hpp *hpp, struct hist_entry *he,
|
||||
bool color);
|
||||
|
||||
struct perf_evlist;
|
||||
|
||||
#ifdef NO_NEWT_SUPPORT
|
||||
#ifdef NEWT_SUPPORT
|
||||
#include "../ui/keysyms.h"
|
||||
int hist_entry__tui_annotate(struct hist_entry *he, int evidx,
|
||||
void(*timer)(void *arg), void *arg, int delay_secs);
|
||||
|
||||
int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
|
||||
void(*timer)(void *arg), void *arg,
|
||||
int refresh);
|
||||
#else
|
||||
static inline
|
||||
int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __maybe_unused,
|
||||
const char *help __maybe_unused,
|
||||
@ -177,17 +188,13 @@ static inline int hist_entry__tui_annotate(struct hist_entry *self
|
||||
}
|
||||
#define K_LEFT -1
|
||||
#define K_RIGHT -2
|
||||
#else
|
||||
#include "../ui/keysyms.h"
|
||||
int hist_entry__tui_annotate(struct hist_entry *he, int evidx,
|
||||
void(*timer)(void *arg), void *arg, int delay_secs);
|
||||
|
||||
int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
|
||||
void(*timer)(void *arg), void *arg,
|
||||
int refresh);
|
||||
#endif
|
||||
|
||||
#ifdef NO_GTK2_SUPPORT
|
||||
#ifdef GTK2_SUPPORT
|
||||
int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, const char *help,
|
||||
void(*timer)(void *arg), void *arg,
|
||||
int refresh);
|
||||
#else
|
||||
static inline
|
||||
int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist __maybe_unused,
|
||||
const char *help __maybe_unused,
|
||||
@ -197,13 +204,12 @@ int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist __maybe_unused,
|
||||
{
|
||||
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);
|
||||
|
||||
double perf_diff__compute_delta(struct hist_entry *he);
|
||||
double perf_diff__compute_ratio(struct hist_entry *he);
|
||||
s64 perf_diff__compute_wdiff(struct hist_entry *he);
|
||||
int perf_diff__formula(char *buf, size_t size, struct hist_entry *he);
|
||||
#endif /* __PERF_HIST_H */
|
||||
|
277
tools/perf/util/machine.c
Normal file
277
tools/perf/util/machine.c
Normal file
@ -0,0 +1,277 @@
|
||||
#include "debug.h"
|
||||
#include "event.h"
|
||||
#include "machine.h"
|
||||
#include "map.h"
|
||||
#include "thread.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
static struct thread *__machine__findnew_thread(struct machine *machine, pid_t pid,
|
||||
bool create)
|
||||
{
|
||||
struct rb_node **p = &machine->threads.rb_node;
|
||||
struct rb_node *parent = NULL;
|
||||
struct thread *th;
|
||||
|
||||
/*
|
||||
* Font-end cache - PID lookups come in blocks,
|
||||
* so most of the time we dont have to look up
|
||||
* the full rbtree:
|
||||
*/
|
||||
if (machine->last_match && machine->last_match->pid == pid)
|
||||
return machine->last_match;
|
||||
|
||||
while (*p != NULL) {
|
||||
parent = *p;
|
||||
th = rb_entry(parent, struct thread, rb_node);
|
||||
|
||||
if (th->pid == pid) {
|
||||
machine->last_match = th;
|
||||
return th;
|
||||
}
|
||||
|
||||
if (pid < th->pid)
|
||||
p = &(*p)->rb_left;
|
||||
else
|
||||
p = &(*p)->rb_right;
|
||||
}
|
||||
|
||||
if (!create)
|
||||
return NULL;
|
||||
|
||||
th = thread__new(pid);
|
||||
if (th != NULL) {
|
||||
rb_link_node(&th->rb_node, parent, p);
|
||||
rb_insert_color(&th->rb_node, &machine->threads);
|
||||
machine->last_match = th;
|
||||
}
|
||||
|
||||
return th;
|
||||
}
|
||||
|
||||
struct thread *machine__findnew_thread(struct machine *machine, pid_t pid)
|
||||
{
|
||||
return __machine__findnew_thread(machine, pid, true);
|
||||
}
|
||||
|
||||
struct thread *machine__find_thread(struct machine *machine, pid_t pid)
|
||||
{
|
||||
return __machine__findnew_thread(machine, pid, false);
|
||||
}
|
||||
|
||||
int machine__process_comm_event(struct machine *machine, union perf_event *event)
|
||||
{
|
||||
struct thread *thread = machine__findnew_thread(machine, event->comm.tid);
|
||||
|
||||
if (dump_trace)
|
||||
perf_event__fprintf_comm(event, stdout);
|
||||
|
||||
if (thread == NULL || thread__set_comm(thread, event->comm.comm)) {
|
||||
dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int machine__process_lost_event(struct machine *machine __maybe_unused,
|
||||
union perf_event *event)
|
||||
{
|
||||
dump_printf(": id:%" PRIu64 ": lost:%" PRIu64 "\n",
|
||||
event->lost.id, event->lost.lost);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void machine__set_kernel_mmap_len(struct machine *machine,
|
||||
union perf_event *event)
|
||||
{
|
||||
machine->vmlinux_maps[MAP__FUNCTION]->start = event->mmap.start;
|
||||
machine->vmlinux_maps[MAP__FUNCTION]->end = (event->mmap.start +
|
||||
event->mmap.len);
|
||||
/*
|
||||
* Be a bit paranoid here, some perf.data file came with
|
||||
* a zero sized synthesized MMAP event for the kernel.
|
||||
*/
|
||||
if (machine->vmlinux_maps[MAP__FUNCTION]->end == 0)
|
||||
machine->vmlinux_maps[MAP__FUNCTION]->end = ~0ULL;
|
||||
}
|
||||
|
||||
static int machine__process_kernel_mmap_event(struct machine *machine,
|
||||
union perf_event *event)
|
||||
{
|
||||
struct map *map;
|
||||
char kmmap_prefix[PATH_MAX];
|
||||
enum dso_kernel_type kernel_type;
|
||||
bool is_kernel_mmap;
|
||||
|
||||
machine__mmap_name(machine, kmmap_prefix, sizeof(kmmap_prefix));
|
||||
if (machine__is_host(machine))
|
||||
kernel_type = DSO_TYPE_KERNEL;
|
||||
else
|
||||
kernel_type = DSO_TYPE_GUEST_KERNEL;
|
||||
|
||||
is_kernel_mmap = memcmp(event->mmap.filename,
|
||||
kmmap_prefix,
|
||||
strlen(kmmap_prefix) - 1) == 0;
|
||||
if (event->mmap.filename[0] == '/' ||
|
||||
(!is_kernel_mmap && event->mmap.filename[0] == '[')) {
|
||||
|
||||
char short_module_name[1024];
|
||||
char *name, *dot;
|
||||
|
||||
if (event->mmap.filename[0] == '/') {
|
||||
name = strrchr(event->mmap.filename, '/');
|
||||
if (name == NULL)
|
||||
goto out_problem;
|
||||
|
||||
++name; /* skip / */
|
||||
dot = strrchr(name, '.');
|
||||
if (dot == NULL)
|
||||
goto out_problem;
|
||||
snprintf(short_module_name, sizeof(short_module_name),
|
||||
"[%.*s]", (int)(dot - name), name);
|
||||
strxfrchar(short_module_name, '-', '_');
|
||||
} else
|
||||
strcpy(short_module_name, event->mmap.filename);
|
||||
|
||||
map = machine__new_module(machine, event->mmap.start,
|
||||
event->mmap.filename);
|
||||
if (map == NULL)
|
||||
goto out_problem;
|
||||
|
||||
name = strdup(short_module_name);
|
||||
if (name == NULL)
|
||||
goto out_problem;
|
||||
|
||||
map->dso->short_name = name;
|
||||
map->dso->sname_alloc = 1;
|
||||
map->end = map->start + event->mmap.len;
|
||||
} else if (is_kernel_mmap) {
|
||||
const char *symbol_name = (event->mmap.filename +
|
||||
strlen(kmmap_prefix));
|
||||
/*
|
||||
* Should be there already, from the build-id table in
|
||||
* the header.
|
||||
*/
|
||||
struct dso *kernel = __dsos__findnew(&machine->kernel_dsos,
|
||||
kmmap_prefix);
|
||||
if (kernel == NULL)
|
||||
goto out_problem;
|
||||
|
||||
kernel->kernel = kernel_type;
|
||||
if (__machine__create_kernel_maps(machine, kernel) < 0)
|
||||
goto out_problem;
|
||||
|
||||
machine__set_kernel_mmap_len(machine, event);
|
||||
|
||||
/*
|
||||
* Avoid using a zero address (kptr_restrict) for the ref reloc
|
||||
* symbol. Effectively having zero here means that at record
|
||||
* time /proc/sys/kernel/kptr_restrict was non zero.
|
||||
*/
|
||||
if (event->mmap.pgoff != 0) {
|
||||
maps__set_kallsyms_ref_reloc_sym(machine->vmlinux_maps,
|
||||
symbol_name,
|
||||
event->mmap.pgoff);
|
||||
}
|
||||
|
||||
if (machine__is_default_guest(machine)) {
|
||||
/*
|
||||
* preload dso of guest kernel and modules
|
||||
*/
|
||||
dso__load(kernel, machine->vmlinux_maps[MAP__FUNCTION],
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
out_problem:
|
||||
return -1;
|
||||
}
|
||||
|
||||
int machine__process_mmap_event(struct machine *machine, union perf_event *event)
|
||||
{
|
||||
u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
|
||||
struct thread *thread;
|
||||
struct map *map;
|
||||
int ret = 0;
|
||||
|
||||
if (dump_trace)
|
||||
perf_event__fprintf_mmap(event, stdout);
|
||||
|
||||
if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL ||
|
||||
cpumode == PERF_RECORD_MISC_KERNEL) {
|
||||
ret = machine__process_kernel_mmap_event(machine, event);
|
||||
if (ret < 0)
|
||||
goto out_problem;
|
||||
return 0;
|
||||
}
|
||||
|
||||
thread = machine__findnew_thread(machine, event->mmap.pid);
|
||||
if (thread == NULL)
|
||||
goto out_problem;
|
||||
map = map__new(&machine->user_dsos, event->mmap.start,
|
||||
event->mmap.len, event->mmap.pgoff,
|
||||
event->mmap.pid, event->mmap.filename,
|
||||
MAP__FUNCTION);
|
||||
if (map == NULL)
|
||||
goto out_problem;
|
||||
|
||||
thread__insert_map(thread, map);
|
||||
return 0;
|
||||
|
||||
out_problem:
|
||||
dump_printf("problem processing PERF_RECORD_MMAP, skipping event.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int machine__process_fork_event(struct machine *machine, union perf_event *event)
|
||||
{
|
||||
struct thread *thread = machine__findnew_thread(machine, event->fork.tid);
|
||||
struct thread *parent = machine__findnew_thread(machine, event->fork.ptid);
|
||||
|
||||
if (dump_trace)
|
||||
perf_event__fprintf_task(event, stdout);
|
||||
|
||||
if (thread == NULL || parent == NULL ||
|
||||
thread__fork(thread, parent) < 0) {
|
||||
dump_printf("problem processing PERF_RECORD_FORK, skipping event.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int machine__process_exit_event(struct machine *machine, union perf_event *event)
|
||||
{
|
||||
struct thread *thread = machine__find_thread(machine, event->fork.tid);
|
||||
|
||||
if (dump_trace)
|
||||
perf_event__fprintf_task(event, stdout);
|
||||
|
||||
if (thread != NULL)
|
||||
machine__remove_thread(machine, thread);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int machine__process_event(struct machine *machine, union perf_event *event)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (event->header.type) {
|
||||
case PERF_RECORD_COMM:
|
||||
ret = machine__process_comm_event(machine, event); break;
|
||||
case PERF_RECORD_MMAP:
|
||||
ret = machine__process_mmap_event(machine, event); break;
|
||||
case PERF_RECORD_FORK:
|
||||
ret = machine__process_fork_event(machine, event); break;
|
||||
case PERF_RECORD_EXIT:
|
||||
ret = machine__process_exit_event(machine, event); break;
|
||||
case PERF_RECORD_LOST:
|
||||
ret = machine__process_lost_event(machine, event); break;
|
||||
default:
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
19
tools/perf/util/machine.h
Normal file
19
tools/perf/util/machine.h
Normal file
@ -0,0 +1,19 @@
|
||||
#ifndef __PERF_MACHINE_H
|
||||
#define __PERF_MACHINE_H
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
struct thread;
|
||||
struct machine;
|
||||
union perf_event;
|
||||
|
||||
struct thread *machine__find_thread(struct machine *machine, pid_t pid);
|
||||
|
||||
int machine__process_comm_event(struct machine *machine, union perf_event *event);
|
||||
int machine__process_exit_event(struct machine *machine, union perf_event *event);
|
||||
int machine__process_fork_event(struct machine *machine, union perf_event *event);
|
||||
int machine__process_lost_event(struct machine *machine, union perf_event *event);
|
||||
int machine__process_mmap_event(struct machine *machine, union perf_event *event);
|
||||
int machine__process_event(struct machine *machine, union perf_event *event);
|
||||
|
||||
#endif /* __PERF_MACHINE_H */
|
@ -162,7 +162,7 @@ int map__load(struct map *self, symbol_filter_t filter)
|
||||
pr_warning(", continuing without symbols\n");
|
||||
return -1;
|
||||
} else if (nr == 0) {
|
||||
#ifndef NO_LIBELF_SUPPORT
|
||||
#ifdef LIBELF_SUPPORT
|
||||
const size_t len = strlen(name);
|
||||
const size_t real_len = len - sizeof(DSO__DELETED);
|
||||
|
||||
|
@ -384,6 +384,8 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
|
||||
return usage_with_options_internal(usagestr, options, 1);
|
||||
if (internal_help && !strcmp(arg + 2, "help"))
|
||||
return parse_options_usage(usagestr, options);
|
||||
if (!strcmp(arg + 2, "list-opts"))
|
||||
return PARSE_OPT_LIST;
|
||||
switch (parse_long_opt(ctx, arg + 2, options)) {
|
||||
case -1:
|
||||
return parse_options_usage(usagestr, options);
|
||||
@ -422,6 +424,12 @@ int parse_options(int argc, const char **argv, const struct option *options,
|
||||
exit(129);
|
||||
case PARSE_OPT_DONE:
|
||||
break;
|
||||
case PARSE_OPT_LIST:
|
||||
while (options->type != OPTION_END) {
|
||||
printf("--%s ", options->long_name);
|
||||
options++;
|
||||
}
|
||||
exit(130);
|
||||
default: /* PARSE_OPT_UNKNOWN */
|
||||
if (ctx.argv[0][1] == '-') {
|
||||
error("unknown option `%s'", ctx.argv[0] + 2);
|
||||
|
@ -140,6 +140,7 @@ extern NORETURN void usage_with_options(const char * const *usagestr,
|
||||
enum {
|
||||
PARSE_OPT_HELP = -1,
|
||||
PARSE_OPT_DONE,
|
||||
PARSE_OPT_LIST,
|
||||
PARSE_OPT_UNKNOWN,
|
||||
};
|
||||
|
||||
|
@ -22,7 +22,7 @@ static const char *get_perf_dir(void)
|
||||
return ".";
|
||||
}
|
||||
|
||||
#ifdef NO_STRLCPY
|
||||
#ifndef HAVE_STRLCPY
|
||||
size_t strlcpy(char *dest, const char *src, size_t size)
|
||||
{
|
||||
size_t ret = strlen(src);
|
||||
|
@ -1,7 +1,7 @@
|
||||
#ifndef __PERF_REGS_H
|
||||
#define __PERF_REGS_H
|
||||
|
||||
#ifndef NO_PERF_REGS
|
||||
#ifdef HAVE_PERF_REGS
|
||||
#include <perf_regs.h>
|
||||
#else
|
||||
#define PERF_REGS_MASK 0
|
||||
@ -10,5 +10,5 @@ static inline const char *perf_reg_name(int id __maybe_unused)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif /* NO_PERF_REGS */
|
||||
#endif /* HAVE_PERF_REGS */
|
||||
#endif /* __PERF_REGS_H */
|
||||
|
@ -32,7 +32,6 @@
|
||||
#include "../event.h"
|
||||
#include "../thread.h"
|
||||
#include "../trace-event.h"
|
||||
#include "../evsel.h"
|
||||
|
||||
PyMODINIT_FUNC initperf_trace_context(void);
|
||||
|
||||
|
@ -1375,15 +1375,13 @@ int __perf_session__process_events(struct perf_session *session,
|
||||
{
|
||||
u64 head, page_offset, file_offset, file_pos, progress_next;
|
||||
int err, mmap_prot, mmap_flags, map_idx = 0;
|
||||
size_t page_size, mmap_size;
|
||||
size_t mmap_size;
|
||||
char *buf, *mmaps[8];
|
||||
union perf_event *event;
|
||||
uint32_t size;
|
||||
|
||||
perf_tool__fill_defaults(tool);
|
||||
|
||||
page_size = sysconf(_SC_PAGESIZE);
|
||||
|
||||
page_offset = page_size * (data_offset / page_size);
|
||||
file_offset = page_offset;
|
||||
head = data_offset - page_offset;
|
||||
|
@ -43,6 +43,31 @@ extern struct sort_entry sort_sym_from;
|
||||
extern struct sort_entry sort_sym_to;
|
||||
extern enum sort_type sort__first_dimension;
|
||||
|
||||
struct he_stat {
|
||||
u64 period;
|
||||
u64 period_sys;
|
||||
u64 period_us;
|
||||
u64 period_guest_sys;
|
||||
u64 period_guest_us;
|
||||
u32 nr_events;
|
||||
};
|
||||
|
||||
struct hist_entry_diff {
|
||||
bool computed;
|
||||
|
||||
/* PERF_HPP__DISPL */
|
||||
int displacement;
|
||||
|
||||
/* PERF_HPP__DELTA */
|
||||
double period_ratio_delta;
|
||||
|
||||
/* PERF_HPP__RATIO */
|
||||
double period_ratio;
|
||||
|
||||
/* HISTC_WEIGHTED_DIFF */
|
||||
s64 wdiff;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct hist_entry - histogram entry
|
||||
*
|
||||
@ -52,16 +77,13 @@ extern enum sort_type sort__first_dimension;
|
||||
struct hist_entry {
|
||||
struct rb_node rb_node_in;
|
||||
struct rb_node rb_node;
|
||||
u64 period;
|
||||
u64 period_sys;
|
||||
u64 period_us;
|
||||
u64 period_guest_sys;
|
||||
u64 period_guest_us;
|
||||
struct he_stat stat;
|
||||
struct map_symbol ms;
|
||||
struct thread *thread;
|
||||
u64 ip;
|
||||
s32 cpu;
|
||||
u32 nr_events;
|
||||
|
||||
struct hist_entry_diff diff;
|
||||
|
||||
/* XXX These two should move to some tree widget lib */
|
||||
u16 row_offset;
|
||||
@ -73,12 +95,13 @@ struct hist_entry {
|
||||
u8 filtered;
|
||||
char *srcline;
|
||||
struct symbol *parent;
|
||||
unsigned long position;
|
||||
union {
|
||||
unsigned long position;
|
||||
struct hist_entry *pair;
|
||||
struct rb_root sorted_chain;
|
||||
};
|
||||
struct branch_info *branch_info;
|
||||
struct hists *hists;
|
||||
struct callchain_root callchain[0];
|
||||
};
|
||||
|
||||
|
@ -12,7 +12,7 @@
|
||||
#include <byteswap.h>
|
||||
#include <libgen.h>
|
||||
|
||||
#ifndef NO_LIBELF_SUPPORT
|
||||
#ifdef LIBELF_SUPPORT
|
||||
#include <libelf.h>
|
||||
#include <gelf.h>
|
||||
#include <elf.h>
|
||||
@ -46,10 +46,10 @@ char *strxfrchar(char *s, char from, char to);
|
||||
* libelf 0.8.x and earlier do not support ELF_C_READ_MMAP;
|
||||
* for newer versions we can use mmap to reduce memory usage:
|
||||
*/
|
||||
#ifdef LIBELF_NO_MMAP
|
||||
# define PERF_ELF_C_READ_MMAP ELF_C_READ
|
||||
#else
|
||||
#ifdef LIBELF_MMAP
|
||||
# define PERF_ELF_C_READ_MMAP ELF_C_READ_MMAP
|
||||
#else
|
||||
# define PERF_ELF_C_READ_MMAP ELF_C_READ
|
||||
#endif
|
||||
|
||||
#ifndef DMGL_PARAMS
|
||||
@ -233,7 +233,7 @@ struct symsrc {
|
||||
int fd;
|
||||
enum dso_binary_type type;
|
||||
|
||||
#ifndef NO_LIBELF_SUPPORT
|
||||
#ifdef LIBELF_SUPPORT
|
||||
Elf *elf;
|
||||
GElf_Ehdr ehdr;
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
#include "util.h"
|
||||
#include "debug.h"
|
||||
|
||||
static struct thread *thread__new(pid_t pid)
|
||||
struct thread *thread__new(pid_t pid)
|
||||
{
|
||||
struct thread *self = zalloc(sizeof(*self));
|
||||
|
||||
@ -61,45 +61,6 @@ static size_t thread__fprintf(struct thread *self, FILE *fp)
|
||||
map_groups__fprintf(&self->mg, verbose, fp);
|
||||
}
|
||||
|
||||
struct thread *machine__findnew_thread(struct machine *self, pid_t pid)
|
||||
{
|
||||
struct rb_node **p = &self->threads.rb_node;
|
||||
struct rb_node *parent = NULL;
|
||||
struct thread *th;
|
||||
|
||||
/*
|
||||
* Font-end cache - PID lookups come in blocks,
|
||||
* so most of the time we dont have to look up
|
||||
* the full rbtree:
|
||||
*/
|
||||
if (self->last_match && self->last_match->pid == pid)
|
||||
return self->last_match;
|
||||
|
||||
while (*p != NULL) {
|
||||
parent = *p;
|
||||
th = rb_entry(parent, struct thread, rb_node);
|
||||
|
||||
if (th->pid == pid) {
|
||||
self->last_match = th;
|
||||
return th;
|
||||
}
|
||||
|
||||
if (pid < th->pid)
|
||||
p = &(*p)->rb_left;
|
||||
else
|
||||
p = &(*p)->rb_right;
|
||||
}
|
||||
|
||||
th = thread__new(pid);
|
||||
if (th != NULL) {
|
||||
rb_link_node(&th->rb_node, parent, p);
|
||||
rb_insert_color(&th->rb_node, &self->threads);
|
||||
self->last_match = th;
|
||||
}
|
||||
|
||||
return th;
|
||||
}
|
||||
|
||||
void thread__insert_map(struct thread *self, struct map *map)
|
||||
{
|
||||
map_groups__fixup_overlappings(&self->mg, map, verbose, stderr);
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include <linux/rbtree.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include "symbol.h"
|
||||
|
||||
struct thread {
|
||||
@ -22,6 +23,7 @@ struct thread {
|
||||
|
||||
struct machine;
|
||||
|
||||
struct thread *thread__new(pid_t pid);
|
||||
void thread__delete(struct thread *self);
|
||||
|
||||
int thread__set_comm(struct thread *self, const char *comm);
|
||||
|
@ -47,8 +47,6 @@ int file_bigendian;
|
||||
int host_bigendian;
|
||||
static int long_size;
|
||||
|
||||
static unsigned long page_size;
|
||||
|
||||
static ssize_t calc_data_size;
|
||||
static bool repipe;
|
||||
|
||||
|
@ -13,7 +13,7 @@ struct unwind_entry {
|
||||
|
||||
typedef int (*unwind_entry_cb_t)(struct unwind_entry *entry, void *arg);
|
||||
|
||||
#ifndef NO_LIBUNWIND_SUPPORT
|
||||
#ifdef LIBUNWIND_SUPPORT
|
||||
int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
|
||||
struct machine *machine,
|
||||
struct thread *thread,
|
||||
@ -31,5 +31,5 @@ unwind__get_entries(unwind_entry_cb_t cb __maybe_unused,
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* NO_LIBUNWIND_SUPPORT */
|
||||
#endif /* LIBUNWIND_SUPPORT */
|
||||
#endif /* __UNWIND_H */
|
||||
|
@ -1,7 +1,7 @@
|
||||
#include "../perf.h"
|
||||
#include "util.h"
|
||||
#include <sys/mman.h>
|
||||
#ifndef NO_BACKTRACE
|
||||
#ifdef BACKTRACE_SUPPORT
|
||||
#include <execinfo.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
@ -10,6 +10,8 @@
|
||||
/*
|
||||
* XXX We need to find a better place for these things...
|
||||
*/
|
||||
unsigned int page_size;
|
||||
|
||||
bool perf_host = true;
|
||||
bool perf_guest = false;
|
||||
|
||||
@ -165,7 +167,7 @@ size_t hex_width(u64 v)
|
||||
}
|
||||
|
||||
/* Obtain a backtrace and print it to stdout. */
|
||||
#ifndef NO_BACKTRACE
|
||||
#ifdef BACKTRACE_SUPPORT
|
||||
void dump_stack(void)
|
||||
{
|
||||
void *array[16];
|
||||
|
@ -263,4 +263,6 @@ char *rtrim(char *s);
|
||||
|
||||
void dump_stack(void);
|
||||
|
||||
extern unsigned int page_size;
|
||||
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user