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:
Ingo Molnar 2012-10-09 15:31:17 +02:00
commit 6fcdb1ed2e
62 changed files with 2370 additions and 1484 deletions

View 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

View File

@ -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]

View File

@ -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

View File

@ -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

View File

@ -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,
},

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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();

View File

@ -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);

View File

@ -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);

View File

@ -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);
}

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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", &params.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,

View File

@ -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

View File

@ -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);

View File

@ -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),

View File

@ -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();

View File

@ -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, "");

View File

@ -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;

View File

@ -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);
}

View File

@ -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)

View File

@ -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);
}

View File

@ -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

View File

@ -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";

View File

@ -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));

View File

@ -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;

View File

@ -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)
{

View File

@ -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_ */

View File

@ -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)

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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,

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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,

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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 "};"

View File

@ -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);
}

View File

@ -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
View 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
View 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 */

View File

@ -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);

View File

@ -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);

View File

@ -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,
};

View File

@ -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);

View File

@ -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 */

View File

@ -32,7 +32,6 @@
#include "../event.h"
#include "../thread.h"
#include "../trace-event.h"
#include "../evsel.h"
PyMODINIT_FUNC initperf_trace_context(void);

View File

@ -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;

View File

@ -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];
};

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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 */

View File

@ -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];

View File

@ -263,4 +263,6 @@ char *rtrim(char *s);
void dump_stack(void);
extern unsigned int page_size;
#endif