Merge branch 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull perf updates from Ingo Molnar: "Lots of activity: 211 files changed, 8328 insertions(+), 4116 deletions(-) most of it on the tooling side. Main changes: * ftrace enhancements and fixes from Steve Rostedt. * uprobes fixes, cleanups and preparation for the ARM port from Oleg Nesterov. * UAPI fixes, from David Howels - prepares the arch/x86 UAPI transition * Separate perf tests into multiple objects, one per test, from Jiri Olsa. * Make hardware event translations available in sysfs, from Jiri Olsa. * Fixes to /proc/pid/maps parsing, preparatory to supporting data maps, from Namhyung Kim * Implement ui_progress for GTK, from Namhyung Kim * Add framework for automated perf_event_attr tests, where tools with different command line options will be run from a 'perf test', via python glue, and the perf syscall will be intercepted to verify that the perf_event_attr fields set by the tool are those expected, from Jiri Olsa * Add a 'link' method for hists, so that we can have the leader with buckets for all the entries in all the hists. This new method is now used in the default 'diff' output, making the sum of the 'baseline' column be 100%, eliminating blind spots. * libtraceevent fixes for compiler warnings trying to make perf it build on some distros, like fedora 14, 32-bit, some of the warnings really pointed to real bugs. * Add a browser for 'perf script' and make it available from the report and annotate browsers. It does filtering to find the scripts that handle events found in the perf.data file used. From Feng Tang * perf inject changes to allow showing where a task sleeps, from Andrew Vagin. * Makefile improvements from Namhyung Kim. * Add --pre and --post command hooks in 'stat', from Peter Zijlstra. * Don't stop synthesizing threads when one vanishes, this is for the existing threads when we start a tool like trace. * Use sched:sched_stat_runtime to provide a thread summary, this produces the same output as the 'trace summary' subcommand of tglx's original "trace" tool. * Support interrupted syscalls in 'trace' * Add an event duration column and filter in 'trace'. * There are references to the man pages in some tools, so try to build Documentation when installing, warning the user if that is not possible, from Borislav Petkov. * Give user better message if precise is not supported, from David Ahern. * Try to find cross-built objdump path by using the session environment information in the perf.data file header, from Irina Tirdea, original patch and idea by Namhyung Kim. * Diplays more output on features check for make V=1, so that one can figure out what is happening by looking at gcc output, etc. From Jiri Olsa. * 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. * ... and much more." * 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (194 commits) uprobes: Use percpu_rw_semaphore to fix register/unregister vs dup_mmap() race perf evsel: Introduce is_group_member method perf powerpc: Use uapi/unistd.h to fix build error tools: Pass the target in descend tools: Honour the O= flag when tool build called from a higher Makefile tools: Define a Makefile function to do subdir processing perf ui: Always compile browser setup code perf ui: Add ui_progress__finish() perf ui gtk: Implement ui_progress functions perf ui: Introduce generic ui_progress helper perf ui tui: Move progress.c under ui/tui directory perf tools: Add basic event modifier sanity check perf tools: Omit group members from perf_evlist__disable/enable perf tools: Ensure single disable call per event in record comand perf tools: Fix 'disabled' attribute config for record command perf tools: Fix attributes for '{}' defined event groups perf tools: Use sscanf for parsing /proc/pid/maps perf tools: Add gtk.<command> config option for launching GTK browser perf tools: Fix compile error on NO_NEWT=1 build perf hists: Initialize all of he->stat with zeroes ...
This commit is contained in:
@@ -253,7 +253,7 @@ all_deps := $(all_objs:%.o=.%.d)
|
||||
# let .d file also depends on the source and header files
|
||||
define check_deps
|
||||
@set -e; $(RM) $@; \
|
||||
$(CC) -M $(CFLAGS) $< > $@.$$$$; \
|
||||
$(CC) -MM $(CFLAGS) $< > $@.$$$$; \
|
||||
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
|
||||
$(RM) $@.$$$$
|
||||
endef
|
||||
|
@@ -174,7 +174,7 @@ static int cmdline_init(struct pevent *pevent)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *find_cmdline(struct pevent *pevent, int pid)
|
||||
static const char *find_cmdline(struct pevent *pevent, int pid)
|
||||
{
|
||||
const struct cmdline *comm;
|
||||
struct cmdline key;
|
||||
@@ -2637,7 +2637,7 @@ process_func_handler(struct event_format *event, struct pevent_function_handler
|
||||
struct print_arg *farg;
|
||||
enum event_type type;
|
||||
char *token;
|
||||
char *test;
|
||||
const char *test;
|
||||
int i;
|
||||
|
||||
arg->type = PRINT_FUNC;
|
||||
@@ -3889,7 +3889,7 @@ static void print_mac_arg(struct trace_seq *s, int mac, void *data, int size,
|
||||
struct event_format *event, struct print_arg *arg)
|
||||
{
|
||||
unsigned char *buf;
|
||||
char *fmt = "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x";
|
||||
const char *fmt = "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x";
|
||||
|
||||
if (arg->type == PRINT_FUNC) {
|
||||
process_defined_func(s, data, size, event, arg);
|
||||
@@ -3931,7 +3931,8 @@ static int is_printable_array(char *p, unsigned int len)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void print_event_fields(struct trace_seq *s, void *data, int size,
|
||||
static void print_event_fields(struct trace_seq *s, void *data,
|
||||
int size __maybe_unused,
|
||||
struct event_format *event)
|
||||
{
|
||||
struct format_field *field;
|
||||
@@ -4408,7 +4409,7 @@ void pevent_event_info(struct trace_seq *s, struct event_format *event,
|
||||
void pevent_print_event(struct pevent *pevent, struct trace_seq *s,
|
||||
struct pevent_record *record)
|
||||
{
|
||||
static char *spaces = " "; /* 20 spaces */
|
||||
static const char *spaces = " "; /* 20 spaces */
|
||||
struct event_format *event;
|
||||
unsigned long secs;
|
||||
unsigned long usecs;
|
||||
@@ -5070,8 +5071,8 @@ static const char * const pevent_error_str[] = {
|
||||
};
|
||||
#undef _PE
|
||||
|
||||
int pevent_strerror(struct pevent *pevent, enum pevent_errno errnum,
|
||||
char *buf, size_t buflen)
|
||||
int pevent_strerror(struct pevent *pevent __maybe_unused,
|
||||
enum pevent_errno errnum, char *buf, size_t buflen)
|
||||
{
|
||||
int idx;
|
||||
const char *msg;
|
||||
@@ -5100,6 +5101,7 @@ int pevent_strerror(struct pevent *pevent, enum pevent_errno errnum,
|
||||
case PEVENT_ERRNO__READ_FORMAT_FAILED:
|
||||
case PEVENT_ERRNO__READ_PRINT_FAILED:
|
||||
case PEVENT_ERRNO__OLD_FTRACE_ARG_FAILED:
|
||||
case PEVENT_ERRNO__INVALID_ARG_TYPE:
|
||||
snprintf(buf, buflen, "%s", msg);
|
||||
break;
|
||||
|
||||
@@ -5362,7 +5364,7 @@ int pevent_register_print_function(struct pevent *pevent,
|
||||
if (type == PEVENT_FUNC_ARG_VOID)
|
||||
break;
|
||||
|
||||
if (type < 0 || type >= PEVENT_FUNC_ARG_MAX_TYPES) {
|
||||
if (type >= PEVENT_FUNC_ARG_MAX_TYPES) {
|
||||
do_warning("Invalid argument type %d", type);
|
||||
ret = PEVENT_ERRNO__INVALID_ARG_TYPE;
|
||||
goto out_free;
|
||||
@@ -5560,7 +5562,7 @@ void pevent_free(struct pevent *pevent)
|
||||
}
|
||||
|
||||
if (pevent->func_map) {
|
||||
for (i = 0; i < pevent->func_count; i++) {
|
||||
for (i = 0; i < (int)pevent->func_count; i++) {
|
||||
free(pevent->func_map[i].func);
|
||||
free(pevent->func_map[i].mod);
|
||||
}
|
||||
@@ -5582,7 +5584,7 @@ void pevent_free(struct pevent *pevent)
|
||||
}
|
||||
|
||||
if (pevent->printk_map) {
|
||||
for (i = 0; i < pevent->printk_count; i++)
|
||||
for (i = 0; i < (int)pevent->printk_count; i++)
|
||||
free(pevent->printk_map[i].printk);
|
||||
free(pevent->printk_map);
|
||||
}
|
||||
|
@@ -1,3 +1,5 @@
|
||||
include ../config/utilities.mak
|
||||
|
||||
OUTPUT := ./
|
||||
ifeq ("$(origin O)", "command line")
|
||||
ifneq ($(O),)
|
||||
@@ -64,6 +66,7 @@ MAKEINFO=makeinfo
|
||||
INSTALL_INFO=install-info
|
||||
DOCBOOK2X_TEXI=docbook2x-texi
|
||||
DBLATEX=dblatex
|
||||
XMLTO=xmlto
|
||||
ifndef PERL_PATH
|
||||
PERL_PATH = /usr/bin/perl
|
||||
endif
|
||||
@@ -71,6 +74,16 @@ endif
|
||||
-include ../config.mak.autogen
|
||||
-include ../config.mak
|
||||
|
||||
_tmp_tool_path := $(call get-executable,$(ASCIIDOC))
|
||||
ifeq ($(_tmp_tool_path),)
|
||||
missing_tools = $(ASCIIDOC)
|
||||
endif
|
||||
|
||||
_tmp_tool_path := $(call get-executable,$(XMLTO))
|
||||
ifeq ($(_tmp_tool_path),)
|
||||
missing_tools += $(XMLTO)
|
||||
endif
|
||||
|
||||
#
|
||||
# For asciidoc ...
|
||||
# -7.1.2, no extra settings are needed.
|
||||
@@ -170,7 +183,12 @@ pdf: $(OUTPUT)user-manual.pdf
|
||||
|
||||
install: install-man
|
||||
|
||||
install-man: man
|
||||
check-man-tools:
|
||||
ifdef missing_tools
|
||||
$(error "You need to install $(missing_tools) for man pages")
|
||||
endif
|
||||
|
||||
do-install-man: man
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(man1dir)
|
||||
# $(INSTALL) -d -m 755 $(DESTDIR)$(man5dir)
|
||||
# $(INSTALL) -d -m 755 $(DESTDIR)$(man7dir)
|
||||
@@ -178,6 +196,15 @@ install-man: man
|
||||
# $(INSTALL) -m 644 $(DOC_MAN5) $(DESTDIR)$(man5dir)
|
||||
# $(INSTALL) -m 644 $(DOC_MAN7) $(DESTDIR)$(man7dir)
|
||||
|
||||
install-man: check-man-tools man
|
||||
|
||||
try-install-man:
|
||||
ifdef missing_tools
|
||||
$(warning Please install $(missing_tools) to have the man pages installed)
|
||||
else
|
||||
$(MAKE) do-install-man
|
||||
endif
|
||||
|
||||
install-info: info
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(infodir)
|
||||
$(INSTALL) -m 644 $(OUTPUT)perf.info $(OUTPUT)perfman.info $(DESTDIR)$(infodir)
|
||||
@@ -246,7 +273,7 @@ $(MAN_HTML): $(OUTPUT)%.html : %.txt
|
||||
|
||||
$(OUTPUT)%.1 $(OUTPUT)%.5 $(OUTPUT)%.7 : $(OUTPUT)%.xml
|
||||
$(QUIET_XMLTO)$(RM) $@ && \
|
||||
xmlto -o $(OUTPUT) -m $(MANPAGE_XSL) $(XMLTO_EXTRA) man $<
|
||||
$(XMLTO) -o $(OUTPUT) -m $(MANPAGE_XSL) $(XMLTO_EXTRA) man $<
|
||||
|
||||
$(OUTPUT)%.xml : %.txt
|
||||
$(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
|
||||
|
78
tools/perf/Documentation/android.txt
Normal file
78
tools/perf/Documentation/android.txt
Normal file
@@ -0,0 +1,78 @@
|
||||
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:
|
||||
For arm:
|
||||
make ARCH=arm CROSS_COMPILE=${NDK_TOOLCHAIN} CFLAGS="--sysroot=${NDK_SYSROOT}"
|
||||
For x86:
|
||||
make ARCH=x86 CROSS_COMPILE=${NDK_TOOLCHAIN} CFLAGS="--sysroot=${NDK_SYSROOT}"
|
||||
|
||||
III. Install perf
|
||||
-----------------------------------------------
|
||||
You need to connect to your Android device/emulator using adb.
|
||||
Install perf using:
|
||||
adb push perf /data/perf
|
||||
|
||||
If you also want to use perf-archive you need busybox tools for Android.
|
||||
For installing perf-archive, you first need to replace #!/bin/bash with #!/system/bin/sh:
|
||||
sed 's/#!\/bin\/bash/#!\/system\/bin\/sh/g' perf-archive >> /tmp/perf-archive
|
||||
chmod +x /tmp/perf-archive
|
||||
adb push /tmp/perf-archive /data/perf-archive
|
||||
|
||||
IV. Environment settings for running perf
|
||||
------------------------------------------------
|
||||
Some perf features need environment variables to run properly.
|
||||
You need to set these before running perf on the target:
|
||||
adb shell
|
||||
# PERF_PAGER=cat
|
||||
|
||||
IV. Run perf
|
||||
------------------------------------------------
|
||||
Run perf on your device/emulator to which you previously connected using adb:
|
||||
# ./data/perf
|
@@ -72,6 +72,66 @@ OPTIONS
|
||||
--symfs=<directory>::
|
||||
Look for files with symbols relative to this directory.
|
||||
|
||||
-b::
|
||||
--baseline-only::
|
||||
Show only items with match in baseline.
|
||||
|
||||
-c::
|
||||
--compute::
|
||||
Differential computation selection - delta,ratio,wdiff (default is delta).
|
||||
If '+' is specified as a first character, the output is sorted based
|
||||
on the computation results.
|
||||
See COMPARISON METHODS section for more info.
|
||||
|
||||
-p::
|
||||
--period::
|
||||
Show period values for both compared hist entries.
|
||||
|
||||
-F::
|
||||
--formula::
|
||||
Show formula for given computation.
|
||||
|
||||
COMPARISON METHODS
|
||||
------------------
|
||||
delta
|
||||
~~~~~
|
||||
If specified the 'Delta' column is displayed with value 'd' computed as:
|
||||
|
||||
d = A->period_percent - B->period_percent
|
||||
|
||||
with:
|
||||
- A/B being matching hist entry from first/second file specified
|
||||
(or perf.data/perf.data.old) respectively.
|
||||
|
||||
- period_percent being the % of the hist entry period value within
|
||||
single data file
|
||||
|
||||
ratio
|
||||
~~~~~
|
||||
If specified the 'Ratio' column is displayed with value 'r' computed as:
|
||||
|
||||
r = A->period / B->period
|
||||
|
||||
with:
|
||||
- A/B being matching hist entry from first/second file specified
|
||||
(or perf.data/perf.data.old) respectively.
|
||||
|
||||
- period being the hist entry period value
|
||||
|
||||
wdiff
|
||||
~~~~~
|
||||
If specified the 'Weighted diff' column is displayed with value 'd' computed as:
|
||||
|
||||
d = B->period * WEIGHT-A - A->period * WEIGHT-B
|
||||
|
||||
- A/B being matching hist entry from first/second file specified
|
||||
(or perf.data/perf.data.old) respectively.
|
||||
|
||||
- period being the hist entry period value
|
||||
|
||||
- WEIGHT-A/WEIGHT-B being user suplied weights in the the '-c' option
|
||||
behind ':' separator like '-c wdiff:1,2'.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-record[1]
|
||||
|
@@ -29,6 +29,17 @@ OPTIONS
|
||||
-v::
|
||||
--verbose::
|
||||
Be more verbose.
|
||||
-i::
|
||||
--input=::
|
||||
Input file name. (default: stdin)
|
||||
-o::
|
||||
--output=::
|
||||
Output file name. (default: stdout)
|
||||
-s::
|
||||
--sched-stat::
|
||||
Merge sched_stat and sched_switch for getting events where and how long
|
||||
tasks slept. sched_switch contains a callchain where a task slept and
|
||||
sched_stat contains a timeslice how long a task slept.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
|
@@ -108,6 +108,11 @@ with it. --append may be used here. Examples:
|
||||
3>results perf stat --log-fd 3 -- $cmd
|
||||
3>>results perf stat --log-fd 3 --append -- $cmd
|
||||
|
||||
--pre::
|
||||
--post::
|
||||
Pre and post measurement hooks, e.g.:
|
||||
|
||||
perf stat --repeat 10 --null --sync --pre 'make -s O=defconfig-build/clean' -- make -s -j64 O=defconfig-build/ bzImage
|
||||
|
||||
|
||||
EXAMPLES
|
||||
|
@@ -48,6 +48,12 @@ comma-separated list with no space: 0,1. Ranges of CPUs are specified with -: 0-
|
||||
In per-thread mode with inheritance mode on (default), Events are captured only when
|
||||
the thread executes on the designated CPUs. Default is to monitor all CPUs.
|
||||
|
||||
--duration:
|
||||
Show only events that had a duration greater than N.M ms.
|
||||
|
||||
--sched:
|
||||
Accrue thread runtime and provide a summary at the end of the session.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-record[1], linkperf:perf-script[1]
|
||||
|
@@ -155,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,-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,-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,-Wvolatile-register-var),y)
|
||||
CFLAGS := $(CFLAGS) -Wvolatile-register-var
|
||||
endif
|
||||
|
||||
@@ -197,8 +197,16 @@ BASIC_CFLAGS = \
|
||||
-I. \
|
||||
-I$(TRACE_EVENT_DIR) \
|
||||
-D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE
|
||||
|
||||
BASIC_LDFLAGS =
|
||||
|
||||
ifeq ($(call try-cc,$(SOURCE_BIONIC),$(CFLAGS),bionic),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 =
|
||||
@@ -330,6 +338,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
|
||||
@@ -346,6 +355,7 @@ LIB_H += util/svghelper.h
|
||||
LIB_H += util/tool.h
|
||||
LIB_H += util/run-command.h
|
||||
LIB_H += util/sigchain.h
|
||||
LIB_H += util/dso.h
|
||||
LIB_H += util/symbol.h
|
||||
LIB_H += util/color.h
|
||||
LIB_H += util/values.h
|
||||
@@ -389,7 +399,6 @@ LIB_OBJS += $(OUTPUT)util/help.o
|
||||
LIB_OBJS += $(OUTPUT)util/levenshtein.o
|
||||
LIB_OBJS += $(OUTPUT)util/parse-options.o
|
||||
LIB_OBJS += $(OUTPUT)util/parse-events.o
|
||||
LIB_OBJS += $(OUTPUT)util/parse-events-test.o
|
||||
LIB_OBJS += $(OUTPUT)util/path.o
|
||||
LIB_OBJS += $(OUTPUT)util/rbtree.o
|
||||
LIB_OBJS += $(OUTPUT)util/bitmap.o
|
||||
@@ -404,15 +413,16 @@ LIB_OBJS += $(OUTPUT)util/top.o
|
||||
LIB_OBJS += $(OUTPUT)util/usage.o
|
||||
LIB_OBJS += $(OUTPUT)util/wrapper.o
|
||||
LIB_OBJS += $(OUTPUT)util/sigchain.o
|
||||
LIB_OBJS += $(OUTPUT)util/dso.o
|
||||
LIB_OBJS += $(OUTPUT)util/symbol.o
|
||||
LIB_OBJS += $(OUTPUT)util/symbol-elf.o
|
||||
LIB_OBJS += $(OUTPUT)util/dso-test-data.o
|
||||
LIB_OBJS += $(OUTPUT)util/color.o
|
||||
LIB_OBJS += $(OUTPUT)util/pager.o
|
||||
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
|
||||
@@ -440,10 +450,29 @@ LIB_OBJS += $(OUTPUT)util/intlist.o
|
||||
LIB_OBJS += $(OUTPUT)util/vdso.o
|
||||
LIB_OBJS += $(OUTPUT)util/stat.o
|
||||
|
||||
LIB_OBJS += $(OUTPUT)ui/setup.o
|
||||
LIB_OBJS += $(OUTPUT)ui/helpline.o
|
||||
LIB_OBJS += $(OUTPUT)ui/progress.o
|
||||
LIB_OBJS += $(OUTPUT)ui/hist.o
|
||||
LIB_OBJS += $(OUTPUT)ui/stdio/hist.o
|
||||
|
||||
LIB_OBJS += $(OUTPUT)arch/common.o
|
||||
|
||||
LIB_OBJS += $(OUTPUT)tests/parse-events.o
|
||||
LIB_OBJS += $(OUTPUT)tests/dso-data.o
|
||||
LIB_OBJS += $(OUTPUT)tests/attr.o
|
||||
LIB_OBJS += $(OUTPUT)tests/vmlinux-kallsyms.o
|
||||
LIB_OBJS += $(OUTPUT)tests/open-syscall.o
|
||||
LIB_OBJS += $(OUTPUT)tests/open-syscall-all-cpus.o
|
||||
LIB_OBJS += $(OUTPUT)tests/open-syscall-tp-fields.o
|
||||
LIB_OBJS += $(OUTPUT)tests/mmap-basic.o
|
||||
LIB_OBJS += $(OUTPUT)tests/perf-record.o
|
||||
LIB_OBJS += $(OUTPUT)tests/rdpmc.o
|
||||
LIB_OBJS += $(OUTPUT)tests/evsel-roundtrip-name.o
|
||||
LIB_OBJS += $(OUTPUT)tests/evsel-tp-sched.o
|
||||
LIB_OBJS += $(OUTPUT)tests/pmu.o
|
||||
LIB_OBJS += $(OUTPUT)tests/util.o
|
||||
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-bench.o
|
||||
# Benchmark modules
|
||||
@@ -473,8 +502,8 @@ BUILTIN_OBJS += $(OUTPUT)builtin-probe.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-kmem.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-lock.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-kvm.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-test.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-inject.o
|
||||
BUILTIN_OBJS += $(OUTPUT)tests/builtin-test.o
|
||||
|
||||
PERFLIBS = $(LIB_FILE) $(LIBTRACEEVENT)
|
||||
|
||||
@@ -495,18 +524,33 @@ ifdef NO_LIBELF
|
||||
NO_LIBUNWIND := 1
|
||||
else
|
||||
FLAGS_LIBELF=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS)
|
||||
ifneq ($(call try-cc,$(SOURCE_LIBELF),$(FLAGS_LIBELF)),y)
|
||||
ifneq ($(call try-cc,$(SOURCE_LIBELF),$(FLAGS_LIBELF),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),glibc),y)
|
||||
LIBC_SUPPORT := 1
|
||||
endif
|
||||
ifeq ($(BIONIC),1)
|
||||
LIBC_SUPPORT := 1
|
||||
endif
|
||||
ifeq ($(LIBC_SUPPORT),1)
|
||||
msg := $(warning No libelf found, disables 'probe' tool, please install elfutils-libelf-devel/libelf-dev);
|
||||
|
||||
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
|
||||
else
|
||||
FLAGS_DWARF=$(ALL_CFLAGS) -ldw -lelf $(ALL_LDFLAGS) $(EXTLIBS)
|
||||
ifneq ($(call try-cc,$(SOURCE_DWARF),$(FLAGS_DWARF)),y)
|
||||
# for linking with debug library, run like:
|
||||
# make DEBUG=1 LIBDW_DIR=/opt/libdw/
|
||||
ifdef LIBDW_DIR
|
||||
LIBDW_CFLAGS := -I$(LIBDW_DIR)/include
|
||||
LIBDW_LDFLAGS := -L$(LIBDW_DIR)/lib
|
||||
endif
|
||||
|
||||
FLAGS_DWARF=$(ALL_CFLAGS) $(LIBDW_CFLAGS) -ldw -lelf $(LIBDW_LDFLAGS) $(ALL_LDFLAGS) $(EXTLIBS)
|
||||
ifneq ($(call try-cc,$(SOURCE_DWARF),$(FLAGS_DWARF),libdw),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
|
||||
@@ -522,7 +566,7 @@ ifdef LIBUNWIND_DIR
|
||||
endif
|
||||
|
||||
FLAGS_UNWIND=$(LIBUNWIND_CFLAGS) $(ALL_CFLAGS) $(LIBUNWIND_LDFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) $(LIBUNWIND_LIBS)
|
||||
ifneq ($(call try-cc,$(SOURCE_LIBUNWIND),$(FLAGS_UNWIND)),y)
|
||||
ifneq ($(call try-cc,$(SOURCE_LIBUNWIND),$(FLAGS_UNWIND),libunwind),y)
|
||||
msg := $(warning No libunwind found, disabling post unwind support. Please install libunwind-dev[el] >= 0.99);
|
||||
NO_LIBUNWIND := 1
|
||||
endif # Libunwind support
|
||||
@@ -551,7 +595,8 @@ LIB_OBJS += $(OUTPUT)util/symbol-minimal.o
|
||||
else # NO_LIBELF
|
||||
BASIC_CFLAGS += -DLIBELF_SUPPORT
|
||||
|
||||
ifeq ($(call try-cc,$(SOURCE_ELF_MMAP),$(FLAGS_COMMON)),y)
|
||||
FLAGS_LIBELF=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS)
|
||||
ifeq ($(call try-cc,$(SOURCE_ELF_MMAP),$(FLAGS_LIBELF),-DLIBELF_MMAP),y)
|
||||
BASIC_CFLAGS += -DLIBELF_MMAP
|
||||
endif
|
||||
|
||||
@@ -559,7 +604,8 @@ 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);
|
||||
else
|
||||
BASIC_CFLAGS += -DDWARF_SUPPORT
|
||||
BASIC_CFLAGS := -DDWARF_SUPPORT $(LIBDW_CFLAGS) $(BASIC_CFLAGS)
|
||||
BASIC_LDFLAGS := $(LIBDW_LDFLAGS) $(BASIC_LDFLAGS)
|
||||
EXTLIBS += -lelf -ldw
|
||||
LIB_OBJS += $(OUTPUT)util/probe-finder.o
|
||||
LIB_OBJS += $(OUTPUT)util/dwarf-aux.o
|
||||
@@ -577,7 +623,7 @@ endif
|
||||
|
||||
ifndef NO_LIBAUDIT
|
||||
FLAGS_LIBAUDIT = $(ALL_CFLAGS) $(ALL_LDFLAGS) -laudit
|
||||
ifneq ($(call try-cc,$(SOURCE_LIBAUDIT),$(FLAGS_LIBAUDIT)),y)
|
||||
ifneq ($(call try-cc,$(SOURCE_LIBAUDIT),$(FLAGS_LIBAUDIT),libaudit),y)
|
||||
msg := $(warning No libaudit.h found, disables 'trace' tool, please install audit-libs-devel or libaudit-dev);
|
||||
else
|
||||
BASIC_CFLAGS += -DLIBAUDIT_SUPPORT
|
||||
@@ -588,23 +634,23 @@ endif
|
||||
|
||||
ifndef NO_NEWT
|
||||
FLAGS_NEWT=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -lnewt
|
||||
ifneq ($(call try-cc,$(SOURCE_NEWT),$(FLAGS_NEWT)),y)
|
||||
ifneq ($(call try-cc,$(SOURCE_NEWT),$(FLAGS_NEWT),libnewt),y)
|
||||
msg := $(warning newt not found, disables TUI support. Please install newt-devel or libnewt-dev);
|
||||
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
|
||||
LIB_OBJS += $(OUTPUT)ui/browsers/annotate.o
|
||||
LIB_OBJS += $(OUTPUT)ui/browsers/hists.o
|
||||
LIB_OBJS += $(OUTPUT)ui/browsers/map.o
|
||||
LIB_OBJS += $(OUTPUT)ui/progress.o
|
||||
LIB_OBJS += $(OUTPUT)ui/browsers/scripts.o
|
||||
LIB_OBJS += $(OUTPUT)ui/util.o
|
||||
LIB_OBJS += $(OUTPUT)ui/tui/setup.o
|
||||
LIB_OBJS += $(OUTPUT)ui/tui/util.o
|
||||
LIB_OBJS += $(OUTPUT)ui/tui/helpline.o
|
||||
LIB_OBJS += $(OUTPUT)ui/tui/progress.o
|
||||
LIB_H += ui/browser.h
|
||||
LIB_H += ui/browsers/map.h
|
||||
LIB_H += ui/keysyms.h
|
||||
@@ -617,10 +663,10 @@ endif
|
||||
|
||||
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)
|
||||
ifneq ($(call try-cc,$(SOURCE_GTK2),$(FLAGS_GTK2),gtk2),y)
|
||||
msg := $(warning GTK2 not found, disables GTK2 support. Please install gtk2-devel or libgtk2.0-dev);
|
||||
else
|
||||
ifeq ($(call try-cc,$(SOURCE_GTK2_INFOBAR),$(FLAGS_GTK2)),y)
|
||||
ifeq ($(call try-cc,$(SOURCE_GTK2_INFOBAR),$(FLAGS_GTK2),-DHAVE_GTK_INFO_BAR),y)
|
||||
BASIC_CFLAGS += -DHAVE_GTK_INFO_BAR
|
||||
endif
|
||||
BASIC_CFLAGS += -DGTK2_SUPPORT
|
||||
@@ -630,9 +676,9 @@ ifndef NO_GTK2
|
||||
LIB_OBJS += $(OUTPUT)ui/gtk/setup.o
|
||||
LIB_OBJS += $(OUTPUT)ui/gtk/util.o
|
||||
LIB_OBJS += $(OUTPUT)ui/gtk/helpline.o
|
||||
LIB_OBJS += $(OUTPUT)ui/gtk/progress.o
|
||||
# Make sure that it'd be included only once.
|
||||
ifeq ($(findstring -DNEWT_SUPPORT,$(BASIC_CFLAGS)),)
|
||||
LIB_OBJS += $(OUTPUT)ui/setup.o
|
||||
LIB_OBJS += $(OUTPUT)ui/util.o
|
||||
endif
|
||||
endif
|
||||
@@ -647,7 +693,7 @@ else
|
||||
PERL_EMBED_CCOPTS = `perl -MExtUtils::Embed -e ccopts 2>/dev/null`
|
||||
FLAGS_PERL_EMBED=$(PERL_EMBED_CCOPTS) $(PERL_EMBED_LDOPTS)
|
||||
|
||||
ifneq ($(call try-cc,$(SOURCE_PERL_EMBED),$(FLAGS_PERL_EMBED)),y)
|
||||
ifneq ($(call try-cc,$(SOURCE_PERL_EMBED),$(FLAGS_PERL_EMBED),perl),y)
|
||||
BASIC_CFLAGS += -DNO_LIBPERL
|
||||
else
|
||||
ALL_LDFLAGS += $(PERL_EMBED_LDFLAGS)
|
||||
@@ -701,11 +747,11 @@ else
|
||||
PYTHON_EMBED_CCOPTS := $(shell $(PYTHON_CONFIG_SQ) --cflags 2>/dev/null)
|
||||
FLAGS_PYTHON_EMBED := $(PYTHON_EMBED_CCOPTS) $(PYTHON_EMBED_LDOPTS)
|
||||
|
||||
ifneq ($(call try-cc,$(SOURCE_PYTHON_EMBED),$(FLAGS_PYTHON_EMBED)),y)
|
||||
ifneq ($(call try-cc,$(SOURCE_PYTHON_EMBED),$(FLAGS_PYTHON_EMBED),python),y)
|
||||
$(call disable-python,Python.h (for Python 2.x))
|
||||
else
|
||||
|
||||
ifneq ($(call try-cc,$(SOURCE_PYTHON_VERSION),$(FLAGS_PYTHON_EMBED)),y)
|
||||
ifneq ($(call try-cc,$(SOURCE_PYTHON_VERSION),$(FLAGS_PYTHON_EMBED),python version),y)
|
||||
$(warning Python 3 is not yet supported; please set)
|
||||
$(warning PYTHON and/or PYTHON_CONFIG appropriately.)
|
||||
$(warning If you also have Python 2 installed, then)
|
||||
@@ -739,22 +785,22 @@ else
|
||||
BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE
|
||||
else
|
||||
FLAGS_BFD=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -DPACKAGE='perf' -lbfd
|
||||
has_bfd := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD))
|
||||
has_bfd := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD),libbfd)
|
||||
ifeq ($(has_bfd),y)
|
||||
EXTLIBS += -lbfd
|
||||
else
|
||||
FLAGS_BFD_IBERTY=$(FLAGS_BFD) -liberty
|
||||
has_bfd_iberty := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD_IBERTY))
|
||||
has_bfd_iberty := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD_IBERTY),liberty)
|
||||
ifeq ($(has_bfd_iberty),y)
|
||||
EXTLIBS += -lbfd -liberty
|
||||
else
|
||||
FLAGS_BFD_IBERTY_Z=$(FLAGS_BFD_IBERTY) -lz
|
||||
has_bfd_iberty_z := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD_IBERTY_Z))
|
||||
has_bfd_iberty_z := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD_IBERTY_Z),libz)
|
||||
ifeq ($(has_bfd_iberty_z),y)
|
||||
EXTLIBS += -lbfd -liberty -lz
|
||||
else
|
||||
FLAGS_CPLUS_DEMANGLE=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -liberty
|
||||
has_cplus_demangle := $(call try-cc,$(SOURCE_CPLUS_DEMANGLE),$(FLAGS_CPLUS_DEMANGLE))
|
||||
has_cplus_demangle := $(call try-cc,$(SOURCE_CPLUS_DEMANGLE),$(FLAGS_CPLUS_DEMANGLE),demangle)
|
||||
ifeq ($(has_cplus_demangle),y)
|
||||
EXTLIBS += -liberty
|
||||
BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE
|
||||
@@ -776,13 +822,19 @@ ifeq ($(NO_PERF_REGS),0)
|
||||
endif
|
||||
|
||||
ifndef NO_STRLCPY
|
||||
ifeq ($(call try-cc,$(SOURCE_STRLCPY),),y)
|
||||
ifeq ($(call try-cc,$(SOURCE_STRLCPY),,-DHAVE_STRLCPY),y)
|
||||
BASIC_CFLAGS += -DHAVE_STRLCPY
|
||||
endif
|
||||
endif
|
||||
|
||||
ifndef NO_ON_EXIT
|
||||
ifeq ($(call try-cc,$(SOURCE_ON_EXIT),,-DHAVE_ON_EXIT),y)
|
||||
BASIC_CFLAGS += -DHAVE_ON_EXIT
|
||||
endif
|
||||
endif
|
||||
|
||||
ifndef NO_BACKTRACE
|
||||
ifeq ($(call try-cc,$(SOURCE_BACKTRACE),),y)
|
||||
ifeq ($(call try-cc,$(SOURCE_BACKTRACE),,-DBACKTRACE_SUPPORT),y)
|
||||
BASIC_CFLAGS += -DBACKTRACE_SUPPORT
|
||||
endif
|
||||
endif
|
||||
@@ -891,10 +943,14 @@ $(OUTPUT)%.s: %.S
|
||||
$(OUTPUT)util/exec_cmd.o: util/exec_cmd.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \
|
||||
'-DPERF_EXEC_PATH="$(perfexecdir_SQ)"' \
|
||||
'-DBINDIR="$(bindir_relative_SQ)"' \
|
||||
'-DPREFIX="$(prefix_SQ)"' \
|
||||
$<
|
||||
|
||||
$(OUTPUT)tests/attr.o: tests/attr.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \
|
||||
'-DBINDIR="$(bindir_SQ)"' \
|
||||
$<
|
||||
|
||||
$(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $<
|
||||
|
||||
@@ -910,6 +966,9 @@ $(OUTPUT)ui/browsers/hists.o: ui/browsers/hists.c $(OUTPUT)PERF-CFLAGS
|
||||
$(OUTPUT)ui/browsers/map.o: ui/browsers/map.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $<
|
||||
|
||||
$(OUTPUT)ui/browsers/scripts.o: ui/browsers/scripts.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $<
|
||||
|
||||
$(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -Wno-unused-parameter -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $<
|
||||
|
||||
@@ -981,20 +1040,15 @@ help:
|
||||
@echo 'Perf maintainer targets:'
|
||||
@echo ' clean - clean all binary objects and build output'
|
||||
|
||||
doc:
|
||||
$(MAKE) -C Documentation all
|
||||
|
||||
man:
|
||||
$(MAKE) -C Documentation man
|
||||
DOC_TARGETS := doc man html info pdf
|
||||
|
||||
html:
|
||||
$(MAKE) -C Documentation html
|
||||
INSTALL_DOC_TARGETS := $(patsubst %,install-%,$(DOC_TARGETS)) try-install-man
|
||||
INSTALL_DOC_TARGETS += quick-install-doc quick-install-man quick-install-html
|
||||
|
||||
info:
|
||||
$(MAKE) -C Documentation info
|
||||
|
||||
pdf:
|
||||
$(MAKE) -C Documentation pdf
|
||||
# 'make doc' should call 'make -C Documentation all'
|
||||
$(DOC_TARGETS):
|
||||
$(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) $(@:doc=all)
|
||||
|
||||
TAGS:
|
||||
$(RM) TAGS
|
||||
@@ -1045,7 +1099,7 @@ perfexec_instdir = $(prefix)/$(perfexecdir)
|
||||
endif
|
||||
perfexec_instdir_SQ = $(subst ','\'',$(perfexec_instdir))
|
||||
|
||||
install: all
|
||||
install: all try-install-man
|
||||
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)'
|
||||
$(INSTALL) $(OUTPUT)perf '$(DESTDIR_SQ)$(bindir_SQ)'
|
||||
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace'
|
||||
@@ -1061,33 +1115,17 @@ install: all
|
||||
$(INSTALL) scripts/python/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/bin'
|
||||
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d'
|
||||
$(INSTALL) bash_completion '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d/perf'
|
||||
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests'
|
||||
$(INSTALL) tests/attr.py '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests'
|
||||
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/attr'
|
||||
$(INSTALL) tests/attr/* '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/attr'
|
||||
|
||||
install-python_ext:
|
||||
$(PYTHON_WORD) util/setup.py --quiet install --root='/$(DESTDIR_SQ)'
|
||||
|
||||
install-doc:
|
||||
$(MAKE) -C Documentation install
|
||||
|
||||
install-man:
|
||||
$(MAKE) -C Documentation install-man
|
||||
|
||||
install-html:
|
||||
$(MAKE) -C Documentation install-html
|
||||
|
||||
install-info:
|
||||
$(MAKE) -C Documentation install-info
|
||||
|
||||
install-pdf:
|
||||
$(MAKE) -C Documentation install-pdf
|
||||
|
||||
quick-install-doc:
|
||||
$(MAKE) -C Documentation quick-install
|
||||
|
||||
quick-install-man:
|
||||
$(MAKE) -C Documentation quick-install-man
|
||||
|
||||
quick-install-html:
|
||||
$(MAKE) -C Documentation quick-install-html
|
||||
# 'make install-doc' should call 'make -C Documentation install'
|
||||
$(INSTALL_DOC_TARGETS):
|
||||
$(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) $(@:-doc=)
|
||||
|
||||
### Cleaning rules
|
||||
|
||||
@@ -1095,7 +1133,7 @@ clean: $(LIBTRACEEVENT)-clean
|
||||
$(RM) $(LIB_OBJS) $(BUILTIN_OBJS) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf.o $(LANG_BINDINGS)
|
||||
$(RM) $(ALL_PROGRAMS) perf
|
||||
$(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope*
|
||||
$(MAKE) -C Documentation/ clean
|
||||
$(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) clean
|
||||
$(RM) $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)PERF-CFLAGS
|
||||
$(RM) $(OUTPUT)util/*-bison*
|
||||
$(RM) $(OUTPUT)util/*-flex*
|
||||
|
211
tools/perf/arch/common.c
Normal file
211
tools/perf/arch/common.c
Normal file
@@ -0,0 +1,211 @@
|
||||
#include <stdio.h>
|
||||
#include <sys/utsname.h>
|
||||
#include "common.h"
|
||||
#include "../util/debug.h"
|
||||
|
||||
const char *const arm_triplets[] = {
|
||||
"arm-eabi-",
|
||||
"arm-linux-androideabi-",
|
||||
"arm-unknown-linux-",
|
||||
"arm-unknown-linux-gnu-",
|
||||
"arm-unknown-linux-gnueabi-",
|
||||
NULL
|
||||
};
|
||||
|
||||
const char *const powerpc_triplets[] = {
|
||||
"powerpc-unknown-linux-gnu-",
|
||||
"powerpc64-unknown-linux-gnu-",
|
||||
NULL
|
||||
};
|
||||
|
||||
const char *const s390_triplets[] = {
|
||||
"s390-ibm-linux-",
|
||||
NULL
|
||||
};
|
||||
|
||||
const char *const sh_triplets[] = {
|
||||
"sh-unknown-linux-gnu-",
|
||||
"sh64-unknown-linux-gnu-",
|
||||
NULL
|
||||
};
|
||||
|
||||
const char *const sparc_triplets[] = {
|
||||
"sparc-unknown-linux-gnu-",
|
||||
"sparc64-unknown-linux-gnu-",
|
||||
NULL
|
||||
};
|
||||
|
||||
const char *const x86_triplets[] = {
|
||||
"x86_64-pc-linux-gnu-",
|
||||
"x86_64-unknown-linux-gnu-",
|
||||
"i686-pc-linux-gnu-",
|
||||
"i586-pc-linux-gnu-",
|
||||
"i486-pc-linux-gnu-",
|
||||
"i386-pc-linux-gnu-",
|
||||
"i686-linux-android-",
|
||||
"i686-android-linux-",
|
||||
NULL
|
||||
};
|
||||
|
||||
const char *const mips_triplets[] = {
|
||||
"mips-unknown-linux-gnu-",
|
||||
"mipsel-linux-android-",
|
||||
NULL
|
||||
};
|
||||
|
||||
static bool lookup_path(char *name)
|
||||
{
|
||||
bool found = false;
|
||||
char *path, *tmp;
|
||||
char buf[PATH_MAX];
|
||||
char *env = getenv("PATH");
|
||||
|
||||
if (!env)
|
||||
return false;
|
||||
|
||||
env = strdup(env);
|
||||
if (!env)
|
||||
return false;
|
||||
|
||||
path = strtok_r(env, ":", &tmp);
|
||||
while (path) {
|
||||
scnprintf(buf, sizeof(buf), "%s/%s", path, name);
|
||||
if (access(buf, F_OK) == 0) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
path = strtok_r(NULL, ":", &tmp);
|
||||
}
|
||||
free(env);
|
||||
return found;
|
||||
}
|
||||
|
||||
static int lookup_triplets(const char *const *triplets, const char *name)
|
||||
{
|
||||
int i;
|
||||
char buf[PATH_MAX];
|
||||
|
||||
for (i = 0; triplets[i] != NULL; i++) {
|
||||
scnprintf(buf, sizeof(buf), "%s%s", triplets[i], name);
|
||||
if (lookup_path(buf))
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return architecture name in a normalized form.
|
||||
* The conversion logic comes from the Makefile.
|
||||
*/
|
||||
static const char *normalize_arch(char *arch)
|
||||
{
|
||||
if (!strcmp(arch, "x86_64"))
|
||||
return "x86";
|
||||
if (arch[0] == 'i' && arch[2] == '8' && arch[3] == '6')
|
||||
return "x86";
|
||||
if (!strcmp(arch, "sun4u") || !strncmp(arch, "sparc", 5))
|
||||
return "sparc";
|
||||
if (!strncmp(arch, "arm", 3) || !strcmp(arch, "sa110"))
|
||||
return "arm";
|
||||
if (!strncmp(arch, "s390", 4))
|
||||
return "s390";
|
||||
if (!strncmp(arch, "parisc", 6))
|
||||
return "parisc";
|
||||
if (!strncmp(arch, "powerpc", 7) || !strncmp(arch, "ppc", 3))
|
||||
return "powerpc";
|
||||
if (!strncmp(arch, "mips", 4))
|
||||
return "mips";
|
||||
if (!strncmp(arch, "sh", 2) && isdigit(arch[2]))
|
||||
return "sh";
|
||||
|
||||
return arch;
|
||||
}
|
||||
|
||||
static int perf_session_env__lookup_binutils_path(struct perf_session_env *env,
|
||||
const char *name,
|
||||
const char **path)
|
||||
{
|
||||
int idx;
|
||||
const char *arch, *cross_env;
|
||||
struct utsname uts;
|
||||
const char *const *path_list;
|
||||
char *buf = NULL;
|
||||
|
||||
arch = normalize_arch(env->arch);
|
||||
|
||||
if (uname(&uts) < 0)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* We don't need to try to find objdump path for native system.
|
||||
* Just use default binutils path (e.g.: "objdump").
|
||||
*/
|
||||
if (!strcmp(normalize_arch(uts.machine), arch))
|
||||
goto out;
|
||||
|
||||
cross_env = getenv("CROSS_COMPILE");
|
||||
if (cross_env) {
|
||||
if (asprintf(&buf, "%s%s", cross_env, name) < 0)
|
||||
goto out_error;
|
||||
if (buf[0] == '/') {
|
||||
if (access(buf, F_OK) == 0)
|
||||
goto out;
|
||||
goto out_error;
|
||||
}
|
||||
if (lookup_path(buf))
|
||||
goto out;
|
||||
free(buf);
|
||||
}
|
||||
|
||||
if (!strcmp(arch, "arm"))
|
||||
path_list = arm_triplets;
|
||||
else if (!strcmp(arch, "powerpc"))
|
||||
path_list = powerpc_triplets;
|
||||
else if (!strcmp(arch, "sh"))
|
||||
path_list = sh_triplets;
|
||||
else if (!strcmp(arch, "s390"))
|
||||
path_list = s390_triplets;
|
||||
else if (!strcmp(arch, "sparc"))
|
||||
path_list = sparc_triplets;
|
||||
else if (!strcmp(arch, "x86"))
|
||||
path_list = x86_triplets;
|
||||
else if (!strcmp(arch, "mips"))
|
||||
path_list = mips_triplets;
|
||||
else {
|
||||
ui__error("binutils for %s not supported.\n", arch);
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
idx = lookup_triplets(path_list, name);
|
||||
if (idx < 0) {
|
||||
ui__error("Please install %s for %s.\n"
|
||||
"You can add it to PATH, set CROSS_COMPILE or "
|
||||
"override the default using --%s.\n",
|
||||
name, arch, name);
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
if (asprintf(&buf, "%s%s", path_list[idx], name) < 0)
|
||||
goto out_error;
|
||||
|
||||
out:
|
||||
*path = buf;
|
||||
return 0;
|
||||
out_error:
|
||||
free(buf);
|
||||
*path = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int perf_session_env__lookup_objdump(struct perf_session_env *env)
|
||||
{
|
||||
/*
|
||||
* For live mode, env->arch will be NULL and we can use
|
||||
* the native objdump tool.
|
||||
*/
|
||||
if (env->arch == NULL)
|
||||
return 0;
|
||||
|
||||
return perf_session_env__lookup_binutils_path(env, "objdump",
|
||||
&objdump_path);
|
||||
}
|
10
tools/perf/arch/common.h
Normal file
10
tools/perf/arch/common.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef ARCH_PERF_COMMON_H
|
||||
#define ARCH_PERF_COMMON_H
|
||||
|
||||
#include "../util/session.h"
|
||||
|
||||
extern const char *objdump_path;
|
||||
|
||||
int perf_session_env__lookup_objdump(struct perf_session_env *env);
|
||||
|
||||
#endif /* ARCH_PERF_COMMON_H */
|
@@ -28,12 +28,12 @@
|
||||
#include "util/hist.h"
|
||||
#include "util/session.h"
|
||||
#include "util/tool.h"
|
||||
#include "arch/common.h"
|
||||
|
||||
#include <linux/bitmap.h>
|
||||
|
||||
struct perf_annotate {
|
||||
struct perf_tool tool;
|
||||
char const *input_name;
|
||||
bool force, use_tui, use_stdio;
|
||||
bool full_paths;
|
||||
bool print_line;
|
||||
@@ -139,7 +139,7 @@ find_next:
|
||||
}
|
||||
|
||||
if (use_browser > 0) {
|
||||
key = hist_entry__tui_annotate(he, evidx, NULL, NULL, 0);
|
||||
key = hist_entry__tui_annotate(he, evidx, NULL);
|
||||
switch (key) {
|
||||
case K_RIGHT:
|
||||
next = rb_next(nd);
|
||||
@@ -174,7 +174,7 @@ static int __cmd_annotate(struct perf_annotate *ann)
|
||||
struct perf_evsel *pos;
|
||||
u64 total_nr_samples;
|
||||
|
||||
session = perf_session__new(ann->input_name, O_RDONLY,
|
||||
session = perf_session__new(input_name, O_RDONLY,
|
||||
ann->force, false, &ann->tool);
|
||||
if (session == NULL)
|
||||
return -ENOMEM;
|
||||
@@ -186,6 +186,12 @@ static int __cmd_annotate(struct perf_annotate *ann)
|
||||
goto out_delete;
|
||||
}
|
||||
|
||||
if (!objdump_path) {
|
||||
ret = perf_session_env__lookup_objdump(&session->header.env);
|
||||
if (ret)
|
||||
goto out_delete;
|
||||
}
|
||||
|
||||
ret = perf_session__process_events(session, &ann->tool);
|
||||
if (ret)
|
||||
goto out_delete;
|
||||
@@ -246,13 +252,14 @@ 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,
|
||||
},
|
||||
};
|
||||
const struct option options[] = {
|
||||
OPT_STRING('i', "input", &annotate.input_name, "file",
|
||||
OPT_STRING('i', "input", &input_name, "file",
|
||||
"input file name"),
|
||||
OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]",
|
||||
"only consider symbols in these dsos"),
|
||||
|
@@ -13,6 +13,7 @@
|
||||
#include "util/header.h"
|
||||
#include "util/parse-options.h"
|
||||
#include "util/strlist.h"
|
||||
#include "util/build-id.h"
|
||||
#include "util/symbol.h"
|
||||
|
||||
static int build_id_cache__add_file(const char *filename, const char *debugdir)
|
||||
|
@@ -44,8 +44,7 @@ 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(const char *input_name,
|
||||
bool force, bool with_hits)
|
||||
static int perf_session__list_build_ids(bool force, bool with_hits)
|
||||
{
|
||||
struct perf_session *session;
|
||||
|
||||
@@ -81,7 +80,6 @@ int cmd_buildid_list(int argc, const char **argv,
|
||||
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"),
|
||||
@@ -101,5 +99,5 @@ int cmd_buildid_list(int argc, const char **argv,
|
||||
if (show_kernel)
|
||||
return sysfs__fprintf_build_id(stdout);
|
||||
|
||||
return perf_session__list_build_ids(input_name, force, with_hits);
|
||||
return perf_session__list_build_ids(force, with_hits);
|
||||
}
|
||||
|
@@ -24,6 +24,228 @@ static char const *input_old = "perf.data.old",
|
||||
static char diff__default_sort_order[] = "dso,symbol";
|
||||
static bool force;
|
||||
static bool show_displacement;
|
||||
static bool show_period;
|
||||
static bool show_formula;
|
||||
static bool show_baseline_only;
|
||||
static bool sort_compute;
|
||||
|
||||
static s64 compute_wdiff_w1;
|
||||
static s64 compute_wdiff_w2;
|
||||
|
||||
enum {
|
||||
COMPUTE_DELTA,
|
||||
COMPUTE_RATIO,
|
||||
COMPUTE_WEIGHTED_DIFF,
|
||||
COMPUTE_MAX,
|
||||
};
|
||||
|
||||
const char *compute_names[COMPUTE_MAX] = {
|
||||
[COMPUTE_DELTA] = "delta",
|
||||
[COMPUTE_RATIO] = "ratio",
|
||||
[COMPUTE_WEIGHTED_DIFF] = "wdiff",
|
||||
};
|
||||
|
||||
static int compute;
|
||||
|
||||
static int setup_compute_opt_wdiff(char *opt)
|
||||
{
|
||||
char *w1_str = opt;
|
||||
char *w2_str;
|
||||
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (!opt)
|
||||
goto out;
|
||||
|
||||
w2_str = strchr(opt, ',');
|
||||
if (!w2_str)
|
||||
goto out;
|
||||
|
||||
*w2_str++ = 0x0;
|
||||
if (!*w2_str)
|
||||
goto out;
|
||||
|
||||
compute_wdiff_w1 = strtol(w1_str, NULL, 10);
|
||||
compute_wdiff_w2 = strtol(w2_str, NULL, 10);
|
||||
|
||||
if (!compute_wdiff_w1 || !compute_wdiff_w2)
|
||||
goto out;
|
||||
|
||||
pr_debug("compute wdiff w1(%" PRId64 ") w2(%" PRId64 ")\n",
|
||||
compute_wdiff_w1, compute_wdiff_w2);
|
||||
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
if (ret)
|
||||
pr_err("Failed: wrong weight data, use 'wdiff:w1,w2'\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int setup_compute_opt(char *opt)
|
||||
{
|
||||
if (compute == COMPUTE_WEIGHTED_DIFF)
|
||||
return setup_compute_opt_wdiff(opt);
|
||||
|
||||
if (opt) {
|
||||
pr_err("Failed: extra option specified '%s'", opt);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int setup_compute(const struct option *opt, const char *str,
|
||||
int unset __maybe_unused)
|
||||
{
|
||||
int *cp = (int *) opt->value;
|
||||
char *cstr = (char *) str;
|
||||
char buf[50];
|
||||
unsigned i;
|
||||
char *option;
|
||||
|
||||
if (!str) {
|
||||
*cp = COMPUTE_DELTA;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (*str == '+') {
|
||||
sort_compute = true;
|
||||
cstr = (char *) ++str;
|
||||
if (!*str)
|
||||
return 0;
|
||||
}
|
||||
|
||||
option = strchr(str, ':');
|
||||
if (option) {
|
||||
unsigned len = option++ - str;
|
||||
|
||||
/*
|
||||
* The str data are not writeable, so we need
|
||||
* to use another buffer.
|
||||
*/
|
||||
|
||||
/* No option value is longer. */
|
||||
if (len >= sizeof(buf))
|
||||
return -EINVAL;
|
||||
|
||||
strncpy(buf, str, len);
|
||||
buf[len] = 0x0;
|
||||
cstr = buf;
|
||||
}
|
||||
|
||||
for (i = 0; i < COMPUTE_MAX; i++)
|
||||
if (!strcmp(cstr, compute_names[i])) {
|
||||
*cp = i;
|
||||
return setup_compute_opt(option);
|
||||
}
|
||||
|
||||
pr_err("Failed: '%s' is not computation method "
|
||||
"(use 'delta','ratio' or 'wdiff')\n", str);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static double get_period_percent(struct hist_entry *he, u64 period)
|
||||
{
|
||||
u64 total = he->hists->stats.total_period;
|
||||
return (period * 100.0) / total;
|
||||
}
|
||||
|
||||
double perf_diff__compute_delta(struct hist_entry *he)
|
||||
{
|
||||
struct hist_entry *pair = hist_entry__next_pair(he);
|
||||
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 = hist_entry__next_pair(he);
|
||||
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 = hist_entry__next_pair(he);
|
||||
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 = hist_entry__next_pair(he);
|
||||
|
||||
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 = hist_entry__next_pair(he);
|
||||
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 = hist_entry__next_pair(he);
|
||||
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,8 +285,8 @@ 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,
|
||||
@@ -112,36 +334,6 @@ static void hists__name_resort(struct hists *self, bool sort)
|
||||
self->entries = tmp;
|
||||
}
|
||||
|
||||
static struct hist_entry *hists__find_entry(struct hists *self,
|
||||
struct hist_entry *he)
|
||||
{
|
||||
struct rb_node *n = self->entries.rb_node;
|
||||
|
||||
while (n) {
|
||||
struct hist_entry *iter = rb_entry(n, struct hist_entry, rb_node);
|
||||
int64_t cmp = hist_entry__cmp(he, iter);
|
||||
|
||||
if (cmp < 0)
|
||||
n = n->rb_left;
|
||||
else if (cmp > 0)
|
||||
n = n->rb_right;
|
||||
else
|
||||
return iter;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void hists__match(struct hists *older, struct hists *newer)
|
||||
{
|
||||
struct rb_node *nd;
|
||||
|
||||
for (nd = rb_first(&newer->entries); nd; nd = rb_next(nd)) {
|
||||
struct hist_entry *pos = rb_entry(nd, struct hist_entry, rb_node);
|
||||
pos->pair = hists__find_entry(older, pos);
|
||||
}
|
||||
}
|
||||
|
||||
static struct perf_evsel *evsel_match(struct perf_evsel *evsel,
|
||||
struct perf_evlist *evlist)
|
||||
{
|
||||
@@ -172,6 +364,144 @@ static void perf_evlist__resort_hists(struct perf_evlist *evlist, bool 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 (!hist_entry__next_pair(he)) {
|
||||
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(new, old);
|
||||
|
||||
if (show_baseline_only)
|
||||
hists__baseline_only(new);
|
||||
else
|
||||
hists__link(new, old);
|
||||
|
||||
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;
|
||||
@@ -213,8 +543,7 @@ static int __cmd_diff(void)
|
||||
|
||||
first = false;
|
||||
|
||||
hists__match(&evsel_old->hists, &evsel->hists);
|
||||
hists__fprintf(&evsel->hists, true, 0, 0, stdout);
|
||||
hists__process(&evsel_old->hists, &evsel->hists);
|
||||
}
|
||||
|
||||
out_delete:
|
||||
@@ -235,6 +564,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"),
|
||||
@@ -263,12 +602,36 @@ static void ui_init(void)
|
||||
/* No overhead column. */
|
||||
perf_hpp__column_enable(PERF_HPP__OVERHEAD, false);
|
||||
|
||||
/* Display baseline/delta/displacement columns. */
|
||||
/*
|
||||
* Display baseline/delta/ratio/displacement/
|
||||
* formula/periods columns.
|
||||
*/
|
||||
perf_hpp__column_enable(PERF_HPP__BASELINE, true);
|
||||
perf_hpp__column_enable(PERF_HPP__DELTA, 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)
|
||||
|
@@ -48,12 +48,12 @@ static int __if_print(bool *first, const char *field, u64 value)
|
||||
|
||||
#define if_print(field) __if_print(&first, #field, pos->attr.field)
|
||||
|
||||
static int __cmd_evlist(const char *input_name, struct perf_attr_details *details)
|
||||
static int __cmd_evlist(const char *file_name, struct perf_attr_details *details)
|
||||
{
|
||||
struct perf_session *session;
|
||||
struct perf_evsel *pos;
|
||||
|
||||
session = perf_session__new(input_name, O_RDONLY, 0, false, NULL);
|
||||
session = perf_session__new(file_name, O_RDONLY, 0, false, NULL);
|
||||
if (session == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -111,7 +111,6 @@ static int __cmd_evlist(const char *input_name, struct perf_attr_details *detail
|
||||
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"),
|
||||
|
@@ -8,33 +8,53 @@
|
||||
#include "builtin.h"
|
||||
|
||||
#include "perf.h"
|
||||
#include "util/color.h"
|
||||
#include "util/evlist.h"
|
||||
#include "util/evsel.h"
|
||||
#include "util/session.h"
|
||||
#include "util/tool.h"
|
||||
#include "util/debug.h"
|
||||
#include "util/build-id.h"
|
||||
|
||||
#include "util/parse-options.h"
|
||||
|
||||
#include <linux/list.h>
|
||||
|
||||
struct perf_inject {
|
||||
struct perf_tool tool;
|
||||
bool build_ids;
|
||||
bool sched_stat;
|
||||
const char *input_name;
|
||||
int pipe_output,
|
||||
output;
|
||||
u64 bytes_written;
|
||||
struct list_head samples;
|
||||
};
|
||||
|
||||
static int perf_event__repipe_synth(struct perf_tool *tool __maybe_unused,
|
||||
struct event_entry {
|
||||
struct list_head node;
|
||||
u32 tid;
|
||||
union perf_event event[0];
|
||||
};
|
||||
|
||||
static int perf_event__repipe_synth(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct machine *machine __maybe_unused)
|
||||
{
|
||||
struct perf_inject *inject = container_of(tool, struct perf_inject, tool);
|
||||
uint32_t size;
|
||||
void *buf = event;
|
||||
|
||||
size = event->header.size;
|
||||
|
||||
while (size) {
|
||||
int ret = write(STDOUT_FILENO, buf, size);
|
||||
int ret = write(inject->output, buf, size);
|
||||
if (ret < 0)
|
||||
return -errno;
|
||||
|
||||
size -= ret;
|
||||
buf += ret;
|
||||
inject->bytes_written += ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -80,12 +100,25 @@ static int perf_event__repipe(struct perf_tool *tool,
|
||||
return perf_event__repipe_synth(tool, event, machine);
|
||||
}
|
||||
|
||||
typedef int (*inject_handler)(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct perf_evsel *evsel,
|
||||
struct machine *machine);
|
||||
|
||||
static int perf_event__repipe_sample(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample __maybe_unused,
|
||||
struct perf_evsel *evsel __maybe_unused,
|
||||
struct machine *machine)
|
||||
struct perf_sample *sample,
|
||||
struct perf_evsel *evsel,
|
||||
struct machine *machine)
|
||||
{
|
||||
if (evsel->handler.func) {
|
||||
inject_handler f = evsel->handler.func;
|
||||
return f(tool, event, sample, evsel, machine);
|
||||
}
|
||||
|
||||
build_id__mark_dso_hit(tool, event, sample, evsel, machine);
|
||||
|
||||
return perf_event__repipe_synth(tool, event, machine);
|
||||
}
|
||||
|
||||
@@ -102,14 +135,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;
|
||||
@@ -210,6 +243,80 @@ repipe:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int perf_inject__sched_process_exit(struct perf_tool *tool,
|
||||
union perf_event *event __maybe_unused,
|
||||
struct perf_sample *sample,
|
||||
struct perf_evsel *evsel __maybe_unused,
|
||||
struct machine *machine __maybe_unused)
|
||||
{
|
||||
struct perf_inject *inject = container_of(tool, struct perf_inject, tool);
|
||||
struct event_entry *ent;
|
||||
|
||||
list_for_each_entry(ent, &inject->samples, node) {
|
||||
if (sample->tid == ent->tid) {
|
||||
list_del_init(&ent->node);
|
||||
free(ent);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int perf_inject__sched_switch(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct perf_evsel *evsel,
|
||||
struct machine *machine)
|
||||
{
|
||||
struct perf_inject *inject = container_of(tool, struct perf_inject, tool);
|
||||
struct event_entry *ent;
|
||||
|
||||
perf_inject__sched_process_exit(tool, event, sample, evsel, machine);
|
||||
|
||||
ent = malloc(event->header.size + sizeof(struct event_entry));
|
||||
if (ent == NULL) {
|
||||
color_fprintf(stderr, PERF_COLOR_RED,
|
||||
"Not enough memory to process sched switch event!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ent->tid = sample->tid;
|
||||
memcpy(&ent->event, event, event->header.size);
|
||||
list_add(&ent->node, &inject->samples);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int perf_inject__sched_stat(struct perf_tool *tool,
|
||||
union perf_event *event __maybe_unused,
|
||||
struct perf_sample *sample,
|
||||
struct perf_evsel *evsel,
|
||||
struct machine *machine)
|
||||
{
|
||||
struct event_entry *ent;
|
||||
union perf_event *event_sw;
|
||||
struct perf_sample sample_sw;
|
||||
struct perf_inject *inject = container_of(tool, struct perf_inject, tool);
|
||||
u32 pid = perf_evsel__intval(evsel, sample, "pid");
|
||||
|
||||
list_for_each_entry(ent, &inject->samples, node) {
|
||||
if (pid == ent->tid)
|
||||
goto found;
|
||||
}
|
||||
|
||||
return 0;
|
||||
found:
|
||||
event_sw = &ent->event[0];
|
||||
perf_evsel__parse_sample(evsel, event_sw, &sample_sw);
|
||||
|
||||
sample_sw.period = sample->period;
|
||||
sample_sw.time = sample->time;
|
||||
perf_event__synthesize_sample(event_sw, evsel->attr.sample_type,
|
||||
&sample_sw, false);
|
||||
build_id__mark_dso_hit(tool, event_sw, &sample_sw, evsel, machine);
|
||||
return perf_event__repipe(tool, event_sw, &sample_sw, machine);
|
||||
}
|
||||
|
||||
extern volatile int session_done;
|
||||
|
||||
static void sig_handler(int sig __maybe_unused)
|
||||
@@ -217,6 +324,21 @@ static void sig_handler(int sig __maybe_unused)
|
||||
session_done = 1;
|
||||
}
|
||||
|
||||
static int perf_evsel__check_stype(struct perf_evsel *evsel,
|
||||
u64 sample_type, const char *sample_msg)
|
||||
{
|
||||
struct perf_event_attr *attr = &evsel->attr;
|
||||
const char *name = perf_evsel__name(evsel);
|
||||
|
||||
if (!(attr->sample_type & sample_type)) {
|
||||
pr_err("Samples for %s event do not have %s attribute set.",
|
||||
name, sample_msg);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __cmd_inject(struct perf_inject *inject)
|
||||
{
|
||||
struct perf_session *session;
|
||||
@@ -224,19 +346,48 @@ static int __cmd_inject(struct perf_inject *inject)
|
||||
|
||||
signal(SIGINT, sig_handler);
|
||||
|
||||
if (inject->build_ids) {
|
||||
inject->tool.sample = perf_event__inject_buildid;
|
||||
if (inject->build_ids || inject->sched_stat) {
|
||||
inject->tool.mmap = perf_event__repipe_mmap;
|
||||
inject->tool.fork = perf_event__repipe_task;
|
||||
inject->tool.fork = perf_event__repipe_fork;
|
||||
inject->tool.tracing_data = perf_event__repipe_tracing_data;
|
||||
}
|
||||
|
||||
session = perf_session__new("-", O_RDONLY, false, true, &inject->tool);
|
||||
session = perf_session__new(inject->input_name, O_RDONLY, false, true, &inject->tool);
|
||||
if (session == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
if (inject->build_ids) {
|
||||
inject->tool.sample = perf_event__inject_buildid;
|
||||
} else if (inject->sched_stat) {
|
||||
struct perf_evsel *evsel;
|
||||
|
||||
inject->tool.ordered_samples = true;
|
||||
|
||||
list_for_each_entry(evsel, &session->evlist->entries, node) {
|
||||
const char *name = perf_evsel__name(evsel);
|
||||
|
||||
if (!strcmp(name, "sched:sched_switch")) {
|
||||
if (perf_evsel__check_stype(evsel, PERF_SAMPLE_TID, "TID"))
|
||||
return -EINVAL;
|
||||
|
||||
evsel->handler.func = perf_inject__sched_switch;
|
||||
} else if (!strcmp(name, "sched:sched_process_exit"))
|
||||
evsel->handler.func = perf_inject__sched_process_exit;
|
||||
else if (!strncmp(name, "sched:sched_stat_", 17))
|
||||
evsel->handler.func = perf_inject__sched_stat;
|
||||
}
|
||||
}
|
||||
|
||||
if (!inject->pipe_output)
|
||||
lseek(inject->output, session->header.data_offset, SEEK_SET);
|
||||
|
||||
ret = perf_session__process_events(session, &inject->tool);
|
||||
|
||||
if (!inject->pipe_output) {
|
||||
session->header.data_size = inject->bytes_written;
|
||||
perf_session__write_header(session, session->evlist, inject->output, true);
|
||||
}
|
||||
|
||||
perf_session__delete(session);
|
||||
|
||||
return ret;
|
||||
@@ -260,10 +411,20 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
.tracing_data = perf_event__repipe_tracing_data_synth,
|
||||
.build_id = perf_event__repipe_op2_synth,
|
||||
},
|
||||
.input_name = "-",
|
||||
.samples = LIST_HEAD_INIT(inject.samples),
|
||||
};
|
||||
const char *output_name = "-";
|
||||
const struct option options[] = {
|
||||
OPT_BOOLEAN('b', "build-ids", &inject.build_ids,
|
||||
"Inject build-ids into the output stream"),
|
||||
OPT_STRING('i', "input", &inject.input_name, "file",
|
||||
"input file name"),
|
||||
OPT_STRING('o', "output", &output_name, "file",
|
||||
"output file name"),
|
||||
OPT_BOOLEAN('s', "sched-stat", &inject.sched_stat,
|
||||
"Merge sched-stat and sched-switch for getting events "
|
||||
"where and how long tasks slept"),
|
||||
OPT_INCR('v', "verbose", &verbose,
|
||||
"be more verbose (show build ids, etc)"),
|
||||
OPT_END()
|
||||
@@ -281,6 +442,18 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
if (argc)
|
||||
usage_with_options(inject_usage, options);
|
||||
|
||||
if (!strcmp(output_name, "-")) {
|
||||
inject.pipe_output = 1;
|
||||
inject.output = STDOUT_FILENO;
|
||||
} else {
|
||||
inject.output = open(output_name, O_CREAT | O_WRONLY | O_TRUNC,
|
||||
S_IRUSR | S_IWUSR);
|
||||
if (inject.output < 0) {
|
||||
perror("failed to create output file");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (symbol__init() < 0)
|
||||
return -1;
|
||||
|
||||
|
@@ -477,7 +477,7 @@ static void sort_result(void)
|
||||
__sort_result(&root_caller_stat, &root_caller_sorted, &caller_sort);
|
||||
}
|
||||
|
||||
static int __cmd_kmem(const char *input_name)
|
||||
static int __cmd_kmem(void)
|
||||
{
|
||||
int err = -EINVAL;
|
||||
struct perf_session *session;
|
||||
@@ -743,7 +743,6 @@ 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,
|
||||
@@ -779,7 +778,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(input_name);
|
||||
return __cmd_kmem();
|
||||
} else
|
||||
usage_with_options(kmem_usage, kmem_options);
|
||||
|
||||
|
@@ -314,9 +314,9 @@ struct vcpu_event_record {
|
||||
|
||||
static void init_kvm_event_record(struct perf_kvm_stat *kvm)
|
||||
{
|
||||
int i;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < (int)EVENTS_CACHE_SIZE; i++)
|
||||
for (i = 0; i < EVENTS_CACHE_SIZE; i++)
|
||||
INIT_LIST_HEAD(&kvm->kvm_events_cache[i]);
|
||||
}
|
||||
|
||||
@@ -370,9 +370,10 @@ static struct kvm_event *find_create_kvm_event(struct perf_kvm_stat *kvm,
|
||||
BUG_ON(key->key == INVALID_KEY);
|
||||
|
||||
head = &kvm->kvm_events_cache[kvm_events_hash_fn(key->key)];
|
||||
list_for_each_entry(event, head, hash_entry)
|
||||
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)
|
||||
@@ -417,7 +418,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;
|
||||
@@ -433,6 +437,12 @@ static bool handle_end_event(struct perf_kvm_stat *kvm,
|
||||
{
|
||||
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;
|
||||
@@ -462,7 +472,7 @@ static bool handle_end_event(struct perf_kvm_stat *kvm,
|
||||
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
|
||||
@@ -499,6 +509,11 @@ static bool handle_kvm_event(struct perf_kvm_stat *kvm,
|
||||
if (!vcpu_record)
|
||||
return true;
|
||||
|
||||
/* only process events for vcpus user cares about */
|
||||
if ((kvm->trace_vcpu != -1) &&
|
||||
(kvm->trace_vcpu != vcpu_record->vcpu_id))
|
||||
return true;
|
||||
|
||||
if (kvm->events_ops->is_begin_event(evsel, sample, &key))
|
||||
return handle_begin_event(kvm, vcpu_record, &key, sample->time);
|
||||
|
||||
@@ -598,13 +613,15 @@ static void sort_result(struct perf_kvm_stat *kvm)
|
||||
int vcpu = kvm->trace_vcpu;
|
||||
struct kvm_event *event;
|
||||
|
||||
for (i = 0; i < EVENTS_CACHE_SIZE; i++)
|
||||
list_for_each_entry(event, &kvm->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(kvm, event);
|
||||
insert_to_result(&kvm->result, event,
|
||||
kvm->compare, vcpu);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* returns left most element of result, and erase it */
|
||||
@@ -661,8 +678,8 @@ static void print_result(struct perf_kvm_stat *kvm)
|
||||
pr_info("\n");
|
||||
}
|
||||
|
||||
pr_info("\nTotal Samples:%lld, Total events handled time:%.2fus.\n\n",
|
||||
(unsigned long long)kvm->total_count, kvm->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,
|
||||
|
@@ -335,8 +335,6 @@ alloc_failed:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *input_name;
|
||||
|
||||
struct trace_lock_handler {
|
||||
int (*acquire_event)(struct perf_evsel *evsel,
|
||||
struct perf_sample *sample);
|
||||
|
@@ -31,6 +31,38 @@
|
||||
#include <sched.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#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))
|
||||
|
||||
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 {
|
||||
WRITE_FORCE,
|
||||
WRITE_APPEND
|
||||
@@ -198,11 +230,15 @@ static int perf_record__open(struct perf_record *rec)
|
||||
struct perf_record_opts *opts = &rec->opts;
|
||||
int rc = 0;
|
||||
|
||||
perf_evlist__config_attrs(evlist, opts);
|
||||
|
||||
/*
|
||||
* Set the evsel leader links before we configure attributes,
|
||||
* since some might depend on this info.
|
||||
*/
|
||||
if (opts->group)
|
||||
perf_evlist__set_leader(evlist);
|
||||
|
||||
perf_evlist__config_attrs(evlist, opts);
|
||||
|
||||
list_for_each_entry(pos, &evlist->entries, node) {
|
||||
struct perf_event_attr *attr = &pos->attr;
|
||||
/*
|
||||
@@ -285,6 +321,11 @@ try_again:
|
||||
perf_evsel__name(pos));
|
||||
rc = -err;
|
||||
goto out;
|
||||
} else if ((err == EOPNOTSUPP) && (attr->precise_ip)) {
|
||||
ui__error("\'precise\' request may not be supported. "
|
||||
"Try removing 'p' modifier\n");
|
||||
rc = -err;
|
||||
goto out;
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
@@ -326,7 +367,8 @@ try_again:
|
||||
"or try again with a smaller value of -m/--mmap_pages.\n"
|
||||
"(current value: %d)\n", opts->mmap_pages);
|
||||
rc = -errno;
|
||||
} else if (!is_power_of_2(opts->mmap_pages)) {
|
||||
} else if (!is_power_of_2(opts->mmap_pages) &&
|
||||
(opts->mmap_pages != UINT_MAX)) {
|
||||
pr_err("--mmap_pages/-m value must be a power of two.");
|
||||
rc = -EINVAL;
|
||||
} else {
|
||||
@@ -460,6 +502,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
|
||||
struct perf_evlist *evsel_list = rec->evlist;
|
||||
const char *output_name = rec->output_name;
|
||||
struct perf_session *session;
|
||||
bool disabled = false;
|
||||
|
||||
rec->progname = argv[0];
|
||||
|
||||
@@ -659,7 +702,13 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
perf_evlist__enable(evsel_list);
|
||||
/*
|
||||
* When perf is starting the traced process, all the events
|
||||
* (apart from group members) have enable_on_exec=1 set,
|
||||
* so don't spoil it by prematurely enabling them.
|
||||
*/
|
||||
if (!perf_target__none(&opts->target))
|
||||
perf_evlist__enable(evsel_list);
|
||||
|
||||
/*
|
||||
* Let the child rip
|
||||
@@ -682,8 +731,15 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
|
||||
waking++;
|
||||
}
|
||||
|
||||
if (done)
|
||||
/*
|
||||
* When perf is starting the traced process, at the end events
|
||||
* die with the process and we wait for that. Thus no need to
|
||||
* disable events in this case.
|
||||
*/
|
||||
if (done && !disabled && !perf_target__none(&opts->target)) {
|
||||
perf_evlist__disable(evsel_list);
|
||||
disabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (quiet || signr == SIGUSR1)
|
||||
|
@@ -33,13 +33,13 @@
|
||||
#include "util/thread.h"
|
||||
#include "util/sort.h"
|
||||
#include "util/hist.h"
|
||||
#include "arch/common.h"
|
||||
|
||||
#include <linux/bitmap.h>
|
||||
|
||||
struct perf_report {
|
||||
struct perf_tool tool;
|
||||
struct perf_session *session;
|
||||
char const *input_name;
|
||||
bool force, use_tui, use_gtk, use_stdio;
|
||||
bool hide_unresolved;
|
||||
bool dont_use_callchains;
|
||||
@@ -428,10 +428,11 @@ static int __cmd_report(struct perf_report *rep)
|
||||
if (use_browser > 0) {
|
||||
if (use_browser == 1) {
|
||||
perf_evlist__tui_browse_hists(session->evlist, help,
|
||||
NULL, NULL, 0);
|
||||
NULL,
|
||||
&session->header.env);
|
||||
} else if (use_browser == 2) {
|
||||
perf_evlist__gtk_browse_hists(session->evlist, help,
|
||||
NULL, NULL, 0);
|
||||
NULL);
|
||||
}
|
||||
} else
|
||||
perf_evlist__tty_browse_hists(session->evlist, rep, help);
|
||||
@@ -556,8 +557,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,
|
||||
@@ -570,7 +571,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
.pretty_printing_style = "normal",
|
||||
};
|
||||
const struct option options[] = {
|
||||
OPT_STRING('i', "input", &report.input_name, "file",
|
||||
OPT_STRING('i', "input", &input_name, "file",
|
||||
"input file name"),
|
||||
OPT_INCR('v', "verbose", &verbose,
|
||||
"be more verbose (show symbol address, etc)"),
|
||||
@@ -656,13 +657,13 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
if (report.inverted_callchain)
|
||||
callchain_param.order = ORDER_CALLER;
|
||||
|
||||
if (!report.input_name || !strlen(report.input_name)) {
|
||||
if (!input_name || !strlen(input_name)) {
|
||||
if (!fstat(STDIN_FILENO, &st) && S_ISFIFO(st.st_mode))
|
||||
report.input_name = "-";
|
||||
input_name = "-";
|
||||
else
|
||||
report.input_name = "perf.data";
|
||||
input_name = "perf.data";
|
||||
}
|
||||
session = perf_session__new(report.input_name, O_RDONLY,
|
||||
session = perf_session__new(input_name, O_RDONLY,
|
||||
report.force, false, &report.tool);
|
||||
if (session == NULL)
|
||||
return -ENOMEM;
|
||||
@@ -687,7 +688,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
|
||||
}
|
||||
|
||||
if (strcmp(report.input_name, "-") != 0)
|
||||
if (strcmp(input_name, "-") != 0)
|
||||
setup_browser(true);
|
||||
else {
|
||||
use_browser = 0;
|
||||
|
@@ -120,7 +120,6 @@ struct trace_sched_handler {
|
||||
|
||||
struct perf_sched {
|
||||
struct perf_tool tool;
|
||||
const char *input_name;
|
||||
const char *sort_order;
|
||||
unsigned long nr_tasks;
|
||||
struct task_desc *pid_to_task[MAX_PID];
|
||||
@@ -1460,7 +1459,7 @@ static int perf_sched__read_events(struct perf_sched *sched, bool destroy,
|
||||
};
|
||||
struct perf_session *session;
|
||||
|
||||
session = perf_session__new(sched->input_name, O_RDONLY, 0, false, &sched->tool);
|
||||
session = perf_session__new(input_name, O_RDONLY, 0, false, &sched->tool);
|
||||
if (session == NULL) {
|
||||
pr_debug("No Memory for session\n");
|
||||
return -1;
|
||||
@@ -1672,7 +1671,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),
|
||||
@@ -1707,7 +1707,7 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
OPT_END()
|
||||
};
|
||||
const struct option sched_options[] = {
|
||||
OPT_STRING('i', "input", &sched.input_name, "file",
|
||||
OPT_STRING('i', "input", &input_name, "file",
|
||||
"input file name"),
|
||||
OPT_INCR('v', "verbose", &verbose,
|
||||
"be more verbose (show symbol address, etc)"),
|
||||
|
@@ -520,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,
|
||||
@@ -1029,6 +1029,68 @@ static int list_available_scripts(const struct option *opt __maybe_unused,
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Some scripts specify the required events in their "xxx-record" file,
|
||||
* this function will check if the events in perf.data match those
|
||||
* mentioned in the "xxx-record".
|
||||
*
|
||||
* Fixme: All existing "xxx-record" are all in good formats "-e event ",
|
||||
* which is covered well now. And new parsing code should be added to
|
||||
* cover the future complexing formats like event groups etc.
|
||||
*/
|
||||
static int check_ev_match(char *dir_name, char *scriptname,
|
||||
struct perf_session *session)
|
||||
{
|
||||
char filename[MAXPATHLEN], evname[128];
|
||||
char line[BUFSIZ], *p;
|
||||
struct perf_evsel *pos;
|
||||
int match, len;
|
||||
FILE *fp;
|
||||
|
||||
sprintf(filename, "%s/bin/%s-record", dir_name, scriptname);
|
||||
|
||||
fp = fopen(filename, "r");
|
||||
if (!fp)
|
||||
return -1;
|
||||
|
||||
while (fgets(line, sizeof(line), fp)) {
|
||||
p = ltrim(line);
|
||||
if (*p == '#')
|
||||
continue;
|
||||
|
||||
while (strlen(p)) {
|
||||
p = strstr(p, "-e");
|
||||
if (!p)
|
||||
break;
|
||||
|
||||
p += 2;
|
||||
p = ltrim(p);
|
||||
len = strcspn(p, " \t");
|
||||
if (!len)
|
||||
break;
|
||||
|
||||
snprintf(evname, len + 1, "%s", p);
|
||||
|
||||
match = 0;
|
||||
list_for_each_entry(pos,
|
||||
&session->evlist->entries, node) {
|
||||
if (!strcmp(perf_evsel__name(pos), evname)) {
|
||||
match = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!match) {
|
||||
fclose(fp);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return -1 if none is found, otherwise the actual scripts number.
|
||||
*
|
||||
@@ -1039,17 +1101,23 @@ static int list_available_scripts(const struct option *opt __maybe_unused,
|
||||
int find_scripts(char **scripts_array, char **scripts_path_array)
|
||||
{
|
||||
struct dirent *script_next, *lang_next, script_dirent, lang_dirent;
|
||||
char scripts_path[MAXPATHLEN];
|
||||
char scripts_path[MAXPATHLEN], lang_path[MAXPATHLEN];
|
||||
DIR *scripts_dir, *lang_dir;
|
||||
char lang_path[MAXPATHLEN];
|
||||
struct perf_session *session;
|
||||
char *temp;
|
||||
int i = 0;
|
||||
|
||||
session = perf_session__new(input_name, O_RDONLY, 0, false, NULL);
|
||||
if (!session)
|
||||
return -1;
|
||||
|
||||
snprintf(scripts_path, MAXPATHLEN, "%s/scripts", perf_exec_path());
|
||||
|
||||
scripts_dir = opendir(scripts_path);
|
||||
if (!scripts_dir)
|
||||
if (!scripts_dir) {
|
||||
perf_session__delete(session);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for_each_lang(scripts_path, scripts_dir, lang_dirent, lang_next) {
|
||||
snprintf(lang_path, MAXPATHLEN, "%s/%s", scripts_path,
|
||||
@@ -1077,10 +1145,18 @@ int find_scripts(char **scripts_array, char **scripts_path_array)
|
||||
snprintf(scripts_array[i],
|
||||
(temp - script_dirent.d_name) + 1,
|
||||
"%s", script_dirent.d_name);
|
||||
|
||||
if (check_ev_match(lang_path,
|
||||
scripts_array[i], session))
|
||||
continue;
|
||||
|
||||
i++;
|
||||
}
|
||||
closedir(lang_dir);
|
||||
}
|
||||
|
||||
closedir(scripts_dir);
|
||||
perf_session__delete(session);
|
||||
return i;
|
||||
}
|
||||
|
||||
@@ -1175,7 +1251,6 @@ 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;
|
||||
|
@@ -57,6 +57,7 @@
|
||||
#include "util/thread.h"
|
||||
#include "util/thread_map.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <locale.h>
|
||||
|
||||
@@ -83,6 +84,9 @@ static const char *csv_sep = NULL;
|
||||
static bool csv_output = false;
|
||||
static bool group = false;
|
||||
static FILE *output = NULL;
|
||||
static const char *pre_cmd = NULL;
|
||||
static const char *post_cmd = NULL;
|
||||
static bool sync_run = false;
|
||||
|
||||
static volatile int done = 0;
|
||||
|
||||
@@ -125,8 +129,7 @@ static struct stats runtime_itlb_cache_stats[MAX_NR_CPUS];
|
||||
static struct stats runtime_dtlb_cache_stats[MAX_NR_CPUS];
|
||||
static struct stats walltime_nsecs_stats;
|
||||
|
||||
static int create_perf_stat_counter(struct perf_evsel *evsel,
|
||||
struct perf_evsel *first)
|
||||
static int create_perf_stat_counter(struct perf_evsel *evsel)
|
||||
{
|
||||
struct perf_event_attr *attr = &evsel->attr;
|
||||
bool exclude_guest_missing = false;
|
||||
@@ -149,7 +152,8 @@ retry:
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!perf_target__has_task(&target) && (!group || evsel == first)) {
|
||||
if (!perf_target__has_task(&target) &&
|
||||
!perf_evsel__is_group_member(evsel)) {
|
||||
attr->disabled = 1;
|
||||
attr->enable_on_exec = 1;
|
||||
}
|
||||
@@ -265,10 +269,10 @@ static int read_counter(struct perf_evsel *counter)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int run_perf_stat(int argc __maybe_unused, const char **argv)
|
||||
static int __run_perf_stat(int argc __maybe_unused, const char **argv)
|
||||
{
|
||||
unsigned long long t0, t1;
|
||||
struct perf_evsel *counter, *first;
|
||||
struct perf_evsel *counter;
|
||||
int status = 0;
|
||||
int child_ready_pipe[2], go_pipe[2];
|
||||
const bool forks = (argc > 0);
|
||||
@@ -328,10 +332,8 @@ static int run_perf_stat(int argc __maybe_unused, const char **argv)
|
||||
if (group)
|
||||
perf_evlist__set_leader(evsel_list);
|
||||
|
||||
first = perf_evlist__first(evsel_list);
|
||||
|
||||
list_for_each_entry(counter, &evsel_list->entries, node) {
|
||||
if (create_perf_stat_counter(counter, first) < 0) {
|
||||
if (create_perf_stat_counter(counter) < 0) {
|
||||
/*
|
||||
* PPC returns ENXIO for HW counters until 2.6.37
|
||||
* (behavior changed with commit b0a873e).
|
||||
@@ -405,6 +407,32 @@ static int run_perf_stat(int argc __maybe_unused, const char **argv)
|
||||
return WEXITSTATUS(status);
|
||||
}
|
||||
|
||||
static int run_perf_stat(int argc __maybe_unused, const char **argv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (pre_cmd) {
|
||||
ret = system(pre_cmd);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (sync_run)
|
||||
sync();
|
||||
|
||||
ret = __run_perf_stat(argc, argv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (post_cmd) {
|
||||
ret = system(post_cmd);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void print_noise_pct(double total, double avg)
|
||||
{
|
||||
double pct = rel_stddev_stats(total, avg);
|
||||
@@ -1069,8 +1097,7 @@ 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;
|
||||
bool append_file = false;
|
||||
int output_fd = 0;
|
||||
const char *output_name = NULL;
|
||||
const struct option options[] = {
|
||||
@@ -1114,6 +1141,10 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
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_STRING(0, "pre", &pre_cmd, "command",
|
||||
"command to run prior to the measured command"),
|
||||
OPT_STRING(0, "post", &post_cmd, "command",
|
||||
"command to run after to the measured command"),
|
||||
OPT_END()
|
||||
};
|
||||
const char * const stat_usage[] = {
|
||||
@@ -1238,9 +1269,6 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
fprintf(output, "[ perf stat: executing run #%d ... ]\n",
|
||||
run_idx + 1);
|
||||
|
||||
if (sync_run)
|
||||
sync();
|
||||
|
||||
status = run_perf_stat(argc, argv);
|
||||
}
|
||||
|
||||
|
文件差異過大導致無法顯示
Load Diff
@@ -965,7 +965,7 @@ static void write_svg_file(const char *filename)
|
||||
svg_close();
|
||||
}
|
||||
|
||||
static int __cmd_timechart(const char *input_name, const char *output_name)
|
||||
static int __cmd_timechart(const char *output_name)
|
||||
{
|
||||
struct perf_tool perf_timechart = {
|
||||
.comm = process_comm_event,
|
||||
@@ -1061,7 +1061,6 @@ parse_process(const struct option *opt __maybe_unused, const char *arg,
|
||||
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"),
|
||||
@@ -1092,5 +1091,5 @@ int cmd_timechart(int argc, const char **argv,
|
||||
|
||||
setup_pager();
|
||||
|
||||
return __cmd_timechart(input_name, output_name);
|
||||
return __cmd_timechart(output_name);
|
||||
}
|
||||
|
@@ -26,6 +26,7 @@
|
||||
#include "util/color.h"
|
||||
#include "util/evlist.h"
|
||||
#include "util/evsel.h"
|
||||
#include "util/machine.h"
|
||||
#include "util/session.h"
|
||||
#include "util/symbol.h"
|
||||
#include "util/thread.h"
|
||||
@@ -581,6 +582,11 @@ static void *display_thread_tui(void *arg)
|
||||
struct perf_evsel *pos;
|
||||
struct perf_top *top = arg;
|
||||
const char *help = "For a higher level overview, try: perf top --sort comm,dso";
|
||||
struct hist_browser_timer hbt = {
|
||||
.timer = perf_top__sort_new_samples,
|
||||
.arg = top,
|
||||
.refresh = top->delay_secs,
|
||||
};
|
||||
|
||||
perf_top__sort_new_samples(top);
|
||||
|
||||
@@ -592,9 +598,8 @@ static void *display_thread_tui(void *arg)
|
||||
list_for_each_entry(pos, &top->evlist->entries, node)
|
||||
pos->hists.uid_filter_str = top->target.uid_str;
|
||||
|
||||
perf_evlist__tui_browse_hists(top->evlist, help,
|
||||
perf_top__sort_new_samples,
|
||||
top, top->delay_secs);
|
||||
perf_evlist__tui_browse_hists(top->evlist, help, &hbt,
|
||||
&top->session->header.env);
|
||||
|
||||
exit_browser(0);
|
||||
exit(0);
|
||||
@@ -871,7 +876,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;
|
||||
}
|
||||
@@ -976,6 +981,10 @@ try_again:
|
||||
ui__error("Too many events are opened.\n"
|
||||
"Try again after reducing the number of events\n");
|
||||
goto out_err;
|
||||
} else if ((err == EOPNOTSUPP) && (attr->precise_ip)) {
|
||||
ui__error("\'precise\' request may not be supported. "
|
||||
"Try removing 'p' modifier\n");
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
ui__error("The sys_perf_event_open() syscall "
|
||||
|
@@ -1,5 +1,8 @@
|
||||
#include "builtin.h"
|
||||
#include "util/color.h"
|
||||
#include "util/evlist.h"
|
||||
#include "util/machine.h"
|
||||
#include "util/thread.h"
|
||||
#include "util/parse-options.h"
|
||||
#include "util/thread_map.h"
|
||||
#include "event-parse.h"
|
||||
@@ -13,15 +16,18 @@ static struct syscall_fmt {
|
||||
bool errmsg;
|
||||
bool timeout;
|
||||
} syscall_fmts[] = {
|
||||
{ .name = "access", .errmsg = true, },
|
||||
{ .name = "arch_prctl", .errmsg = true, .alias = "prctl", },
|
||||
{ .name = "fstat", .errmsg = true, .alias = "newfstat", },
|
||||
{ .name = "fstatat", .errmsg = true, .alias = "newfstatat", },
|
||||
{ .name = "futex", .errmsg = true, },
|
||||
{ .name = "open", .errmsg = true, },
|
||||
{ .name = "poll", .errmsg = true, .timeout = true, },
|
||||
{ .name = "ppoll", .errmsg = true, .timeout = true, },
|
||||
{ .name = "read", .errmsg = true, },
|
||||
{ .name = "recvfrom", .errmsg = true, },
|
||||
{ .name = "select", .errmsg = true, .timeout = true, },
|
||||
{ .name = "socket", .errmsg = true, },
|
||||
{ .name = "stat", .errmsg = true, .alias = "newstat", },
|
||||
};
|
||||
|
||||
@@ -43,6 +49,57 @@ struct syscall {
|
||||
struct syscall_fmt *fmt;
|
||||
};
|
||||
|
||||
static size_t fprintf_duration(unsigned long t, FILE *fp)
|
||||
{
|
||||
double duration = (double)t / NSEC_PER_MSEC;
|
||||
size_t printed = fprintf(fp, "(");
|
||||
|
||||
if (duration >= 1.0)
|
||||
printed += color_fprintf(fp, PERF_COLOR_RED, "%6.3f ms", duration);
|
||||
else if (duration >= 0.01)
|
||||
printed += color_fprintf(fp, PERF_COLOR_YELLOW, "%6.3f ms", duration);
|
||||
else
|
||||
printed += color_fprintf(fp, PERF_COLOR_NORMAL, "%6.3f ms", duration);
|
||||
return printed + fprintf(stdout, "): ");
|
||||
}
|
||||
|
||||
struct thread_trace {
|
||||
u64 entry_time;
|
||||
u64 exit_time;
|
||||
bool entry_pending;
|
||||
unsigned long nr_events;
|
||||
char *entry_str;
|
||||
double runtime_ms;
|
||||
};
|
||||
|
||||
static struct thread_trace *thread_trace__new(void)
|
||||
{
|
||||
return zalloc(sizeof(struct thread_trace));
|
||||
}
|
||||
|
||||
static struct thread_trace *thread__trace(struct thread *thread)
|
||||
{
|
||||
struct thread_trace *ttrace;
|
||||
|
||||
if (thread == NULL)
|
||||
goto fail;
|
||||
|
||||
if (thread->priv == NULL)
|
||||
thread->priv = thread_trace__new();
|
||||
|
||||
if (thread->priv == NULL)
|
||||
goto fail;
|
||||
|
||||
ttrace = thread->priv;
|
||||
++ttrace->nr_events;
|
||||
|
||||
return ttrace;
|
||||
fail:
|
||||
color_fprintf(stdout, PERF_COLOR_RED,
|
||||
"WARNING: not enough memory, dropping samples!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct trace {
|
||||
int audit_machine;
|
||||
struct {
|
||||
@@ -50,8 +107,96 @@ struct trace {
|
||||
struct syscall *table;
|
||||
} syscalls;
|
||||
struct perf_record_opts opts;
|
||||
struct machine host;
|
||||
u64 base_time;
|
||||
unsigned long nr_events;
|
||||
bool sched;
|
||||
bool multiple_threads;
|
||||
double duration_filter;
|
||||
double runtime_ms;
|
||||
};
|
||||
|
||||
static bool trace__filter_duration(struct trace *trace, double t)
|
||||
{
|
||||
return t < (trace->duration_filter * NSEC_PER_MSEC);
|
||||
}
|
||||
|
||||
static size_t trace__fprintf_tstamp(struct trace *trace, u64 tstamp, FILE *fp)
|
||||
{
|
||||
double ts = (double)(tstamp - trace->base_time) / NSEC_PER_MSEC;
|
||||
|
||||
return fprintf(fp, "%10.3f ", ts);
|
||||
}
|
||||
|
||||
static bool done = false;
|
||||
|
||||
static void sig_handler(int sig __maybe_unused)
|
||||
{
|
||||
done = true;
|
||||
}
|
||||
|
||||
static size_t trace__fprintf_entry_head(struct trace *trace, struct thread *thread,
|
||||
u64 duration, u64 tstamp, FILE *fp)
|
||||
{
|
||||
size_t printed = trace__fprintf_tstamp(trace, tstamp, fp);
|
||||
printed += fprintf_duration(duration, fp);
|
||||
|
||||
if (trace->multiple_threads)
|
||||
printed += fprintf(fp, "%d ", thread->pid);
|
||||
|
||||
return printed;
|
||||
}
|
||||
|
||||
static int trace__process_event(struct machine *machine, union perf_event *event)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
switch (event->header.type) {
|
||||
case PERF_RECORD_LOST:
|
||||
color_fprintf(stdout, PERF_COLOR_RED,
|
||||
"LOST %" PRIu64 " events!\n", event->lost.lost);
|
||||
ret = machine__process_lost_event(machine, event);
|
||||
default:
|
||||
ret = machine__process_event(machine, event);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int trace__tool_process(struct perf_tool *tool __maybe_unused,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample __maybe_unused,
|
||||
struct machine *machine)
|
||||
{
|
||||
return trace__process_event(machine, event);
|
||||
}
|
||||
|
||||
static int trace__symbols_init(struct trace *trace, struct perf_evlist *evlist)
|
||||
{
|
||||
int err = symbol__init();
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
machine__init(&trace->host, "", HOST_KERNEL_ID);
|
||||
machine__create_kernel_maps(&trace->host);
|
||||
|
||||
if (perf_target__has_task(&trace->opts.target)) {
|
||||
err = perf_event__synthesize_thread_map(NULL, evlist->threads,
|
||||
trace__tool_process,
|
||||
&trace->host);
|
||||
} else {
|
||||
err = perf_event__synthesize_threads(NULL, trace__tool_process,
|
||||
&trace->host);
|
||||
}
|
||||
|
||||
if (err)
|
||||
symbol__exit();
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int trace__read_syscall_info(struct trace *trace, int id)
|
||||
{
|
||||
char tp_name[128];
|
||||
@@ -93,7 +238,8 @@ static int trace__read_syscall_info(struct trace *trace, int id)
|
||||
return sc->tp_format != NULL ? 0 : -1;
|
||||
}
|
||||
|
||||
static size_t syscall__fprintf_args(struct syscall *sc, unsigned long *args, FILE *fp)
|
||||
static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size,
|
||||
unsigned long *args)
|
||||
{
|
||||
int i = 0;
|
||||
size_t printed = 0;
|
||||
@@ -102,12 +248,15 @@ static size_t syscall__fprintf_args(struct syscall *sc, unsigned long *args, FIL
|
||||
struct format_field *field;
|
||||
|
||||
for (field = sc->tp_format->format.fields->next; field; field = field->next) {
|
||||
printed += fprintf(fp, "%s%s: %ld", printed ? ", " : "",
|
||||
field->name, args[i++]);
|
||||
printed += scnprintf(bf + printed, size - printed,
|
||||
"%s%s: %ld", printed ? ", " : "",
|
||||
field->name, args[i++]);
|
||||
}
|
||||
} else {
|
||||
while (i < 6) {
|
||||
printed += fprintf(fp, "%sarg%d: %ld", printed ? ", " : "", i, args[i]);
|
||||
printed += scnprintf(bf + printed, size - printed,
|
||||
"%sarg%d: %ld",
|
||||
printed ? ", " : "", i, args[i]);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
@@ -139,17 +288,24 @@ static struct syscall *trace__syscall_info(struct trace *trace,
|
||||
return &trace->syscalls.table[id];
|
||||
|
||||
out_cant_read:
|
||||
printf("Problems reading syscall %d information\n", id);
|
||||
printf("Problems reading syscall %d", id);
|
||||
if (id <= trace->syscalls.max && trace->syscalls.table[id].name != NULL)
|
||||
printf("(%s)", trace->syscalls.table[id].name);
|
||||
puts(" information");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel,
|
||||
struct perf_sample *sample)
|
||||
{
|
||||
char *msg;
|
||||
void *args;
|
||||
size_t printed = 0;
|
||||
struct thread *thread = machine__findnew_thread(&trace->host, sample->tid);
|
||||
struct syscall *sc = trace__syscall_info(trace, evsel, sample);
|
||||
struct thread_trace *ttrace = thread__trace(thread);
|
||||
|
||||
if (sc == NULL)
|
||||
if (ttrace == NULL || sc == NULL)
|
||||
return -1;
|
||||
|
||||
args = perf_evsel__rawptr(evsel, sample, "args");
|
||||
@@ -158,8 +314,27 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel,
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("%s(", sc->name);
|
||||
syscall__fprintf_args(sc, args, stdout);
|
||||
ttrace = thread->priv;
|
||||
|
||||
if (ttrace->entry_str == NULL) {
|
||||
ttrace->entry_str = malloc(1024);
|
||||
if (!ttrace->entry_str)
|
||||
return -1;
|
||||
}
|
||||
|
||||
ttrace->entry_time = sample->time;
|
||||
msg = ttrace->entry_str;
|
||||
printed += scnprintf(msg + printed, 1024 - printed, "%s(", sc->name);
|
||||
|
||||
printed += syscall__scnprintf_args(sc, msg + printed, 1024 - printed, args);
|
||||
|
||||
if (!strcmp(sc->name, "exit_group") || !strcmp(sc->name, "exit")) {
|
||||
if (!trace->duration_filter) {
|
||||
trace__fprintf_entry_head(trace, thread, 1, sample->time, stdout);
|
||||
printf("%-70s\n", ttrace->entry_str);
|
||||
}
|
||||
} else
|
||||
ttrace->entry_pending = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -168,13 +343,37 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel,
|
||||
struct perf_sample *sample)
|
||||
{
|
||||
int ret;
|
||||
u64 duration = 0;
|
||||
struct thread *thread = machine__findnew_thread(&trace->host, sample->tid);
|
||||
struct thread_trace *ttrace = thread__trace(thread);
|
||||
struct syscall *sc = trace__syscall_info(trace, evsel, sample);
|
||||
|
||||
if (sc == NULL)
|
||||
if (ttrace == NULL || sc == NULL)
|
||||
return -1;
|
||||
|
||||
ret = perf_evsel__intval(evsel, sample, "ret");
|
||||
|
||||
ttrace = thread->priv;
|
||||
|
||||
ttrace->exit_time = sample->time;
|
||||
|
||||
if (ttrace->entry_time) {
|
||||
duration = sample->time - ttrace->entry_time;
|
||||
if (trace__filter_duration(trace, duration))
|
||||
goto out;
|
||||
} else if (trace->duration_filter)
|
||||
goto out;
|
||||
|
||||
trace__fprintf_entry_head(trace, thread, duration, sample->time, stdout);
|
||||
|
||||
if (ttrace->entry_pending) {
|
||||
printf("%-70s", ttrace->entry_str);
|
||||
} else {
|
||||
printf(" ... [");
|
||||
color_fprintf(stdout, PERF_COLOR_YELLOW, "continued");
|
||||
printf("]: %s()", sc->name);
|
||||
}
|
||||
|
||||
if (ret < 0 && sc->fmt && sc->fmt->errmsg) {
|
||||
char bf[256];
|
||||
const char *emsg = strerror_r(-ret, bf, sizeof(bf)),
|
||||
@@ -187,14 +386,44 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel,
|
||||
printf(") = %d", ret);
|
||||
|
||||
putchar('\n');
|
||||
out:
|
||||
ttrace->entry_pending = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int trace__run(struct trace *trace)
|
||||
static int trace__sched_stat_runtime(struct trace *trace, struct perf_evsel *evsel,
|
||||
struct perf_sample *sample)
|
||||
{
|
||||
u64 runtime = perf_evsel__intval(evsel, sample, "runtime");
|
||||
double runtime_ms = (double)runtime / NSEC_PER_MSEC;
|
||||
struct thread *thread = machine__findnew_thread(&trace->host, sample->tid);
|
||||
struct thread_trace *ttrace = thread__trace(thread);
|
||||
|
||||
if (ttrace == NULL)
|
||||
goto out_dump;
|
||||
|
||||
ttrace->runtime_ms += runtime_ms;
|
||||
trace->runtime_ms += runtime_ms;
|
||||
return 0;
|
||||
|
||||
out_dump:
|
||||
printf("%s: comm=%s,pid=%u,runtime=%" PRIu64 ",vruntime=%" PRIu64 ")\n",
|
||||
evsel->name,
|
||||
perf_evsel__strval(evsel, sample, "comm"),
|
||||
(pid_t)perf_evsel__intval(evsel, sample, "pid"),
|
||||
runtime,
|
||||
perf_evsel__intval(evsel, sample, "vruntime"));
|
||||
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;
|
||||
int err = -1, i, nr_events = 0, before;
|
||||
int err = -1, i;
|
||||
unsigned long before;
|
||||
const bool forks = argc > 0;
|
||||
|
||||
if (evlist == NULL) {
|
||||
printf("Not enough memory to run!\n");
|
||||
@@ -207,14 +436,38 @@ static int trace__run(struct trace *trace)
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
if (trace->sched &&
|
||||
perf_evlist__add_newtp(evlist, "sched", "sched_stat_runtime",
|
||||
trace__sched_stat_runtime)) {
|
||||
printf("Couldn't read the sched_stat_runtime tracepoint information!\n");
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
err = perf_evlist__create_maps(evlist, &trace->opts.target);
|
||||
if (err < 0) {
|
||||
printf("Problems parsing the target to trace, check your options!\n");
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
err = trace__symbols_init(trace, evlist);
|
||||
if (err < 0) {
|
||||
printf("Problems initializing symbol libraries!\n");
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
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));
|
||||
@@ -228,8 +481,13 @@ static int trace__run(struct trace *trace)
|
||||
}
|
||||
|
||||
perf_evlist__enable(evlist);
|
||||
|
||||
if (forks)
|
||||
perf_evlist__start_workload(evlist);
|
||||
|
||||
trace->multiple_threads = evlist->threads->map[0] == -1 || evlist->threads->nr > 1;
|
||||
again:
|
||||
before = nr_events;
|
||||
before = trace->nr_events;
|
||||
|
||||
for (i = 0; i < evlist->nr_mmaps; i++) {
|
||||
union perf_event *event;
|
||||
@@ -239,19 +497,7 @@ again:
|
||||
tracepoint_handler handler;
|
||||
struct perf_sample sample;
|
||||
|
||||
++nr_events;
|
||||
|
||||
switch (type) {
|
||||
case PERF_RECORD_SAMPLE:
|
||||
break;
|
||||
case PERF_RECORD_LOST:
|
||||
printf("LOST %" PRIu64 " events!\n", event->lost.lost);
|
||||
continue;
|
||||
default:
|
||||
printf("Unexpected %s event, skipping...\n",
|
||||
perf_event__name(type));
|
||||
continue;
|
||||
}
|
||||
++trace->nr_events;
|
||||
|
||||
err = perf_evlist__parse_sample(evlist, event, &sample);
|
||||
if (err) {
|
||||
@@ -259,14 +505,26 @@ again:
|
||||
continue;
|
||||
}
|
||||
|
||||
if (trace->base_time == 0)
|
||||
trace->base_time = sample.time;
|
||||
|
||||
if (type != PERF_RECORD_SAMPLE) {
|
||||
trace__process_event(&trace->host, event);
|
||||
continue;
|
||||
}
|
||||
|
||||
evsel = perf_evlist__id2evsel(evlist, sample.id);
|
||||
if (evsel == NULL) {
|
||||
printf("Unknown tp ID %" PRIu64 ", skipping...\n", sample.id);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (evlist->threads->map[0] == -1 || evlist->threads->nr > 1)
|
||||
printf("%d ", sample.tid);
|
||||
if (sample.raw_data == NULL) {
|
||||
printf("%s sample with no payload for tid: %d, cpu %d, raw_size=%d, skipping...\n",
|
||||
perf_evsel__name(evsel), sample.tid,
|
||||
sample.cpu, sample.raw_size);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sample.raw_data == NULL) {
|
||||
printf("%s sample with no payload for tid: %d, cpu %d, raw_size=%d, skipping...\n",
|
||||
@@ -280,8 +538,15 @@ again:
|
||||
}
|
||||
}
|
||||
|
||||
if (nr_events == before)
|
||||
if (trace->nr_events == before) {
|
||||
if (done)
|
||||
goto out_delete_evlist;
|
||||
|
||||
poll(evlist->pollfd, evlist->nr_fds, -1);
|
||||
}
|
||||
|
||||
if (done)
|
||||
perf_evlist__disable(evlist);
|
||||
|
||||
goto again;
|
||||
|
||||
@@ -291,10 +556,65 @@ out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static size_t trace__fprintf_threads_header(FILE *fp)
|
||||
{
|
||||
size_t printed;
|
||||
|
||||
printed = fprintf(fp, "\n _____________________________________________________________________\n");
|
||||
printed += fprintf(fp," __) Summary of events (__\n\n");
|
||||
printed += fprintf(fp," [ task - pid ] [ events ] [ ratio ] [ runtime ]\n");
|
||||
printed += fprintf(fp," _____________________________________________________________________\n\n");
|
||||
|
||||
return printed;
|
||||
}
|
||||
|
||||
static size_t trace__fprintf_thread_summary(struct trace *trace, FILE *fp)
|
||||
{
|
||||
size_t printed = trace__fprintf_threads_header(fp);
|
||||
struct rb_node *nd;
|
||||
|
||||
for (nd = rb_first(&trace->host.threads); nd; nd = rb_next(nd)) {
|
||||
struct thread *thread = rb_entry(nd, struct thread, rb_node);
|
||||
struct thread_trace *ttrace = thread->priv;
|
||||
const char *color;
|
||||
double ratio;
|
||||
|
||||
if (ttrace == NULL)
|
||||
continue;
|
||||
|
||||
ratio = (double)ttrace->nr_events / trace->nr_events * 100.0;
|
||||
|
||||
color = PERF_COLOR_NORMAL;
|
||||
if (ratio > 50.0)
|
||||
color = PERF_COLOR_RED;
|
||||
else if (ratio > 25.0)
|
||||
color = PERF_COLOR_GREEN;
|
||||
else if (ratio > 5.0)
|
||||
color = PERF_COLOR_YELLOW;
|
||||
|
||||
printed += color_fprintf(fp, color, "%20s", thread->comm);
|
||||
printed += fprintf(fp, " - %-5d :%11lu [", thread->pid, ttrace->nr_events);
|
||||
printed += color_fprintf(fp, color, "%5.1f%%", ratio);
|
||||
printed += fprintf(fp, " ] %10.3f ms\n", ttrace->runtime_ms);
|
||||
}
|
||||
|
||||
return printed;
|
||||
}
|
||||
|
||||
static int trace__set_duration(const struct option *opt, const char *str,
|
||||
int unset __maybe_unused)
|
||||
{
|
||||
struct trace *trace = opt->value;
|
||||
|
||||
trace->duration_filter = atof(str);
|
||||
return 0;
|
||||
}
|
||||
|
||||
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 = {
|
||||
@@ -328,21 +648,38 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
"number of mmap data pages"),
|
||||
OPT_STRING(0, "uid", &trace.opts.target.uid_str, "user",
|
||||
"user to profile"),
|
||||
OPT_CALLBACK(0, "duration", &trace, "float",
|
||||
"show only events with duration > N.M ms",
|
||||
trace__set_duration),
|
||||
OPT_BOOLEAN(0, "sched", &trace.sched, "show blocking scheduler events"),
|
||||
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;
|
||||
|
||||
err = trace__run(&trace, argc, argv);
|
||||
|
||||
if (trace.sched && !err)
|
||||
trace__fprintf_thread_summary(&trace, stdout);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
@@ -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)
|
||||
@@ -112,7 +121,10 @@ define SOURCE_PYTHON_VERSION
|
||||
#if PY_VERSION_HEX >= 0x03000000
|
||||
#error
|
||||
#endif
|
||||
int main(void){}
|
||||
int main(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
endef
|
||||
define SOURCE_PYTHON_EMBED
|
||||
#include <Python.h>
|
||||
@@ -203,4 +215,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
|
||||
|
@@ -180,9 +180,15 @@ _gea_warn = $(warning The path '$(1)' is not executable.)
|
||||
_gea_err = $(if $(1),$(error Please set '$(1)' appropriately))
|
||||
|
||||
# try-cc
|
||||
# Usage: option = $(call try-cc, source-to-build, cc-options)
|
||||
# Usage: option = $(call try-cc, source-to-build, cc-options, msg)
|
||||
ifndef V
|
||||
TRY_CC_OUTPUT= > /dev/null 2>&1
|
||||
endif
|
||||
TRY_CC_MSG=echo " CHK $(3)" 1>&2;
|
||||
|
||||
try-cc = $(shell sh -c \
|
||||
'TMP="$(OUTPUT)$(TMPOUT).$$$$"; \
|
||||
$(TRY_CC_MSG) \
|
||||
echo "$(1)" | \
|
||||
$(CC) -x c - $(2) -o "$$TMP" > /dev/null 2>&1 && echo y; \
|
||||
$(CC) -x c - $(2) -o "$$TMP" $(TRY_CC_OUTPUT) && echo y; \
|
||||
rm -f "$$TMP"')
|
||||
|
@@ -24,6 +24,7 @@ const char perf_more_info_string[] =
|
||||
|
||||
int use_browser = -1;
|
||||
static int use_pager = -1;
|
||||
const char *input_name;
|
||||
|
||||
struct cmd_struct {
|
||||
const char *cmd;
|
||||
@@ -84,21 +85,26 @@ int check_pager_config(const char *cmd)
|
||||
return c.val;
|
||||
}
|
||||
|
||||
static int tui_command_config(const char *var, const char *value, void *data)
|
||||
static int browser_command_config(const char *var, const char *value, void *data)
|
||||
{
|
||||
struct pager_config *c = data;
|
||||
if (!prefixcmp(var, "tui.") && !strcmp(var + 4, c->cmd))
|
||||
c->val = perf_config_bool(var, value);
|
||||
if (!prefixcmp(var, "gtk.") && !strcmp(var + 4, c->cmd))
|
||||
c->val = perf_config_bool(var, value) ? 2 : 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* returns 0 for "no tui", 1 for "use tui", and -1 for "not specified" */
|
||||
static int check_tui_config(const char *cmd)
|
||||
/*
|
||||
* returns 0 for "no tui", 1 for "use tui", 2 for "use gtk",
|
||||
* and -1 for "not specified"
|
||||
*/
|
||||
static int check_browser_config(const char *cmd)
|
||||
{
|
||||
struct pager_config c;
|
||||
c.cmd = cmd;
|
||||
c.val = -1;
|
||||
perf_config(tui_command_config, &c);
|
||||
perf_config(browser_command_config, &c);
|
||||
return c.val;
|
||||
}
|
||||
|
||||
@@ -301,7 +307,7 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
|
||||
prefix = NULL; /* setup_perf_directory(); */
|
||||
|
||||
if (use_browser == -1)
|
||||
use_browser = check_tui_config(p->cmd);
|
||||
use_browser = check_browser_config(p->cmd);
|
||||
|
||||
if (use_pager == -1 && p->option & RUN_SETUP)
|
||||
use_pager = check_pager_config(p->cmd);
|
||||
@@ -440,6 +446,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";
|
||||
@@ -481,6 +489,8 @@ int main(int argc, const char **argv)
|
||||
}
|
||||
cmd = argv[0];
|
||||
|
||||
test_attr__init();
|
||||
|
||||
/*
|
||||
* We use PATH to find perf commands, but we prepend some higher
|
||||
* precedence paths: the "--exec-path" option, the PERF_EXEC_PATH
|
||||
|
@@ -26,6 +26,7 @@ void get_term_dimensions(struct winsize *ws);
|
||||
#endif
|
||||
|
||||
#ifdef __powerpc__
|
||||
#include "../../arch/powerpc/include/uapi/asm/unistd.h"
|
||||
#define rmb() asm volatile ("sync" ::: "memory")
|
||||
#define cpu_relax() asm volatile ("" ::: "memory");
|
||||
#define CPUINFO_PROC "cpu"
|
||||
@@ -164,13 +165,25 @@ static inline unsigned long long rdclock(void)
|
||||
(void) (&_min1 == &_min2); \
|
||||
_min1 < _min2 ? _min1 : _min2; })
|
||||
|
||||
extern bool test_attr__enabled;
|
||||
void test_attr__init(void);
|
||||
void test_attr__open(struct perf_event_attr *attr, pid_t pid, int cpu,
|
||||
int fd, int group_fd, unsigned long flags);
|
||||
|
||||
static inline int
|
||||
sys_perf_event_open(struct perf_event_attr *attr,
|
||||
pid_t pid, int cpu, int group_fd,
|
||||
unsigned long flags)
|
||||
{
|
||||
return syscall(__NR_perf_event_open, attr, pid, cpu,
|
||||
group_fd, flags);
|
||||
int fd;
|
||||
|
||||
fd = syscall(__NR_perf_event_open, attr, pid, cpu,
|
||||
group_fd, flags);
|
||||
|
||||
if (unlikely(test_attr__enabled))
|
||||
test_attr__open(attr, pid, cpu, fd, group_fd, flags);
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
#define MAX_COUNTERS 256
|
||||
@@ -198,6 +211,7 @@ struct branch_stack {
|
||||
struct branch_entry entries[0];
|
||||
};
|
||||
|
||||
extern const char *input_name;
|
||||
extern bool perf_host, perf_guest;
|
||||
extern const char perf_version_string[];
|
||||
|
||||
|
175
tools/perf/tests/attr.c
Normal file
175
tools/perf/tests/attr.c
Normal file
@@ -0,0 +1,175 @@
|
||||
|
||||
/*
|
||||
* The struct perf_event_attr test support.
|
||||
*
|
||||
* This test is embedded inside into perf directly and is governed
|
||||
* by the PERF_TEST_ATTR environment variable and hook inside
|
||||
* sys_perf_event_open function.
|
||||
*
|
||||
* The general idea is to store 'struct perf_event_attr' details for
|
||||
* each event created within single perf command. Each event details
|
||||
* are stored into separate text file. Once perf command is finished
|
||||
* these files can be checked for values we expect for command.
|
||||
*
|
||||
* Besides 'struct perf_event_attr' values we also store 'fd' and
|
||||
* 'group_fd' values to allow checking for groups created.
|
||||
*
|
||||
* This all is triggered by setting PERF_TEST_ATTR environment variable.
|
||||
* It must contain name of existing directory with access and write
|
||||
* permissions. All the event text files are stored there.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include "../perf.h"
|
||||
#include "util.h"
|
||||
#include "exec_cmd.h"
|
||||
#include "tests.h"
|
||||
|
||||
#define ENV "PERF_TEST_ATTR"
|
||||
|
||||
extern int verbose;
|
||||
|
||||
bool test_attr__enabled;
|
||||
|
||||
static char *dir;
|
||||
|
||||
void test_attr__init(void)
|
||||
{
|
||||
dir = getenv(ENV);
|
||||
test_attr__enabled = (dir != NULL);
|
||||
}
|
||||
|
||||
#define BUFSIZE 1024
|
||||
|
||||
#define __WRITE_ASS(str, fmt, data) \
|
||||
do { \
|
||||
char buf[BUFSIZE]; \
|
||||
size_t size; \
|
||||
\
|
||||
size = snprintf(buf, BUFSIZE, #str "=%"fmt "\n", data); \
|
||||
if (1 != fwrite(buf, size, 1, file)) { \
|
||||
perror("test attr - failed to write event file"); \
|
||||
fclose(file); \
|
||||
return -1; \
|
||||
} \
|
||||
\
|
||||
} while (0)
|
||||
|
||||
#define WRITE_ASS(field, fmt) __WRITE_ASS(field, fmt, attr->field)
|
||||
|
||||
static int store_event(struct perf_event_attr *attr, pid_t pid, int cpu,
|
||||
int fd, int group_fd, unsigned long flags)
|
||||
{
|
||||
FILE *file;
|
||||
char path[PATH_MAX];
|
||||
|
||||
snprintf(path, PATH_MAX, "%s/event-%d-%llu-%d", dir,
|
||||
attr->type, attr->config, fd);
|
||||
|
||||
file = fopen(path, "w+");
|
||||
if (!file) {
|
||||
perror("test attr - failed to open event file");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fprintf(file, "[event-%d-%llu-%d]\n",
|
||||
attr->type, attr->config, fd) < 0) {
|
||||
perror("test attr - failed to write event file");
|
||||
fclose(file);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* syscall arguments */
|
||||
__WRITE_ASS(fd, "d", fd);
|
||||
__WRITE_ASS(group_fd, "d", group_fd);
|
||||
__WRITE_ASS(cpu, "d", cpu);
|
||||
__WRITE_ASS(pid, "d", pid);
|
||||
__WRITE_ASS(flags, "lu", flags);
|
||||
|
||||
/* struct perf_event_attr */
|
||||
WRITE_ASS(type, PRIu32);
|
||||
WRITE_ASS(size, PRIu32);
|
||||
WRITE_ASS(config, "llu");
|
||||
WRITE_ASS(sample_period, "llu");
|
||||
WRITE_ASS(sample_type, "llu");
|
||||
WRITE_ASS(read_format, "llu");
|
||||
WRITE_ASS(disabled, "d");
|
||||
WRITE_ASS(inherit, "d");
|
||||
WRITE_ASS(pinned, "d");
|
||||
WRITE_ASS(exclusive, "d");
|
||||
WRITE_ASS(exclude_user, "d");
|
||||
WRITE_ASS(exclude_kernel, "d");
|
||||
WRITE_ASS(exclude_hv, "d");
|
||||
WRITE_ASS(exclude_idle, "d");
|
||||
WRITE_ASS(mmap, "d");
|
||||
WRITE_ASS(comm, "d");
|
||||
WRITE_ASS(freq, "d");
|
||||
WRITE_ASS(inherit_stat, "d");
|
||||
WRITE_ASS(enable_on_exec, "d");
|
||||
WRITE_ASS(task, "d");
|
||||
WRITE_ASS(watermark, "d");
|
||||
WRITE_ASS(precise_ip, "d");
|
||||
WRITE_ASS(mmap_data, "d");
|
||||
WRITE_ASS(sample_id_all, "d");
|
||||
WRITE_ASS(exclude_host, "d");
|
||||
WRITE_ASS(exclude_guest, "d");
|
||||
WRITE_ASS(exclude_callchain_kernel, "d");
|
||||
WRITE_ASS(exclude_callchain_user, "d");
|
||||
WRITE_ASS(wakeup_events, PRIu32);
|
||||
WRITE_ASS(bp_type, PRIu32);
|
||||
WRITE_ASS(config1, "llu");
|
||||
WRITE_ASS(config2, "llu");
|
||||
WRITE_ASS(branch_sample_type, "llu");
|
||||
WRITE_ASS(sample_regs_user, "llu");
|
||||
WRITE_ASS(sample_stack_user, PRIu32);
|
||||
|
||||
fclose(file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void test_attr__open(struct perf_event_attr *attr, pid_t pid, int cpu,
|
||||
int fd, int group_fd, unsigned long flags)
|
||||
{
|
||||
int errno_saved = errno;
|
||||
|
||||
if (store_event(attr, pid, cpu, fd, group_fd, flags))
|
||||
die("test attr FAILED");
|
||||
|
||||
errno = errno_saved;
|
||||
}
|
||||
|
||||
static int run_dir(const char *d, const char *perf)
|
||||
{
|
||||
char cmd[3*PATH_MAX];
|
||||
|
||||
snprintf(cmd, 3*PATH_MAX, "python %s/attr.py -d %s/attr/ -p %s %s",
|
||||
d, d, perf, verbose ? "-v" : "");
|
||||
|
||||
return system(cmd);
|
||||
}
|
||||
|
||||
int test__attr(void)
|
||||
{
|
||||
struct stat st;
|
||||
char path_perf[PATH_MAX];
|
||||
char path_dir[PATH_MAX];
|
||||
|
||||
/* First try developement tree tests. */
|
||||
if (!lstat("./tests", &st))
|
||||
return run_dir("./tests", "./perf");
|
||||
|
||||
/* Then installed path. */
|
||||
snprintf(path_dir, PATH_MAX, "%s/tests", perf_exec_path());
|
||||
snprintf(path_perf, PATH_MAX, "%s/perf", BINDIR);
|
||||
|
||||
if (!lstat(path_dir, &st) &&
|
||||
!lstat(path_perf, &st))
|
||||
return run_dir(path_dir, path_perf);
|
||||
|
||||
fprintf(stderr, " (ommitted)");
|
||||
return 0;
|
||||
}
|
322
tools/perf/tests/attr.py
Normal file
322
tools/perf/tests/attr.py
Normal file
@@ -0,0 +1,322 @@
|
||||
#! /usr/bin/python
|
||||
|
||||
import os
|
||||
import sys
|
||||
import glob
|
||||
import optparse
|
||||
import tempfile
|
||||
import logging
|
||||
import shutil
|
||||
import ConfigParser
|
||||
|
||||
class Fail(Exception):
|
||||
def __init__(self, test, msg):
|
||||
self.msg = msg
|
||||
self.test = test
|
||||
def getMsg(self):
|
||||
return '\'%s\' - %s' % (self.test.path, self.msg)
|
||||
|
||||
class Unsup(Exception):
|
||||
def __init__(self, test):
|
||||
self.test = test
|
||||
def getMsg(self):
|
||||
return '\'%s\'' % self.test.path
|
||||
|
||||
class Event(dict):
|
||||
terms = [
|
||||
'flags',
|
||||
'type',
|
||||
'size',
|
||||
'config',
|
||||
'sample_period',
|
||||
'sample_type',
|
||||
'read_format',
|
||||
'disabled',
|
||||
'inherit',
|
||||
'pinned',
|
||||
'exclusive',
|
||||
'exclude_user',
|
||||
'exclude_kernel',
|
||||
'exclude_hv',
|
||||
'exclude_idle',
|
||||
'mmap',
|
||||
'comm',
|
||||
'freq',
|
||||
'inherit_stat',
|
||||
'enable_on_exec',
|
||||
'task',
|
||||
'watermark',
|
||||
'precise_ip',
|
||||
'mmap_data',
|
||||
'sample_id_all',
|
||||
'exclude_host',
|
||||
'exclude_guest',
|
||||
'exclude_callchain_kernel',
|
||||
'exclude_callchain_user',
|
||||
'wakeup_events',
|
||||
'bp_type',
|
||||
'config1',
|
||||
'config2',
|
||||
'branch_sample_type',
|
||||
'sample_regs_user',
|
||||
'sample_stack_user',
|
||||
]
|
||||
|
||||
def add(self, data):
|
||||
for key, val in data:
|
||||
log.debug(" %s = %s" % (key, val))
|
||||
self[key] = val
|
||||
|
||||
def __init__(self, name, data, base):
|
||||
log.info(" Event %s" % name);
|
||||
self.name = name;
|
||||
self.group = ''
|
||||
self.add(base)
|
||||
self.add(data)
|
||||
|
||||
def compare_data(self, a, b):
|
||||
# Allow multiple values in assignment separated by '|'
|
||||
a_list = a.split('|')
|
||||
b_list = b.split('|')
|
||||
|
||||
for a_item in a_list:
|
||||
for b_item in b_list:
|
||||
if (a_item == b_item):
|
||||
return True
|
||||
elif (a_item == '*') or (b_item == '*'):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def equal(self, other):
|
||||
for t in Event.terms:
|
||||
log.debug(" [%s] %s %s" % (t, self[t], other[t]));
|
||||
if not self.has_key(t) or not other.has_key(t):
|
||||
return False
|
||||
if not self.compare_data(self[t], other[t]):
|
||||
return False
|
||||
return True
|
||||
|
||||
# Test file description needs to have following sections:
|
||||
# [config]
|
||||
# - just single instance in file
|
||||
# - needs to specify:
|
||||
# 'command' - perf command name
|
||||
# 'args' - special command arguments
|
||||
# 'ret' - expected command return value (0 by default)
|
||||
#
|
||||
# [eventX:base]
|
||||
# - one or multiple instances in file
|
||||
# - expected values assignments
|
||||
class Test(object):
|
||||
def __init__(self, path, options):
|
||||
parser = ConfigParser.SafeConfigParser()
|
||||
parser.read(path)
|
||||
|
||||
log.warning("running '%s'" % path)
|
||||
|
||||
self.path = path
|
||||
self.test_dir = options.test_dir
|
||||
self.perf = options.perf
|
||||
self.command = parser.get('config', 'command')
|
||||
self.args = parser.get('config', 'args')
|
||||
|
||||
try:
|
||||
self.ret = parser.get('config', 'ret')
|
||||
except:
|
||||
self.ret = 0
|
||||
|
||||
self.expect = {}
|
||||
self.result = {}
|
||||
log.info(" loading expected events");
|
||||
self.load_events(path, self.expect)
|
||||
|
||||
def is_event(self, name):
|
||||
if name.find("event") == -1:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def load_events(self, path, events):
|
||||
parser_event = ConfigParser.SafeConfigParser()
|
||||
parser_event.read(path)
|
||||
|
||||
# The event record section header contains 'event' word,
|
||||
# optionaly followed by ':' allowing to load 'parent
|
||||
# event' first as a base
|
||||
for section in filter(self.is_event, parser_event.sections()):
|
||||
|
||||
parser_items = parser_event.items(section);
|
||||
base_items = {}
|
||||
|
||||
# Read parent event if there's any
|
||||
if (':' in section):
|
||||
base = section[section.index(':') + 1:]
|
||||
parser_base = ConfigParser.SafeConfigParser()
|
||||
parser_base.read(self.test_dir + '/' + base)
|
||||
base_items = parser_base.items('event')
|
||||
|
||||
e = Event(section, parser_items, base_items)
|
||||
events[section] = e
|
||||
|
||||
def run_cmd(self, tempdir):
|
||||
cmd = "PERF_TEST_ATTR=%s %s %s -o %s/perf.data %s" % (tempdir,
|
||||
self.perf, self.command, tempdir, self.args)
|
||||
ret = os.WEXITSTATUS(os.system(cmd))
|
||||
|
||||
log.info(" running '%s' ret %d " % (cmd, ret))
|
||||
|
||||
if ret != int(self.ret):
|
||||
raise Unsup(self)
|
||||
|
||||
def compare(self, expect, result):
|
||||
match = {}
|
||||
|
||||
log.info(" compare");
|
||||
|
||||
# For each expected event find all matching
|
||||
# events in result. Fail if there's not any.
|
||||
for exp_name, exp_event in expect.items():
|
||||
exp_list = []
|
||||
log.debug(" matching [%s]" % exp_name)
|
||||
for res_name, res_event in result.items():
|
||||
log.debug(" to [%s]" % res_name)
|
||||
if (exp_event.equal(res_event)):
|
||||
exp_list.append(res_name)
|
||||
log.debug(" ->OK")
|
||||
else:
|
||||
log.debug(" ->FAIL");
|
||||
|
||||
log.info(" match: [%s] matches %s" % (exp_name, str(exp_list)))
|
||||
|
||||
# we did not any matching event - fail
|
||||
if (not exp_list):
|
||||
raise Fail(self, 'match failure');
|
||||
|
||||
match[exp_name] = exp_list
|
||||
|
||||
# For each defined group in the expected events
|
||||
# check we match the same group in the result.
|
||||
for exp_name, exp_event in expect.items():
|
||||
group = exp_event.group
|
||||
|
||||
if (group == ''):
|
||||
continue
|
||||
|
||||
for res_name in match[exp_name]:
|
||||
res_group = result[res_name].group
|
||||
if res_group not in match[group]:
|
||||
raise Fail(self, 'group failure')
|
||||
|
||||
log.info(" group: [%s] matches group leader %s" %
|
||||
(exp_name, str(match[group])))
|
||||
|
||||
log.info(" matched")
|
||||
|
||||
def resolve_groups(self, events):
|
||||
for name, event in events.items():
|
||||
group_fd = event['group_fd'];
|
||||
if group_fd == '-1':
|
||||
continue;
|
||||
|
||||
for iname, ievent in events.items():
|
||||
if (ievent['fd'] == group_fd):
|
||||
event.group = iname
|
||||
log.debug('[%s] has group leader [%s]' % (name, iname))
|
||||
break;
|
||||
|
||||
def run(self):
|
||||
tempdir = tempfile.mkdtemp();
|
||||
|
||||
try:
|
||||
# run the test script
|
||||
self.run_cmd(tempdir);
|
||||
|
||||
# load events expectation for the test
|
||||
log.info(" loading result events");
|
||||
for f in glob.glob(tempdir + '/event*'):
|
||||
self.load_events(f, self.result);
|
||||
|
||||
# resolve group_fd to event names
|
||||
self.resolve_groups(self.expect);
|
||||
self.resolve_groups(self.result);
|
||||
|
||||
# do the expectation - results matching - both ways
|
||||
self.compare(self.expect, self.result)
|
||||
self.compare(self.result, self.expect)
|
||||
|
||||
finally:
|
||||
# cleanup
|
||||
shutil.rmtree(tempdir)
|
||||
|
||||
|
||||
def run_tests(options):
|
||||
for f in glob.glob(options.test_dir + '/' + options.test):
|
||||
try:
|
||||
Test(f, options).run()
|
||||
except Unsup, obj:
|
||||
log.warning("unsupp %s" % obj.getMsg())
|
||||
|
||||
def setup_log(verbose):
|
||||
global log
|
||||
level = logging.CRITICAL
|
||||
|
||||
if verbose == 1:
|
||||
level = logging.WARNING
|
||||
if verbose == 2:
|
||||
level = logging.INFO
|
||||
if verbose >= 3:
|
||||
level = logging.DEBUG
|
||||
|
||||
log = logging.getLogger('test')
|
||||
log.setLevel(level)
|
||||
ch = logging.StreamHandler()
|
||||
ch.setLevel(level)
|
||||
formatter = logging.Formatter('%(message)s')
|
||||
ch.setFormatter(formatter)
|
||||
log.addHandler(ch)
|
||||
|
||||
USAGE = '''%s [OPTIONS]
|
||||
-d dir # tests dir
|
||||
-p path # perf binary
|
||||
-t test # single test
|
||||
-v # verbose level
|
||||
''' % sys.argv[0]
|
||||
|
||||
def main():
|
||||
parser = optparse.OptionParser(usage=USAGE)
|
||||
|
||||
parser.add_option("-t", "--test",
|
||||
action="store", type="string", dest="test")
|
||||
parser.add_option("-d", "--test-dir",
|
||||
action="store", type="string", dest="test_dir")
|
||||
parser.add_option("-p", "--perf",
|
||||
action="store", type="string", dest="perf")
|
||||
parser.add_option("-v", "--verbose",
|
||||
action="count", dest="verbose")
|
||||
|
||||
options, args = parser.parse_args()
|
||||
if args:
|
||||
parser.error('FAILED wrong arguments %s' % ' '.join(args))
|
||||
return -1
|
||||
|
||||
setup_log(options.verbose)
|
||||
|
||||
if not options.test_dir:
|
||||
print 'FAILED no -d option specified'
|
||||
sys.exit(-1)
|
||||
|
||||
if not options.test:
|
||||
options.test = 'test*'
|
||||
|
||||
try:
|
||||
run_tests(options)
|
||||
|
||||
except Fail, obj:
|
||||
print "FAILED %s" % obj.getMsg();
|
||||
sys.exit(-1)
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
64
tools/perf/tests/attr/README
Normal file
64
tools/perf/tests/attr/README
Normal file
@@ -0,0 +1,64 @@
|
||||
The struct perf_event_attr test (attr tests) support
|
||||
====================================================
|
||||
This testing support is embedded into perf directly and is governed
|
||||
by the PERF_TEST_ATTR environment variable and hook inside the
|
||||
sys_perf_event_open function.
|
||||
|
||||
The general idea is to store 'struct perf_event_attr' details for
|
||||
each event created within single perf command. Each event details
|
||||
are stored into separate text file. Once perf command is finished
|
||||
these files are checked for values we expect for command.
|
||||
|
||||
The attr tests consist of following parts:
|
||||
|
||||
tests/attr.c
|
||||
------------
|
||||
This is the sys_perf_event_open hook implementation. The hook
|
||||
is triggered when the PERF_TEST_ATTR environment variable is
|
||||
defined. It must contain name of existing directory with access
|
||||
and write permissions.
|
||||
|
||||
For each sys_perf_event_open call event details are stored in
|
||||
separate file. Besides 'struct perf_event_attr' values we also
|
||||
store 'fd' and 'group_fd' values to allow checking for groups.
|
||||
|
||||
tests/attr.py
|
||||
-------------
|
||||
This is the python script that does all the hard work. It reads
|
||||
the test definition, executes it and checks results.
|
||||
|
||||
tests/attr/
|
||||
-----------
|
||||
Directory containing all attr test definitions.
|
||||
Following tests are defined (with perf commands):
|
||||
|
||||
perf record kill (test-record-basic)
|
||||
perf record -b kill (test-record-branch-any)
|
||||
perf record -j any kill (test-record-branch-filter-any)
|
||||
perf record -j any_call kill (test-record-branch-filter-any_call)
|
||||
perf record -j any_ret kill (test-record-branch-filter-any_ret)
|
||||
perf record -j hv kill (test-record-branch-filter-hv)
|
||||
perf record -j ind_call kill (test-record-branch-filter-ind_call)
|
||||
perf record -j k kill (test-record-branch-filter-k)
|
||||
perf record -j u kill (test-record-branch-filter-u)
|
||||
perf record -c 123 kill (test-record-count)
|
||||
perf record -d kill (test-record-data)
|
||||
perf record -F 100 kill (test-record-freq)
|
||||
perf record -g -- kill (test-record-graph-default)
|
||||
perf record -g dwarf -- kill (test-record-graph-dwarf)
|
||||
perf record -g fp kill (test-record-graph-fp)
|
||||
perf record --group -e cycles,instructions kill (test-record-group)
|
||||
perf record -e '{cycles,instructions}' kill (test-record-group1)
|
||||
perf record -D kill (test-record-no-delay)
|
||||
perf record -i kill (test-record-no-inherit)
|
||||
perf record -n kill (test-record-no-samples)
|
||||
perf record -c 100 -P kill (test-record-period)
|
||||
perf record -R kill (test-record-raw)
|
||||
perf stat -e cycles kill (test-stat-basic)
|
||||
perf stat kill (test-stat-default)
|
||||
perf stat -d kill (test-stat-detailed-1)
|
||||
perf stat -dd kill (test-stat-detailed-2)
|
||||
perf stat -ddd kill (test-stat-detailed-3)
|
||||
perf stat --group -e cycles,instructions kill (test-stat-group)
|
||||
perf stat -e '{cycles,instructions}' kill (test-stat-group1)
|
||||
perf stat -i -e cycles kill (test-stat-no-inherit)
|
39
tools/perf/tests/attr/base-record
Normal file
39
tools/perf/tests/attr/base-record
Normal file
@@ -0,0 +1,39 @@
|
||||
[event]
|
||||
fd=1
|
||||
group_fd=-1
|
||||
flags=0
|
||||
type=0|1
|
||||
size=96
|
||||
config=0
|
||||
sample_period=4000
|
||||
sample_type=263
|
||||
read_format=7
|
||||
disabled=1
|
||||
inherit=1
|
||||
pinned=0
|
||||
exclusive=0
|
||||
exclude_user=0
|
||||
exclude_kernel=0
|
||||
exclude_hv=0
|
||||
exclude_idle=0
|
||||
mmap=1
|
||||
comm=1
|
||||
freq=1
|
||||
inherit_stat=0
|
||||
enable_on_exec=1
|
||||
task=0
|
||||
watermark=0
|
||||
precise_ip=0
|
||||
mmap_data=0
|
||||
sample_id_all=1
|
||||
exclude_host=0
|
||||
exclude_guest=1
|
||||
exclude_callchain_kernel=0
|
||||
exclude_callchain_user=0
|
||||
wakeup_events=0
|
||||
bp_type=0
|
||||
config1=0
|
||||
config2=0
|
||||
branch_sample_type=0
|
||||
sample_regs_user=0
|
||||
sample_stack_user=0
|
39
tools/perf/tests/attr/base-stat
Normal file
39
tools/perf/tests/attr/base-stat
Normal file
@@ -0,0 +1,39 @@
|
||||
[event]
|
||||
fd=1
|
||||
group_fd=-1
|
||||
flags=0
|
||||
type=0
|
||||
size=96
|
||||
config=0
|
||||
sample_period=0
|
||||
sample_type=0
|
||||
read_format=3
|
||||
disabled=1
|
||||
inherit=1
|
||||
pinned=0
|
||||
exclusive=0
|
||||
exclude_user=0
|
||||
exclude_kernel=0
|
||||
exclude_hv=0
|
||||
exclude_idle=0
|
||||
mmap=0
|
||||
comm=0
|
||||
freq=0
|
||||
inherit_stat=0
|
||||
enable_on_exec=1
|
||||
task=0
|
||||
watermark=0
|
||||
precise_ip=0
|
||||
mmap_data=0
|
||||
sample_id_all=0
|
||||
exclude_host=0
|
||||
exclude_guest=1
|
||||
exclude_callchain_kernel=0
|
||||
exclude_callchain_user=0
|
||||
wakeup_events=0
|
||||
bp_type=0
|
||||
config1=0
|
||||
config2=0
|
||||
branch_sample_type=0
|
||||
sample_regs_user=0
|
||||
sample_stack_user=0
|
5
tools/perf/tests/attr/test-record-basic
Normal file
5
tools/perf/tests/attr/test-record-basic
Normal file
@@ -0,0 +1,5 @@
|
||||
[config]
|
||||
command = record
|
||||
args = kill >/dev/null 2>&1
|
||||
|
||||
[event:base-record]
|
8
tools/perf/tests/attr/test-record-branch-any
Normal file
8
tools/perf/tests/attr/test-record-branch-any
Normal file
@@ -0,0 +1,8 @@
|
||||
[config]
|
||||
command = record
|
||||
args = -b kill >/dev/null 2>&1
|
||||
|
||||
[event:base-record]
|
||||
sample_period=4000
|
||||
sample_type=2311
|
||||
branch_sample_type=8
|
8
tools/perf/tests/attr/test-record-branch-filter-any
Normal file
8
tools/perf/tests/attr/test-record-branch-filter-any
Normal file
@@ -0,0 +1,8 @@
|
||||
[config]
|
||||
command = record
|
||||
args = -j any kill >/dev/null 2>&1
|
||||
|
||||
[event:base-record]
|
||||
sample_period=4000
|
||||
sample_type=2311
|
||||
branch_sample_type=8
|
8
tools/perf/tests/attr/test-record-branch-filter-any_call
Normal file
8
tools/perf/tests/attr/test-record-branch-filter-any_call
Normal file
@@ -0,0 +1,8 @@
|
||||
[config]
|
||||
command = record
|
||||
args = -j any_call kill >/dev/null 2>&1
|
||||
|
||||
[event:base-record]
|
||||
sample_period=4000
|
||||
sample_type=2311
|
||||
branch_sample_type=16
|
8
tools/perf/tests/attr/test-record-branch-filter-any_ret
Normal file
8
tools/perf/tests/attr/test-record-branch-filter-any_ret
Normal file
@@ -0,0 +1,8 @@
|
||||
[config]
|
||||
command = record
|
||||
args = -j any_ret kill >/dev/null 2>&1
|
||||
|
||||
[event:base-record]
|
||||
sample_period=4000
|
||||
sample_type=2311
|
||||
branch_sample_type=32
|
8
tools/perf/tests/attr/test-record-branch-filter-hv
Normal file
8
tools/perf/tests/attr/test-record-branch-filter-hv
Normal file
@@ -0,0 +1,8 @@
|
||||
[config]
|
||||
command = record
|
||||
args = -j hv kill >/dev/null 2>&1
|
||||
|
||||
[event:base-record]
|
||||
sample_period=4000
|
||||
sample_type=2311
|
||||
branch_sample_type=8
|
8
tools/perf/tests/attr/test-record-branch-filter-ind_call
Normal file
8
tools/perf/tests/attr/test-record-branch-filter-ind_call
Normal file
@@ -0,0 +1,8 @@
|
||||
[config]
|
||||
command = record
|
||||
args = -j ind_call kill >/dev/null 2>&1
|
||||
|
||||
[event:base-record]
|
||||
sample_period=4000
|
||||
sample_type=2311
|
||||
branch_sample_type=64
|
8
tools/perf/tests/attr/test-record-branch-filter-k
Normal file
8
tools/perf/tests/attr/test-record-branch-filter-k
Normal file
@@ -0,0 +1,8 @@
|
||||
[config]
|
||||
command = record
|
||||
args = -j k kill >/dev/null 2>&1
|
||||
|
||||
[event:base-record]
|
||||
sample_period=4000
|
||||
sample_type=2311
|
||||
branch_sample_type=8
|
8
tools/perf/tests/attr/test-record-branch-filter-u
Normal file
8
tools/perf/tests/attr/test-record-branch-filter-u
Normal file
@@ -0,0 +1,8 @@
|
||||
[config]
|
||||
command = record
|
||||
args = -j u kill >/dev/null 2>&1
|
||||
|
||||
[event:base-record]
|
||||
sample_period=4000
|
||||
sample_type=2311
|
||||
branch_sample_type=8
|
8
tools/perf/tests/attr/test-record-count
Normal file
8
tools/perf/tests/attr/test-record-count
Normal file
@@ -0,0 +1,8 @@
|
||||
[config]
|
||||
command = record
|
||||
args = -c 123 kill >/dev/null 2>&1
|
||||
|
||||
[event:base-record]
|
||||
sample_period=123
|
||||
sample_type=7
|
||||
freq=0
|
8
tools/perf/tests/attr/test-record-data
Normal file
8
tools/perf/tests/attr/test-record-data
Normal file
@@ -0,0 +1,8 @@
|
||||
[config]
|
||||
command = record
|
||||
args = -d kill >/dev/null 2>&1
|
||||
|
||||
[event:base-record]
|
||||
sample_period=4000
|
||||
sample_type=271
|
||||
mmap_data=1
|
6
tools/perf/tests/attr/test-record-freq
Normal file
6
tools/perf/tests/attr/test-record-freq
Normal file
@@ -0,0 +1,6 @@
|
||||
[config]
|
||||
command = record
|
||||
args = -F 100 kill >/dev/null 2>&1
|
||||
|
||||
[event:base-record]
|
||||
sample_period=100
|
6
tools/perf/tests/attr/test-record-graph-default
Normal file
6
tools/perf/tests/attr/test-record-graph-default
Normal file
@@ -0,0 +1,6 @@
|
||||
[config]
|
||||
command = record
|
||||
args = -g -- kill >/dev/null 2>&1
|
||||
|
||||
[event:base-record]
|
||||
sample_type=295
|
10
tools/perf/tests/attr/test-record-graph-dwarf
Normal file
10
tools/perf/tests/attr/test-record-graph-dwarf
Normal file
@@ -0,0 +1,10 @@
|
||||
[config]
|
||||
command = record
|
||||
args = -g dwarf -- kill >/dev/null 2>&1
|
||||
|
||||
[event:base-record]
|
||||
sample_type=12583
|
||||
exclude_callchain_user=1
|
||||
sample_stack_user=8192
|
||||
# TODO different for each arch, no support for that now
|
||||
sample_regs_user=*
|
6
tools/perf/tests/attr/test-record-graph-fp
Normal file
6
tools/perf/tests/attr/test-record-graph-fp
Normal file
@@ -0,0 +1,6 @@
|
||||
[config]
|
||||
command = record
|
||||
args = -g fp kill >/dev/null 2>&1
|
||||
|
||||
[event:base-record]
|
||||
sample_type=295
|
18
tools/perf/tests/attr/test-record-group
Normal file
18
tools/perf/tests/attr/test-record-group
Normal file
@@ -0,0 +1,18 @@
|
||||
[config]
|
||||
command = record
|
||||
args = --group -e cycles,instructions kill >/dev/null 2>&1
|
||||
|
||||
[event-1:base-record]
|
||||
fd=1
|
||||
group_fd=-1
|
||||
sample_type=327
|
||||
|
||||
[event-2:base-record]
|
||||
fd=2
|
||||
group_fd=1
|
||||
config=1
|
||||
sample_type=327
|
||||
mmap=0
|
||||
comm=0
|
||||
enable_on_exec=0
|
||||
disabled=0
|
19
tools/perf/tests/attr/test-record-group1
Normal file
19
tools/perf/tests/attr/test-record-group1
Normal file
@@ -0,0 +1,19 @@
|
||||
[config]
|
||||
command = record
|
||||
args = -e '{cycles,instructions}' kill >/tmp/krava 2>&1
|
||||
|
||||
[event-1:base-record]
|
||||
fd=1
|
||||
group_fd=-1
|
||||
sample_type=327
|
||||
|
||||
[event-2:base-record]
|
||||
fd=2
|
||||
group_fd=1
|
||||
type=0
|
||||
config=1
|
||||
sample_type=327
|
||||
mmap=0
|
||||
comm=0
|
||||
enable_on_exec=0
|
||||
disabled=0
|
9
tools/perf/tests/attr/test-record-no-delay
Normal file
9
tools/perf/tests/attr/test-record-no-delay
Normal file
@@ -0,0 +1,9 @@
|
||||
[config]
|
||||
command = record
|
||||
args = -D kill >/dev/null 2>&1
|
||||
|
||||
[event:base-record]
|
||||
sample_period=4000
|
||||
sample_type=263
|
||||
watermark=0
|
||||
wakeup_events=1
|
7
tools/perf/tests/attr/test-record-no-inherit
Normal file
7
tools/perf/tests/attr/test-record-no-inherit
Normal file
@@ -0,0 +1,7 @@
|
||||
[config]
|
||||
command = record
|
||||
args = -i kill >/dev/null 2>&1
|
||||
|
||||
[event:base-record]
|
||||
sample_type=259
|
||||
inherit=0
|
6
tools/perf/tests/attr/test-record-no-samples
Normal file
6
tools/perf/tests/attr/test-record-no-samples
Normal file
@@ -0,0 +1,6 @@
|
||||
[config]
|
||||
command = record
|
||||
args = -n kill >/dev/null 2>&1
|
||||
|
||||
[event:base-record]
|
||||
sample_period=0
|
7
tools/perf/tests/attr/test-record-period
Normal file
7
tools/perf/tests/attr/test-record-period
Normal file
@@ -0,0 +1,7 @@
|
||||
[config]
|
||||
command = record
|
||||
args = -c 100 -P kill >/dev/null 2>&1
|
||||
|
||||
[event:base-record]
|
||||
sample_period=100
|
||||
freq=0
|
7
tools/perf/tests/attr/test-record-raw
Normal file
7
tools/perf/tests/attr/test-record-raw
Normal file
@@ -0,0 +1,7 @@
|
||||
[config]
|
||||
command = record
|
||||
args = -R kill >/dev/null 2>&1
|
||||
|
||||
[event:base-record]
|
||||
sample_period=4000
|
||||
sample_type=1415
|
6
tools/perf/tests/attr/test-stat-basic
Normal file
6
tools/perf/tests/attr/test-stat-basic
Normal file
@@ -0,0 +1,6 @@
|
||||
[config]
|
||||
command = stat
|
||||
args = -e cycles kill >/dev/null 2>&1
|
||||
ret = 1
|
||||
|
||||
[event:base-stat]
|
64
tools/perf/tests/attr/test-stat-default
Normal file
64
tools/perf/tests/attr/test-stat-default
Normal file
@@ -0,0 +1,64 @@
|
||||
[config]
|
||||
command = stat
|
||||
args = kill >/dev/null 2>&1
|
||||
ret = 1
|
||||
|
||||
# PERF_TYPE_SOFTWARE / PERF_COUNT_SW_TASK_CLOCK
|
||||
[event1:base-stat]
|
||||
fd=1
|
||||
type=1
|
||||
config=1
|
||||
|
||||
# PERF_TYPE_SOFTWARE / PERF_COUNT_SW_CONTEXT_SWITCHES
|
||||
[event2:base-stat]
|
||||
fd=2
|
||||
type=1
|
||||
config=3
|
||||
|
||||
# PERF_TYPE_SOFTWARE / PERF_COUNT_SW_CPU_MIGRATIONS
|
||||
[event3:base-stat]
|
||||
fd=3
|
||||
type=1
|
||||
config=4
|
||||
|
||||
# PERF_TYPE_SOFTWARE / PERF_COUNT_SW_PAGE_FAULTS
|
||||
[event4:base-stat]
|
||||
fd=4
|
||||
type=1
|
||||
config=2
|
||||
|
||||
# PERF_TYPE_HARDWARE / PERF_COUNT_HW_CPU_CYCLES
|
||||
[event5:base-stat]
|
||||
fd=5
|
||||
type=0
|
||||
config=0
|
||||
|
||||
# PERF_TYPE_HARDWARE / PERF_COUNT_HW_STALLED_CYCLES_FRONTEND
|
||||
[event6:base-stat]
|
||||
fd=6
|
||||
type=0
|
||||
config=7
|
||||
|
||||
# PERF_TYPE_HARDWARE / PERF_COUNT_HW_STALLED_CYCLES_BACKEND
|
||||
[event7:base-stat]
|
||||
fd=7
|
||||
type=0
|
||||
config=8
|
||||
|
||||
# PERF_TYPE_HARDWARE / PERF_COUNT_HW_INSTRUCTIONS
|
||||
[event8:base-stat]
|
||||
fd=8
|
||||
type=0
|
||||
config=1
|
||||
|
||||
# PERF_TYPE_HARDWARE / PERF_COUNT_HW_BRANCH_INSTRUCTIONS
|
||||
[event9:base-stat]
|
||||
fd=9
|
||||
type=0
|
||||
config=4
|
||||
|
||||
# PERF_TYPE_HARDWARE / PERF_COUNT_HW_BRANCH_MISSES
|
||||
[event10:base-stat]
|
||||
fd=10
|
||||
type=0
|
||||
config=5
|
101
tools/perf/tests/attr/test-stat-detailed-1
Normal file
101
tools/perf/tests/attr/test-stat-detailed-1
Normal file
@@ -0,0 +1,101 @@
|
||||
[config]
|
||||
command = stat
|
||||
args = -d kill >/dev/null 2>&1
|
||||
ret = 1
|
||||
|
||||
|
||||
# PERF_TYPE_SOFTWARE / PERF_COUNT_SW_TASK_CLOCK
|
||||
[event1:base-stat]
|
||||
fd=1
|
||||
type=1
|
||||
config=1
|
||||
|
||||
# PERF_TYPE_SOFTWARE / PERF_COUNT_SW_CONTEXT_SWITCHES
|
||||
[event2:base-stat]
|
||||
fd=2
|
||||
type=1
|
||||
config=3
|
||||
|
||||
# PERF_TYPE_SOFTWARE / PERF_COUNT_SW_CPU_MIGRATIONS
|
||||
[event3:base-stat]
|
||||
fd=3
|
||||
type=1
|
||||
config=4
|
||||
|
||||
# PERF_TYPE_SOFTWARE / PERF_COUNT_SW_PAGE_FAULTS
|
||||
[event4:base-stat]
|
||||
fd=4
|
||||
type=1
|
||||
config=2
|
||||
|
||||
# PERF_TYPE_HARDWARE / PERF_COUNT_HW_CPU_CYCLES
|
||||
[event5:base-stat]
|
||||
fd=5
|
||||
type=0
|
||||
config=0
|
||||
|
||||
# PERF_TYPE_HARDWARE / PERF_COUNT_HW_STALLED_CYCLES_FRONTEND
|
||||
[event6:base-stat]
|
||||
fd=6
|
||||
type=0
|
||||
config=7
|
||||
|
||||
# PERF_TYPE_HARDWARE / PERF_COUNT_HW_STALLED_CYCLES_BACKEND
|
||||
[event7:base-stat]
|
||||
fd=7
|
||||
type=0
|
||||
config=8
|
||||
|
||||
# PERF_TYPE_HARDWARE / PERF_COUNT_HW_INSTRUCTIONS
|
||||
[event8:base-stat]
|
||||
fd=8
|
||||
type=0
|
||||
config=1
|
||||
|
||||
# PERF_TYPE_HARDWARE / PERF_COUNT_HW_BRANCH_INSTRUCTIONS
|
||||
[event9:base-stat]
|
||||
fd=9
|
||||
type=0
|
||||
config=4
|
||||
|
||||
# PERF_TYPE_HARDWARE / PERF_COUNT_HW_BRANCH_MISSES
|
||||
[event10:base-stat]
|
||||
fd=10
|
||||
type=0
|
||||
config=5
|
||||
|
||||
# PERF_TYPE_HW_CACHE /
|
||||
# PERF_COUNT_HW_CACHE_L1D << 0 |
|
||||
# (PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
# (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16)
|
||||
[event11:base-stat]
|
||||
fd=11
|
||||
type=3
|
||||
config=0
|
||||
|
||||
# PERF_TYPE_HW_CACHE /
|
||||
# PERF_COUNT_HW_CACHE_L1D << 0 |
|
||||
# (PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
# (PERF_COUNT_HW_CACHE_RESULT_MISS << 16)
|
||||
[event12:base-stat]
|
||||
fd=12
|
||||
type=3
|
||||
config=65536
|
||||
|
||||
# PERF_TYPE_HW_CACHE /
|
||||
# PERF_COUNT_HW_CACHE_LL << 0 |
|
||||
# (PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
# (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16)
|
||||
[event13:base-stat]
|
||||
fd=13
|
||||
type=3
|
||||
config=2
|
||||
|
||||
# PERF_TYPE_HW_CACHE,
|
||||
# PERF_COUNT_HW_CACHE_LL << 0 |
|
||||
# (PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
# (PERF_COUNT_HW_CACHE_RESULT_MISS << 16)
|
||||
[event14:base-stat]
|
||||
fd=14
|
||||
type=3
|
||||
config=65538
|
155
tools/perf/tests/attr/test-stat-detailed-2
Normal file
155
tools/perf/tests/attr/test-stat-detailed-2
Normal file
@@ -0,0 +1,155 @@
|
||||
[config]
|
||||
command = stat
|
||||
args = -dd kill >/dev/null 2>&1
|
||||
ret = 1
|
||||
|
||||
|
||||
# PERF_TYPE_SOFTWARE / PERF_COUNT_SW_TASK_CLOCK
|
||||
[event1:base-stat]
|
||||
fd=1
|
||||
type=1
|
||||
config=1
|
||||
|
||||
# PERF_TYPE_SOFTWARE / PERF_COUNT_SW_CONTEXT_SWITCHES
|
||||
[event2:base-stat]
|
||||
fd=2
|
||||
type=1
|
||||
config=3
|
||||
|
||||
# PERF_TYPE_SOFTWARE / PERF_COUNT_SW_CPU_MIGRATIONS
|
||||
[event3:base-stat]
|
||||
fd=3
|
||||
type=1
|
||||
config=4
|
||||
|
||||
# PERF_TYPE_SOFTWARE / PERF_COUNT_SW_PAGE_FAULTS
|
||||
[event4:base-stat]
|
||||
fd=4
|
||||
type=1
|
||||
config=2
|
||||
|
||||
# PERF_TYPE_HARDWARE / PERF_COUNT_HW_CPU_CYCLES
|
||||
[event5:base-stat]
|
||||
fd=5
|
||||
type=0
|
||||
config=0
|
||||
|
||||
# PERF_TYPE_HARDWARE / PERF_COUNT_HW_STALLED_CYCLES_FRONTEND
|
||||
[event6:base-stat]
|
||||
fd=6
|
||||
type=0
|
||||
config=7
|
||||
|
||||
# PERF_TYPE_HARDWARE / PERF_COUNT_HW_STALLED_CYCLES_BACKEND
|
||||
[event7:base-stat]
|
||||
fd=7
|
||||
type=0
|
||||
config=8
|
||||
|
||||
# PERF_TYPE_HARDWARE / PERF_COUNT_HW_INSTRUCTIONS
|
||||
[event8:base-stat]
|
||||
fd=8
|
||||
type=0
|
||||
config=1
|
||||
|
||||
# PERF_TYPE_HARDWARE / PERF_COUNT_HW_BRANCH_INSTRUCTIONS
|
||||
[event9:base-stat]
|
||||
fd=9
|
||||
type=0
|
||||
config=4
|
||||
|
||||
# PERF_TYPE_HARDWARE / PERF_COUNT_HW_BRANCH_MISSES
|
||||
[event10:base-stat]
|
||||
fd=10
|
||||
type=0
|
||||
config=5
|
||||
|
||||
# PERF_TYPE_HW_CACHE /
|
||||
# PERF_COUNT_HW_CACHE_L1D << 0 |
|
||||
# (PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
# (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16)
|
||||
[event11:base-stat]
|
||||
fd=11
|
||||
type=3
|
||||
config=0
|
||||
|
||||
# PERF_TYPE_HW_CACHE /
|
||||
# PERF_COUNT_HW_CACHE_L1D << 0 |
|
||||
# (PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
# (PERF_COUNT_HW_CACHE_RESULT_MISS << 16)
|
||||
[event12:base-stat]
|
||||
fd=12
|
||||
type=3
|
||||
config=65536
|
||||
|
||||
# PERF_TYPE_HW_CACHE /
|
||||
# PERF_COUNT_HW_CACHE_LL << 0 |
|
||||
# (PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
# (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16)
|
||||
[event13:base-stat]
|
||||
fd=13
|
||||
type=3
|
||||
config=2
|
||||
|
||||
# PERF_TYPE_HW_CACHE,
|
||||
# PERF_COUNT_HW_CACHE_LL << 0 |
|
||||
# (PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
# (PERF_COUNT_HW_CACHE_RESULT_MISS << 16)
|
||||
[event14:base-stat]
|
||||
fd=14
|
||||
type=3
|
||||
config=65538
|
||||
|
||||
# PERF_TYPE_HW_CACHE,
|
||||
# PERF_COUNT_HW_CACHE_L1I << 0 |
|
||||
# (PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
# (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16)
|
||||
[event15:base-stat]
|
||||
fd=15
|
||||
type=3
|
||||
config=1
|
||||
|
||||
# PERF_TYPE_HW_CACHE,
|
||||
# PERF_COUNT_HW_CACHE_L1I << 0 |
|
||||
# (PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
# (PERF_COUNT_HW_CACHE_RESULT_MISS << 16)
|
||||
[event16:base-stat]
|
||||
fd=16
|
||||
type=3
|
||||
config=65537
|
||||
|
||||
# PERF_TYPE_HW_CACHE,
|
||||
# PERF_COUNT_HW_CACHE_DTLB << 0 |
|
||||
# (PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
# (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16)
|
||||
[event17:base-stat]
|
||||
fd=17
|
||||
type=3
|
||||
config=3
|
||||
|
||||
# PERF_TYPE_HW_CACHE,
|
||||
# PERF_COUNT_HW_CACHE_DTLB << 0 |
|
||||
# (PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
# (PERF_COUNT_HW_CACHE_RESULT_MISS << 16)
|
||||
[event18:base-stat]
|
||||
fd=18
|
||||
type=3
|
||||
config=65539
|
||||
|
||||
# PERF_TYPE_HW_CACHE,
|
||||
# PERF_COUNT_HW_CACHE_ITLB << 0 |
|
||||
# (PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
# (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16)
|
||||
[event19:base-stat]
|
||||
fd=19
|
||||
type=3
|
||||
config=4
|
||||
|
||||
# PERF_TYPE_HW_CACHE,
|
||||
# PERF_COUNT_HW_CACHE_ITLB << 0 |
|
||||
# (PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
# (PERF_COUNT_HW_CACHE_RESULT_MISS << 16)
|
||||
[event20:base-stat]
|
||||
fd=20
|
||||
type=3
|
||||
config=65540
|
173
tools/perf/tests/attr/test-stat-detailed-3
Normal file
173
tools/perf/tests/attr/test-stat-detailed-3
Normal file
@@ -0,0 +1,173 @@
|
||||
[config]
|
||||
command = stat
|
||||
args = -ddd kill >/dev/null 2>&1
|
||||
ret = 1
|
||||
|
||||
|
||||
# PERF_TYPE_SOFTWARE / PERF_COUNT_SW_TASK_CLOCK
|
||||
[event1:base-stat]
|
||||
fd=1
|
||||
type=1
|
||||
config=1
|
||||
|
||||
# PERF_TYPE_SOFTWARE / PERF_COUNT_SW_CONTEXT_SWITCHES
|
||||
[event2:base-stat]
|
||||
fd=2
|
||||
type=1
|
||||
config=3
|
||||
|
||||
# PERF_TYPE_SOFTWARE / PERF_COUNT_SW_CPU_MIGRATIONS
|
||||
[event3:base-stat]
|
||||
fd=3
|
||||
type=1
|
||||
config=4
|
||||
|
||||
# PERF_TYPE_SOFTWARE / PERF_COUNT_SW_PAGE_FAULTS
|
||||
[event4:base-stat]
|
||||
fd=4
|
||||
type=1
|
||||
config=2
|
||||
|
||||
# PERF_TYPE_HARDWARE / PERF_COUNT_HW_CPU_CYCLES
|
||||
[event5:base-stat]
|
||||
fd=5
|
||||
type=0
|
||||
config=0
|
||||
|
||||
# PERF_TYPE_HARDWARE / PERF_COUNT_HW_STALLED_CYCLES_FRONTEND
|
||||
[event6:base-stat]
|
||||
fd=6
|
||||
type=0
|
||||
config=7
|
||||
|
||||
# PERF_TYPE_HARDWARE / PERF_COUNT_HW_STALLED_CYCLES_BACKEND
|
||||
[event7:base-stat]
|
||||
fd=7
|
||||
type=0
|
||||
config=8
|
||||
|
||||
# PERF_TYPE_HARDWARE / PERF_COUNT_HW_INSTRUCTIONS
|
||||
[event8:base-stat]
|
||||
fd=8
|
||||
type=0
|
||||
config=1
|
||||
|
||||
# PERF_TYPE_HARDWARE / PERF_COUNT_HW_BRANCH_INSTRUCTIONS
|
||||
[event9:base-stat]
|
||||
fd=9
|
||||
type=0
|
||||
config=4
|
||||
|
||||
# PERF_TYPE_HARDWARE / PERF_COUNT_HW_BRANCH_MISSES
|
||||
[event10:base-stat]
|
||||
fd=10
|
||||
type=0
|
||||
config=5
|
||||
|
||||
# PERF_TYPE_HW_CACHE /
|
||||
# PERF_COUNT_HW_CACHE_L1D << 0 |
|
||||
# (PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
# (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16)
|
||||
[event11:base-stat]
|
||||
fd=11
|
||||
type=3
|
||||
config=0
|
||||
|
||||
# PERF_TYPE_HW_CACHE /
|
||||
# PERF_COUNT_HW_CACHE_L1D << 0 |
|
||||
# (PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
# (PERF_COUNT_HW_CACHE_RESULT_MISS << 16)
|
||||
[event12:base-stat]
|
||||
fd=12
|
||||
type=3
|
||||
config=65536
|
||||
|
||||
# PERF_TYPE_HW_CACHE /
|
||||
# PERF_COUNT_HW_CACHE_LL << 0 |
|
||||
# (PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
# (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16)
|
||||
[event13:base-stat]
|
||||
fd=13
|
||||
type=3
|
||||
config=2
|
||||
|
||||
# PERF_TYPE_HW_CACHE,
|
||||
# PERF_COUNT_HW_CACHE_LL << 0 |
|
||||
# (PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
# (PERF_COUNT_HW_CACHE_RESULT_MISS << 16)
|
||||
[event14:base-stat]
|
||||
fd=14
|
||||
type=3
|
||||
config=65538
|
||||
|
||||
# PERF_TYPE_HW_CACHE,
|
||||
# PERF_COUNT_HW_CACHE_L1I << 0 |
|
||||
# (PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
# (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16)
|
||||
[event15:base-stat]
|
||||
fd=15
|
||||
type=3
|
||||
config=1
|
||||
|
||||
# PERF_TYPE_HW_CACHE,
|
||||
# PERF_COUNT_HW_CACHE_L1I << 0 |
|
||||
# (PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
# (PERF_COUNT_HW_CACHE_RESULT_MISS << 16)
|
||||
[event16:base-stat]
|
||||
fd=16
|
||||
type=3
|
||||
config=65537
|
||||
|
||||
# PERF_TYPE_HW_CACHE,
|
||||
# PERF_COUNT_HW_CACHE_DTLB << 0 |
|
||||
# (PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
# (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16)
|
||||
[event17:base-stat]
|
||||
fd=17
|
||||
type=3
|
||||
config=3
|
||||
|
||||
# PERF_TYPE_HW_CACHE,
|
||||
# PERF_COUNT_HW_CACHE_DTLB << 0 |
|
||||
# (PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
# (PERF_COUNT_HW_CACHE_RESULT_MISS << 16)
|
||||
[event18:base-stat]
|
||||
fd=18
|
||||
type=3
|
||||
config=65539
|
||||
|
||||
# PERF_TYPE_HW_CACHE,
|
||||
# PERF_COUNT_HW_CACHE_ITLB << 0 |
|
||||
# (PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
# (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16)
|
||||
[event19:base-stat]
|
||||
fd=19
|
||||
type=3
|
||||
config=4
|
||||
|
||||
# PERF_TYPE_HW_CACHE,
|
||||
# PERF_COUNT_HW_CACHE_ITLB << 0 |
|
||||
# (PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
# (PERF_COUNT_HW_CACHE_RESULT_MISS << 16)
|
||||
[event20:base-stat]
|
||||
fd=20
|
||||
type=3
|
||||
config=65540
|
||||
|
||||
# PERF_TYPE_HW_CACHE,
|
||||
# PERF_COUNT_HW_CACHE_L1D << 0 |
|
||||
# (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) |
|
||||
# (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16)
|
||||
[event21:base-stat]
|
||||
fd=21
|
||||
type=3
|
||||
config=512
|
||||
|
||||
# PERF_TYPE_HW_CACHE,
|
||||
# PERF_COUNT_HW_CACHE_L1D << 0 |
|
||||
# (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) |
|
||||
# (PERF_COUNT_HW_CACHE_RESULT_MISS << 16)
|
||||
[event22:base-stat]
|
||||
fd=22
|
||||
type=3
|
||||
config=66048
|
15
tools/perf/tests/attr/test-stat-group
Normal file
15
tools/perf/tests/attr/test-stat-group
Normal file
@@ -0,0 +1,15 @@
|
||||
[config]
|
||||
command = stat
|
||||
args = --group -e cycles,instructions kill >/dev/null 2>&1
|
||||
ret = 1
|
||||
|
||||
[event-1:base-stat]
|
||||
fd=1
|
||||
group_fd=-1
|
||||
|
||||
[event-2:base-stat]
|
||||
fd=2
|
||||
group_fd=1
|
||||
config=1
|
||||
disabled=0
|
||||
enable_on_exec=0
|
15
tools/perf/tests/attr/test-stat-group1
Normal file
15
tools/perf/tests/attr/test-stat-group1
Normal file
@@ -0,0 +1,15 @@
|
||||
[config]
|
||||
command = stat
|
||||
args = -e '{cycles,instructions}' kill >/dev/null 2>&1
|
||||
ret = 1
|
||||
|
||||
[event-1:base-stat]
|
||||
fd=1
|
||||
group_fd=-1
|
||||
|
||||
[event-2:base-stat]
|
||||
fd=2
|
||||
group_fd=1
|
||||
config=1
|
||||
disabled=0
|
||||
enable_on_exec=0
|
7
tools/perf/tests/attr/test-stat-no-inherit
Normal file
7
tools/perf/tests/attr/test-stat-no-inherit
Normal file
@@ -0,0 +1,7 @@
|
||||
[config]
|
||||
command = stat
|
||||
args = -i -e cycles kill >/dev/null 2>&1
|
||||
ret = 1
|
||||
|
||||
[event:base-stat]
|
||||
inherit=0
|
173
tools/perf/tests/builtin-test.c
Normal file
173
tools/perf/tests/builtin-test.c
Normal file
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* builtin-test.c
|
||||
*
|
||||
* Builtin regression testing command: ever growing number of sanity tests
|
||||
*/
|
||||
#include "builtin.h"
|
||||
#include "tests.h"
|
||||
#include "debug.h"
|
||||
#include "color.h"
|
||||
#include "parse-options.h"
|
||||
#include "symbol.h"
|
||||
|
||||
static struct test {
|
||||
const char *desc;
|
||||
int (*func)(void);
|
||||
} tests[] = {
|
||||
{
|
||||
.desc = "vmlinux symtab matches kallsyms",
|
||||
.func = test__vmlinux_matches_kallsyms,
|
||||
},
|
||||
{
|
||||
.desc = "detect open syscall event",
|
||||
.func = test__open_syscall_event,
|
||||
},
|
||||
{
|
||||
.desc = "detect open syscall event on all cpus",
|
||||
.func = test__open_syscall_event_on_all_cpus,
|
||||
},
|
||||
{
|
||||
.desc = "read samples using the mmap interface",
|
||||
.func = test__basic_mmap,
|
||||
},
|
||||
{
|
||||
.desc = "parse events tests",
|
||||
.func = test__parse_events,
|
||||
},
|
||||
#if defined(__x86_64__) || defined(__i386__)
|
||||
{
|
||||
.desc = "x86 rdpmc test",
|
||||
.func = test__rdpmc,
|
||||
},
|
||||
#endif
|
||||
{
|
||||
.desc = "Validate PERF_RECORD_* events & perf_sample fields",
|
||||
.func = test__PERF_RECORD,
|
||||
},
|
||||
{
|
||||
.desc = "Test perf pmu format parsing",
|
||||
.func = test__pmu,
|
||||
},
|
||||
{
|
||||
.desc = "Test dso data interface",
|
||||
.func = test__dso_data,
|
||||
},
|
||||
{
|
||||
.desc = "roundtrip evsel->name check",
|
||||
.func = test__perf_evsel__roundtrip_name_test,
|
||||
},
|
||||
{
|
||||
.desc = "Check parsing of sched tracepoints fields",
|
||||
.func = test__perf_evsel__tp_sched_test,
|
||||
},
|
||||
{
|
||||
.desc = "Generate and check syscalls:sys_enter_open event fields",
|
||||
.func = test__syscall_open_tp_fields,
|
||||
},
|
||||
{
|
||||
.desc = "struct perf_event_attr setup",
|
||||
.func = test__attr,
|
||||
},
|
||||
{
|
||||
.func = NULL,
|
||||
},
|
||||
};
|
||||
|
||||
static bool perf_test__matches(int curr, int argc, const char *argv[])
|
||||
{
|
||||
int i;
|
||||
|
||||
if (argc == 0)
|
||||
return true;
|
||||
|
||||
for (i = 0; i < argc; ++i) {
|
||||
char *end;
|
||||
long nr = strtoul(argv[i], &end, 10);
|
||||
|
||||
if (*end == '\0') {
|
||||
if (nr == curr + 1)
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strstr(tests[curr].desc, argv[i]))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int __cmd_test(int argc, const char *argv[])
|
||||
{
|
||||
int i = 0;
|
||||
int width = 0;
|
||||
|
||||
while (tests[i].func) {
|
||||
int len = strlen(tests[i].desc);
|
||||
|
||||
if (width < len)
|
||||
width = len;
|
||||
++i;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
while (tests[i].func) {
|
||||
int curr = i++, err;
|
||||
|
||||
if (!perf_test__matches(curr, argc, argv))
|
||||
continue;
|
||||
|
||||
pr_info("%2d: %-*s:", i, width, tests[curr].desc);
|
||||
pr_debug("\n--- start ---\n");
|
||||
err = tests[curr].func();
|
||||
pr_debug("---- end ----\n%s:", tests[curr].desc);
|
||||
if (err)
|
||||
color_fprintf(stderr, PERF_COLOR_RED, " FAILED!\n");
|
||||
else
|
||||
pr_info(" Ok\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int perf_test__list(int argc, const char **argv)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
while (tests[i].func) {
|
||||
int curr = i++;
|
||||
|
||||
if (argc > 1 && !strstr(tests[curr].desc, argv[1]))
|
||||
continue;
|
||||
|
||||
pr_info("%2d: %s\n", i, tests[curr].desc);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmd_test(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
{
|
||||
const char * const test_usage[] = {
|
||||
"perf test [<options>] [{list <test-name-fragment>|[<test-name-fragments>|<test-numbers>]}]",
|
||||
NULL,
|
||||
};
|
||||
const struct option test_options[] = {
|
||||
OPT_INCR('v', "verbose", &verbose,
|
||||
"be more verbose (show symbol address, etc)"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
argc = parse_options(argc, argv, test_options, test_usage, 0);
|
||||
if (argc >= 1 && !strcmp(argv[0], "list"))
|
||||
return perf_test__list(argc, argv);
|
||||
|
||||
symbol_conf.priv_size = sizeof(int);
|
||||
symbol_conf.sort_by_name = true;
|
||||
symbol_conf.try_vmlinux_path = true;
|
||||
|
||||
if (symbol__init() < 0)
|
||||
return -1;
|
||||
|
||||
return __cmd_test(argc, argv);
|
||||
}
|
@@ -6,7 +6,9 @@
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "machine.h"
|
||||
#include "symbol.h"
|
||||
#include "tests.h"
|
||||
|
||||
#define TEST_ASSERT_VAL(text, cond) \
|
||||
do { \
|
||||
@@ -24,6 +26,10 @@ static char *test_file(int size)
|
||||
unsigned char *buf;
|
||||
|
||||
fd = mkstemp(templ);
|
||||
if (fd < 0) {
|
||||
perror("mkstemp failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buf = malloc(size);
|
||||
if (!buf) {
|
||||
@@ -94,7 +100,7 @@ struct test_data_offset offsets[] = {
|
||||
},
|
||||
};
|
||||
|
||||
int dso__test_data(void)
|
||||
int test__dso_data(void)
|
||||
{
|
||||
struct machine machine;
|
||||
struct dso *dso;
|
114
tools/perf/tests/evsel-roundtrip-name.c
Normal file
114
tools/perf/tests/evsel-roundtrip-name.c
Normal file
@@ -0,0 +1,114 @@
|
||||
#include "evlist.h"
|
||||
#include "evsel.h"
|
||||
#include "parse-events.h"
|
||||
#include "tests.h"
|
||||
|
||||
static int perf_evsel__roundtrip_cache_name_test(void)
|
||||
{
|
||||
char name[128];
|
||||
int type, op, err = 0, ret = 0, i, idx;
|
||||
struct perf_evsel *evsel;
|
||||
struct perf_evlist *evlist = perf_evlist__new(NULL, NULL);
|
||||
|
||||
if (evlist == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) {
|
||||
for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) {
|
||||
/* skip invalid cache type */
|
||||
if (!perf_evsel__is_cache_op_valid(type, op))
|
||||
continue;
|
||||
|
||||
for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) {
|
||||
__perf_evsel__hw_cache_type_op_res_name(type, op, i,
|
||||
name, sizeof(name));
|
||||
err = parse_events(evlist, name, 0);
|
||||
if (err)
|
||||
ret = err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
idx = 0;
|
||||
evsel = perf_evlist__first(evlist);
|
||||
|
||||
for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) {
|
||||
for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) {
|
||||
/* skip invalid cache type */
|
||||
if (!perf_evsel__is_cache_op_valid(type, op))
|
||||
continue;
|
||||
|
||||
for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) {
|
||||
__perf_evsel__hw_cache_type_op_res_name(type, op, i,
|
||||
name, sizeof(name));
|
||||
if (evsel->idx != idx)
|
||||
continue;
|
||||
|
||||
++idx;
|
||||
|
||||
if (strcmp(perf_evsel__name(evsel), name)) {
|
||||
pr_debug("%s != %s\n", perf_evsel__name(evsel), name);
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
evsel = perf_evsel__next(evsel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
perf_evlist__delete(evlist);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __perf_evsel__name_array_test(const char *names[], int nr_names)
|
||||
{
|
||||
int i, err;
|
||||
struct perf_evsel *evsel;
|
||||
struct perf_evlist *evlist = perf_evlist__new(NULL, NULL);
|
||||
|
||||
if (evlist == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < nr_names; ++i) {
|
||||
err = parse_events(evlist, names[i], 0);
|
||||
if (err) {
|
||||
pr_debug("failed to parse event '%s', err %d\n",
|
||||
names[i], err);
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
}
|
||||
|
||||
err = 0;
|
||||
list_for_each_entry(evsel, &evlist->entries, node) {
|
||||
if (strcmp(perf_evsel__name(evsel), names[evsel->idx])) {
|
||||
--err;
|
||||
pr_debug("%s != %s\n", perf_evsel__name(evsel), names[evsel->idx]);
|
||||
}
|
||||
}
|
||||
|
||||
out_delete_evlist:
|
||||
perf_evlist__delete(evlist);
|
||||
return err;
|
||||
}
|
||||
|
||||
#define perf_evsel__name_array_test(names) \
|
||||
__perf_evsel__name_array_test(names, ARRAY_SIZE(names))
|
||||
|
||||
int test__perf_evsel__roundtrip_name_test(void)
|
||||
{
|
||||
int err = 0, ret = 0;
|
||||
|
||||
err = perf_evsel__name_array_test(perf_evsel__hw_names);
|
||||
if (err)
|
||||
ret = err;
|
||||
|
||||
err = perf_evsel__name_array_test(perf_evsel__sw_names);
|
||||
if (err)
|
||||
ret = err;
|
||||
|
||||
err = perf_evsel__roundtrip_cache_name_test();
|
||||
if (err)
|
||||
ret = err;
|
||||
|
||||
return ret;
|
||||
}
|
84
tools/perf/tests/evsel-tp-sched.c
Normal file
84
tools/perf/tests/evsel-tp-sched.c
Normal file
@@ -0,0 +1,84 @@
|
||||
#include "evsel.h"
|
||||
#include "tests.h"
|
||||
#include "event-parse.h"
|
||||
|
||||
static int perf_evsel__test_field(struct perf_evsel *evsel, const char *name,
|
||||
int size, bool should_be_signed)
|
||||
{
|
||||
struct format_field *field = perf_evsel__field(evsel, name);
|
||||
int is_signed;
|
||||
int ret = 0;
|
||||
|
||||
if (field == NULL) {
|
||||
pr_debug("%s: \"%s\" field not found!\n", evsel->name, name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
is_signed = !!(field->flags | FIELD_IS_SIGNED);
|
||||
if (should_be_signed && !is_signed) {
|
||||
pr_debug("%s: \"%s\" signedness(%d) is wrong, should be %d\n",
|
||||
evsel->name, name, is_signed, should_be_signed);
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
if (field->size != size) {
|
||||
pr_debug("%s: \"%s\" size (%d) should be %d!\n",
|
||||
evsel->name, name, field->size, size);
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int test__perf_evsel__tp_sched_test(void)
|
||||
{
|
||||
struct perf_evsel *evsel = perf_evsel__newtp("sched", "sched_switch", 0);
|
||||
int ret = 0;
|
||||
|
||||
if (evsel == NULL) {
|
||||
pr_debug("perf_evsel__new\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (perf_evsel__test_field(evsel, "prev_comm", 16, true))
|
||||
ret = -1;
|
||||
|
||||
if (perf_evsel__test_field(evsel, "prev_pid", 4, true))
|
||||
ret = -1;
|
||||
|
||||
if (perf_evsel__test_field(evsel, "prev_prio", 4, true))
|
||||
ret = -1;
|
||||
|
||||
if (perf_evsel__test_field(evsel, "prev_state", 8, true))
|
||||
ret = -1;
|
||||
|
||||
if (perf_evsel__test_field(evsel, "next_comm", 16, true))
|
||||
ret = -1;
|
||||
|
||||
if (perf_evsel__test_field(evsel, "next_pid", 4, true))
|
||||
ret = -1;
|
||||
|
||||
if (perf_evsel__test_field(evsel, "next_prio", 4, true))
|
||||
ret = -1;
|
||||
|
||||
perf_evsel__delete(evsel);
|
||||
|
||||
evsel = perf_evsel__newtp("sched", "sched_wakeup", 0);
|
||||
|
||||
if (perf_evsel__test_field(evsel, "comm", 16, true))
|
||||
ret = -1;
|
||||
|
||||
if (perf_evsel__test_field(evsel, "pid", 4, true))
|
||||
ret = -1;
|
||||
|
||||
if (perf_evsel__test_field(evsel, "prio", 4, true))
|
||||
ret = -1;
|
||||
|
||||
if (perf_evsel__test_field(evsel, "success", 4, true))
|
||||
ret = -1;
|
||||
|
||||
if (perf_evsel__test_field(evsel, "target_cpu", 4, true))
|
||||
ret = -1;
|
||||
|
||||
return ret;
|
||||
}
|
162
tools/perf/tests/mmap-basic.c
Normal file
162
tools/perf/tests/mmap-basic.c
Normal file
@@ -0,0 +1,162 @@
|
||||
#include "evlist.h"
|
||||
#include "evsel.h"
|
||||
#include "thread_map.h"
|
||||
#include "cpumap.h"
|
||||
#include "tests.h"
|
||||
|
||||
/*
|
||||
* This test will generate random numbers of calls to some getpid syscalls,
|
||||
* then establish an mmap for a group of events that are created to monitor
|
||||
* the syscalls.
|
||||
*
|
||||
* It will receive the events, using mmap, use its PERF_SAMPLE_ID generated
|
||||
* sample.id field to map back to its respective perf_evsel instance.
|
||||
*
|
||||
* Then it checks if the number of syscalls reported as perf events by
|
||||
* the kernel corresponds to the number of syscalls made.
|
||||
*/
|
||||
int test__basic_mmap(void)
|
||||
{
|
||||
int err = -1;
|
||||
union perf_event *event;
|
||||
struct thread_map *threads;
|
||||
struct cpu_map *cpus;
|
||||
struct perf_evlist *evlist;
|
||||
struct perf_event_attr attr = {
|
||||
.type = PERF_TYPE_TRACEPOINT,
|
||||
.read_format = PERF_FORMAT_ID,
|
||||
.sample_type = PERF_SAMPLE_ID,
|
||||
.watermark = 0,
|
||||
};
|
||||
cpu_set_t cpu_set;
|
||||
const char *syscall_names[] = { "getsid", "getppid", "getpgrp",
|
||||
"getpgid", };
|
||||
pid_t (*syscalls[])(void) = { (void *)getsid, getppid, getpgrp,
|
||||
(void*)getpgid };
|
||||
#define nsyscalls ARRAY_SIZE(syscall_names)
|
||||
int ids[nsyscalls];
|
||||
unsigned int nr_events[nsyscalls],
|
||||
expected_nr_events[nsyscalls], i, j;
|
||||
struct perf_evsel *evsels[nsyscalls], *evsel;
|
||||
|
||||
for (i = 0; i < nsyscalls; ++i) {
|
||||
char name[64];
|
||||
|
||||
snprintf(name, sizeof(name), "sys_enter_%s", syscall_names[i]);
|
||||
ids[i] = trace_event__id(name);
|
||||
if (ids[i] < 0) {
|
||||
pr_debug("Is debugfs mounted on /sys/kernel/debug?\n");
|
||||
return -1;
|
||||
}
|
||||
nr_events[i] = 0;
|
||||
expected_nr_events[i] = random() % 257;
|
||||
}
|
||||
|
||||
threads = thread_map__new(-1, getpid(), UINT_MAX);
|
||||
if (threads == NULL) {
|
||||
pr_debug("thread_map__new\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
cpus = cpu_map__new(NULL);
|
||||
if (cpus == NULL) {
|
||||
pr_debug("cpu_map__new\n");
|
||||
goto out_free_threads;
|
||||
}
|
||||
|
||||
CPU_ZERO(&cpu_set);
|
||||
CPU_SET(cpus->map[0], &cpu_set);
|
||||
sched_setaffinity(0, sizeof(cpu_set), &cpu_set);
|
||||
if (sched_setaffinity(0, sizeof(cpu_set), &cpu_set) < 0) {
|
||||
pr_debug("sched_setaffinity() failed on CPU %d: %s ",
|
||||
cpus->map[0], strerror(errno));
|
||||
goto out_free_cpus;
|
||||
}
|
||||
|
||||
evlist = perf_evlist__new(cpus, threads);
|
||||
if (evlist == NULL) {
|
||||
pr_debug("perf_evlist__new\n");
|
||||
goto out_free_cpus;
|
||||
}
|
||||
|
||||
/* anonymous union fields, can't be initialized above */
|
||||
attr.wakeup_events = 1;
|
||||
attr.sample_period = 1;
|
||||
|
||||
for (i = 0; i < nsyscalls; ++i) {
|
||||
attr.config = ids[i];
|
||||
evsels[i] = perf_evsel__new(&attr, i);
|
||||
if (evsels[i] == NULL) {
|
||||
pr_debug("perf_evsel__new\n");
|
||||
goto out_free_evlist;
|
||||
}
|
||||
|
||||
perf_evlist__add(evlist, evsels[i]);
|
||||
|
||||
if (perf_evsel__open(evsels[i], cpus, threads) < 0) {
|
||||
pr_debug("failed to open counter: %s, "
|
||||
"tweak /proc/sys/kernel/perf_event_paranoid?\n",
|
||||
strerror(errno));
|
||||
goto out_close_fd;
|
||||
}
|
||||
}
|
||||
|
||||
if (perf_evlist__mmap(evlist, 128, true) < 0) {
|
||||
pr_debug("failed to mmap events: %d (%s)\n", errno,
|
||||
strerror(errno));
|
||||
goto out_close_fd;
|
||||
}
|
||||
|
||||
for (i = 0; i < nsyscalls; ++i)
|
||||
for (j = 0; j < expected_nr_events[i]; ++j) {
|
||||
int foo = syscalls[i]();
|
||||
++foo;
|
||||
}
|
||||
|
||||
while ((event = perf_evlist__mmap_read(evlist, 0)) != NULL) {
|
||||
struct perf_sample sample;
|
||||
|
||||
if (event->header.type != PERF_RECORD_SAMPLE) {
|
||||
pr_debug("unexpected %s event\n",
|
||||
perf_event__name(event->header.type));
|
||||
goto out_munmap;
|
||||
}
|
||||
|
||||
err = perf_evlist__parse_sample(evlist, event, &sample);
|
||||
if (err) {
|
||||
pr_err("Can't parse sample, err = %d\n", err);
|
||||
goto out_munmap;
|
||||
}
|
||||
|
||||
evsel = perf_evlist__id2evsel(evlist, sample.id);
|
||||
if (evsel == NULL) {
|
||||
pr_debug("event with id %" PRIu64
|
||||
" doesn't map to an evsel\n", sample.id);
|
||||
goto out_munmap;
|
||||
}
|
||||
nr_events[evsel->idx]++;
|
||||
}
|
||||
|
||||
list_for_each_entry(evsel, &evlist->entries, node) {
|
||||
if (nr_events[evsel->idx] != expected_nr_events[evsel->idx]) {
|
||||
pr_debug("expected %d %s events, got %d\n",
|
||||
expected_nr_events[evsel->idx],
|
||||
perf_evsel__name(evsel), nr_events[evsel->idx]);
|
||||
goto out_munmap;
|
||||
}
|
||||
}
|
||||
|
||||
err = 0;
|
||||
out_munmap:
|
||||
perf_evlist__munmap(evlist);
|
||||
out_close_fd:
|
||||
for (i = 0; i < nsyscalls; ++i)
|
||||
perf_evsel__close_fd(evsels[i], 1, threads->nr);
|
||||
out_free_evlist:
|
||||
perf_evlist__delete(evlist);
|
||||
out_free_cpus:
|
||||
cpu_map__delete(cpus);
|
||||
out_free_threads:
|
||||
thread_map__delete(threads);
|
||||
return err;
|
||||
}
|
120
tools/perf/tests/open-syscall-all-cpus.c
Normal file
120
tools/perf/tests/open-syscall-all-cpus.c
Normal file
@@ -0,0 +1,120 @@
|
||||
#include "evsel.h"
|
||||
#include "tests.h"
|
||||
#include "thread_map.h"
|
||||
#include "cpumap.h"
|
||||
#include "debug.h"
|
||||
|
||||
int test__open_syscall_event_on_all_cpus(void)
|
||||
{
|
||||
int err = -1, fd, cpu;
|
||||
struct thread_map *threads;
|
||||
struct cpu_map *cpus;
|
||||
struct perf_evsel *evsel;
|
||||
struct perf_event_attr attr;
|
||||
unsigned int nr_open_calls = 111, i;
|
||||
cpu_set_t cpu_set;
|
||||
int id = trace_event__id("sys_enter_open");
|
||||
|
||||
if (id < 0) {
|
||||
pr_debug("is debugfs mounted on /sys/kernel/debug?\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
threads = thread_map__new(-1, getpid(), UINT_MAX);
|
||||
if (threads == NULL) {
|
||||
pr_debug("thread_map__new\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
cpus = cpu_map__new(NULL);
|
||||
if (cpus == NULL) {
|
||||
pr_debug("cpu_map__new\n");
|
||||
goto out_thread_map_delete;
|
||||
}
|
||||
|
||||
|
||||
CPU_ZERO(&cpu_set);
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
attr.type = PERF_TYPE_TRACEPOINT;
|
||||
attr.config = id;
|
||||
evsel = perf_evsel__new(&attr, 0);
|
||||
if (evsel == NULL) {
|
||||
pr_debug("perf_evsel__new\n");
|
||||
goto out_thread_map_delete;
|
||||
}
|
||||
|
||||
if (perf_evsel__open(evsel, cpus, threads) < 0) {
|
||||
pr_debug("failed to open counter: %s, "
|
||||
"tweak /proc/sys/kernel/perf_event_paranoid?\n",
|
||||
strerror(errno));
|
||||
goto out_evsel_delete;
|
||||
}
|
||||
|
||||
for (cpu = 0; cpu < cpus->nr; ++cpu) {
|
||||
unsigned int ncalls = nr_open_calls + cpu;
|
||||
/*
|
||||
* XXX eventually lift this restriction in a way that
|
||||
* keeps perf building on older glibc installations
|
||||
* without CPU_ALLOC. 1024 cpus in 2010 still seems
|
||||
* a reasonable upper limit tho :-)
|
||||
*/
|
||||
if (cpus->map[cpu] >= CPU_SETSIZE) {
|
||||
pr_debug("Ignoring CPU %d\n", cpus->map[cpu]);
|
||||
continue;
|
||||
}
|
||||
|
||||
CPU_SET(cpus->map[cpu], &cpu_set);
|
||||
if (sched_setaffinity(0, sizeof(cpu_set), &cpu_set) < 0) {
|
||||
pr_debug("sched_setaffinity() failed on CPU %d: %s ",
|
||||
cpus->map[cpu],
|
||||
strerror(errno));
|
||||
goto out_close_fd;
|
||||
}
|
||||
for (i = 0; i < ncalls; ++i) {
|
||||
fd = open("/etc/passwd", O_RDONLY);
|
||||
close(fd);
|
||||
}
|
||||
CPU_CLR(cpus->map[cpu], &cpu_set);
|
||||
}
|
||||
|
||||
/*
|
||||
* Here we need to explicitely preallocate the counts, as if
|
||||
* we use the auto allocation it will allocate just for 1 cpu,
|
||||
* as we start by cpu 0.
|
||||
*/
|
||||
if (perf_evsel__alloc_counts(evsel, cpus->nr) < 0) {
|
||||
pr_debug("perf_evsel__alloc_counts(ncpus=%d)\n", cpus->nr);
|
||||
goto out_close_fd;
|
||||
}
|
||||
|
||||
err = 0;
|
||||
|
||||
for (cpu = 0; cpu < cpus->nr; ++cpu) {
|
||||
unsigned int expected;
|
||||
|
||||
if (cpus->map[cpu] >= CPU_SETSIZE)
|
||||
continue;
|
||||
|
||||
if (perf_evsel__read_on_cpu(evsel, cpu, 0) < 0) {
|
||||
pr_debug("perf_evsel__read_on_cpu\n");
|
||||
err = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
expected = nr_open_calls + cpu;
|
||||
if (evsel->counts->cpu[cpu].val != expected) {
|
||||
pr_debug("perf_evsel__read_on_cpu: expected to intercept %d calls on cpu %d, got %" PRIu64 "\n",
|
||||
expected, cpus->map[cpu], evsel->counts->cpu[cpu].val);
|
||||
err = -1;
|
||||
}
|
||||
}
|
||||
|
||||
out_close_fd:
|
||||
perf_evsel__close_fd(evsel, 1, threads->nr);
|
||||
out_evsel_delete:
|
||||
perf_evsel__delete(evsel);
|
||||
out_thread_map_delete:
|
||||
thread_map__delete(threads);
|
||||
return err;
|
||||
}
|
117
tools/perf/tests/open-syscall-tp-fields.c
Normal file
117
tools/perf/tests/open-syscall-tp-fields.c
Normal file
@@ -0,0 +1,117 @@
|
||||
#include "perf.h"
|
||||
#include "evlist.h"
|
||||
#include "evsel.h"
|
||||
#include "thread_map.h"
|
||||
#include "tests.h"
|
||||
|
||||
int test__syscall_open_tp_fields(void)
|
||||
{
|
||||
struct perf_record_opts opts = {
|
||||
.target = {
|
||||
.uid = UINT_MAX,
|
||||
.uses_mmap = true,
|
||||
},
|
||||
.no_delay = true,
|
||||
.freq = 1,
|
||||
.mmap_pages = 256,
|
||||
.raw_samples = true,
|
||||
};
|
||||
const char *filename = "/etc/passwd";
|
||||
int flags = O_RDONLY | O_DIRECTORY;
|
||||
struct perf_evlist *evlist = perf_evlist__new(NULL, NULL);
|
||||
struct perf_evsel *evsel;
|
||||
int err = -1, i, nr_events = 0, nr_polls = 0;
|
||||
|
||||
if (evlist == NULL) {
|
||||
pr_debug("%s: perf_evlist__new\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
evsel = perf_evsel__newtp("syscalls", "sys_enter_open", 0);
|
||||
if (evsel == NULL) {
|
||||
pr_debug("%s: perf_evsel__newtp\n", __func__);
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
perf_evlist__add(evlist, evsel);
|
||||
|
||||
err = perf_evlist__create_maps(evlist, &opts.target);
|
||||
if (err < 0) {
|
||||
pr_debug("%s: perf_evlist__create_maps\n", __func__);
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
perf_evsel__config(evsel, &opts);
|
||||
|
||||
evlist->threads->map[0] = getpid();
|
||||
|
||||
err = perf_evlist__open(evlist);
|
||||
if (err < 0) {
|
||||
pr_debug("perf_evlist__open: %s\n", strerror(errno));
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
err = perf_evlist__mmap(evlist, UINT_MAX, false);
|
||||
if (err < 0) {
|
||||
pr_debug("perf_evlist__mmap: %s\n", strerror(errno));
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
perf_evlist__enable(evlist);
|
||||
|
||||
/*
|
||||
* Generate the event:
|
||||
*/
|
||||
open(filename, flags);
|
||||
|
||||
while (1) {
|
||||
int before = nr_events;
|
||||
|
||||
for (i = 0; i < evlist->nr_mmaps; i++) {
|
||||
union perf_event *event;
|
||||
|
||||
while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) {
|
||||
const u32 type = event->header.type;
|
||||
int tp_flags;
|
||||
struct perf_sample sample;
|
||||
|
||||
++nr_events;
|
||||
|
||||
if (type != PERF_RECORD_SAMPLE)
|
||||
continue;
|
||||
|
||||
err = perf_evsel__parse_sample(evsel, event, &sample);
|
||||
if (err) {
|
||||
pr_err("Can't parse sample, err = %d\n", err);
|
||||
goto out_munmap;
|
||||
}
|
||||
|
||||
tp_flags = perf_evsel__intval(evsel, &sample, "flags");
|
||||
|
||||
if (flags != tp_flags) {
|
||||
pr_debug("%s: Expected flags=%#x, got %#x\n",
|
||||
__func__, flags, tp_flags);
|
||||
goto out_munmap;
|
||||
}
|
||||
|
||||
goto out_ok;
|
||||
}
|
||||
}
|
||||
|
||||
if (nr_events == before)
|
||||
poll(evlist->pollfd, evlist->nr_fds, 10);
|
||||
|
||||
if (++nr_polls > 5) {
|
||||
pr_debug("%s: no events!\n", __func__);
|
||||
goto out_munmap;
|
||||
}
|
||||
}
|
||||
out_ok:
|
||||
err = 0;
|
||||
out_munmap:
|
||||
perf_evlist__munmap(evlist);
|
||||
out_delete_evlist:
|
||||
perf_evlist__delete(evlist);
|
||||
out:
|
||||
return err;
|
||||
}
|
66
tools/perf/tests/open-syscall.c
Normal file
66
tools/perf/tests/open-syscall.c
Normal file
@@ -0,0 +1,66 @@
|
||||
#include "thread_map.h"
|
||||
#include "evsel.h"
|
||||
#include "debug.h"
|
||||
#include "tests.h"
|
||||
|
||||
int test__open_syscall_event(void)
|
||||
{
|
||||
int err = -1, fd;
|
||||
struct thread_map *threads;
|
||||
struct perf_evsel *evsel;
|
||||
struct perf_event_attr attr;
|
||||
unsigned int nr_open_calls = 111, i;
|
||||
int id = trace_event__id("sys_enter_open");
|
||||
|
||||
if (id < 0) {
|
||||
pr_debug("is debugfs mounted on /sys/kernel/debug?\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
threads = thread_map__new(-1, getpid(), UINT_MAX);
|
||||
if (threads == NULL) {
|
||||
pr_debug("thread_map__new\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
attr.type = PERF_TYPE_TRACEPOINT;
|
||||
attr.config = id;
|
||||
evsel = perf_evsel__new(&attr, 0);
|
||||
if (evsel == NULL) {
|
||||
pr_debug("perf_evsel__new\n");
|
||||
goto out_thread_map_delete;
|
||||
}
|
||||
|
||||
if (perf_evsel__open_per_thread(evsel, threads) < 0) {
|
||||
pr_debug("failed to open counter: %s, "
|
||||
"tweak /proc/sys/kernel/perf_event_paranoid?\n",
|
||||
strerror(errno));
|
||||
goto out_evsel_delete;
|
||||
}
|
||||
|
||||
for (i = 0; i < nr_open_calls; ++i) {
|
||||
fd = open("/etc/passwd", O_RDONLY);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
if (perf_evsel__read_on_cpu(evsel, 0, 0) < 0) {
|
||||
pr_debug("perf_evsel__read_on_cpu\n");
|
||||
goto out_close_fd;
|
||||
}
|
||||
|
||||
if (evsel->counts->cpu[0].val != nr_open_calls) {
|
||||
pr_debug("perf_evsel__read_on_cpu: expected to intercept %d calls, got %" PRIu64 "\n",
|
||||
nr_open_calls, evsel->counts->cpu[0].val);
|
||||
goto out_close_fd;
|
||||
}
|
||||
|
||||
err = 0;
|
||||
out_close_fd:
|
||||
perf_evsel__close_fd(evsel, 1, threads->nr);
|
||||
out_evsel_delete:
|
||||
perf_evsel__delete(evsel);
|
||||
out_thread_map_delete:
|
||||
thread_map__delete(threads);
|
||||
return err;
|
||||
}
|
@@ -3,6 +3,7 @@
|
||||
#include "evsel.h"
|
||||
#include "evlist.h"
|
||||
#include "sysfs.h"
|
||||
#include "tests.h"
|
||||
#include <linux/hw_breakpoint.h>
|
||||
|
||||
#define TEST_ASSERT_VAL(text, cond) \
|
||||
@@ -443,6 +444,23 @@ static int test__checkevent_pmu_name(struct perf_evlist *evlist)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test__checkevent_pmu_events(struct perf_evlist *evlist)
|
||||
{
|
||||
struct perf_evsel *evsel;
|
||||
|
||||
evsel = list_entry(evlist->entries.next, struct perf_evsel, node);
|
||||
TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
|
||||
TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type);
|
||||
TEST_ASSERT_VAL("wrong exclude_user",
|
||||
!evsel->attr.exclude_user);
|
||||
TEST_ASSERT_VAL("wrong exclude_kernel",
|
||||
evsel->attr.exclude_kernel);
|
||||
TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
|
||||
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test__checkterms_simple(struct list_head *terms)
|
||||
{
|
||||
struct parse_events__term *term;
|
||||
@@ -503,7 +521,7 @@ static int test__group1(struct perf_evlist *evlist)
|
||||
TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
|
||||
TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
|
||||
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
|
||||
TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL);
|
||||
TEST_ASSERT_VAL("wrong leader", !perf_evsel__is_group_member(evsel));
|
||||
|
||||
/* cycles:upp */
|
||||
evsel = perf_evsel__next(evsel);
|
||||
@@ -539,7 +557,7 @@ static int test__group2(struct perf_evlist *evlist)
|
||||
TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
|
||||
TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
|
||||
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
|
||||
TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL);
|
||||
TEST_ASSERT_VAL("wrong leader", !perf_evsel__is_group_member(evsel));
|
||||
|
||||
/* cache-references + :u modifier */
|
||||
evsel = perf_evsel__next(evsel);
|
||||
@@ -565,7 +583,7 @@ static int test__group2(struct perf_evlist *evlist)
|
||||
TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
|
||||
TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
|
||||
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
|
||||
TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL);
|
||||
TEST_ASSERT_VAL("wrong leader", !perf_evsel__is_group_member(evsel));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -588,7 +606,7 @@ static int test__group3(struct perf_evlist *evlist __maybe_unused)
|
||||
TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest);
|
||||
TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
|
||||
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
|
||||
TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL);
|
||||
TEST_ASSERT_VAL("wrong leader", !perf_evsel__is_group_member(evsel));
|
||||
TEST_ASSERT_VAL("wrong group name",
|
||||
!strcmp(leader->group_name, "group1"));
|
||||
|
||||
@@ -618,7 +636,7 @@ static int test__group3(struct perf_evlist *evlist __maybe_unused)
|
||||
TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
|
||||
TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host);
|
||||
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
|
||||
TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL);
|
||||
TEST_ASSERT_VAL("wrong leader", !perf_evsel__is_group_member(evsel));
|
||||
TEST_ASSERT_VAL("wrong group name",
|
||||
!strcmp(leader->group_name, "group2"));
|
||||
|
||||
@@ -645,7 +663,7 @@ static int test__group3(struct perf_evlist *evlist __maybe_unused)
|
||||
TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
|
||||
TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
|
||||
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
|
||||
TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL);
|
||||
TEST_ASSERT_VAL("wrong leader", !perf_evsel__is_group_member(evsel));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -669,7 +687,7 @@ static int test__group4(struct perf_evlist *evlist __maybe_unused)
|
||||
TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
|
||||
TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 1);
|
||||
TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
|
||||
TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL);
|
||||
TEST_ASSERT_VAL("wrong leader", !perf_evsel__is_group_member(evsel));
|
||||
|
||||
/* instructions:kp + p */
|
||||
evsel = perf_evsel__next(evsel);
|
||||
@@ -706,7 +724,7 @@ static int test__group5(struct perf_evlist *evlist __maybe_unused)
|
||||
TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host);
|
||||
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
|
||||
TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
|
||||
TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL);
|
||||
TEST_ASSERT_VAL("wrong leader", !perf_evsel__is_group_member(evsel));
|
||||
|
||||
/* instructions + G */
|
||||
evsel = perf_evsel__next(evsel);
|
||||
@@ -733,7 +751,7 @@ static int test__group5(struct perf_evlist *evlist __maybe_unused)
|
||||
TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host);
|
||||
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
|
||||
TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
|
||||
TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL);
|
||||
TEST_ASSERT_VAL("wrong leader", !perf_evsel__is_group_member(evsel));
|
||||
|
||||
/* instructions:G */
|
||||
evsel = perf_evsel__next(evsel);
|
||||
@@ -759,7 +777,7 @@ static int test__group5(struct perf_evlist *evlist __maybe_unused)
|
||||
TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest);
|
||||
TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
|
||||
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
|
||||
TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL);
|
||||
TEST_ASSERT_VAL("wrong leader", !perf_evsel__is_group_member(evsel));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1024,7 +1042,52 @@ static int test_pmu(void)
|
||||
return !ret;
|
||||
}
|
||||
|
||||
int parse_events__test(void)
|
||||
static int test_pmu_events(void)
|
||||
{
|
||||
struct stat st;
|
||||
char path[PATH_MAX];
|
||||
struct dirent *ent;
|
||||
DIR *dir;
|
||||
int ret;
|
||||
|
||||
snprintf(path, PATH_MAX, "%s/bus/event_source/devices/cpu/events/",
|
||||
sysfs_find_mountpoint());
|
||||
|
||||
ret = stat(path, &st);
|
||||
if (ret) {
|
||||
pr_debug("ommiting PMU cpu events tests\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
dir = opendir(path);
|
||||
if (!dir) {
|
||||
pr_debug("can't open pmu event dir");
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (!ret && (ent = readdir(dir))) {
|
||||
#define MAX_NAME 100
|
||||
struct test__event_st e;
|
||||
char name[MAX_NAME];
|
||||
|
||||
if (!strcmp(ent->d_name, ".") ||
|
||||
!strcmp(ent->d_name, ".."))
|
||||
continue;
|
||||
|
||||
snprintf(name, MAX_NAME, "cpu/event=%s/u", ent->d_name);
|
||||
|
||||
e.name = name;
|
||||
e.check = test__checkevent_pmu_events;
|
||||
|
||||
ret = test_event(&e);
|
||||
#undef MAX_NAME
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int test__parse_events(void)
|
||||
{
|
||||
int ret1, ret2 = 0;
|
||||
|
||||
@@ -1040,6 +1103,12 @@ do { \
|
||||
if (test_pmu())
|
||||
TEST_EVENTS(test__events_pmu);
|
||||
|
||||
if (test_pmu()) {
|
||||
int ret = test_pmu_events();
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret1 = test_terms(test__terms, ARRAY_SIZE(test__terms));
|
||||
if (!ret2)
|
||||
ret2 = ret1;
|
312
tools/perf/tests/perf-record.c
Normal file
312
tools/perf/tests/perf-record.c
Normal file
@@ -0,0 +1,312 @@
|
||||
#include <sched.h>
|
||||
#include "evlist.h"
|
||||
#include "evsel.h"
|
||||
#include "perf.h"
|
||||
#include "debug.h"
|
||||
#include "tests.h"
|
||||
|
||||
static int sched__get_first_possible_cpu(pid_t pid, cpu_set_t *maskp)
|
||||
{
|
||||
int i, cpu = -1, nrcpus = 1024;
|
||||
realloc:
|
||||
CPU_ZERO(maskp);
|
||||
|
||||
if (sched_getaffinity(pid, sizeof(*maskp), maskp) == -1) {
|
||||
if (errno == EINVAL && nrcpus < (1024 << 8)) {
|
||||
nrcpus = nrcpus << 2;
|
||||
goto realloc;
|
||||
}
|
||||
perror("sched_getaffinity");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < nrcpus; i++) {
|
||||
if (CPU_ISSET(i, maskp)) {
|
||||
if (cpu == -1)
|
||||
cpu = i;
|
||||
else
|
||||
CPU_CLR(i, maskp);
|
||||
}
|
||||
}
|
||||
|
||||
return cpu;
|
||||
}
|
||||
|
||||
int test__PERF_RECORD(void)
|
||||
{
|
||||
struct perf_record_opts opts = {
|
||||
.target = {
|
||||
.uid = UINT_MAX,
|
||||
.uses_mmap = true,
|
||||
},
|
||||
.no_delay = true,
|
||||
.freq = 10,
|
||||
.mmap_pages = 256,
|
||||
};
|
||||
cpu_set_t cpu_mask;
|
||||
size_t cpu_mask_size = sizeof(cpu_mask);
|
||||
struct perf_evlist *evlist = perf_evlist__new(NULL, NULL);
|
||||
struct perf_evsel *evsel;
|
||||
struct perf_sample sample;
|
||||
const char *cmd = "sleep";
|
||||
const char *argv[] = { cmd, "1", NULL, };
|
||||
char *bname;
|
||||
u64 prev_time = 0;
|
||||
bool found_cmd_mmap = false,
|
||||
found_libc_mmap = false,
|
||||
found_vdso_mmap = false,
|
||||
found_ld_mmap = false;
|
||||
int err = -1, errs = 0, i, wakeups = 0;
|
||||
u32 cpu;
|
||||
int total_events = 0, nr_events[PERF_RECORD_MAX] = { 0, };
|
||||
|
||||
if (evlist == NULL || argv == NULL) {
|
||||
pr_debug("Not enough memory to create evlist\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* We need at least one evsel in the evlist, use the default
|
||||
* one: "cycles".
|
||||
*/
|
||||
err = perf_evlist__add_default(evlist);
|
||||
if (err < 0) {
|
||||
pr_debug("Not enough memory to create evsel\n");
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create maps of threads and cpus to monitor. In this case
|
||||
* we start with all threads and cpus (-1, -1) but then in
|
||||
* perf_evlist__prepare_workload we'll fill in the only thread
|
||||
* we're monitoring, the one forked there.
|
||||
*/
|
||||
err = perf_evlist__create_maps(evlist, &opts.target);
|
||||
if (err < 0) {
|
||||
pr_debug("Not enough memory to create thread/cpu maps\n");
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
/*
|
||||
* Prepare the workload in argv[] to run, it'll fork it, and then wait
|
||||
* for perf_evlist__start_workload() to exec it. This is done this way
|
||||
* so that we have time to open the evlist (calling sys_perf_event_open
|
||||
* on all the fds) and then mmap them.
|
||||
*/
|
||||
err = perf_evlist__prepare_workload(evlist, &opts, argv);
|
||||
if (err < 0) {
|
||||
pr_debug("Couldn't run the workload!\n");
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
/*
|
||||
* Config the evsels, setting attr->comm on the first one, etc.
|
||||
*/
|
||||
evsel = perf_evlist__first(evlist);
|
||||
evsel->attr.sample_type |= PERF_SAMPLE_CPU;
|
||||
evsel->attr.sample_type |= PERF_SAMPLE_TID;
|
||||
evsel->attr.sample_type |= PERF_SAMPLE_TIME;
|
||||
perf_evlist__config_attrs(evlist, &opts);
|
||||
|
||||
err = sched__get_first_possible_cpu(evlist->workload.pid, &cpu_mask);
|
||||
if (err < 0) {
|
||||
pr_debug("sched__get_first_possible_cpu: %s\n", strerror(errno));
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
cpu = err;
|
||||
|
||||
/*
|
||||
* So that we can check perf_sample.cpu on all the samples.
|
||||
*/
|
||||
if (sched_setaffinity(evlist->workload.pid, cpu_mask_size, &cpu_mask) < 0) {
|
||||
pr_debug("sched_setaffinity: %s\n", strerror(errno));
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
/*
|
||||
* Call sys_perf_event_open on all the fds on all the evsels,
|
||||
* grouping them if asked to.
|
||||
*/
|
||||
err = perf_evlist__open(evlist);
|
||||
if (err < 0) {
|
||||
pr_debug("perf_evlist__open: %s\n", strerror(errno));
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
/*
|
||||
* mmap the first fd on a given CPU and ask for events for the other
|
||||
* fds in the same CPU to be injected in the same mmap ring buffer
|
||||
* (using ioctl(PERF_EVENT_IOC_SET_OUTPUT)).
|
||||
*/
|
||||
err = perf_evlist__mmap(evlist, opts.mmap_pages, false);
|
||||
if (err < 0) {
|
||||
pr_debug("perf_evlist__mmap: %s\n", strerror(errno));
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now that all is properly set up, enable the events, they will
|
||||
* count just on workload.pid, which will start...
|
||||
*/
|
||||
perf_evlist__enable(evlist);
|
||||
|
||||
/*
|
||||
* Now!
|
||||
*/
|
||||
perf_evlist__start_workload(evlist);
|
||||
|
||||
while (1) {
|
||||
int before = total_events;
|
||||
|
||||
for (i = 0; i < evlist->nr_mmaps; i++) {
|
||||
union perf_event *event;
|
||||
|
||||
while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) {
|
||||
const u32 type = event->header.type;
|
||||
const char *name = perf_event__name(type);
|
||||
|
||||
++total_events;
|
||||
if (type < PERF_RECORD_MAX)
|
||||
nr_events[type]++;
|
||||
|
||||
err = perf_evlist__parse_sample(evlist, event, &sample);
|
||||
if (err < 0) {
|
||||
if (verbose)
|
||||
perf_event__fprintf(event, stderr);
|
||||
pr_debug("Couldn't parse sample\n");
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
pr_info("%" PRIu64" %d ", sample.time, sample.cpu);
|
||||
perf_event__fprintf(event, stderr);
|
||||
}
|
||||
|
||||
if (prev_time > sample.time) {
|
||||
pr_debug("%s going backwards in time, prev=%" PRIu64 ", curr=%" PRIu64 "\n",
|
||||
name, prev_time, sample.time);
|
||||
++errs;
|
||||
}
|
||||
|
||||
prev_time = sample.time;
|
||||
|
||||
if (sample.cpu != cpu) {
|
||||
pr_debug("%s with unexpected cpu, expected %d, got %d\n",
|
||||
name, cpu, sample.cpu);
|
||||
++errs;
|
||||
}
|
||||
|
||||
if ((pid_t)sample.pid != evlist->workload.pid) {
|
||||
pr_debug("%s with unexpected pid, expected %d, got %d\n",
|
||||
name, evlist->workload.pid, sample.pid);
|
||||
++errs;
|
||||
}
|
||||
|
||||
if ((pid_t)sample.tid != evlist->workload.pid) {
|
||||
pr_debug("%s with unexpected tid, expected %d, got %d\n",
|
||||
name, evlist->workload.pid, sample.tid);
|
||||
++errs;
|
||||
}
|
||||
|
||||
if ((type == PERF_RECORD_COMM ||
|
||||
type == PERF_RECORD_MMAP ||
|
||||
type == PERF_RECORD_FORK ||
|
||||
type == PERF_RECORD_EXIT) &&
|
||||
(pid_t)event->comm.pid != evlist->workload.pid) {
|
||||
pr_debug("%s with unexpected pid/tid\n", name);
|
||||
++errs;
|
||||
}
|
||||
|
||||
if ((type == PERF_RECORD_COMM ||
|
||||
type == PERF_RECORD_MMAP) &&
|
||||
event->comm.pid != event->comm.tid) {
|
||||
pr_debug("%s with different pid/tid!\n", name);
|
||||
++errs;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case PERF_RECORD_COMM:
|
||||
if (strcmp(event->comm.comm, cmd)) {
|
||||
pr_debug("%s with unexpected comm!\n", name);
|
||||
++errs;
|
||||
}
|
||||
break;
|
||||
case PERF_RECORD_EXIT:
|
||||
goto found_exit;
|
||||
case PERF_RECORD_MMAP:
|
||||
bname = strrchr(event->mmap.filename, '/');
|
||||
if (bname != NULL) {
|
||||
if (!found_cmd_mmap)
|
||||
found_cmd_mmap = !strcmp(bname + 1, cmd);
|
||||
if (!found_libc_mmap)
|
||||
found_libc_mmap = !strncmp(bname + 1, "libc", 4);
|
||||
if (!found_ld_mmap)
|
||||
found_ld_mmap = !strncmp(bname + 1, "ld", 2);
|
||||
} else if (!found_vdso_mmap)
|
||||
found_vdso_mmap = !strcmp(event->mmap.filename, "[vdso]");
|
||||
break;
|
||||
|
||||
case PERF_RECORD_SAMPLE:
|
||||
/* Just ignore samples for now */
|
||||
break;
|
||||
default:
|
||||
pr_debug("Unexpected perf_event->header.type %d!\n",
|
||||
type);
|
||||
++errs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We don't use poll here because at least at 3.1 times the
|
||||
* PERF_RECORD_{!SAMPLE} events don't honour
|
||||
* perf_event_attr.wakeup_events, just PERF_EVENT_SAMPLE does.
|
||||
*/
|
||||
if (total_events == before && false)
|
||||
poll(evlist->pollfd, evlist->nr_fds, -1);
|
||||
|
||||
sleep(1);
|
||||
if (++wakeups > 5) {
|
||||
pr_debug("No PERF_RECORD_EXIT event!\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
found_exit:
|
||||
if (nr_events[PERF_RECORD_COMM] > 1) {
|
||||
pr_debug("Excessive number of PERF_RECORD_COMM events!\n");
|
||||
++errs;
|
||||
}
|
||||
|
||||
if (nr_events[PERF_RECORD_COMM] == 0) {
|
||||
pr_debug("Missing PERF_RECORD_COMM for %s!\n", cmd);
|
||||
++errs;
|
||||
}
|
||||
|
||||
if (!found_cmd_mmap) {
|
||||
pr_debug("PERF_RECORD_MMAP for %s missing!\n", cmd);
|
||||
++errs;
|
||||
}
|
||||
|
||||
if (!found_libc_mmap) {
|
||||
pr_debug("PERF_RECORD_MMAP for %s missing!\n", "libc");
|
||||
++errs;
|
||||
}
|
||||
|
||||
if (!found_ld_mmap) {
|
||||
pr_debug("PERF_RECORD_MMAP for %s missing!\n", "ld");
|
||||
++errs;
|
||||
}
|
||||
|
||||
if (!found_vdso_mmap) {
|
||||
pr_debug("PERF_RECORD_MMAP for %s missing!\n", "[vdso]");
|
||||
++errs;
|
||||
}
|
||||
out_err:
|
||||
perf_evlist__munmap(evlist);
|
||||
out_delete_evlist:
|
||||
perf_evlist__delete(evlist);
|
||||
out:
|
||||
return (err < 0 || errs > 0) ? -1 : 0;
|
||||
}
|
178
tools/perf/tests/pmu.c
Normal file
178
tools/perf/tests/pmu.c
Normal file
@@ -0,0 +1,178 @@
|
||||
#include "parse-events.h"
|
||||
#include "pmu.h"
|
||||
#include "util.h"
|
||||
#include "tests.h"
|
||||
|
||||
/* Simulated format definitions. */
|
||||
static struct test_format {
|
||||
const char *name;
|
||||
const char *value;
|
||||
} test_formats[] = {
|
||||
{ "krava01", "config:0-1,62-63\n", },
|
||||
{ "krava02", "config:10-17\n", },
|
||||
{ "krava03", "config:5\n", },
|
||||
{ "krava11", "config1:0,2,4,6,8,20-28\n", },
|
||||
{ "krava12", "config1:63\n", },
|
||||
{ "krava13", "config1:45-47\n", },
|
||||
{ "krava21", "config2:0-3,10-13,20-23,30-33,40-43,50-53,60-63\n", },
|
||||
{ "krava22", "config2:8,18,48,58\n", },
|
||||
{ "krava23", "config2:28-29,38\n", },
|
||||
};
|
||||
|
||||
#define TEST_FORMATS_CNT (sizeof(test_formats) / sizeof(struct test_format))
|
||||
|
||||
/* Simulated users input. */
|
||||
static struct parse_events__term test_terms[] = {
|
||||
{
|
||||
.config = (char *) "krava01",
|
||||
.val.num = 15,
|
||||
.type_val = PARSE_EVENTS__TERM_TYPE_NUM,
|
||||
.type_term = PARSE_EVENTS__TERM_TYPE_USER,
|
||||
},
|
||||
{
|
||||
.config = (char *) "krava02",
|
||||
.val.num = 170,
|
||||
.type_val = PARSE_EVENTS__TERM_TYPE_NUM,
|
||||
.type_term = PARSE_EVENTS__TERM_TYPE_USER,
|
||||
},
|
||||
{
|
||||
.config = (char *) "krava03",
|
||||
.val.num = 1,
|
||||
.type_val = PARSE_EVENTS__TERM_TYPE_NUM,
|
||||
.type_term = PARSE_EVENTS__TERM_TYPE_USER,
|
||||
},
|
||||
{
|
||||
.config = (char *) "krava11",
|
||||
.val.num = 27,
|
||||
.type_val = PARSE_EVENTS__TERM_TYPE_NUM,
|
||||
.type_term = PARSE_EVENTS__TERM_TYPE_USER,
|
||||
},
|
||||
{
|
||||
.config = (char *) "krava12",
|
||||
.val.num = 1,
|
||||
.type_val = PARSE_EVENTS__TERM_TYPE_NUM,
|
||||
.type_term = PARSE_EVENTS__TERM_TYPE_USER,
|
||||
},
|
||||
{
|
||||
.config = (char *) "krava13",
|
||||
.val.num = 2,
|
||||
.type_val = PARSE_EVENTS__TERM_TYPE_NUM,
|
||||
.type_term = PARSE_EVENTS__TERM_TYPE_USER,
|
||||
},
|
||||
{
|
||||
.config = (char *) "krava21",
|
||||
.val.num = 119,
|
||||
.type_val = PARSE_EVENTS__TERM_TYPE_NUM,
|
||||
.type_term = PARSE_EVENTS__TERM_TYPE_USER,
|
||||
},
|
||||
{
|
||||
.config = (char *) "krava22",
|
||||
.val.num = 11,
|
||||
.type_val = PARSE_EVENTS__TERM_TYPE_NUM,
|
||||
.type_term = PARSE_EVENTS__TERM_TYPE_USER,
|
||||
},
|
||||
{
|
||||
.config = (char *) "krava23",
|
||||
.val.num = 2,
|
||||
.type_val = PARSE_EVENTS__TERM_TYPE_NUM,
|
||||
.type_term = PARSE_EVENTS__TERM_TYPE_USER,
|
||||
},
|
||||
};
|
||||
#define TERMS_CNT (sizeof(test_terms) / sizeof(struct parse_events__term))
|
||||
|
||||
/*
|
||||
* Prepare format directory data, exported by kernel
|
||||
* at /sys/bus/event_source/devices/<dev>/format.
|
||||
*/
|
||||
static char *test_format_dir_get(void)
|
||||
{
|
||||
static char dir[PATH_MAX];
|
||||
unsigned int i;
|
||||
|
||||
snprintf(dir, PATH_MAX, "/tmp/perf-pmu-test-format-XXXXXX");
|
||||
if (!mkdtemp(dir))
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < TEST_FORMATS_CNT; i++) {
|
||||
static char name[PATH_MAX];
|
||||
struct test_format *format = &test_formats[i];
|
||||
FILE *file;
|
||||
|
||||
snprintf(name, PATH_MAX, "%s/%s", dir, format->name);
|
||||
|
||||
file = fopen(name, "w");
|
||||
if (!file)
|
||||
return NULL;
|
||||
|
||||
if (1 != fwrite(format->value, strlen(format->value), 1, file))
|
||||
break;
|
||||
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
return dir;
|
||||
}
|
||||
|
||||
/* Cleanup format directory. */
|
||||
static int test_format_dir_put(char *dir)
|
||||
{
|
||||
char buf[PATH_MAX];
|
||||
snprintf(buf, PATH_MAX, "rm -f %s/*\n", dir);
|
||||
if (system(buf))
|
||||
return -1;
|
||||
|
||||
snprintf(buf, PATH_MAX, "rmdir %s\n", dir);
|
||||
return system(buf);
|
||||
}
|
||||
|
||||
static struct list_head *test_terms_list(void)
|
||||
{
|
||||
static LIST_HEAD(terms);
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < TERMS_CNT; i++)
|
||||
list_add_tail(&test_terms[i].list, &terms);
|
||||
|
||||
return &terms;
|
||||
}
|
||||
|
||||
#undef TERMS_CNT
|
||||
|
||||
int test__pmu(void)
|
||||
{
|
||||
char *format = test_format_dir_get();
|
||||
LIST_HEAD(formats);
|
||||
struct list_head *terms = test_terms_list();
|
||||
int ret;
|
||||
|
||||
if (!format)
|
||||
return -EINVAL;
|
||||
|
||||
do {
|
||||
struct perf_event_attr attr;
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
|
||||
ret = perf_pmu__format_parse(format, &formats);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
ret = perf_pmu__config_terms(&formats, &attr, terms);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
ret = -EINVAL;
|
||||
|
||||
if (attr.config != 0xc00000000002a823)
|
||||
break;
|
||||
if (attr.config1 != 0x8000400000000145)
|
||||
break;
|
||||
if (attr.config2 != 0x0400000020041d07)
|
||||
break;
|
||||
|
||||
ret = 0;
|
||||
} while (0);
|
||||
|
||||
test_format_dir_put(format);
|
||||
return ret;
|
||||
}
|
175
tools/perf/tests/rdpmc.c
Normal file
175
tools/perf/tests/rdpmc.c
Normal file
@@ -0,0 +1,175 @@
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <sys/mman.h>
|
||||
#include "types.h"
|
||||
#include "perf.h"
|
||||
#include "debug.h"
|
||||
#include "tests.h"
|
||||
|
||||
#if defined(__x86_64__) || defined(__i386__)
|
||||
|
||||
#define barrier() asm volatile("" ::: "memory")
|
||||
|
||||
static u64 rdpmc(unsigned int counter)
|
||||
{
|
||||
unsigned int low, high;
|
||||
|
||||
asm volatile("rdpmc" : "=a" (low), "=d" (high) : "c" (counter));
|
||||
|
||||
return low | ((u64)high) << 32;
|
||||
}
|
||||
|
||||
static u64 rdtsc(void)
|
||||
{
|
||||
unsigned int low, high;
|
||||
|
||||
asm volatile("rdtsc" : "=a" (low), "=d" (high));
|
||||
|
||||
return low | ((u64)high) << 32;
|
||||
}
|
||||
|
||||
static u64 mmap_read_self(void *addr)
|
||||
{
|
||||
struct perf_event_mmap_page *pc = addr;
|
||||
u32 seq, idx, time_mult = 0, time_shift = 0;
|
||||
u64 count, cyc = 0, time_offset = 0, enabled, running, delta;
|
||||
|
||||
do {
|
||||
seq = pc->lock;
|
||||
barrier();
|
||||
|
||||
enabled = pc->time_enabled;
|
||||
running = pc->time_running;
|
||||
|
||||
if (enabled != running) {
|
||||
cyc = rdtsc();
|
||||
time_mult = pc->time_mult;
|
||||
time_shift = pc->time_shift;
|
||||
time_offset = pc->time_offset;
|
||||
}
|
||||
|
||||
idx = pc->index;
|
||||
count = pc->offset;
|
||||
if (idx)
|
||||
count += rdpmc(idx - 1);
|
||||
|
||||
barrier();
|
||||
} while (pc->lock != seq);
|
||||
|
||||
if (enabled != running) {
|
||||
u64 quot, rem;
|
||||
|
||||
quot = (cyc >> time_shift);
|
||||
rem = cyc & ((1 << time_shift) - 1);
|
||||
delta = time_offset + quot * time_mult +
|
||||
((rem * time_mult) >> time_shift);
|
||||
|
||||
enabled += delta;
|
||||
if (idx)
|
||||
running += delta;
|
||||
|
||||
quot = count / running;
|
||||
rem = count % running;
|
||||
count = quot * enabled + (rem * enabled) / running;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the RDPMC instruction faults then signal this back to the test parent task:
|
||||
*/
|
||||
static void segfault_handler(int sig __maybe_unused,
|
||||
siginfo_t *info __maybe_unused,
|
||||
void *uc __maybe_unused)
|
||||
{
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
static int __test__rdpmc(void)
|
||||
{
|
||||
volatile int tmp = 0;
|
||||
u64 i, loops = 1000;
|
||||
int n;
|
||||
int fd;
|
||||
void *addr;
|
||||
struct perf_event_attr attr = {
|
||||
.type = PERF_TYPE_HARDWARE,
|
||||
.config = PERF_COUNT_HW_INSTRUCTIONS,
|
||||
.exclude_kernel = 1,
|
||||
};
|
||||
u64 delta_sum = 0;
|
||||
struct sigaction sa;
|
||||
|
||||
sigfillset(&sa.sa_mask);
|
||||
sa.sa_sigaction = segfault_handler;
|
||||
sigaction(SIGSEGV, &sa, NULL);
|
||||
|
||||
fd = sys_perf_event_open(&attr, 0, -1, -1, 0);
|
||||
if (fd < 0) {
|
||||
pr_err("Error: sys_perf_event_open() syscall returned "
|
||||
"with %d (%s)\n", fd, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
addr = mmap(NULL, page_size, PROT_READ, MAP_SHARED, fd, 0);
|
||||
if (addr == (void *)(-1)) {
|
||||
pr_err("Error: mmap() syscall returned with (%s)\n",
|
||||
strerror(errno));
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
for (n = 0; n < 6; n++) {
|
||||
u64 stamp, now, delta;
|
||||
|
||||
stamp = mmap_read_self(addr);
|
||||
|
||||
for (i = 0; i < loops; i++)
|
||||
tmp++;
|
||||
|
||||
now = mmap_read_self(addr);
|
||||
loops *= 10;
|
||||
|
||||
delta = now - stamp;
|
||||
pr_debug("%14d: %14Lu\n", n, (long long)delta);
|
||||
|
||||
delta_sum += delta;
|
||||
}
|
||||
|
||||
munmap(addr, page_size);
|
||||
pr_debug(" ");
|
||||
out_close:
|
||||
close(fd);
|
||||
|
||||
if (!delta_sum)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test__rdpmc(void)
|
||||
{
|
||||
int status = 0;
|
||||
int wret = 0;
|
||||
int ret;
|
||||
int pid;
|
||||
|
||||
pid = fork();
|
||||
if (pid < 0)
|
||||
return -1;
|
||||
|
||||
if (!pid) {
|
||||
ret = __test__rdpmc();
|
||||
|
||||
exit(ret);
|
||||
}
|
||||
|
||||
wret = waitpid(pid, &status, 0);
|
||||
if (wret < 0 || status)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
22
tools/perf/tests/tests.h
Normal file
22
tools/perf/tests/tests.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#ifndef TESTS_H
|
||||
#define TESTS_H
|
||||
|
||||
/* Tests */
|
||||
int test__vmlinux_matches_kallsyms(void);
|
||||
int test__open_syscall_event(void);
|
||||
int test__open_syscall_event_on_all_cpus(void);
|
||||
int test__basic_mmap(void);
|
||||
int test__PERF_RECORD(void);
|
||||
int test__rdpmc(void);
|
||||
int test__perf_evsel__roundtrip_name_test(void);
|
||||
int test__perf_evsel__tp_sched_test(void);
|
||||
int test__syscall_open_tp_fields(void);
|
||||
int test__pmu(void);
|
||||
int test__attr(void);
|
||||
int test__dso_data(void);
|
||||
int test__parse_events(void);
|
||||
|
||||
/* Util */
|
||||
int trace_event__id(const char *evname);
|
||||
|
||||
#endif /* TESTS_H */
|
30
tools/perf/tests/util.c
Normal file
30
tools/perf/tests/util.c
Normal file
@@ -0,0 +1,30 @@
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include "tests.h"
|
||||
#include "debugfs.h"
|
||||
|
||||
int trace_event__id(const char *evname)
|
||||
{
|
||||
char *filename;
|
||||
int err = -1, fd;
|
||||
|
||||
if (asprintf(&filename,
|
||||
"%s/syscalls/%s/id",
|
||||
tracing_events_path, evname) < 0)
|
||||
return -1;
|
||||
|
||||
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;
|
||||
}
|
230
tools/perf/tests/vmlinux-kallsyms.c
Normal file
230
tools/perf/tests/vmlinux-kallsyms.c
Normal file
@@ -0,0 +1,230 @@
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/rbtree.h>
|
||||
#include <string.h>
|
||||
#include "map.h"
|
||||
#include "symbol.h"
|
||||
#include "util.h"
|
||||
#include "tests.h"
|
||||
#include "debug.h"
|
||||
#include "machine.h"
|
||||
|
||||
static int vmlinux_matches_kallsyms_filter(struct map *map __maybe_unused,
|
||||
struct symbol *sym)
|
||||
{
|
||||
bool *visited = symbol__priv(sym);
|
||||
*visited = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test__vmlinux_matches_kallsyms(void)
|
||||
{
|
||||
int err = -1;
|
||||
struct rb_node *nd;
|
||||
struct symbol *sym;
|
||||
struct map *kallsyms_map, *vmlinux_map;
|
||||
struct machine kallsyms, vmlinux;
|
||||
enum map_type type = MAP__FUNCTION;
|
||||
struct ref_reloc_sym ref_reloc_sym = { .name = "_stext", };
|
||||
|
||||
/*
|
||||
* Step 1:
|
||||
*
|
||||
* Init the machines that will hold kernel, modules obtained from
|
||||
* both vmlinux + .ko files and from /proc/kallsyms split by modules.
|
||||
*/
|
||||
machine__init(&kallsyms, "", HOST_KERNEL_ID);
|
||||
machine__init(&vmlinux, "", HOST_KERNEL_ID);
|
||||
|
||||
/*
|
||||
* Step 2:
|
||||
*
|
||||
* Create the kernel maps for kallsyms and the DSO where we will then
|
||||
* load /proc/kallsyms. Also create the modules maps from /proc/modules
|
||||
* and find the .ko files that match them in /lib/modules/`uname -r`/.
|
||||
*/
|
||||
if (machine__create_kernel_maps(&kallsyms) < 0) {
|
||||
pr_debug("machine__create_kernel_maps ");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Step 3:
|
||||
*
|
||||
* Load and split /proc/kallsyms into multiple maps, one per module.
|
||||
*/
|
||||
if (machine__load_kallsyms(&kallsyms, "/proc/kallsyms", type, NULL) <= 0) {
|
||||
pr_debug("dso__load_kallsyms ");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Step 4:
|
||||
*
|
||||
* kallsyms will be internally on demand sorted by name so that we can
|
||||
* find the reference relocation * symbol, i.e. the symbol we will use
|
||||
* to see if the running kernel was relocated by checking if it has the
|
||||
* same value in the vmlinux file we load.
|
||||
*/
|
||||
kallsyms_map = machine__kernel_map(&kallsyms, type);
|
||||
|
||||
sym = map__find_symbol_by_name(kallsyms_map, ref_reloc_sym.name, NULL);
|
||||
if (sym == NULL) {
|
||||
pr_debug("dso__find_symbol_by_name ");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ref_reloc_sym.addr = sym->start;
|
||||
|
||||
/*
|
||||
* Step 5:
|
||||
*
|
||||
* Now repeat step 2, this time for the vmlinux file we'll auto-locate.
|
||||
*/
|
||||
if (machine__create_kernel_maps(&vmlinux) < 0) {
|
||||
pr_debug("machine__create_kernel_maps ");
|
||||
goto out;
|
||||
}
|
||||
|
||||
vmlinux_map = machine__kernel_map(&vmlinux, type);
|
||||
map__kmap(vmlinux_map)->ref_reloc_sym = &ref_reloc_sym;
|
||||
|
||||
/*
|
||||
* Step 6:
|
||||
*
|
||||
* Locate a vmlinux file in the vmlinux path that has a buildid that
|
||||
* matches the one of the running kernel.
|
||||
*
|
||||
* While doing that look if we find the ref reloc symbol, if we find it
|
||||
* we'll have its ref_reloc_symbol.unrelocated_addr and then
|
||||
* maps__reloc_vmlinux will notice and set proper ->[un]map_ip routines
|
||||
* to fixup the symbols.
|
||||
*/
|
||||
if (machine__load_vmlinux_path(&vmlinux, type,
|
||||
vmlinux_matches_kallsyms_filter) <= 0) {
|
||||
pr_debug("machine__load_vmlinux_path ");
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = 0;
|
||||
/*
|
||||
* Step 7:
|
||||
*
|
||||
* Now look at the symbols in the vmlinux DSO and check if we find all of them
|
||||
* in the kallsyms dso. For the ones that are in both, check its names and
|
||||
* end addresses too.
|
||||
*/
|
||||
for (nd = rb_first(&vmlinux_map->dso->symbols[type]); nd; nd = rb_next(nd)) {
|
||||
struct symbol *pair, *first_pair;
|
||||
bool backwards = true;
|
||||
|
||||
sym = rb_entry(nd, struct symbol, rb_node);
|
||||
|
||||
if (sym->start == sym->end)
|
||||
continue;
|
||||
|
||||
first_pair = machine__find_kernel_symbol(&kallsyms, type, sym->start, NULL, NULL);
|
||||
pair = first_pair;
|
||||
|
||||
if (pair && pair->start == sym->start) {
|
||||
next_pair:
|
||||
if (strcmp(sym->name, pair->name) == 0) {
|
||||
/*
|
||||
* kallsyms don't have the symbol end, so we
|
||||
* set that by using the next symbol start - 1,
|
||||
* in some cases we get this up to a page
|
||||
* wrong, trace_kmalloc when I was developing
|
||||
* this code was one such example, 2106 bytes
|
||||
* off the real size. More than that and we
|
||||
* _really_ have a problem.
|
||||
*/
|
||||
s64 skew = sym->end - pair->end;
|
||||
if (llabs(skew) < page_size)
|
||||
continue;
|
||||
|
||||
pr_debug("%#" PRIx64 ": diff end addr for %s v: %#" PRIx64 " k: %#" PRIx64 "\n",
|
||||
sym->start, sym->name, sym->end, pair->end);
|
||||
} else {
|
||||
struct rb_node *nnd;
|
||||
detour:
|
||||
nnd = backwards ? rb_prev(&pair->rb_node) :
|
||||
rb_next(&pair->rb_node);
|
||||
if (nnd) {
|
||||
struct symbol *next = rb_entry(nnd, struct symbol, rb_node);
|
||||
|
||||
if (next->start == sym->start) {
|
||||
pair = next;
|
||||
goto next_pair;
|
||||
}
|
||||
}
|
||||
|
||||
if (backwards) {
|
||||
backwards = false;
|
||||
pair = first_pair;
|
||||
goto detour;
|
||||
}
|
||||
|
||||
pr_debug("%#" PRIx64 ": diff name v: %s k: %s\n",
|
||||
sym->start, sym->name, pair->name);
|
||||
}
|
||||
} else
|
||||
pr_debug("%#" PRIx64 ": %s not on kallsyms\n", sym->start, sym->name);
|
||||
|
||||
err = -1;
|
||||
}
|
||||
|
||||
if (!verbose)
|
||||
goto out;
|
||||
|
||||
pr_info("Maps only in vmlinux:\n");
|
||||
|
||||
for (nd = rb_first(&vmlinux.kmaps.maps[type]); nd; nd = rb_next(nd)) {
|
||||
struct map *pos = rb_entry(nd, struct map, rb_node), *pair;
|
||||
/*
|
||||
* If it is the kernel, kallsyms is always "[kernel.kallsyms]", while
|
||||
* the kernel will have the path for the vmlinux file being used,
|
||||
* so use the short name, less descriptive but the same ("[kernel]" in
|
||||
* both cases.
|
||||
*/
|
||||
pair = map_groups__find_by_name(&kallsyms.kmaps, type,
|
||||
(pos->dso->kernel ?
|
||||
pos->dso->short_name :
|
||||
pos->dso->name));
|
||||
if (pair)
|
||||
pair->priv = 1;
|
||||
else
|
||||
map__fprintf(pos, stderr);
|
||||
}
|
||||
|
||||
pr_info("Maps in vmlinux with a different name in kallsyms:\n");
|
||||
|
||||
for (nd = rb_first(&vmlinux.kmaps.maps[type]); nd; nd = rb_next(nd)) {
|
||||
struct map *pos = rb_entry(nd, struct map, rb_node), *pair;
|
||||
|
||||
pair = map_groups__find(&kallsyms.kmaps, type, pos->start);
|
||||
if (pair == NULL || pair->priv)
|
||||
continue;
|
||||
|
||||
if (pair->start == pos->start) {
|
||||
pair->priv = 1;
|
||||
pr_info(" %" PRIx64 "-%" PRIx64 " %" PRIx64 " %s in kallsyms as",
|
||||
pos->start, pos->end, pos->pgoff, pos->dso->name);
|
||||
if (pos->pgoff != pair->pgoff || pos->end != pair->end)
|
||||
pr_info(": \n*%" PRIx64 "-%" PRIx64 " %" PRIx64 "",
|
||||
pair->start, pair->end, pair->pgoff);
|
||||
pr_info(" %s\n", pair->dso->name);
|
||||
pair->priv = 1;
|
||||
}
|
||||
}
|
||||
|
||||
pr_info("Maps only in kallsyms:\n");
|
||||
|
||||
for (nd = rb_first(&kallsyms.kmaps.maps[type]);
|
||||
nd; nd = rb_next(nd)) {
|
||||
struct map *pos = rb_entry(nd, struct map, rb_node);
|
||||
|
||||
if (!pos->priv)
|
||||
map__fprintf(pos, stderr);
|
||||
}
|
||||
out:
|
||||
return err;
|
||||
}
|
@@ -188,6 +188,12 @@ static void annotate_browser__draw_current_jump(struct ui_browser *browser)
|
||||
struct disasm_line *cursor = ab->selection, *target;
|
||||
struct browser_disasm_line *btarget, *bcursor;
|
||||
unsigned int from, to;
|
||||
struct map_symbol *ms = ab->b.priv;
|
||||
struct symbol *sym = ms->sym;
|
||||
|
||||
/* PLT symbols contain external offsets */
|
||||
if (strstr(sym->name, "@plt"))
|
||||
return;
|
||||
|
||||
if (!cursor || !cursor->ins || !ins__is_jump(cursor->ins) ||
|
||||
!disasm_line__has_offset(cursor))
|
||||
@@ -386,9 +392,8 @@ static void annotate_browser__init_asm_mode(struct annotate_browser *browser)
|
||||
browser->b.nr_entries = browser->nr_asm_entries;
|
||||
}
|
||||
|
||||
static bool annotate_browser__callq(struct annotate_browser *browser,
|
||||
int evidx, void (*timer)(void *arg),
|
||||
void *arg, int delay_secs)
|
||||
static bool annotate_browser__callq(struct annotate_browser *browser, int evidx,
|
||||
struct hist_browser_timer *hbt)
|
||||
{
|
||||
struct map_symbol *ms = browser->b.priv;
|
||||
struct disasm_line *dl = browser->selection;
|
||||
@@ -418,7 +423,7 @@ static bool annotate_browser__callq(struct annotate_browser *browser,
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(¬es->lock);
|
||||
symbol__tui_annotate(target, ms->map, evidx, timer, arg, delay_secs);
|
||||
symbol__tui_annotate(target, ms->map, evidx, hbt);
|
||||
ui_browser__show_title(&browser->b, sym->name);
|
||||
return true;
|
||||
}
|
||||
@@ -602,13 +607,13 @@ static void annotate_browser__update_addr_width(struct annotate_browser *browser
|
||||
}
|
||||
|
||||
static int annotate_browser__run(struct annotate_browser *browser, int evidx,
|
||||
void(*timer)(void *arg),
|
||||
void *arg, int delay_secs)
|
||||
struct hist_browser_timer *hbt)
|
||||
{
|
||||
struct rb_node *nd = NULL;
|
||||
struct map_symbol *ms = browser->b.priv;
|
||||
struct symbol *sym = ms->sym;
|
||||
const char *help = "Press 'h' for help on key bindings";
|
||||
int delay_secs = hbt ? hbt->refresh : 0;
|
||||
int key;
|
||||
|
||||
if (ui_browser__show(&browser->b, sym->name, help) < 0)
|
||||
@@ -639,8 +644,8 @@ static int annotate_browser__run(struct annotate_browser *browser, int evidx,
|
||||
|
||||
switch (key) {
|
||||
case K_TIMER:
|
||||
if (timer != NULL)
|
||||
timer(arg);
|
||||
if (hbt)
|
||||
hbt->timer(hbt->arg);
|
||||
|
||||
if (delay_secs != 0)
|
||||
symbol__annotate_decay_histogram(sym, evidx);
|
||||
@@ -676,8 +681,14 @@ static int annotate_browser__run(struct annotate_browser *browser, int evidx,
|
||||
"o Toggle disassembler output/simplified view\n"
|
||||
"s Toggle source code view\n"
|
||||
"/ Search string\n"
|
||||
"r Run available scripts\n"
|
||||
"? Search previous string\n");
|
||||
continue;
|
||||
case 'r':
|
||||
{
|
||||
script_browse(NULL);
|
||||
continue;
|
||||
}
|
||||
case 'H':
|
||||
nd = browser->curr_hot;
|
||||
break;
|
||||
@@ -734,7 +745,7 @@ show_help:
|
||||
goto show_sup_ins;
|
||||
goto out;
|
||||
} else if (!(annotate_browser__jump(browser) ||
|
||||
annotate_browser__callq(browser, evidx, timer, arg, delay_secs))) {
|
||||
annotate_browser__callq(browser, evidx, hbt))) {
|
||||
show_sup_ins:
|
||||
ui_helpline__puts("Actions are only available for 'callq', 'retq' & jump instructions.");
|
||||
}
|
||||
@@ -757,16 +768,21 @@ out:
|
||||
}
|
||||
|
||||
int hist_entry__tui_annotate(struct hist_entry *he, int evidx,
|
||||
void(*timer)(void *arg), void *arg, int delay_secs)
|
||||
struct hist_browser_timer *hbt)
|
||||
{
|
||||
return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx,
|
||||
timer, arg, delay_secs);
|
||||
return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx, hbt);
|
||||
}
|
||||
|
||||
static void annotate_browser__mark_jump_targets(struct annotate_browser *browser,
|
||||
size_t size)
|
||||
{
|
||||
u64 offset;
|
||||
struct map_symbol *ms = browser->b.priv;
|
||||
struct symbol *sym = ms->sym;
|
||||
|
||||
/* PLT symbols contain external offsets */
|
||||
if (strstr(sym->name, "@plt"))
|
||||
return;
|
||||
|
||||
for (offset = 0; offset < size; ++offset) {
|
||||
struct disasm_line *dl = browser->offsets[offset], *dlt;
|
||||
@@ -810,8 +826,7 @@ static inline int width_jumps(int n)
|
||||
}
|
||||
|
||||
int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
|
||||
void(*timer)(void *arg), void *arg,
|
||||
int delay_secs)
|
||||
struct hist_browser_timer *hbt)
|
||||
{
|
||||
struct disasm_line *pos, *n;
|
||||
struct annotation *notes;
|
||||
@@ -893,7 +908,7 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
|
||||
|
||||
annotate_browser__update_addr_width(&browser);
|
||||
|
||||
ret = annotate_browser__run(&browser, evidx, timer, arg, delay_secs);
|
||||
ret = annotate_browser__run(&browser, evidx, hbt);
|
||||
list_for_each_entry_safe(pos, n, ¬es->src->source, node) {
|
||||
list_del(&pos->node);
|
||||
disasm_line__free(pos);
|
||||
|
@@ -11,6 +11,7 @@
|
||||
#include "../../util/pstack.h"
|
||||
#include "../../util/sort.h"
|
||||
#include "../../util/util.h"
|
||||
#include "../../arch/common.h"
|
||||
|
||||
#include "../browser.h"
|
||||
#include "../helpline.h"
|
||||
@@ -310,10 +311,11 @@ static void ui_browser__warn_lost_events(struct ui_browser *browser)
|
||||
}
|
||||
|
||||
static int hist_browser__run(struct hist_browser *browser, const char *ev_name,
|
||||
void(*timer)(void *arg), void *arg, int delay_secs)
|
||||
struct hist_browser_timer *hbt)
|
||||
{
|
||||
int key;
|
||||
char title[160];
|
||||
int delay_secs = hbt ? hbt->refresh : 0;
|
||||
|
||||
browser->b.entries = &browser->hists->entries;
|
||||
browser->b.nr_entries = browser->hists->nr_entries;
|
||||
@@ -330,7 +332,7 @@ static int hist_browser__run(struct hist_browser *browser, const char *ev_name,
|
||||
|
||||
switch (key) {
|
||||
case K_TIMER:
|
||||
timer(arg);
|
||||
hbt->timer(hbt->arg);
|
||||
ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
|
||||
|
||||
if (browser->hists->stats.nr_lost_warned !=
|
||||
@@ -1127,11 +1129,17 @@ static inline void free_popup_options(char **options, int n)
|
||||
}
|
||||
}
|
||||
|
||||
/* Check whether the browser is for 'top' or 'report' */
|
||||
static inline bool is_report_browser(void *timer)
|
||||
{
|
||||
return timer == NULL;
|
||||
}
|
||||
|
||||
static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
|
||||
const char *helpline, const char *ev_name,
|
||||
bool left_exits,
|
||||
void(*timer)(void *arg), void *arg,
|
||||
int delay_secs)
|
||||
struct hist_browser_timer *hbt,
|
||||
struct perf_session_env *env)
|
||||
{
|
||||
struct hists *hists = &evsel->hists;
|
||||
struct hist_browser *browser = hist_browser__new(hists);
|
||||
@@ -1141,6 +1149,8 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
|
||||
int nr_options = 0;
|
||||
int key = -1;
|
||||
char buf[64];
|
||||
char script_opt[64];
|
||||
int delay_secs = hbt ? hbt->refresh : 0;
|
||||
|
||||
if (browser == NULL)
|
||||
return -1;
|
||||
@@ -1159,10 +1169,11 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
|
||||
int choice = 0,
|
||||
annotate = -2, zoom_dso = -2, zoom_thread = -2,
|
||||
annotate_f = -2, annotate_t = -2, browse_map = -2;
|
||||
int scripts_comm = -2, scripts_symbol = -2, scripts_all = -2;
|
||||
|
||||
nr_options = 0;
|
||||
|
||||
key = hist_browser__run(browser, ev_name, timer, arg, delay_secs);
|
||||
key = hist_browser__run(browser, ev_name, hbt);
|
||||
|
||||
if (browser->he_selection != NULL) {
|
||||
thread = hist_browser__selected_thread(browser);
|
||||
@@ -1211,6 +1222,10 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
|
||||
hist_browser__reset(browser);
|
||||
}
|
||||
continue;
|
||||
case 'r':
|
||||
if (is_report_browser(hbt))
|
||||
goto do_scripts;
|
||||
continue;
|
||||
case K_F1:
|
||||
case 'h':
|
||||
case '?':
|
||||
@@ -1229,6 +1244,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
|
||||
"E Expand all callchains\n"
|
||||
"d Zoom into current DSO\n"
|
||||
"t Zoom into current Thread\n"
|
||||
"r Run available scripts('perf report' only)\n"
|
||||
"P Print histograms to perf.hist.N\n"
|
||||
"V Verbose (DSO names in callchains, etc)\n"
|
||||
"/ Filter symbol by name");
|
||||
@@ -1317,6 +1333,25 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
|
||||
browser->selection->map != NULL &&
|
||||
asprintf(&options[nr_options], "Browse map details") > 0)
|
||||
browse_map = nr_options++;
|
||||
|
||||
/* perf script support */
|
||||
if (browser->he_selection) {
|
||||
struct symbol *sym;
|
||||
|
||||
if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]",
|
||||
browser->he_selection->thread->comm) > 0)
|
||||
scripts_comm = nr_options++;
|
||||
|
||||
sym = browser->he_selection->ms.sym;
|
||||
if (sym && sym->namelen &&
|
||||
asprintf(&options[nr_options], "Run scripts for samples of symbol [%s]",
|
||||
sym->name) > 0)
|
||||
scripts_symbol = nr_options++;
|
||||
}
|
||||
|
||||
if (asprintf(&options[nr_options], "Run scripts for all samples") > 0)
|
||||
scripts_all = nr_options++;
|
||||
|
||||
add_exit_option:
|
||||
options[nr_options++] = (char *)"Exit";
|
||||
retry_popup_menu:
|
||||
@@ -1334,6 +1369,9 @@ retry_popup_menu:
|
||||
struct hist_entry *he;
|
||||
int err;
|
||||
do_annotate:
|
||||
if (!objdump_path && perf_session_env__lookup_objdump(env))
|
||||
continue;
|
||||
|
||||
he = hist_browser__selected_entry(browser);
|
||||
if (he == NULL)
|
||||
continue;
|
||||
@@ -1356,8 +1394,7 @@ do_annotate:
|
||||
* Don't let this be freed, say, by hists__decay_entry.
|
||||
*/
|
||||
he->used = true;
|
||||
err = hist_entry__tui_annotate(he, evsel->idx,
|
||||
timer, arg, delay_secs);
|
||||
err = hist_entry__tui_annotate(he, evsel->idx, hbt);
|
||||
he->used = false;
|
||||
/*
|
||||
* offer option to annotate the other branch source or target
|
||||
@@ -1411,6 +1448,20 @@ zoom_out_thread:
|
||||
hists__filter_by_thread(hists);
|
||||
hist_browser__reset(browser);
|
||||
}
|
||||
/* perf scripts support */
|
||||
else if (choice == scripts_all || choice == scripts_comm ||
|
||||
choice == scripts_symbol) {
|
||||
do_scripts:
|
||||
memset(script_opt, 0, 64);
|
||||
|
||||
if (choice == scripts_comm)
|
||||
sprintf(script_opt, " -c %s ", browser->he_selection->thread->comm);
|
||||
|
||||
if (choice == scripts_symbol)
|
||||
sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name);
|
||||
|
||||
script_browse(script_opt);
|
||||
}
|
||||
}
|
||||
out_free_stack:
|
||||
pstack__delete(fstack);
|
||||
@@ -1424,6 +1475,7 @@ struct perf_evsel_menu {
|
||||
struct ui_browser b;
|
||||
struct perf_evsel *selection;
|
||||
bool lost_events, lost_events_warned;
|
||||
struct perf_session_env *env;
|
||||
};
|
||||
|
||||
static void perf_evsel_menu__write(struct ui_browser *browser,
|
||||
@@ -1466,11 +1518,12 @@ static void perf_evsel_menu__write(struct ui_browser *browser,
|
||||
|
||||
static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
|
||||
int nr_events, const char *help,
|
||||
void(*timer)(void *arg), void *arg, int delay_secs)
|
||||
struct hist_browser_timer *hbt)
|
||||
{
|
||||
struct perf_evlist *evlist = menu->b.priv;
|
||||
struct perf_evsel *pos;
|
||||
const char *ev_name, *title = "Available samples";
|
||||
int delay_secs = hbt ? hbt->refresh : 0;
|
||||
int key;
|
||||
|
||||
if (ui_browser__show(&menu->b, title,
|
||||
@@ -1482,7 +1535,7 @@ static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
|
||||
|
||||
switch (key) {
|
||||
case K_TIMER:
|
||||
timer(arg);
|
||||
hbt->timer(hbt->arg);
|
||||
|
||||
if (!menu->lost_events_warned && menu->lost_events) {
|
||||
ui_browser__warn_lost_events(&menu->b);
|
||||
@@ -1500,12 +1553,12 @@ browse_hists:
|
||||
* Give the calling tool a chance to populate the non
|
||||
* default evsel resorted hists tree.
|
||||
*/
|
||||
if (timer)
|
||||
timer(arg);
|
||||
if (hbt)
|
||||
hbt->timer(hbt->arg);
|
||||
ev_name = perf_evsel__name(pos);
|
||||
key = perf_evsel__hists_browse(pos, nr_events, help,
|
||||
ev_name, true, timer,
|
||||
arg, delay_secs);
|
||||
ev_name, true, hbt,
|
||||
menu->env);
|
||||
ui_browser__show_title(&menu->b, title);
|
||||
switch (key) {
|
||||
case K_TAB:
|
||||
@@ -1553,8 +1606,8 @@ out:
|
||||
|
||||
static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
|
||||
const char *help,
|
||||
void(*timer)(void *arg), void *arg,
|
||||
int delay_secs)
|
||||
struct hist_browser_timer *hbt,
|
||||
struct perf_session_env *env)
|
||||
{
|
||||
struct perf_evsel *pos;
|
||||
struct perf_evsel_menu menu = {
|
||||
@@ -1566,6 +1619,7 @@ static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
|
||||
.nr_entries = evlist->nr_entries,
|
||||
.priv = evlist,
|
||||
},
|
||||
.env = env,
|
||||
};
|
||||
|
||||
ui_helpline__push("Press ESC to exit");
|
||||
@@ -1578,23 +1632,20 @@ static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
|
||||
menu.b.width = line_len;
|
||||
}
|
||||
|
||||
return perf_evsel_menu__run(&menu, evlist->nr_entries, help, timer,
|
||||
arg, delay_secs);
|
||||
return perf_evsel_menu__run(&menu, evlist->nr_entries, help, hbt);
|
||||
}
|
||||
|
||||
int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
|
||||
void(*timer)(void *arg), void *arg,
|
||||
int delay_secs)
|
||||
struct hist_browser_timer *hbt,
|
||||
struct perf_session_env *env)
|
||||
{
|
||||
if (evlist->nr_entries == 1) {
|
||||
struct perf_evsel *first = list_entry(evlist->entries.next,
|
||||
struct perf_evsel, node);
|
||||
const char *ev_name = perf_evsel__name(first);
|
||||
return perf_evsel__hists_browse(first, evlist->nr_entries, help,
|
||||
ev_name, false, timer, arg,
|
||||
delay_secs);
|
||||
ev_name, false, hbt, env);
|
||||
}
|
||||
|
||||
return __perf_evlist__tui_browse_hists(evlist, help,
|
||||
timer, arg, delay_secs);
|
||||
return __perf_evlist__tui_browse_hists(evlist, help, hbt, env);
|
||||
}
|
||||
|
189
tools/perf/ui/browsers/scripts.c
Normal file
189
tools/perf/ui/browsers/scripts.c
Normal file
@@ -0,0 +1,189 @@
|
||||
#include <elf.h>
|
||||
#include <newt.h>
|
||||
#include <inttypes.h>
|
||||
#include <sys/ttydefaults.h>
|
||||
#include <string.h>
|
||||
#include "../../util/sort.h"
|
||||
#include "../../util/util.h"
|
||||
#include "../../util/hist.h"
|
||||
#include "../../util/debug.h"
|
||||
#include "../../util/symbol.h"
|
||||
#include "../browser.h"
|
||||
#include "../helpline.h"
|
||||
#include "../libslang.h"
|
||||
|
||||
/* 2048 lines should be enough for a script output */
|
||||
#define MAX_LINES 2048
|
||||
|
||||
/* 160 bytes for one output line */
|
||||
#define AVERAGE_LINE_LEN 160
|
||||
|
||||
struct script_line {
|
||||
struct list_head node;
|
||||
char line[AVERAGE_LINE_LEN];
|
||||
};
|
||||
|
||||
struct perf_script_browser {
|
||||
struct ui_browser b;
|
||||
struct list_head entries;
|
||||
const char *script_name;
|
||||
int nr_lines;
|
||||
};
|
||||
|
||||
#define SCRIPT_NAMELEN 128
|
||||
#define SCRIPT_MAX_NO 64
|
||||
/*
|
||||
* Usually the full path for a script is:
|
||||
* /home/username/libexec/perf-core/scripts/python/xxx.py
|
||||
* /home/username/libexec/perf-core/scripts/perl/xxx.pl
|
||||
* So 256 should be long enough to contain the full path.
|
||||
*/
|
||||
#define SCRIPT_FULLPATH_LEN 256
|
||||
|
||||
/*
|
||||
* When success, will copy the full path of the selected script
|
||||
* into the buffer pointed by script_name, and return 0.
|
||||
* Return -1 on failure.
|
||||
*/
|
||||
static int list_scripts(char *script_name)
|
||||
{
|
||||
char *buf, *names[SCRIPT_MAX_NO], *paths[SCRIPT_MAX_NO];
|
||||
int i, num, choice, ret = -1;
|
||||
|
||||
/* Preset the script name to SCRIPT_NAMELEN */
|
||||
buf = malloc(SCRIPT_MAX_NO * (SCRIPT_NAMELEN + SCRIPT_FULLPATH_LEN));
|
||||
if (!buf)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < SCRIPT_MAX_NO; i++) {
|
||||
names[i] = buf + i * (SCRIPT_NAMELEN + SCRIPT_FULLPATH_LEN);
|
||||
paths[i] = names[i] + SCRIPT_NAMELEN;
|
||||
}
|
||||
|
||||
num = find_scripts(names, paths);
|
||||
if (num > 0) {
|
||||
choice = ui__popup_menu(num, names);
|
||||
if (choice < num && choice >= 0) {
|
||||
strcpy(script_name, paths[choice]);
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
|
||||
free(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void script_browser__write(struct ui_browser *browser,
|
||||
void *entry, int row)
|
||||
{
|
||||
struct script_line *sline = list_entry(entry, struct script_line, node);
|
||||
bool current_entry = ui_browser__is_current_entry(browser, row);
|
||||
|
||||
ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
|
||||
HE_COLORSET_NORMAL);
|
||||
|
||||
slsmg_write_nstring(sline->line, browser->width);
|
||||
}
|
||||
|
||||
static int script_browser__run(struct perf_script_browser *self)
|
||||
{
|
||||
int key;
|
||||
|
||||
if (ui_browser__show(&self->b, self->script_name,
|
||||
"Press <- or ESC to exit") < 0)
|
||||
return -1;
|
||||
|
||||
while (1) {
|
||||
key = ui_browser__run(&self->b, 0);
|
||||
|
||||
/* We can add some special key handling here if needed */
|
||||
break;
|
||||
}
|
||||
|
||||
ui_browser__hide(&self->b);
|
||||
return key;
|
||||
}
|
||||
|
||||
|
||||
int script_browse(const char *script_opt)
|
||||
{
|
||||
char cmd[SCRIPT_FULLPATH_LEN*2], script_name[SCRIPT_FULLPATH_LEN];
|
||||
char *line = NULL;
|
||||
size_t len = 0;
|
||||
ssize_t retlen;
|
||||
int ret = -1, nr_entries = 0;
|
||||
FILE *fp;
|
||||
void *buf;
|
||||
struct script_line *sline;
|
||||
|
||||
struct perf_script_browser script = {
|
||||
.b = {
|
||||
.refresh = ui_browser__list_head_refresh,
|
||||
.seek = ui_browser__list_head_seek,
|
||||
.write = script_browser__write,
|
||||
},
|
||||
.script_name = script_name,
|
||||
};
|
||||
|
||||
INIT_LIST_HEAD(&script.entries);
|
||||
|
||||
/* Save each line of the output in one struct script_line object. */
|
||||
buf = zalloc((sizeof(*sline)) * MAX_LINES);
|
||||
if (!buf)
|
||||
return -1;
|
||||
sline = buf;
|
||||
|
||||
memset(script_name, 0, SCRIPT_FULLPATH_LEN);
|
||||
if (list_scripts(script_name))
|
||||
goto exit;
|
||||
|
||||
sprintf(cmd, "perf script -s %s ", script_name);
|
||||
|
||||
if (script_opt)
|
||||
strcat(cmd, script_opt);
|
||||
|
||||
if (input_name) {
|
||||
strcat(cmd, " -i ");
|
||||
strcat(cmd, input_name);
|
||||
}
|
||||
|
||||
strcat(cmd, " 2>&1");
|
||||
|
||||
fp = popen(cmd, "r");
|
||||
if (!fp)
|
||||
goto exit;
|
||||
|
||||
while ((retlen = getline(&line, &len, fp)) != -1) {
|
||||
strncpy(sline->line, line, AVERAGE_LINE_LEN);
|
||||
|
||||
/* If one output line is very large, just cut it short */
|
||||
if (retlen >= AVERAGE_LINE_LEN) {
|
||||
sline->line[AVERAGE_LINE_LEN - 1] = '\0';
|
||||
sline->line[AVERAGE_LINE_LEN - 2] = '\n';
|
||||
}
|
||||
list_add_tail(&sline->node, &script.entries);
|
||||
|
||||
if (script.b.width < retlen)
|
||||
script.b.width = retlen;
|
||||
|
||||
if (nr_entries++ >= MAX_LINES - 1)
|
||||
break;
|
||||
sline++;
|
||||
}
|
||||
|
||||
if (script.b.width > AVERAGE_LINE_LEN)
|
||||
script.b.width = AVERAGE_LINE_LEN;
|
||||
|
||||
if (line)
|
||||
free(line);
|
||||
pclose(fp);
|
||||
|
||||
script.nr_lines = nr_entries;
|
||||
script.b.nr_entries = nr_entries;
|
||||
script.b.entries = &script.entries;
|
||||
|
||||
ret = script_browser__run(&script);
|
||||
exit:
|
||||
free(buf);
|
||||
return ret;
|
||||
}
|
@@ -237,9 +237,7 @@ static GtkWidget *perf_gtk__setup_statusbar(void)
|
||||
|
||||
int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist,
|
||||
const char *help,
|
||||
void (*timer) (void *arg)__maybe_unused,
|
||||
void *arg __maybe_unused,
|
||||
int delay_secs __maybe_unused)
|
||||
struct hist_browser_timer *hbt __maybe_unused)
|
||||
{
|
||||
struct perf_evsel *pos;
|
||||
GtkWidget *vbox;
|
||||
|
@@ -30,6 +30,7 @@ struct perf_gtk_context *perf_gtk__activate_context(GtkWidget *window);
|
||||
int perf_gtk__deactivate_context(struct perf_gtk_context **ctx);
|
||||
|
||||
void perf_gtk__init_helpline(void);
|
||||
void perf_gtk__init_progress(void);
|
||||
void perf_gtk__init_hpp(void);
|
||||
|
||||
#ifndef HAVE_GTK_INFO_BAR
|
||||
|
59
tools/perf/ui/gtk/progress.c
Normal file
59
tools/perf/ui/gtk/progress.c
Normal file
@@ -0,0 +1,59 @@
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "gtk.h"
|
||||
#include "../progress.h"
|
||||
#include "util.h"
|
||||
|
||||
static GtkWidget *dialog;
|
||||
static GtkWidget *progress;
|
||||
|
||||
static void gtk_progress_update(u64 curr, u64 total, const char *title)
|
||||
{
|
||||
double fraction = total ? 1.0 * curr / total : 0.0;
|
||||
char buf[1024];
|
||||
|
||||
if (dialog == NULL) {
|
||||
GtkWidget *vbox = gtk_vbox_new(TRUE, 5);
|
||||
GtkWidget *label = gtk_label_new(title);
|
||||
|
||||
dialog = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
||||
progress = gtk_progress_bar_new();
|
||||
|
||||
gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, FALSE, 3);
|
||||
gtk_box_pack_start(GTK_BOX(vbox), progress, TRUE, TRUE, 3);
|
||||
|
||||
gtk_container_add(GTK_CONTAINER(dialog), vbox);
|
||||
|
||||
gtk_window_set_title(GTK_WINDOW(dialog), "perf");
|
||||
gtk_window_resize(GTK_WINDOW(dialog), 300, 80);
|
||||
gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER);
|
||||
|
||||
gtk_widget_show_all(dialog);
|
||||
}
|
||||
|
||||
gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress), fraction);
|
||||
snprintf(buf, sizeof(buf), "%"PRIu64" / %"PRIu64, curr, total);
|
||||
gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress), buf);
|
||||
|
||||
/* we didn't call gtk_main yet, so do it manually */
|
||||
while (gtk_events_pending())
|
||||
gtk_main_iteration();
|
||||
}
|
||||
|
||||
static void gtk_progress_finish(void)
|
||||
{
|
||||
/* this will also destroy all of its children */
|
||||
gtk_widget_destroy(dialog);
|
||||
|
||||
dialog = NULL;
|
||||
}
|
||||
|
||||
static struct ui_progress gtk_progress_fns = {
|
||||
.update = gtk_progress_update,
|
||||
.finish = gtk_progress_finish,
|
||||
};
|
||||
|
||||
void perf_gtk__init_progress(void)
|
||||
{
|
||||
progress_fns = >k_progress_fns;
|
||||
}
|
@@ -8,7 +8,9 @@ int perf_gtk__init(void)
|
||||
{
|
||||
perf_error__register(&perf_gtk_eops);
|
||||
perf_gtk__init_helpline();
|
||||
perf_gtk__init_progress();
|
||||
perf_gtk__init_hpp();
|
||||
|
||||
return gtk_init_check(NULL, NULL) ? 0 : -1;
|
||||
}
|
||||
|
||||
|
@@ -111,14 +111,3 @@ struct perf_error_ops perf_gtk_eops = {
|
||||
.warning = perf_gtk__warning_statusbar,
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
* FIXME: Functions below should be implemented properly.
|
||||
* For now, just add stubs for NO_NEWT=1 build.
|
||||
*/
|
||||
#ifndef NEWT_SUPPORT
|
||||
void ui_progress__update(u64 curr __maybe_unused, u64 total __maybe_unused,
|
||||
const char *title __maybe_unused)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
@@ -161,7 +161,7 @@ static int hpp__width_baseline(struct perf_hpp *hpp __maybe_unused)
|
||||
|
||||
static double baseline_percent(struct hist_entry *he)
|
||||
{
|
||||
struct hist_entry *pair = he->pair;
|
||||
struct hist_entry *pair = hist_entry__next_pair(he);
|
||||
struct hists *pair_hists = pair ? pair->hists : NULL;
|
||||
double percent = 0.0;
|
||||
|
||||
@@ -179,7 +179,10 @@ static int hpp__color_baseline(struct perf_hpp *hpp, struct hist_entry *he)
|
||||
{
|
||||
double percent = baseline_percent(he);
|
||||
|
||||
return percent_color_snprintf(hpp->buf, hpp->size, " %6.2f%%", percent);
|
||||
if (hist_entry__has_pairs(he))
|
||||
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)
|
||||
@@ -187,7 +190,10 @@ 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%%";
|
||||
|
||||
return scnprintf(hpp->buf, hpp->size, fmt, percent);
|
||||
if (hist_entry__has_pairs(he) || 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)
|
||||
@@ -228,6 +234,26 @@ static int hpp__entry_period(struct perf_hpp *hpp, struct hist_entry *he)
|
||||
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 = hist_entry__next_pair(he);
|
||||
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";
|
||||
@@ -242,30 +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 hist_entry *pair = he->pair;
|
||||
struct hists *pair_hists = pair ? pair->hists : NULL;
|
||||
struct hists *hists = he->hists;
|
||||
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 ? pair_hists->stats.total_period : 0;
|
||||
if (old_total > 0 && pair)
|
||||
old_percent = 100.0 * pair->stat.period / old_total;
|
||||
if (he->diff.computed)
|
||||
diff = he->diff.period_ratio_delta;
|
||||
else
|
||||
diff = perf_diff__compute_delta(he);
|
||||
|
||||
new_total = hists->stats.total_period;
|
||||
if (new_total > 0)
|
||||
new_percent = 100.0 * he->stat.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.");
|
||||
@@ -279,7 +354,7 @@ static int hpp__width_displ(struct perf_hpp *hpp __maybe_unused)
|
||||
static int hpp__entry_displ(struct perf_hpp *hpp,
|
||||
struct hist_entry *he)
|
||||
{
|
||||
struct hist_entry *pair = he->pair;
|
||||
struct hist_entry *pair = hist_entry__next_pair(he);
|
||||
long displacement = pair ? pair->position - he->position : 0;
|
||||
const char *fmt = symbol_conf.field_sep ? "%s" : "%6.6s";
|
||||
char buf[32] = " ";
|
||||
@@ -290,6 +365,27 @@ static int hpp__entry_displ(struct perf_hpp *hpp,
|
||||
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, \
|
||||
@@ -310,8 +406,12 @@ 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
|
||||
|
@@ -1,32 +1,26 @@
|
||||
#include "../cache.h"
|
||||
#include "progress.h"
|
||||
#include "libslang.h"
|
||||
#include "ui.h"
|
||||
#include "browser.h"
|
||||
|
||||
static void nop_progress_update(u64 curr __maybe_unused,
|
||||
u64 total __maybe_unused,
|
||||
const char *title __maybe_unused)
|
||||
{
|
||||
}
|
||||
|
||||
static struct ui_progress default_progress_fns =
|
||||
{
|
||||
.update = nop_progress_update,
|
||||
};
|
||||
|
||||
struct ui_progress *progress_fns = &default_progress_fns;
|
||||
|
||||
void ui_progress__update(u64 curr, u64 total, const char *title)
|
||||
{
|
||||
int bar, y;
|
||||
/*
|
||||
* FIXME: We should have a per UI backend way of showing progress,
|
||||
* stdio will just show a percentage as NN%, etc.
|
||||
*/
|
||||
if (use_browser <= 0)
|
||||
return;
|
||||
|
||||
if (total == 0)
|
||||
return;
|
||||
|
||||
ui__refresh_dimensions(true);
|
||||
pthread_mutex_lock(&ui__lock);
|
||||
y = SLtt_Screen_Rows / 2 - 2;
|
||||
SLsmg_set_color(0);
|
||||
SLsmg_draw_box(y, 0, 3, SLtt_Screen_Cols);
|
||||
SLsmg_gotorc(y++, 1);
|
||||
SLsmg_write_string((char *)title);
|
||||
SLsmg_set_color(HE_COLORSET_SELECTED);
|
||||
bar = ((SLtt_Screen_Cols - 2) * curr) / total;
|
||||
SLsmg_fill_region(y, 1, 1, bar, ' ');
|
||||
SLsmg_refresh();
|
||||
pthread_mutex_unlock(&ui__lock);
|
||||
return progress_fns->update(curr, total, title);
|
||||
}
|
||||
|
||||
void ui_progress__finish(void)
|
||||
{
|
||||
if (progress_fns->finish)
|
||||
progress_fns->finish();
|
||||
}
|
||||
|
@@ -3,6 +3,16 @@
|
||||
|
||||
#include <../types.h>
|
||||
|
||||
struct ui_progress {
|
||||
void (*update)(u64, u64, const char *);
|
||||
void (*finish)(void);
|
||||
};
|
||||
|
||||
extern struct ui_progress *progress_fns;
|
||||
|
||||
void ui_progress__init(void);
|
||||
|
||||
void ui_progress__update(u64 curr, u64 total, const char *title);
|
||||
void ui_progress__finish(void);
|
||||
|
||||
#endif
|
||||
|
@@ -342,7 +342,7 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
|
||||
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),
|
||||
|
42
tools/perf/ui/tui/progress.c
Normal file
42
tools/perf/ui/tui/progress.c
Normal file
@@ -0,0 +1,42 @@
|
||||
#include "../cache.h"
|
||||
#include "../progress.h"
|
||||
#include "../libslang.h"
|
||||
#include "../ui.h"
|
||||
#include "../browser.h"
|
||||
|
||||
static void tui_progress__update(u64 curr, u64 total, const char *title)
|
||||
{
|
||||
int bar, y;
|
||||
/*
|
||||
* FIXME: We should have a per UI backend way of showing progress,
|
||||
* stdio will just show a percentage as NN%, etc.
|
||||
*/
|
||||
if (use_browser <= 0)
|
||||
return;
|
||||
|
||||
if (total == 0)
|
||||
return;
|
||||
|
||||
ui__refresh_dimensions(true);
|
||||
pthread_mutex_lock(&ui__lock);
|
||||
y = SLtt_Screen_Rows / 2 - 2;
|
||||
SLsmg_set_color(0);
|
||||
SLsmg_draw_box(y, 0, 3, SLtt_Screen_Cols);
|
||||
SLsmg_gotorc(y++, 1);
|
||||
SLsmg_write_string((char *)title);
|
||||
SLsmg_set_color(HE_COLORSET_SELECTED);
|
||||
bar = ((SLtt_Screen_Cols - 2) * curr) / total;
|
||||
SLsmg_fill_region(y, 1, 1, bar, ' ');
|
||||
SLsmg_refresh();
|
||||
pthread_mutex_unlock(&ui__lock);
|
||||
}
|
||||
|
||||
static struct ui_progress tui_progress_fns =
|
||||
{
|
||||
.update = tui_progress__update,
|
||||
};
|
||||
|
||||
void ui_progress__init(void)
|
||||
{
|
||||
progress_fns = &tui_progress_fns;
|
||||
}
|
@@ -118,6 +118,7 @@ int ui__init(void)
|
||||
newtSetSuspendCallback(newt_suspend, NULL);
|
||||
ui_helpline__init();
|
||||
ui_browser__init();
|
||||
ui_progress__init();
|
||||
|
||||
signal(SIGSEGV, ui__signal);
|
||||
signal(SIGFPE, ui__signal);
|
||||
|
@@ -3,9 +3,37 @@
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdbool.h>
|
||||
#include <linux/compiler.h>
|
||||
|
||||
extern pthread_mutex_t ui__lock;
|
||||
|
||||
extern int use_browser;
|
||||
|
||||
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
|
||||
|
||||
void ui__refresh_dimensions(bool force);
|
||||
|
||||
#endif /* _PERF_UI_H_ */
|
||||
|
@@ -9,18 +9,14 @@ GVF=${OUTPUT}PERF-VERSION-FILE
|
||||
LF='
|
||||
'
|
||||
|
||||
#
|
||||
# First check if there is a .git to get the version from git describe
|
||||
# otherwise try to get the version from the kernel makefile
|
||||
# otherwise try to get the version from the kernel Makefile
|
||||
#
|
||||
if test -d ../../.git -o -f ../../.git &&
|
||||
VN=$(git describe --match 'v[0-9].[0-9]*' --abbrev=4 HEAD 2>/dev/null) &&
|
||||
case "$VN" in
|
||||
*$LF*) (exit 1) ;;
|
||||
v[0-9]*)
|
||||
git update-index -q --refresh
|
||||
test -z "$(git diff-index --name-only HEAD --)" ||
|
||||
VN="$VN-dirty" ;;
|
||||
esac
|
||||
VN=$(git tag 2>/dev/null | tail -1 | grep -E "v[0-9].[0-9]*")
|
||||
then
|
||||
VN=$(echo $VN"-g"$(git log -1 --abbrev=4 --pretty=format:"%h" HEAD))
|
||||
VN=$(echo "$VN" | sed -e 's/-/./g');
|
||||
else
|
||||
VN=$(MAKEFLAGS= make -sC ../.. kernelversion)
|
||||
|
@@ -15,6 +15,7 @@
|
||||
#include "debug.h"
|
||||
#include "annotate.h"
|
||||
#include <pthread.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
const char *disassembler_style;
|
||||
const char *objdump_path;
|
||||
@@ -170,15 +171,15 @@ static int lock__parse(struct ins_operands *ops)
|
||||
if (disasm_line__parse(ops->raw, &name, &ops->locked.ops->raw) < 0)
|
||||
goto out_free_ops;
|
||||
|
||||
ops->locked.ins = ins__find(name);
|
||||
if (ops->locked.ins == NULL)
|
||||
goto out_free_ops;
|
||||
ops->locked.ins = ins__find(name);
|
||||
if (ops->locked.ins == NULL)
|
||||
goto out_free_ops;
|
||||
|
||||
if (!ops->locked.ins->ops)
|
||||
return 0;
|
||||
if (!ops->locked.ins->ops)
|
||||
return 0;
|
||||
|
||||
if (ops->locked.ins->ops->parse)
|
||||
ops->locked.ins->ops->parse(ops->locked.ops);
|
||||
if (ops->locked.ins->ops->parse)
|
||||
ops->locked.ins->ops->parse(ops->locked.ops);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -400,6 +401,8 @@ static struct ins instructions[] = {
|
||||
{ .name = "testb", .ops = &mov_ops, },
|
||||
{ .name = "testl", .ops = &mov_ops, },
|
||||
{ .name = "xadd", .ops = &mov_ops, },
|
||||
{ .name = "xbeginl", .ops = &jump_ops, },
|
||||
{ .name = "xbeginq", .ops = &jump_ops, },
|
||||
};
|
||||
|
||||
static int ins__cmp(const void *name, const void *insp)
|
||||
@@ -855,12 +858,41 @@ static void insert_source_line(struct rb_root *root, struct source_line *src_lin
|
||||
struct source_line *iter;
|
||||
struct rb_node **p = &root->rb_node;
|
||||
struct rb_node *parent = NULL;
|
||||
int ret;
|
||||
|
||||
while (*p != NULL) {
|
||||
parent = *p;
|
||||
iter = rb_entry(parent, struct source_line, node);
|
||||
|
||||
if (src_line->percent > iter->percent)
|
||||
ret = strcmp(iter->path, src_line->path);
|
||||
if (ret == 0) {
|
||||
iter->percent_sum += src_line->percent;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
p = &(*p)->rb_left;
|
||||
else
|
||||
p = &(*p)->rb_right;
|
||||
}
|
||||
|
||||
src_line->percent_sum = src_line->percent;
|
||||
|
||||
rb_link_node(&src_line->node, parent, p);
|
||||
rb_insert_color(&src_line->node, root);
|
||||
}
|
||||
|
||||
static void __resort_source_line(struct rb_root *root, struct source_line *src_line)
|
||||
{
|
||||
struct source_line *iter;
|
||||
struct rb_node **p = &root->rb_node;
|
||||
struct rb_node *parent = NULL;
|
||||
|
||||
while (*p != NULL) {
|
||||
parent = *p;
|
||||
iter = rb_entry(parent, struct source_line, node);
|
||||
|
||||
if (src_line->percent_sum > iter->percent_sum)
|
||||
p = &(*p)->rb_left;
|
||||
else
|
||||
p = &(*p)->rb_right;
|
||||
@@ -870,6 +902,24 @@ static void insert_source_line(struct rb_root *root, struct source_line *src_lin
|
||||
rb_insert_color(&src_line->node, root);
|
||||
}
|
||||
|
||||
static void resort_source_line(struct rb_root *dest_root, struct rb_root *src_root)
|
||||
{
|
||||
struct source_line *src_line;
|
||||
struct rb_node *node;
|
||||
|
||||
node = rb_first(src_root);
|
||||
while (node) {
|
||||
struct rb_node *next;
|
||||
|
||||
src_line = rb_entry(node, struct source_line, node);
|
||||
next = rb_next(node);
|
||||
rb_erase(node, src_root);
|
||||
|
||||
__resort_source_line(dest_root, src_line);
|
||||
node = next;
|
||||
}
|
||||
}
|
||||
|
||||
static void symbol__free_source_line(struct symbol *sym, int len)
|
||||
{
|
||||
struct annotation *notes = symbol__annotation(sym);
|
||||
@@ -894,6 +944,7 @@ static int symbol__get_source_line(struct symbol *sym, struct map *map,
|
||||
struct source_line *src_line;
|
||||
struct annotation *notes = symbol__annotation(sym);
|
||||
struct sym_hist *h = annotation__histogram(notes, evidx);
|
||||
struct rb_root tmp_root = RB_ROOT;
|
||||
|
||||
if (!h->sum)
|
||||
return 0;
|
||||
@@ -928,12 +979,13 @@ static int symbol__get_source_line(struct symbol *sym, struct map *map,
|
||||
goto next;
|
||||
|
||||
strcpy(src_line[i].path, path);
|
||||
insert_source_line(root, &src_line[i]);
|
||||
insert_source_line(&tmp_root, &src_line[i]);
|
||||
|
||||
next:
|
||||
pclose(fp);
|
||||
}
|
||||
|
||||
resort_source_line(root, &tmp_root);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -957,7 +1009,7 @@ static void print_summary(struct rb_root *root, const char *filename)
|
||||
char *path;
|
||||
|
||||
src_line = rb_entry(node, struct source_line, node);
|
||||
percent = src_line->percent;
|
||||
percent = src_line->percent_sum;
|
||||
color = get_percent_color(percent);
|
||||
path = src_line->path;
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user