Merge branch 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull perf changes from Ingo Molnar: "There are lots of improvements, the biggest changes are: Main kernel side changes: - Improve uprobes performance by adding 'pre-filtering' support, by Oleg Nesterov. - Make some POWER7 events available in sysfs, equivalent to what was done on x86, from Sukadev Bhattiprolu. - tracing updates by Steve Rostedt - mostly misc fixes and smaller improvements. - Use perf/event tracing to report PCI Express advanced errors, by Tony Luck. - Enable northbridge performance counters on AMD family 15h, by Jacob Shin. - This tracing commit: tracing: Remove the extra 4 bytes of padding in events changes the ABI. All involved parties (PowerTop in particular) seem to agree that it's safe to do now with the introduction of libtraceevent, but the devil is in the details ... Main tooling side changes: - Add 'event group view', from Namyung Kim: To use it, 'perf record' should group events when recording. And then perf report parses the saved group relation from file header and prints them together if --group option is provided. You can use the 'perf evlist' command to see event group information: $ perf record -e '{ref-cycles,cycles}' noploop 1 [ perf record: Woken up 2 times to write data ] [ perf record: Captured and wrote 0.385 MB perf.data (~16807 samples) ] $ perf evlist --group {ref-cycles,cycles} With this example, default perf report will show you each event separately. You can use --group option to enable event group view: $ perf report --group ... # group: {ref-cycles,cycles} # ======== # Samples: 7K of event 'anon group { ref-cycles, cycles }' # Event count (approx.): 6876107743 # # Overhead Command Shared Object Symbol # ................ ....... ................. .......................... 99.84% 99.76% noploop noploop [.] main 0.07% 0.00% noploop ld-2.15.so [.] strcmp 0.03% 0.00% noploop [kernel.kallsyms] [k] timerqueue_del 0.03% 0.03% noploop [kernel.kallsyms] [k] sched_clock_cpu 0.02% 0.00% noploop [kernel.kallsyms] [k] account_user_time 0.01% 0.00% noploop [kernel.kallsyms] [k] __alloc_pages_nodemask 0.00% 0.00% noploop [kernel.kallsyms] [k] native_write_msr_safe 0.00% 0.11% noploop [kernel.kallsyms] [k] _raw_spin_lock 0.00% 0.06% noploop [kernel.kallsyms] [k] find_get_page 0.00% 0.02% noploop [kernel.kallsyms] [k] rcu_check_callbacks 0.00% 0.02% noploop [kernel.kallsyms] [k] __current_kernel_time As you can see the Overhead column now contains both of ref-cycles and cycles and header line shows group information also - 'anon group { ref-cycles, cycles }'. The output is sorted by period of group leader first. - Initial GTK+ annotate browser, from Namhyung Kim. - Add option for runtime switching perf data file in perf report, just press 's' and a menu with the valid files found in the current directory will be presented, from Feng Tang. - Add support to display whole group data for raw columns, from Jiri Olsa. - Add per processor socket count aggregation in perf stat, from Stephane Eranian. - Add interval printing in 'perf stat', from Stephane Eranian. - 'perf test' improvements - Add support for wildcards in tracepoint system name, from Jiri Olsa. - Add anonymous huge page recognition, from Joshua Zhu. - perf build-id cache now can show DSOs present in a perf.data file that are not in the cache, to integrate with build-id servers being put in place by organizations such as Fedora. - perf top now shares more of the evsel config/creation routines with 'record', paving the way for further integration like 'top' snapshots, etc. - perf top now supports DWARF callchains. - Fix mmap limitations on 32-bit, fix from David Miller. - 'perf bench numa mem' NUMA performance measurement suite - ... and lots of fixes, performance improvements, cleanups and other improvements I failed to list - see the shortlog and git log for details." * 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (270 commits) perf/x86/amd: Enable northbridge performance counters on AMD family 15h perf/hwbp: Fix cleanup in case of kzalloc failure perf tools: Fix build with bison 2.3 and older. perf tools: Limit unwind support to x86 archs perf annotate: Make it to be able to skip unannotatable symbols perf gtk/annotate: Fail early if it can't annotate perf gtk/annotate: Show source lines with gray color perf gtk/annotate: Support multiple event annotation perf ui/gtk: Implement basic GTK2 annotation browser perf annotate: Fix warning message on a missing vmlinux perf buildid-cache: Add --update option uprobes/perf: Avoid uprobe_apply() whenever possible uprobes/perf: Teach trace_uprobe/perf code to use UPROBE_HANDLER_REMOVE uprobes/perf: Teach trace_uprobe/perf code to pre-filter uprobes/perf: Teach trace_uprobe/perf code to track the active perf_event's uprobes: Introduce uprobe_apply() perf: Introduce hw_perf_event->tp_target and ->tp_list uprobes/perf: Always increment trace_uprobe->nhit uprobes/tracing: Kill uprobe_trace_consumer, embed uprobe_consumer into trace_uprobe uprobes/tracing: Introduce is_trace_uprobe_enabled() ...
This commit is contained in:
@@ -15,7 +15,7 @@ help:
|
||||
@echo ' x86_energy_perf_policy - Intel energy policy tool'
|
||||
@echo ''
|
||||
@echo 'You can do:'
|
||||
@echo ' $$ make -C tools/<tool>_install'
|
||||
@echo ' $$ make -C tools/ <tool>_install'
|
||||
@echo ''
|
||||
@echo ' from the kernel command line to build and install one of'
|
||||
@echo ' the tools above'
|
||||
|
@@ -13,8 +13,7 @@
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* License along with this program; if not, see <http://www.gnu.org/licenses>
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
@@ -1224,6 +1223,34 @@ static int field_is_long(struct format_field *field)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int type_size(const char *name)
|
||||
{
|
||||
/* This covers all FIELD_IS_STRING types. */
|
||||
static struct {
|
||||
const char *type;
|
||||
unsigned int size;
|
||||
} table[] = {
|
||||
{ "u8", 1 },
|
||||
{ "u16", 2 },
|
||||
{ "u32", 4 },
|
||||
{ "u64", 8 },
|
||||
{ "s8", 1 },
|
||||
{ "s16", 2 },
|
||||
{ "s32", 4 },
|
||||
{ "s64", 8 },
|
||||
{ "char", 1 },
|
||||
{ },
|
||||
};
|
||||
int i;
|
||||
|
||||
for (i = 0; table[i].type; i++) {
|
||||
if (!strcmp(table[i].type, name))
|
||||
return table[i].size;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int event_read_fields(struct event_format *event, struct format_field **fields)
|
||||
{
|
||||
struct format_field *field = NULL;
|
||||
@@ -1233,6 +1260,8 @@ static int event_read_fields(struct event_format *event, struct format_field **f
|
||||
int count = 0;
|
||||
|
||||
do {
|
||||
unsigned int size_dynamic = 0;
|
||||
|
||||
type = read_token(&token);
|
||||
if (type == EVENT_NEWLINE) {
|
||||
free_token(token);
|
||||
@@ -1391,6 +1420,7 @@ static int event_read_fields(struct event_format *event, struct format_field **f
|
||||
field->type = new_type;
|
||||
strcat(field->type, " ");
|
||||
strcat(field->type, field->name);
|
||||
size_dynamic = type_size(field->name);
|
||||
free_token(field->name);
|
||||
strcat(field->type, brackets);
|
||||
field->name = token;
|
||||
@@ -1463,7 +1493,8 @@ static int event_read_fields(struct event_format *event, struct format_field **f
|
||||
if (read_expect_type(EVENT_ITEM, &token))
|
||||
goto fail;
|
||||
|
||||
/* add signed type */
|
||||
if (strtoul(token, NULL, 0))
|
||||
field->flags |= FIELD_IS_SIGNED;
|
||||
|
||||
free_token(token);
|
||||
if (read_expected(EVENT_OP, ";") < 0)
|
||||
@@ -1478,10 +1509,14 @@ static int event_read_fields(struct event_format *event, struct format_field **f
|
||||
if (field->flags & FIELD_IS_ARRAY) {
|
||||
if (field->arraylen)
|
||||
field->elementsize = field->size / field->arraylen;
|
||||
else if (field->flags & FIELD_IS_DYNAMIC)
|
||||
field->elementsize = size_dynamic;
|
||||
else if (field->flags & FIELD_IS_STRING)
|
||||
field->elementsize = 1;
|
||||
else
|
||||
field->elementsize = event->pevent->long_size;
|
||||
else if (field->flags & FIELD_IS_LONG)
|
||||
field->elementsize = event->pevent ?
|
||||
event->pevent->long_size :
|
||||
sizeof(long);
|
||||
} else
|
||||
field->elementsize = field->size;
|
||||
|
||||
@@ -1785,6 +1820,8 @@ process_op(struct event_format *event, struct print_arg *arg, char **tok)
|
||||
strcmp(token, "/") == 0 ||
|
||||
strcmp(token, "<") == 0 ||
|
||||
strcmp(token, ">") == 0 ||
|
||||
strcmp(token, "<=") == 0 ||
|
||||
strcmp(token, ">=") == 0 ||
|
||||
strcmp(token, "==") == 0 ||
|
||||
strcmp(token, "!=") == 0) {
|
||||
|
||||
@@ -2481,7 +2518,7 @@ process_dynamic_array(struct event_format *event, struct print_arg *arg, char **
|
||||
|
||||
free_token(token);
|
||||
arg = alloc_arg();
|
||||
if (!field) {
|
||||
if (!arg) {
|
||||
do_warning("%s: not enough memory!", __func__);
|
||||
*tok = NULL;
|
||||
return EVENT_ERROR;
|
||||
|
@@ -13,8 +13,7 @@
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* License along with this program; if not, see <http://www.gnu.org/licenses>
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*/
|
||||
|
@@ -13,8 +13,7 @@
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* License along with this program; if not, see <http://www.gnu.org/licenses>
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*/
|
||||
|
@@ -13,8 +13,7 @@
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* License along with this program; if not, see <http://www.gnu.org/licenses>
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*/
|
||||
|
@@ -1,3 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License (not later!)
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this program; if not, see <http://www.gnu.org/licenses>
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
@@ -13,8 +13,7 @@
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* License along with this program; if not, see <http://www.gnu.org/licenses>
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*/
|
||||
|
@@ -222,10 +222,14 @@ install-pdf: pdf
|
||||
#install-html: html
|
||||
# '$(SHELL_PATH_SQ)' ./install-webdoc.sh $(DESTDIR)$(htmldir)
|
||||
|
||||
ifneq ($(MAKECMDGOALS),clean)
|
||||
ifneq ($(MAKECMDGOALS),tags)
|
||||
$(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE
|
||||
$(QUIET_SUBDIR0)../ $(QUIET_SUBDIR1) $(OUTPUT)PERF-VERSION-FILE
|
||||
|
||||
-include $(OUTPUT)PERF-VERSION-FILE
|
||||
endif
|
||||
endif
|
||||
|
||||
#
|
||||
# Determine "include::" file references in asciidoc files.
|
||||
|
@@ -61,11 +61,13 @@ OPTIONS
|
||||
|
||||
--stdio:: Use the stdio interface.
|
||||
|
||||
--tui:: Use the TUI interface Use of --tui requires a tty, if one is not
|
||||
--tui:: Use the TUI interface. Use of --tui requires a tty, if one is not
|
||||
present, as when piping to other commands, the stdio interface is
|
||||
used. This interfaces starts by centering on the line with more
|
||||
samples, TAB/UNTAB cycles through the lines with more samples.
|
||||
|
||||
--gtk:: Use the GTK interface.
|
||||
|
||||
-C::
|
||||
--cpu:: Only report samples for the list of CPUs provided. Multiple CPUs can
|
||||
be provided as a comma-separated list with no space: 0,1. Ranges of
|
||||
@@ -88,6 +90,9 @@ OPTIONS
|
||||
--objdump=<path>::
|
||||
Path to objdump binary.
|
||||
|
||||
--skip-missing::
|
||||
Skip symbols that cannot be annotated.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-record[1], linkperf:perf-report[1]
|
||||
|
@@ -24,6 +24,13 @@ OPTIONS
|
||||
-r::
|
||||
--remove=::
|
||||
Remove specified file from the cache.
|
||||
-M::
|
||||
--missing=::
|
||||
List missing build ids in the cache for the specified file.
|
||||
-u::
|
||||
--update::
|
||||
Update specified file of the cache. It can be used to update kallsyms
|
||||
kernel dso to vmlinux in order to support annotation.
|
||||
-v::
|
||||
--verbose::
|
||||
Be more verbose.
|
||||
|
@@ -22,10 +22,6 @@ specified perf.data files.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
-M::
|
||||
--displacement::
|
||||
Show position displacement relative to baseline.
|
||||
|
||||
-D::
|
||||
--dump-raw-trace::
|
||||
Dump raw trace in ASCII.
|
||||
|
@@ -28,6 +28,10 @@ OPTIONS
|
||||
--verbose=::
|
||||
Show all fields.
|
||||
|
||||
-g::
|
||||
--group::
|
||||
Show event group information.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-record[1], linkperf:perf-list[1],
|
||||
|
@@ -57,11 +57,44 @@ OPTIONS
|
||||
|
||||
-s::
|
||||
--sort=::
|
||||
Sort by key(s): pid, comm, dso, symbol, parent, srcline.
|
||||
Sort histogram entries by given key(s) - multiple keys can be specified
|
||||
in CSV format. Following sort keys are available:
|
||||
pid, comm, dso, symbol, parent, cpu, srcline.
|
||||
|
||||
Each key has following meaning:
|
||||
|
||||
- comm: command (name) of the task which can be read via /proc/<pid>/comm
|
||||
- pid: command and tid of the task
|
||||
- dso: name of library or module executed at the time of sample
|
||||
- symbol: name of function executed at the time of sample
|
||||
- parent: name of function matched to the parent regex filter. Unmatched
|
||||
entries are displayed as "[other]".
|
||||
- cpu: cpu number the task ran at the time of sample
|
||||
- srcline: filename and line number executed at the time of sample. The
|
||||
DWARF debuggin info must be provided.
|
||||
|
||||
By default, comm, dso and symbol keys are used.
|
||||
(i.e. --sort comm,dso,symbol)
|
||||
|
||||
If --branch-stack option is used, following sort keys are also
|
||||
available:
|
||||
dso_from, dso_to, symbol_from, symbol_to, mispredict.
|
||||
|
||||
- dso_from: name of library or module branched from
|
||||
- dso_to: name of library or module branched to
|
||||
- symbol_from: name of function branched from
|
||||
- symbol_to: name of function branched to
|
||||
- mispredict: "N" for predicted branch, "Y" for mispredicted branch
|
||||
|
||||
And default sort keys are changed to comm, dso_from, symbol_from, dso_to
|
||||
and symbol_to, see '--branch-stack'.
|
||||
|
||||
-p::
|
||||
--parent=<regex>::
|
||||
regex filter to identify parent, see: '--sort parent'
|
||||
A regex filter to identify parent. The parent is a caller of this
|
||||
function and searched through the callchain, thus it requires callchain
|
||||
information recorded. The pattern is in the exteneded regex format and
|
||||
defaults to "\^sys_|^do_page_fault", see '--sort parent'.
|
||||
|
||||
-x::
|
||||
--exclude-other::
|
||||
@@ -74,7 +107,6 @@ OPTIONS
|
||||
|
||||
-t::
|
||||
--field-separator=::
|
||||
|
||||
Use a special separator character and don't pad with spaces, replacing
|
||||
all occurrences of this separator in symbol names (and other output)
|
||||
with a '.' character, that thus it's the only non valid separator.
|
||||
@@ -171,6 +203,9 @@ OPTIONS
|
||||
--objdump=<path>::
|
||||
Path to objdump binary.
|
||||
|
||||
--group::
|
||||
Show event group information together.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-stat[1], linkperf:perf-annotate[1]
|
||||
|
@@ -336,7 +336,6 @@ scripts listed by the 'perf script -l' command e.g.:
|
||||
----
|
||||
root@tropicana:~# perf script -l
|
||||
List of available trace scripts:
|
||||
workqueue-stats workqueue stats (ins/exe/create/destroy)
|
||||
wakeup-latency system-wide min/max/avg wakeup latency
|
||||
rw-by-file <comm> r/w activity for a program, by file
|
||||
rw-by-pid system-wide r/w activity
|
||||
@@ -402,7 +401,6 @@ should show a new entry for your script:
|
||||
----
|
||||
root@tropicana:~# perf script -l
|
||||
List of available trace scripts:
|
||||
workqueue-stats workqueue stats (ins/exe/create/destroy)
|
||||
wakeup-latency system-wide min/max/avg wakeup latency
|
||||
rw-by-file <comm> r/w activity for a program, by file
|
||||
rw-by-pid system-wide r/w activity
|
||||
|
@@ -114,6 +114,17 @@ with it. --append may be used here. Examples:
|
||||
|
||||
perf stat --repeat 10 --null --sync --pre 'make -s O=defconfig-build/clean' -- make -s -j64 O=defconfig-build/ bzImage
|
||||
|
||||
-I msecs::
|
||||
--interval-print msecs::
|
||||
Print count deltas every N milliseconds (minimum: 100ms)
|
||||
example: perf stat -I 1000 -e cycles -a sleep 5
|
||||
|
||||
--aggr-socket::
|
||||
Aggregate counts per processor socket for system-wide mode measurements. This
|
||||
is a useful mode to detect imbalance between sockets. To enable this mode,
|
||||
use --aggr-socket in addition to -a. (system-wide). The output includes the
|
||||
socket number and the number of online processors on that socket. This is
|
||||
useful to gauge the amount of aggregation.
|
||||
|
||||
EXAMPLES
|
||||
--------
|
||||
|
@@ -23,6 +23,10 @@ from 'perf test list'.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
-s::
|
||||
--skip::
|
||||
Tests to skip (comma separater numeric list).
|
||||
|
||||
-v::
|
||||
--verbose::
|
||||
Be more verbose.
|
||||
|
@@ -60,7 +60,7 @@ Default is to monitor all CPUS.
|
||||
|
||||
-i::
|
||||
--inherit::
|
||||
Child tasks inherit counters, only makes sens with -p option.
|
||||
Child tasks do not inherit counters.
|
||||
|
||||
-k <path>::
|
||||
--vmlinux=<path>::
|
||||
|
@@ -47,10 +47,11 @@ include config/utilities.mak
|
||||
# backtrace post unwind.
|
||||
#
|
||||
# Define NO_BACKTRACE if you do not want stack backtrace debug feature
|
||||
#
|
||||
# Define NO_LIBNUMA if you do not want numa perf benchmark
|
||||
|
||||
$(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE
|
||||
@$(SHELL_PATH) util/PERF-VERSION-GEN $(OUTPUT)
|
||||
-include $(OUTPUT)PERF-VERSION-FILE
|
||||
|
||||
uname_M := $(shell uname -m 2>/dev/null || echo not)
|
||||
|
||||
@@ -148,13 +149,25 @@ RM = rm -f
|
||||
MKDIR = mkdir
|
||||
FIND = find
|
||||
INSTALL = install
|
||||
FLEX = flex
|
||||
BISON= bison
|
||||
|
||||
# sparse is architecture-neutral, which means that we need to tell it
|
||||
# explicitly what architecture to check for. Fix this up for yours..
|
||||
SPARSE_FLAGS = -D__BIG_ENDIAN__ -D__powerpc__
|
||||
|
||||
ifneq ($(MAKECMDGOALS),clean)
|
||||
ifneq ($(MAKECMDGOALS),tags)
|
||||
-include config/feature-tests.mak
|
||||
|
||||
ifeq ($(call get-executable,$(FLEX)),)
|
||||
dummy := $(error Error: $(FLEX) is missing on this system, please install it)
|
||||
endif
|
||||
|
||||
ifeq ($(call get-executable,$(BISON)),)
|
||||
dummy := $(error Error: $(BISON) is missing on this system, please install it)
|
||||
endif
|
||||
|
||||
ifeq ($(call try-cc,$(SOURCE_HELLO),$(CFLAGS) -Werror -fstack-protector-all,-fstack-protector-all),y)
|
||||
CFLAGS := $(CFLAGS) -fstack-protector-all
|
||||
endif
|
||||
@@ -206,6 +219,8 @@ ifeq ($(call try-cc,$(SOURCE_BIONIC),$(CFLAGS),bionic),y)
|
||||
EXTLIBS := $(filter-out -lpthread,$(EXTLIBS))
|
||||
BASIC_CFLAGS += -I.
|
||||
endif
|
||||
endif # MAKECMDGOALS != tags
|
||||
endif # MAKECMDGOALS != clean
|
||||
|
||||
# Guard against environment variables
|
||||
BUILTIN_OBJS =
|
||||
@@ -230,11 +245,19 @@ endif
|
||||
LIBTRACEEVENT = $(TE_PATH)libtraceevent.a
|
||||
TE_LIB := -L$(TE_PATH) -ltraceevent
|
||||
|
||||
export LIBTRACEEVENT
|
||||
|
||||
# python extension build directories
|
||||
PYTHON_EXTBUILD := $(OUTPUT)python_ext_build/
|
||||
PYTHON_EXTBUILD_LIB := $(PYTHON_EXTBUILD)lib/
|
||||
PYTHON_EXTBUILD_TMP := $(PYTHON_EXTBUILD)tmp/
|
||||
export PYTHON_EXTBUILD_LIB PYTHON_EXTBUILD_TMP
|
||||
|
||||
python-clean := rm -rf $(PYTHON_EXTBUILD) $(OUTPUT)python/perf.so
|
||||
|
||||
PYTHON_EXT_SRCS := $(shell grep -v ^\# util/python-ext-sources)
|
||||
PYTHON_EXT_DEPS := util/python-ext-sources util/setup.py
|
||||
|
||||
export LIBTRACEEVENT
|
||||
|
||||
$(OUTPUT)python/perf.so: $(PYTHON_EXT_SRCS) $(PYTHON_EXT_DEPS)
|
||||
$(QUIET_GEN)CFLAGS='$(BASIC_CFLAGS)' $(PYTHON_WORD) util/setup.py \
|
||||
--quiet build_ext; \
|
||||
@@ -269,20 +292,17 @@ endif
|
||||
|
||||
export PERL_PATH
|
||||
|
||||
FLEX = flex
|
||||
BISON= bison
|
||||
|
||||
$(OUTPUT)util/parse-events-flex.c: util/parse-events.l $(OUTPUT)util/parse-events-bison.c
|
||||
$(QUIET_FLEX)$(FLEX) --header-file=$(OUTPUT)util/parse-events-flex.h $(PARSER_DEBUG_FLEX) -t util/parse-events.l > $(OUTPUT)util/parse-events-flex.c
|
||||
|
||||
$(OUTPUT)util/parse-events-bison.c: util/parse-events.y
|
||||
$(QUIET_BISON)$(BISON) -v util/parse-events.y -d $(PARSER_DEBUG_BISON) -o $(OUTPUT)util/parse-events-bison.c
|
||||
$(QUIET_BISON)$(BISON) -v util/parse-events.y -d $(PARSER_DEBUG_BISON) -o $(OUTPUT)util/parse-events-bison.c -p parse_events_
|
||||
|
||||
$(OUTPUT)util/pmu-flex.c: util/pmu.l $(OUTPUT)util/pmu-bison.c
|
||||
$(QUIET_FLEX)$(FLEX) --header-file=$(OUTPUT)util/pmu-flex.h -t util/pmu.l > $(OUTPUT)util/pmu-flex.c
|
||||
|
||||
$(OUTPUT)util/pmu-bison.c: util/pmu.y
|
||||
$(QUIET_BISON)$(BISON) -v util/pmu.y -d -o $(OUTPUT)util/pmu-bison.c
|
||||
$(QUIET_BISON)$(BISON) -v util/pmu.y -d -o $(OUTPUT)util/pmu-bison.c -p perf_pmu_
|
||||
|
||||
$(OUTPUT)util/parse-events.o: $(OUTPUT)util/parse-events-flex.c $(OUTPUT)util/parse-events-bison.c
|
||||
$(OUTPUT)util/pmu.o: $(OUTPUT)util/pmu-flex.c $(OUTPUT)util/pmu-bison.c
|
||||
@@ -378,8 +398,11 @@ LIB_H += util/rblist.h
|
||||
LIB_H += util/intlist.h
|
||||
LIB_H += util/perf_regs.h
|
||||
LIB_H += util/unwind.h
|
||||
LIB_H += ui/helpline.h
|
||||
LIB_H += util/vdso.h
|
||||
LIB_H += ui/helpline.h
|
||||
LIB_H += ui/progress.h
|
||||
LIB_H += ui/util.h
|
||||
LIB_H += ui/ui.h
|
||||
|
||||
LIB_OBJS += $(OUTPUT)util/abspath.o
|
||||
LIB_OBJS += $(OUTPUT)util/alias.o
|
||||
@@ -453,6 +476,7 @@ 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/util.o
|
||||
LIB_OBJS += $(OUTPUT)ui/hist.o
|
||||
LIB_OBJS += $(OUTPUT)ui/stdio/hist.o
|
||||
|
||||
@@ -471,7 +495,8 @@ 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
|
||||
LIB_OBJS += $(OUTPUT)tests/hists_link.o
|
||||
LIB_OBJS += $(OUTPUT)tests/python-use.o
|
||||
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-bench.o
|
||||
@@ -510,14 +535,13 @@ PERFLIBS = $(LIB_FILE) $(LIBTRACEEVENT)
|
||||
#
|
||||
# Platform specific tweaks
|
||||
#
|
||||
ifneq ($(MAKECMDGOALS),clean)
|
||||
ifneq ($(MAKECMDGOALS),tags)
|
||||
|
||||
# We choose to avoid "if .. else if .. else .. endif endif"
|
||||
# because maintaining the nesting to match is a pain. If
|
||||
# we had "elif" things would have been much nicer...
|
||||
|
||||
-include config.mak.autogen
|
||||
-include config.mak
|
||||
|
||||
ifdef NO_LIBELF
|
||||
NO_DWARF := 1
|
||||
NO_DEMANGLE := 1
|
||||
@@ -557,6 +581,11 @@ else
|
||||
endif # SOURCE_LIBELF
|
||||
endif # NO_LIBELF
|
||||
|
||||
# There's only x86 (both 32 and 64) support for CFI unwind so far
|
||||
ifneq ($(ARCH),x86)
|
||||
NO_LIBUNWIND := 1
|
||||
endif
|
||||
|
||||
ifndef NO_LIBUNWIND
|
||||
# for linking with debug library, run like:
|
||||
# make DEBUG=1 LIBUNWIND_DIR=/opt/libunwind/
|
||||
@@ -646,7 +675,6 @@ ifndef NO_NEWT
|
||||
LIB_OBJS += $(OUTPUT)ui/browsers/hists.o
|
||||
LIB_OBJS += $(OUTPUT)ui/browsers/map.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
|
||||
@@ -655,9 +683,6 @@ ifndef NO_NEWT
|
||||
LIB_H += ui/browsers/map.h
|
||||
LIB_H += ui/keysyms.h
|
||||
LIB_H += ui/libslang.h
|
||||
LIB_H += ui/progress.h
|
||||
LIB_H += ui/util.h
|
||||
LIB_H += ui/ui.h
|
||||
endif
|
||||
endif
|
||||
|
||||
@@ -673,14 +698,12 @@ ifndef NO_GTK2
|
||||
BASIC_CFLAGS += $(shell pkg-config --cflags gtk+-2.0 2>/dev/null)
|
||||
EXTLIBS += $(shell pkg-config --libs gtk+-2.0 2>/dev/null)
|
||||
LIB_OBJS += $(OUTPUT)ui/gtk/browser.o
|
||||
LIB_OBJS += $(OUTPUT)ui/gtk/hists.o
|
||||
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/util.o
|
||||
endif
|
||||
LIB_OBJS += $(OUTPUT)ui/gtk/annotate.o
|
||||
endif
|
||||
endif
|
||||
|
||||
@@ -707,7 +730,7 @@ disable-python = $(eval $(disable-python_code))
|
||||
define disable-python_code
|
||||
BASIC_CFLAGS += -DNO_LIBPYTHON
|
||||
$(if $(1),$(warning No $(1) was found))
|
||||
$(warning Python support won't be built)
|
||||
$(warning Python support will not be built)
|
||||
endef
|
||||
|
||||
override PYTHON := \
|
||||
@@ -715,19 +738,10 @@ override PYTHON := \
|
||||
|
||||
ifndef PYTHON
|
||||
$(call disable-python,python interpreter)
|
||||
python-clean :=
|
||||
else
|
||||
|
||||
PYTHON_WORD := $(call shell-wordify,$(PYTHON))
|
||||
|
||||
# python extension build directories
|
||||
PYTHON_EXTBUILD := $(OUTPUT)python_ext_build/
|
||||
PYTHON_EXTBUILD_LIB := $(PYTHON_EXTBUILD)lib/
|
||||
PYTHON_EXTBUILD_TMP := $(PYTHON_EXTBUILD)tmp/
|
||||
export PYTHON_EXTBUILD_LIB PYTHON_EXTBUILD_TMP
|
||||
|
||||
python-clean := rm -rf $(PYTHON_EXTBUILD) $(OUTPUT)python/perf.so
|
||||
|
||||
ifdef NO_LIBPYTHON
|
||||
$(call disable-python)
|
||||
else
|
||||
@@ -839,10 +853,24 @@ ifndef NO_BACKTRACE
|
||||
endif
|
||||
endif
|
||||
|
||||
ifndef NO_LIBNUMA
|
||||
FLAGS_LIBNUMA = $(ALL_CFLAGS) $(ALL_LDFLAGS) -lnuma
|
||||
ifneq ($(call try-cc,$(SOURCE_LIBNUMA),$(FLAGS_LIBNUMA),libnuma),y)
|
||||
msg := $(warning No numa.h found, disables 'perf bench numa mem' benchmark, please install numa-libs-devel or libnuma-dev);
|
||||
else
|
||||
BASIC_CFLAGS += -DLIBNUMA_SUPPORT
|
||||
BUILTIN_OBJS += $(OUTPUT)bench/numa.o
|
||||
EXTLIBS += -lnuma
|
||||
endif
|
||||
endif
|
||||
|
||||
ifdef ASCIIDOC8
|
||||
export ASCIIDOC8
|
||||
endif
|
||||
|
||||
endif # MAKECMDGOALS != tags
|
||||
endif # MAKECMDGOALS != clean
|
||||
|
||||
# Shell quote (do not use $(call) to accommodate ancient setups);
|
||||
|
||||
ETC_PERFCONFIG_SQ = $(subst ','\'',$(ETC_PERFCONFIG))
|
||||
@@ -884,7 +912,7 @@ strip: $(PROGRAMS) $(OUTPUT)perf
|
||||
$(STRIP) $(STRIP_OPTS) $(PROGRAMS) $(OUTPUT)perf
|
||||
|
||||
$(OUTPUT)perf.o: perf.c $(OUTPUT)common-cmds.h $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -DPERF_VERSION='"$(PERF_VERSION)"' \
|
||||
$(QUIET_CC)$(CC) -include $(OUTPUT)PERF-VERSION-FILE \
|
||||
'-DPERF_HTML_PATH="$(htmldir_SQ)"' \
|
||||
$(ALL_CFLAGS) -c $(filter %.c,$^) -o $@
|
||||
|
||||
@@ -948,7 +976,13 @@ $(OUTPUT)util/exec_cmd.o: util/exec_cmd.c $(OUTPUT)PERF-CFLAGS
|
||||
|
||||
$(OUTPUT)tests/attr.o: tests/attr.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \
|
||||
'-DBINDIR="$(bindir_SQ)"' \
|
||||
'-DBINDIR="$(bindir_SQ)"' -DPYTHON='"$(PYTHON_WORD)"' \
|
||||
$<
|
||||
|
||||
$(OUTPUT)tests/python-use.o: tests/python-use.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \
|
||||
-DPYTHONPATH='"$(OUTPUT)python"' \
|
||||
-DPYTHON='"$(PYTHON_WORD)"' \
|
||||
$<
|
||||
|
||||
$(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS
|
||||
@@ -1099,7 +1133,7 @@ perfexec_instdir = $(prefix)/$(perfexecdir)
|
||||
endif
|
||||
perfexec_instdir_SQ = $(subst ','\'',$(perfexec_instdir))
|
||||
|
||||
install: all try-install-man
|
||||
install-bin: all
|
||||
$(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'
|
||||
@@ -1120,6 +1154,8 @@ install: all try-install-man
|
||||
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/attr'
|
||||
$(INSTALL) tests/attr/* '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/attr'
|
||||
|
||||
install: install-bin try-install-man
|
||||
|
||||
install-python_ext:
|
||||
$(PYTHON_WORD) util/setup.py --quiet install --root='/$(DESTDIR_SQ)'
|
||||
|
||||
|
@@ -155,6 +155,7 @@ static int perf_session_env__lookup_binutils_path(struct perf_session_env *env,
|
||||
if (lookup_path(buf))
|
||||
goto out;
|
||||
free(buf);
|
||||
buf = NULL;
|
||||
}
|
||||
|
||||
if (!strcmp(arch, "arm"))
|
||||
|
@@ -1,6 +1,7 @@
|
||||
#ifndef BENCH_H
|
||||
#define BENCH_H
|
||||
|
||||
extern int bench_numa(int argc, const char **argv, const char *prefix);
|
||||
extern int bench_sched_messaging(int argc, const char **argv, const char *prefix);
|
||||
extern int bench_sched_pipe(int argc, const char **argv, const char *prefix);
|
||||
extern int bench_mem_memcpy(int argc, const char **argv,
|
||||
|
1731
tools/perf/bench/numa.c
Normal file
1731
tools/perf/bench/numa.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -34,9 +34,10 @@
|
||||
|
||||
struct perf_annotate {
|
||||
struct perf_tool tool;
|
||||
bool force, use_tui, use_stdio;
|
||||
bool force, use_tui, use_stdio, use_gtk;
|
||||
bool full_paths;
|
||||
bool print_line;
|
||||
bool skip_missing;
|
||||
const char *sym_hist_filter;
|
||||
const char *cpu_list;
|
||||
DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
|
||||
@@ -138,9 +139,22 @@ find_next:
|
||||
continue;
|
||||
}
|
||||
|
||||
if (use_browser > 0) {
|
||||
if (use_browser == 2) {
|
||||
int ret;
|
||||
|
||||
ret = hist_entry__gtk_annotate(he, evidx, NULL);
|
||||
if (!ret || !ann->skip_missing)
|
||||
return;
|
||||
|
||||
/* skip missing symbols */
|
||||
nd = rb_next(nd);
|
||||
} else if (use_browser == 1) {
|
||||
key = hist_entry__tui_annotate(he, evidx, NULL);
|
||||
switch (key) {
|
||||
case -1:
|
||||
if (!ann->skip_missing)
|
||||
return;
|
||||
/* fall through */
|
||||
case K_RIGHT:
|
||||
next = rb_next(nd);
|
||||
break;
|
||||
@@ -224,6 +238,10 @@ static int __cmd_annotate(struct perf_annotate *ann)
|
||||
ui__error("The %s file has no samples!\n", session->filename);
|
||||
goto out_delete;
|
||||
}
|
||||
|
||||
if (use_browser == 2)
|
||||
perf_gtk__show_annotations();
|
||||
|
||||
out_delete:
|
||||
/*
|
||||
* Speed up the exit process, for large files this can
|
||||
@@ -270,6 +288,7 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
"be more verbose (show symbol address, etc)"),
|
||||
OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
|
||||
"dump raw trace in ASCII"),
|
||||
OPT_BOOLEAN(0, "gtk", &annotate.use_gtk, "Use the GTK interface"),
|
||||
OPT_BOOLEAN(0, "tui", &annotate.use_tui, "Use the TUI interface"),
|
||||
OPT_BOOLEAN(0, "stdio", &annotate.use_stdio, "Use the stdio interface"),
|
||||
OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
|
||||
@@ -280,6 +299,8 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
"print matching source lines (may be slow)"),
|
||||
OPT_BOOLEAN('P', "full-paths", &annotate.full_paths,
|
||||
"Don't shorten the displayed pathnames"),
|
||||
OPT_BOOLEAN(0, "skip-missing", &annotate.skip_missing,
|
||||
"Skip symbols that cannot be annotated"),
|
||||
OPT_STRING('C', "cpu", &annotate.cpu_list, "cpu", "list of cpus to profile"),
|
||||
OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
|
||||
"Look for files with symbols relative to this directory"),
|
||||
@@ -300,6 +321,8 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
use_browser = 0;
|
||||
else if (annotate.use_tui)
|
||||
use_browser = 1;
|
||||
else if (annotate.use_gtk)
|
||||
use_browser = 2;
|
||||
|
||||
setup_browser(true);
|
||||
|
||||
@@ -309,7 +332,8 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
if (symbol__init() < 0)
|
||||
return -1;
|
||||
|
||||
setup_sorting(annotate_usage, options);
|
||||
if (setup_sorting() < 0)
|
||||
usage_with_options(annotate_usage, options);
|
||||
|
||||
if (argc) {
|
||||
/*
|
||||
|
@@ -35,6 +35,18 @@ struct bench_suite {
|
||||
/* sentinel: easy for help */
|
||||
#define suite_all { "all", "Test all benchmark suites", NULL }
|
||||
|
||||
#ifdef LIBNUMA_SUPPORT
|
||||
static struct bench_suite numa_suites[] = {
|
||||
{ "mem",
|
||||
"Benchmark for NUMA workloads",
|
||||
bench_numa },
|
||||
suite_all,
|
||||
{ NULL,
|
||||
NULL,
|
||||
NULL }
|
||||
};
|
||||
#endif
|
||||
|
||||
static struct bench_suite sched_suites[] = {
|
||||
{ "messaging",
|
||||
"Benchmark for scheduler and IPC mechanisms",
|
||||
@@ -68,6 +80,11 @@ struct bench_subsys {
|
||||
};
|
||||
|
||||
static struct bench_subsys subsystems[] = {
|
||||
#ifdef LIBNUMA_SUPPORT
|
||||
{ "numa",
|
||||
"NUMA scheduling and MM behavior",
|
||||
numa_suites },
|
||||
#endif
|
||||
{ "sched",
|
||||
"scheduler and IPC mechanism",
|
||||
sched_suites },
|
||||
@@ -159,6 +176,7 @@ static void all_suite(struct bench_subsys *subsys) /* FROM HERE */
|
||||
printf("# Running %s/%s benchmark...\n",
|
||||
subsys->name,
|
||||
suites[i].name);
|
||||
fflush(stdout);
|
||||
|
||||
argv[1] = suites[i].name;
|
||||
suites[i].fn(1, argv, NULL);
|
||||
@@ -225,6 +243,7 @@ int cmd_bench(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
printf("# Running %s/%s benchmark...\n",
|
||||
subsystems[i].name,
|
||||
subsystems[i].suites[j].name);
|
||||
fflush(stdout);
|
||||
status = subsystems[i].suites[j].fn(argc - 1,
|
||||
argv + 1, prefix);
|
||||
goto end;
|
||||
|
@@ -14,6 +14,7 @@
|
||||
#include "util/parse-options.h"
|
||||
#include "util/strlist.h"
|
||||
#include "util/build-id.h"
|
||||
#include "util/session.h"
|
||||
#include "util/symbol.h"
|
||||
|
||||
static int build_id_cache__add_file(const char *filename, const char *debugdir)
|
||||
@@ -58,19 +59,89 @@ static int build_id_cache__remove_file(const char *filename,
|
||||
return err;
|
||||
}
|
||||
|
||||
static bool dso__missing_buildid_cache(struct dso *dso, int parm __maybe_unused)
|
||||
{
|
||||
char filename[PATH_MAX];
|
||||
u8 build_id[BUILD_ID_SIZE];
|
||||
|
||||
if (dso__build_id_filename(dso, filename, sizeof(filename)) &&
|
||||
filename__read_build_id(filename, build_id,
|
||||
sizeof(build_id)) != sizeof(build_id)) {
|
||||
if (errno == ENOENT)
|
||||
return false;
|
||||
|
||||
pr_warning("Problems with %s file, consider removing it from the cache\n",
|
||||
filename);
|
||||
} else if (memcmp(dso->build_id, build_id, sizeof(dso->build_id))) {
|
||||
pr_warning("Problems with %s file, consider removing it from the cache\n",
|
||||
filename);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int build_id_cache__fprintf_missing(const char *filename, bool force, FILE *fp)
|
||||
{
|
||||
struct perf_session *session = perf_session__new(filename, O_RDONLY,
|
||||
force, false, NULL);
|
||||
if (session == NULL)
|
||||
return -1;
|
||||
|
||||
perf_session__fprintf_dsos_buildid(session, fp, dso__missing_buildid_cache, 0);
|
||||
perf_session__delete(session);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int build_id_cache__update_file(const char *filename,
|
||||
const char *debugdir)
|
||||
{
|
||||
u8 build_id[BUILD_ID_SIZE];
|
||||
char sbuild_id[BUILD_ID_SIZE * 2 + 1];
|
||||
|
||||
int err;
|
||||
|
||||
if (filename__read_build_id(filename, &build_id, sizeof(build_id)) < 0) {
|
||||
pr_debug("Couldn't read a build-id in %s\n", filename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
build_id__sprintf(build_id, sizeof(build_id), sbuild_id);
|
||||
err = build_id_cache__remove_s(sbuild_id, debugdir);
|
||||
if (!err) {
|
||||
err = build_id_cache__add_s(sbuild_id, debugdir, filename,
|
||||
false, false);
|
||||
}
|
||||
if (verbose)
|
||||
pr_info("Updating %s %s: %s\n", sbuild_id, filename,
|
||||
err ? "FAIL" : "Ok");
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int cmd_buildid_cache(int argc, const char **argv,
|
||||
const char *prefix __maybe_unused)
|
||||
{
|
||||
struct strlist *list;
|
||||
struct str_node *pos;
|
||||
int ret = 0;
|
||||
bool force = false;
|
||||
char debugdir[PATH_MAX];
|
||||
char const *add_name_list_str = NULL,
|
||||
*remove_name_list_str = NULL;
|
||||
*remove_name_list_str = NULL,
|
||||
*missing_filename = NULL,
|
||||
*update_name_list_str = NULL;
|
||||
|
||||
const struct option buildid_cache_options[] = {
|
||||
OPT_STRING('a', "add", &add_name_list_str,
|
||||
"file list", "file(s) to add"),
|
||||
OPT_STRING('r', "remove", &remove_name_list_str, "file list",
|
||||
"file(s) to remove"),
|
||||
OPT_STRING('M', "missing", &missing_filename, "file",
|
||||
"to find missing build ids in the cache"),
|
||||
OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
|
||||
OPT_STRING('u', "update", &update_name_list_str, "file list",
|
||||
"file(s) to update"),
|
||||
OPT_INCR('v', "verbose", &verbose, "be more verbose"),
|
||||
OPT_END()
|
||||
};
|
||||
@@ -125,5 +196,26 @@ int cmd_buildid_cache(int argc, const char **argv,
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
if (missing_filename)
|
||||
ret = build_id_cache__fprintf_missing(missing_filename, force, stdout);
|
||||
|
||||
if (update_name_list_str) {
|
||||
list = strlist__new(true, update_name_list_str);
|
||||
if (list) {
|
||||
strlist__for_each(pos, list)
|
||||
if (build_id_cache__update_file(pos->s, debugdir)) {
|
||||
if (errno == ENOENT) {
|
||||
pr_debug("%s wasn't in the cache\n",
|
||||
pos->s);
|
||||
continue;
|
||||
}
|
||||
pr_warning("Couldn't update %s: %s\n",
|
||||
pos->s, strerror(errno));
|
||||
}
|
||||
|
||||
strlist__delete(list);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@@ -44,23 +44,26 @@ static int filename__fprintf_build_id(const char *name, FILE *fp)
|
||||
return fprintf(fp, "%s\n", sbuild_id);
|
||||
}
|
||||
|
||||
static bool dso__skip_buildid(struct dso *dso, int with_hits)
|
||||
{
|
||||
return with_hits && !dso->hit;
|
||||
}
|
||||
|
||||
static int perf_session__list_build_ids(bool force, bool with_hits)
|
||||
{
|
||||
struct perf_session *session;
|
||||
|
||||
symbol__elf_init();
|
||||
/*
|
||||
* See if this is an ELF file first:
|
||||
*/
|
||||
if (filename__fprintf_build_id(input_name, stdout))
|
||||
goto out;
|
||||
|
||||
session = perf_session__new(input_name, O_RDONLY, force, false,
|
||||
&build_id__mark_dso_hit_ops);
|
||||
if (session == NULL)
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* See if this is an ELF file first:
|
||||
*/
|
||||
if (filename__fprintf_build_id(session->filename, stdout))
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* in pipe-mode, the only way to get the buildids is to parse
|
||||
* the record stream. Buildids are stored as RECORD_HEADER_BUILD_ID
|
||||
@@ -68,9 +71,9 @@ static int perf_session__list_build_ids(bool force, bool with_hits)
|
||||
if (with_hits || session->fd_pipe)
|
||||
perf_session__process_events(session, &build_id__mark_dso_hit_ops);
|
||||
|
||||
perf_session__fprintf_dsos_buildid(session, stdout, with_hits);
|
||||
out:
|
||||
perf_session__fprintf_dsos_buildid(session, stdout, dso__skip_buildid, with_hits);
|
||||
perf_session__delete(session);
|
||||
out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@@ -23,7 +23,6 @@ static char const *input_old = "perf.data.old",
|
||||
*input_new = "perf.data";
|
||||
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;
|
||||
@@ -146,58 +145,47 @@ static int setup_compute(const struct option *opt, const char *str,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static double get_period_percent(struct hist_entry *he, u64 period)
|
||||
double perf_diff__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)
|
||||
double perf_diff__compute_delta(struct hist_entry *he, struct hist_entry *pair)
|
||||
{
|
||||
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;
|
||||
double new_percent = perf_diff__period_percent(he, he->stat.period);
|
||||
double old_percent = perf_diff__period_percent(pair, pair->stat.period);
|
||||
|
||||
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)
|
||||
double perf_diff__compute_ratio(struct hist_entry *he, struct hist_entry *pair)
|
||||
{
|
||||
struct hist_entry *pair = hist_entry__next_pair(he);
|
||||
double new_period = he->stat.period;
|
||||
double old_period = pair ? pair->stat.period : 0;
|
||||
double old_period = pair->stat.period;
|
||||
|
||||
he->diff.computed = true;
|
||||
he->diff.period_ratio = pair ? (new_period / old_period) : 0;
|
||||
he->diff.period_ratio = new_period / old_period;
|
||||
return he->diff.period_ratio;
|
||||
}
|
||||
|
||||
s64 perf_diff__compute_wdiff(struct hist_entry *he)
|
||||
s64 perf_diff__compute_wdiff(struct hist_entry *he, struct hist_entry *pair)
|
||||
{
|
||||
struct hist_entry *pair = hist_entry__next_pair(he);
|
||||
u64 new_period = he->stat.period;
|
||||
u64 old_period = pair ? pair->stat.period : 0;
|
||||
u64 old_period = pair->stat.period;
|
||||
|
||||
he->diff.computed = true;
|
||||
|
||||
if (!pair)
|
||||
he->diff.wdiff = 0;
|
||||
else
|
||||
he->diff.wdiff = new_period * compute_wdiff_w2 -
|
||||
old_period * compute_wdiff_w1;
|
||||
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)
|
||||
static int formula_delta(struct hist_entry *he, struct hist_entry *pair,
|
||||
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 ")",
|
||||
@@ -205,41 +193,36 @@ static int formula_delta(struct hist_entry *he, char *buf, size_t size)
|
||||
pair->stat.period, pair->hists->stats.total_period);
|
||||
}
|
||||
|
||||
static int formula_ratio(struct hist_entry *he, char *buf, size_t size)
|
||||
static int formula_ratio(struct hist_entry *he, struct hist_entry *pair,
|
||||
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;
|
||||
double old_period = pair->stat.period;
|
||||
|
||||
return scnprintf(buf, size, "%.0F / %.0F", new_period, old_period);
|
||||
}
|
||||
|
||||
static int formula_wdiff(struct hist_entry *he, char *buf, size_t size)
|
||||
static int formula_wdiff(struct hist_entry *he, struct hist_entry *pair,
|
||||
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;
|
||||
u64 old_period = pair->stat.period;
|
||||
|
||||
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)
|
||||
int perf_diff__formula(struct hist_entry *he, struct hist_entry *pair,
|
||||
char *buf, size_t size)
|
||||
{
|
||||
switch (compute) {
|
||||
case COMPUTE_DELTA:
|
||||
return formula_delta(he, buf, size);
|
||||
return formula_delta(he, pair, buf, size);
|
||||
case COMPUTE_RATIO:
|
||||
return formula_ratio(he, buf, size);
|
||||
return formula_ratio(he, pair, buf, size);
|
||||
case COMPUTE_WEIGHTED_DIFF:
|
||||
return formula_wdiff(he, buf, size);
|
||||
return formula_wdiff(he, pair, buf, size);
|
||||
default:
|
||||
BUG_ON(1);
|
||||
}
|
||||
@@ -292,48 +275,6 @@ static struct perf_tool tool = {
|
||||
.ordering_requires_timestamps = true,
|
||||
};
|
||||
|
||||
static void insert_hist_entry_by_name(struct rb_root *root,
|
||||
struct hist_entry *he)
|
||||
{
|
||||
struct rb_node **p = &root->rb_node;
|
||||
struct rb_node *parent = NULL;
|
||||
struct hist_entry *iter;
|
||||
|
||||
while (*p != NULL) {
|
||||
parent = *p;
|
||||
iter = rb_entry(parent, struct hist_entry, rb_node);
|
||||
if (hist_entry__cmp(he, iter) < 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__name_resort(struct hists *self, bool sort)
|
||||
{
|
||||
unsigned long position = 1;
|
||||
struct rb_root tmp = RB_ROOT;
|
||||
struct rb_node *next = rb_first(&self->entries);
|
||||
|
||||
while (next != NULL) {
|
||||
struct hist_entry *n = rb_entry(next, struct hist_entry, rb_node);
|
||||
|
||||
next = rb_next(&n->rb_node);
|
||||
n->position = position++;
|
||||
|
||||
if (sort) {
|
||||
rb_erase(&n->rb_node, &self->entries);
|
||||
insert_hist_entry_by_name(&tmp, n);
|
||||
}
|
||||
}
|
||||
|
||||
if (sort)
|
||||
self->entries = tmp;
|
||||
}
|
||||
|
||||
static struct perf_evsel *evsel_match(struct perf_evsel *evsel,
|
||||
struct perf_evlist *evlist)
|
||||
{
|
||||
@@ -346,34 +287,34 @@ static struct perf_evsel *evsel_match(struct perf_evsel *evsel,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void perf_evlist__resort_hists(struct perf_evlist *evlist, bool name)
|
||||
static void perf_evlist__collapse_resort(struct perf_evlist *evlist)
|
||||
{
|
||||
struct perf_evsel *evsel;
|
||||
|
||||
list_for_each_entry(evsel, &evlist->entries, node) {
|
||||
struct hists *hists = &evsel->hists;
|
||||
|
||||
hists__output_resort(hists);
|
||||
|
||||
/*
|
||||
* The hists__name_resort only sets possition
|
||||
* if name is false.
|
||||
*/
|
||||
if (name || ((!name) && show_displacement))
|
||||
hists__name_resort(hists, name);
|
||||
hists__collapse_resort(hists);
|
||||
}
|
||||
}
|
||||
|
||||
static void hists__baseline_only(struct hists *hists)
|
||||
{
|
||||
struct rb_node *next = rb_first(&hists->entries);
|
||||
struct rb_root *root;
|
||||
struct rb_node *next;
|
||||
|
||||
if (sort__need_collapse)
|
||||
root = &hists->entries_collapsed;
|
||||
else
|
||||
root = hists->entries_in;
|
||||
|
||||
next = rb_first(root);
|
||||
while (next != NULL) {
|
||||
struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node);
|
||||
struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node_in);
|
||||
|
||||
next = rb_next(&he->rb_node);
|
||||
next = rb_next(&he->rb_node_in);
|
||||
if (!hist_entry__next_pair(he)) {
|
||||
rb_erase(&he->rb_node, &hists->entries);
|
||||
rb_erase(&he->rb_node_in, root);
|
||||
hist_entry__free(he);
|
||||
}
|
||||
}
|
||||
@@ -385,18 +326,21 @@ static void hists__precompute(struct hists *hists)
|
||||
|
||||
while (next != NULL) {
|
||||
struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node);
|
||||
struct hist_entry *pair = hist_entry__next_pair(he);
|
||||
|
||||
next = rb_next(&he->rb_node);
|
||||
if (!pair)
|
||||
continue;
|
||||
|
||||
switch (compute) {
|
||||
case COMPUTE_DELTA:
|
||||
perf_diff__compute_delta(he);
|
||||
perf_diff__compute_delta(he, pair);
|
||||
break;
|
||||
case COMPUTE_RATIO:
|
||||
perf_diff__compute_ratio(he);
|
||||
perf_diff__compute_ratio(he, pair);
|
||||
break;
|
||||
case COMPUTE_WEIGHTED_DIFF:
|
||||
perf_diff__compute_wdiff(he);
|
||||
perf_diff__compute_wdiff(he, pair);
|
||||
break;
|
||||
default:
|
||||
BUG_ON(1);
|
||||
@@ -470,19 +414,30 @@ static void insert_hist_entry_by_compute(struct rb_root *root,
|
||||
|
||||
static void hists__compute_resort(struct hists *hists)
|
||||
{
|
||||
struct rb_root tmp = RB_ROOT;
|
||||
struct rb_node *next = rb_first(&hists->entries);
|
||||
struct rb_root *root;
|
||||
struct rb_node *next;
|
||||
|
||||
if (sort__need_collapse)
|
||||
root = &hists->entries_collapsed;
|
||||
else
|
||||
root = hists->entries_in;
|
||||
|
||||
hists->entries = RB_ROOT;
|
||||
next = rb_first(root);
|
||||
|
||||
hists->nr_entries = 0;
|
||||
hists->stats.total_period = 0;
|
||||
hists__reset_col_len(hists);
|
||||
|
||||
while (next != NULL) {
|
||||
struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node);
|
||||
struct hist_entry *he;
|
||||
|
||||
next = rb_next(&he->rb_node);
|
||||
he = rb_entry(next, struct hist_entry, rb_node_in);
|
||||
next = rb_next(&he->rb_node_in);
|
||||
|
||||
rb_erase(&he->rb_node, &hists->entries);
|
||||
insert_hist_entry_by_compute(&tmp, he, compute);
|
||||
insert_hist_entry_by_compute(&hists->entries, he, compute);
|
||||
hists__inc_nr_entries(hists, he);
|
||||
}
|
||||
|
||||
hists->entries = tmp;
|
||||
}
|
||||
|
||||
static void hists__process(struct hists *old, struct hists *new)
|
||||
@@ -497,6 +452,8 @@ static void hists__process(struct hists *old, struct hists *new)
|
||||
if (sort_compute) {
|
||||
hists__precompute(new);
|
||||
hists__compute_resort(new);
|
||||
} else {
|
||||
hists__output_resort(new);
|
||||
}
|
||||
|
||||
hists__fprintf(new, true, 0, 0, stdout);
|
||||
@@ -528,8 +485,8 @@ static int __cmd_diff(void)
|
||||
evlist_old = older->evlist;
|
||||
evlist_new = newer->evlist;
|
||||
|
||||
perf_evlist__resort_hists(evlist_old, true);
|
||||
perf_evlist__resort_hists(evlist_new, false);
|
||||
perf_evlist__collapse_resort(evlist_old);
|
||||
perf_evlist__collapse_resort(evlist_new);
|
||||
|
||||
list_for_each_entry(evsel, &evlist_new->entries, node) {
|
||||
struct perf_evsel *evsel_old;
|
||||
@@ -562,8 +519,6 @@ static const char * const diff_usage[] = {
|
||||
static const struct option options[] = {
|
||||
OPT_INCR('v', "verbose", &verbose,
|
||||
"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,
|
||||
@@ -597,40 +552,32 @@ static const struct option options[] = {
|
||||
|
||||
static void ui_init(void)
|
||||
{
|
||||
perf_hpp__init();
|
||||
|
||||
/* No overhead column. */
|
||||
perf_hpp__column_enable(PERF_HPP__OVERHEAD, false);
|
||||
|
||||
/*
|
||||
* Display baseline/delta/ratio/displacement/
|
||||
* Display baseline/delta/ratio
|
||||
* formula/periods columns.
|
||||
*/
|
||||
perf_hpp__column_enable(PERF_HPP__BASELINE, true);
|
||||
perf_hpp__column_enable(PERF_HPP__BASELINE);
|
||||
|
||||
switch (compute) {
|
||||
case COMPUTE_DELTA:
|
||||
perf_hpp__column_enable(PERF_HPP__DELTA, true);
|
||||
perf_hpp__column_enable(PERF_HPP__DELTA);
|
||||
break;
|
||||
case COMPUTE_RATIO:
|
||||
perf_hpp__column_enable(PERF_HPP__RATIO, true);
|
||||
perf_hpp__column_enable(PERF_HPP__RATIO);
|
||||
break;
|
||||
case COMPUTE_WEIGHTED_DIFF:
|
||||
perf_hpp__column_enable(PERF_HPP__WEIGHTED_DIFF, true);
|
||||
perf_hpp__column_enable(PERF_HPP__WEIGHTED_DIFF);
|
||||
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);
|
||||
perf_hpp__column_enable(PERF_HPP__FORMULA);
|
||||
|
||||
if (show_period) {
|
||||
perf_hpp__column_enable(PERF_HPP__PERIOD, true);
|
||||
perf_hpp__column_enable(PERF_HPP__PERIOD_BASELINE, true);
|
||||
perf_hpp__column_enable(PERF_HPP__PERIOD);
|
||||
perf_hpp__column_enable(PERF_HPP__PERIOD_BASELINE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -658,7 +605,9 @@ int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
|
||||
ui_init();
|
||||
|
||||
setup_sorting(diff_usage, options);
|
||||
if (setup_sorting() < 0)
|
||||
usage_with_options(diff_usage, options);
|
||||
|
||||
setup_pager();
|
||||
|
||||
sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso", NULL);
|
||||
|
@@ -15,39 +15,6 @@
|
||||
#include "util/parse-options.h"
|
||||
#include "util/session.h"
|
||||
|
||||
struct perf_attr_details {
|
||||
bool freq;
|
||||
bool verbose;
|
||||
};
|
||||
|
||||
static int comma_printf(bool *first, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
int ret = 0;
|
||||
|
||||
if (!*first) {
|
||||
ret += printf(",");
|
||||
} else {
|
||||
ret += printf(":");
|
||||
*first = false;
|
||||
}
|
||||
|
||||
va_start(args, fmt);
|
||||
ret += vprintf(fmt, args);
|
||||
va_end(args);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __if_print(bool *first, const char *field, u64 value)
|
||||
{
|
||||
if (value == 0)
|
||||
return 0;
|
||||
|
||||
return comma_printf(first, " %s: %" PRIu64, field, value);
|
||||
}
|
||||
|
||||
#define if_print(field) __if_print(&first, #field, pos->attr.field)
|
||||
|
||||
static int __cmd_evlist(const char *file_name, struct perf_attr_details *details)
|
||||
{
|
||||
struct perf_session *session;
|
||||
@@ -57,52 +24,8 @@ static int __cmd_evlist(const char *file_name, struct perf_attr_details *details
|
||||
if (session == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
list_for_each_entry(pos, &session->evlist->entries, node) {
|
||||
bool first = true;
|
||||
|
||||
printf("%s", perf_evsel__name(pos));
|
||||
|
||||
if (details->verbose || details->freq) {
|
||||
comma_printf(&first, " sample_freq=%" PRIu64,
|
||||
(u64)pos->attr.sample_freq);
|
||||
}
|
||||
|
||||
if (details->verbose) {
|
||||
if_print(type);
|
||||
if_print(config);
|
||||
if_print(config1);
|
||||
if_print(config2);
|
||||
if_print(size);
|
||||
if_print(sample_type);
|
||||
if_print(read_format);
|
||||
if_print(disabled);
|
||||
if_print(inherit);
|
||||
if_print(pinned);
|
||||
if_print(exclusive);
|
||||
if_print(exclude_user);
|
||||
if_print(exclude_kernel);
|
||||
if_print(exclude_hv);
|
||||
if_print(exclude_idle);
|
||||
if_print(mmap);
|
||||
if_print(comm);
|
||||
if_print(freq);
|
||||
if_print(inherit_stat);
|
||||
if_print(enable_on_exec);
|
||||
if_print(task);
|
||||
if_print(watermark);
|
||||
if_print(precise_ip);
|
||||
if_print(mmap_data);
|
||||
if_print(sample_id_all);
|
||||
if_print(exclude_host);
|
||||
if_print(exclude_guest);
|
||||
if_print(__reserved_1);
|
||||
if_print(wakeup_events);
|
||||
if_print(bp_type);
|
||||
if_print(branch_sample_type);
|
||||
}
|
||||
|
||||
putchar('\n');
|
||||
}
|
||||
list_for_each_entry(pos, &session->evlist->entries, node)
|
||||
perf_evsel__fprintf(pos, details, stdout);
|
||||
|
||||
perf_session__delete(session);
|
||||
return 0;
|
||||
@@ -116,6 +39,8 @@ int cmd_evlist(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
OPT_BOOLEAN('F', "freq", &details.freq, "Show the sample frequency"),
|
||||
OPT_BOOLEAN('v', "verbose", &details.verbose,
|
||||
"Show all event attr details"),
|
||||
OPT_BOOLEAN('g', "group", &details.event_group,
|
||||
"Show event group information"),
|
||||
OPT_END()
|
||||
};
|
||||
const char * const evlist_usage[] = {
|
||||
@@ -127,5 +52,10 @@ int cmd_evlist(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
if (argc)
|
||||
usage_with_options(evlist_usage, options);
|
||||
|
||||
if (details.event_group && (details.verbose || details.freq)) {
|
||||
pr_err("--group option is not compatible with other options\n");
|
||||
usage_with_options(evlist_usage, options);
|
||||
}
|
||||
|
||||
return __cmd_evlist(input_name, &details);
|
||||
}
|
||||
|
@@ -17,6 +17,7 @@
|
||||
#include "util/debug.h"
|
||||
|
||||
#include <linux/rbtree.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
struct alloc_stat;
|
||||
typedef int (*sort_fn_t)(struct alloc_stat *, struct alloc_stat *);
|
||||
@@ -340,7 +341,7 @@ static void __print_result(struct rb_root *root, struct perf_session *session,
|
||||
int n_lines, int is_caller)
|
||||
{
|
||||
struct rb_node *next;
|
||||
struct machine *machine;
|
||||
struct machine *machine = &session->machines.host;
|
||||
|
||||
printf("%.102s\n", graph_dotted_line);
|
||||
printf(" %-34s |", is_caller ? "Callsite": "Alloc Ptr");
|
||||
@@ -349,11 +350,6 @@ static void __print_result(struct rb_root *root, struct perf_session *session,
|
||||
|
||||
next = rb_first(root);
|
||||
|
||||
machine = perf_session__find_host_machine(session);
|
||||
if (!machine) {
|
||||
pr_err("__print_result: couldn't find kernel information\n");
|
||||
return;
|
||||
}
|
||||
while (next && n_lines--) {
|
||||
struct alloc_stat *data = rb_entry(next, struct alloc_stat,
|
||||
node);
|
||||
@@ -614,8 +610,7 @@ static struct sort_dimension *avail_sorts[] = {
|
||||
&pingpong_sort_dimension,
|
||||
};
|
||||
|
||||
#define NUM_AVAIL_SORTS \
|
||||
(int)(sizeof(avail_sorts) / sizeof(struct sort_dimension *))
|
||||
#define NUM_AVAIL_SORTS ((int)ARRAY_SIZE(avail_sorts))
|
||||
|
||||
static int sort_dimension__add(const char *tok, struct list_head *list)
|
||||
{
|
||||
@@ -624,12 +619,11 @@ static int sort_dimension__add(const char *tok, struct list_head *list)
|
||||
|
||||
for (i = 0; i < NUM_AVAIL_SORTS; i++) {
|
||||
if (!strcmp(avail_sorts[i]->name, tok)) {
|
||||
sort = malloc(sizeof(*sort));
|
||||
sort = memdup(avail_sorts[i], sizeof(*avail_sorts[i]));
|
||||
if (!sort) {
|
||||
pr_err("%s: malloc failed\n", __func__);
|
||||
pr_err("%s: memdup failed\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
memcpy(sort, avail_sorts[i], sizeof(*sort));
|
||||
list_add_tail(&sort->list, list);
|
||||
return 0;
|
||||
}
|
||||
|
@@ -973,8 +973,7 @@ __cmd_buildid_list(const char *file_name, int argc, const char **argv)
|
||||
|
||||
int cmd_kvm(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
{
|
||||
const char *file_name;
|
||||
|
||||
const char *file_name = NULL;
|
||||
const struct option kvm_options[] = {
|
||||
OPT_STRING('i', "input", &file_name, "file",
|
||||
"Input file name"),
|
||||
|
@@ -224,130 +224,28 @@ static bool perf_evlist__equal(struct perf_evlist *evlist,
|
||||
|
||||
static int perf_record__open(struct perf_record *rec)
|
||||
{
|
||||
char msg[512];
|
||||
struct perf_evsel *pos;
|
||||
struct perf_evlist *evlist = rec->evlist;
|
||||
struct perf_session *session = rec->session;
|
||||
struct perf_record_opts *opts = &rec->opts;
|
||||
int rc = 0;
|
||||
|
||||
/*
|
||||
* 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);
|
||||
perf_evlist__config(evlist, opts);
|
||||
|
||||
list_for_each_entry(pos, &evlist->entries, node) {
|
||||
struct perf_event_attr *attr = &pos->attr;
|
||||
/*
|
||||
* Check if parse_single_tracepoint_event has already asked for
|
||||
* PERF_SAMPLE_TIME.
|
||||
*
|
||||
* XXX this is kludgy but short term fix for problems introduced by
|
||||
* eac23d1c that broke 'perf script' by having different sample_types
|
||||
* when using multiple tracepoint events when we use a perf binary
|
||||
* that tries to use sample_id_all on an older kernel.
|
||||
*
|
||||
* We need to move counter creation to perf_session, support
|
||||
* different sample_types, etc.
|
||||
*/
|
||||
bool time_needed = attr->sample_type & PERF_SAMPLE_TIME;
|
||||
|
||||
fallback_missing_features:
|
||||
if (opts->exclude_guest_missing)
|
||||
attr->exclude_guest = attr->exclude_host = 0;
|
||||
retry_sample_id:
|
||||
attr->sample_id_all = opts->sample_id_all_missing ? 0 : 1;
|
||||
try_again:
|
||||
if (perf_evsel__open(pos, evlist->cpus, evlist->threads) < 0) {
|
||||
int err = errno;
|
||||
|
||||
if (err == EPERM || err == EACCES) {
|
||||
ui__error_paranoid();
|
||||
rc = -err;
|
||||
goto out;
|
||||
} else if (err == ENODEV && opts->target.cpu_list) {
|
||||
pr_err("No such device - did you specify"
|
||||
" an out-of-range profile CPU?\n");
|
||||
rc = -err;
|
||||
goto out;
|
||||
} else if (err == EINVAL) {
|
||||
if (!opts->exclude_guest_missing &&
|
||||
(attr->exclude_guest || attr->exclude_host)) {
|
||||
pr_debug("Old kernel, cannot exclude "
|
||||
"guest or host samples.\n");
|
||||
opts->exclude_guest_missing = true;
|
||||
goto fallback_missing_features;
|
||||
} else if (!opts->sample_id_all_missing) {
|
||||
/*
|
||||
* Old kernel, no attr->sample_id_type_all field
|
||||
*/
|
||||
opts->sample_id_all_missing = true;
|
||||
if (!opts->sample_time && !opts->raw_samples && !time_needed)
|
||||
attr->sample_type &= ~PERF_SAMPLE_TIME;
|
||||
|
||||
goto retry_sample_id;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If it's cycles then fall back to hrtimer
|
||||
* based cpu-clock-tick sw counter, which
|
||||
* is always available even if no PMU support.
|
||||
*
|
||||
* PPC returns ENXIO until 2.6.37 (behavior changed
|
||||
* with commit b0a873e).
|
||||
*/
|
||||
if ((err == ENOENT || err == ENXIO)
|
||||
&& attr->type == PERF_TYPE_HARDWARE
|
||||
&& attr->config == PERF_COUNT_HW_CPU_CYCLES) {
|
||||
|
||||
if (perf_evsel__fallback(pos, errno, msg, sizeof(msg))) {
|
||||
if (verbose)
|
||||
ui__warning("The cycles event is not supported, "
|
||||
"trying to fall back to cpu-clock-ticks\n");
|
||||
attr->type = PERF_TYPE_SOFTWARE;
|
||||
attr->config = PERF_COUNT_SW_CPU_CLOCK;
|
||||
if (pos->name) {
|
||||
free(pos->name);
|
||||
pos->name = NULL;
|
||||
}
|
||||
ui__warning("%s\n", msg);
|
||||
goto try_again;
|
||||
}
|
||||
|
||||
if (err == ENOENT) {
|
||||
ui__error("The %s event is not supported.\n",
|
||||
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");
|
||||
error("sys_perf_event_open() syscall returned with %d "
|
||||
"(%s) for event %s. /bin/dmesg may provide "
|
||||
"additional information.\n",
|
||||
err, strerror(err), perf_evsel__name(pos));
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
if (attr->type == PERF_TYPE_HARDWARE &&
|
||||
err == EOPNOTSUPP) {
|
||||
pr_err("No hardware sampling interrupt available."
|
||||
" No APIC? If so then you can boot the kernel"
|
||||
" with the \"lapic\" boot parameter to"
|
||||
" force-enable it.\n");
|
||||
rc = -err;
|
||||
goto out;
|
||||
}
|
||||
#endif
|
||||
|
||||
pr_err("No CONFIG_PERF_EVENTS=y kernel support configured?\n");
|
||||
rc = -err;
|
||||
rc = -errno;
|
||||
perf_evsel__open_strerror(pos, &opts->target,
|
||||
errno, msg, sizeof(msg));
|
||||
ui__error("%s\n", msg);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
@@ -430,10 +328,6 @@ static void perf_event__synthesize_guest_os(struct machine *machine, void *data)
|
||||
{
|
||||
int err;
|
||||
struct perf_tool *tool = data;
|
||||
|
||||
if (machine__is_host(machine))
|
||||
return;
|
||||
|
||||
/*
|
||||
*As for guest kernel when processing subcommand record&report,
|
||||
*we arrange module mmap prior to guest kernel mmap and trigger
|
||||
@@ -592,6 +486,9 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
|
||||
goto out_delete_session;
|
||||
}
|
||||
|
||||
if (!evsel_list->nr_groups)
|
||||
perf_header__clear_feat(&session->header, HEADER_GROUP_DESC);
|
||||
|
||||
/*
|
||||
* perf_session__delete(session) will be called at perf_record__exit()
|
||||
*/
|
||||
@@ -618,12 +515,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
|
||||
|
||||
rec->post_processing_offset = lseek(output, 0, SEEK_CUR);
|
||||
|
||||
machine = perf_session__find_host_machine(session);
|
||||
if (!machine) {
|
||||
pr_err("Couldn't find native kernel information.\n");
|
||||
err = -1;
|
||||
goto out_delete_session;
|
||||
}
|
||||
machine = &session->machines.host;
|
||||
|
||||
if (opts->pipe_output) {
|
||||
err = perf_event__synthesize_attrs(tool, session,
|
||||
@@ -676,9 +568,10 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
|
||||
"Symbol resolution may be skewed if relocation was used (e.g. kexec).\n"
|
||||
"Check /proc/modules permission or run as root.\n");
|
||||
|
||||
if (perf_guest)
|
||||
perf_session__process_machines(session, tool,
|
||||
perf_event__synthesize_guest_os);
|
||||
if (perf_guest) {
|
||||
machines__process_guests(&session->machines,
|
||||
perf_event__synthesize_guest_os, tool);
|
||||
}
|
||||
|
||||
if (!opts->target.system_wide)
|
||||
err = perf_event__synthesize_thread_map(tool, evsel_list->threads,
|
||||
@@ -875,11 +768,10 @@ static int get_stack_size(char *str, unsigned long *_size)
|
||||
}
|
||||
#endif /* LIBUNWIND_SUPPORT */
|
||||
|
||||
static int
|
||||
parse_callchain_opt(const struct option *opt __maybe_unused, const char *arg,
|
||||
int unset)
|
||||
int record_parse_callchain_opt(const struct option *opt,
|
||||
const char *arg, int unset)
|
||||
{
|
||||
struct perf_record *rec = (struct perf_record *)opt->value;
|
||||
struct perf_record_opts *opts = opt->value;
|
||||
char *tok, *name, *saveptr = NULL;
|
||||
char *buf;
|
||||
int ret = -1;
|
||||
@@ -905,7 +797,7 @@ parse_callchain_opt(const struct option *opt __maybe_unused, const char *arg,
|
||||
/* Framepointer style */
|
||||
if (!strncmp(name, "fp", sizeof("fp"))) {
|
||||
if (!strtok_r(NULL, ",", &saveptr)) {
|
||||
rec->opts.call_graph = CALLCHAIN_FP;
|
||||
opts->call_graph = CALLCHAIN_FP;
|
||||
ret = 0;
|
||||
} else
|
||||
pr_err("callchain: No more arguments "
|
||||
@@ -918,20 +810,20 @@ parse_callchain_opt(const struct option *opt __maybe_unused, const char *arg,
|
||||
const unsigned long default_stack_dump_size = 8192;
|
||||
|
||||
ret = 0;
|
||||
rec->opts.call_graph = CALLCHAIN_DWARF;
|
||||
rec->opts.stack_dump_size = default_stack_dump_size;
|
||||
opts->call_graph = CALLCHAIN_DWARF;
|
||||
opts->stack_dump_size = default_stack_dump_size;
|
||||
|
||||
tok = strtok_r(NULL, ",", &saveptr);
|
||||
if (tok) {
|
||||
unsigned long size = 0;
|
||||
|
||||
ret = get_stack_size(tok, &size);
|
||||
rec->opts.stack_dump_size = size;
|
||||
opts->stack_dump_size = size;
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
pr_debug("callchain: stack dump size %d\n",
|
||||
rec->opts.stack_dump_size);
|
||||
opts->stack_dump_size);
|
||||
#endif /* LIBUNWIND_SUPPORT */
|
||||
} else {
|
||||
pr_err("callchain: Unknown -g option "
|
||||
@@ -944,7 +836,7 @@ parse_callchain_opt(const struct option *opt __maybe_unused, const char *arg,
|
||||
free(buf);
|
||||
|
||||
if (!ret)
|
||||
pr_debug("callchain: type %d\n", rec->opts.call_graph);
|
||||
pr_debug("callchain: type %d\n", opts->call_graph);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -982,9 +874,9 @@ static struct perf_record record = {
|
||||
#define CALLCHAIN_HELP "do call-graph (stack chain/backtrace) recording: "
|
||||
|
||||
#ifdef LIBUNWIND_SUPPORT
|
||||
static const char callchain_help[] = CALLCHAIN_HELP "[fp] dwarf";
|
||||
const char record_callchain_help[] = CALLCHAIN_HELP "[fp] dwarf";
|
||||
#else
|
||||
static const char callchain_help[] = CALLCHAIN_HELP "[fp]";
|
||||
const char record_callchain_help[] = CALLCHAIN_HELP "[fp]";
|
||||
#endif
|
||||
|
||||
/*
|
||||
@@ -1028,9 +920,9 @@ const struct option record_options[] = {
|
||||
"number of mmap data pages"),
|
||||
OPT_BOOLEAN(0, "group", &record.opts.group,
|
||||
"put the counters into a counter group"),
|
||||
OPT_CALLBACK_DEFAULT('g', "call-graph", &record, "mode[,dump_size]",
|
||||
callchain_help, &parse_callchain_opt,
|
||||
"fp"),
|
||||
OPT_CALLBACK_DEFAULT('g', "call-graph", &record.opts,
|
||||
"mode[,dump_size]", record_callchain_help,
|
||||
&record_parse_callchain_opt, "fp"),
|
||||
OPT_INCR('v', "verbose", &verbose,
|
||||
"be more verbose (show counter open errors, etc)"),
|
||||
OPT_BOOLEAN('q', "quiet", &quiet, "don't print any message"),
|
||||
|
@@ -8,6 +8,7 @@
|
||||
#include "builtin.h"
|
||||
|
||||
#include "util/util.h"
|
||||
#include "util/cache.h"
|
||||
|
||||
#include "util/annotate.h"
|
||||
#include "util/color.h"
|
||||
@@ -54,6 +55,16 @@ struct perf_report {
|
||||
DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
|
||||
};
|
||||
|
||||
static int perf_report_config(const char *var, const char *value, void *cb)
|
||||
{
|
||||
if (!strcmp(var, "report.group")) {
|
||||
symbol_conf.event_group = perf_config_bool(var, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return perf_default_config(var, value, cb);
|
||||
}
|
||||
|
||||
static int perf_report__add_branch_hist_entry(struct perf_tool *tool,
|
||||
struct addr_location *al,
|
||||
struct perf_sample *sample,
|
||||
@@ -299,6 +310,21 @@ static size_t hists__fprintf_nr_sample_events(struct hists *self,
|
||||
char unit;
|
||||
unsigned long nr_samples = self->stats.nr_events[PERF_RECORD_SAMPLE];
|
||||
u64 nr_events = self->stats.total_period;
|
||||
struct perf_evsel *evsel = hists_to_evsel(self);
|
||||
char buf[512];
|
||||
size_t size = sizeof(buf);
|
||||
|
||||
if (symbol_conf.event_group && evsel->nr_members > 1) {
|
||||
struct perf_evsel *pos;
|
||||
|
||||
perf_evsel__group_desc(evsel, buf, size);
|
||||
evname = buf;
|
||||
|
||||
for_each_group_member(pos, evsel) {
|
||||
nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
|
||||
nr_events += pos->hists.stats.total_period;
|
||||
}
|
||||
}
|
||||
|
||||
nr_samples = convert_unit(nr_samples, &unit);
|
||||
ret = fprintf(fp, "# Samples: %lu%c", nr_samples, unit);
|
||||
@@ -319,6 +345,10 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist,
|
||||
struct hists *hists = &pos->hists;
|
||||
const char *evname = perf_evsel__name(pos);
|
||||
|
||||
if (symbol_conf.event_group &&
|
||||
!perf_evsel__is_group_leader(pos))
|
||||
continue;
|
||||
|
||||
hists__fprintf_nr_sample_events(hists, evname, stdout);
|
||||
hists__fprintf(hists, true, 0, 0, stdout);
|
||||
fprintf(stdout, "\n\n");
|
||||
@@ -372,7 +402,7 @@ static int __cmd_report(struct perf_report *rep)
|
||||
if (ret)
|
||||
goto out_delete;
|
||||
|
||||
kernel_map = session->host_machine.vmlinux_maps[MAP__FUNCTION];
|
||||
kernel_map = session->machines.host.vmlinux_maps[MAP__FUNCTION];
|
||||
kernel_kmap = map__kmap(kernel_map);
|
||||
if (kernel_map == NULL ||
|
||||
(kernel_map->dso->hit &&
|
||||
@@ -416,8 +446,16 @@ static int __cmd_report(struct perf_report *rep)
|
||||
hists->symbol_filter_str = rep->symbol_filter_str;
|
||||
|
||||
hists__collapse_resort(hists);
|
||||
hists__output_resort(hists);
|
||||
nr_samples += hists->stats.nr_events[PERF_RECORD_SAMPLE];
|
||||
|
||||
/* Non-group events are considered as leader */
|
||||
if (symbol_conf.event_group &&
|
||||
!perf_evsel__is_group_leader(pos)) {
|
||||
struct hists *leader_hists = &pos->leader->hists;
|
||||
|
||||
hists__match(leader_hists, hists);
|
||||
hists__link(leader_hists, hists);
|
||||
}
|
||||
}
|
||||
|
||||
if (nr_samples == 0) {
|
||||
@@ -425,11 +463,22 @@ static int __cmd_report(struct perf_report *rep)
|
||||
goto out_delete;
|
||||
}
|
||||
|
||||
list_for_each_entry(pos, &session->evlist->entries, node)
|
||||
hists__output_resort(&pos->hists);
|
||||
|
||||
if (use_browser > 0) {
|
||||
if (use_browser == 1) {
|
||||
perf_evlist__tui_browse_hists(session->evlist, help,
|
||||
NULL,
|
||||
&session->header.env);
|
||||
ret = perf_evlist__tui_browse_hists(session->evlist,
|
||||
help,
|
||||
NULL,
|
||||
&session->header.env);
|
||||
/*
|
||||
* Usually "ret" is the last pressed key, and we only
|
||||
* care if the key notifies us to switch data file.
|
||||
*/
|
||||
if (ret != K_SWITCH_INPUT_DATA)
|
||||
ret = 0;
|
||||
|
||||
} else if (use_browser == 2) {
|
||||
perf_evlist__gtk_browse_hists(session->evlist, help,
|
||||
NULL);
|
||||
@@ -595,8 +644,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
OPT_BOOLEAN(0, "stdio", &report.use_stdio,
|
||||
"Use the stdio interface"),
|
||||
OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
|
||||
"sort by key(s): pid, comm, dso, symbol, parent, dso_to,"
|
||||
" dso_from, symbol_to, symbol_from, mispredict"),
|
||||
"sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline,"
|
||||
" dso_to, dso_from, symbol_to, symbol_from, mispredict"),
|
||||
OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization,
|
||||
"Show sample percentage for different cpu modes"),
|
||||
OPT_STRING('p', "parent", &parent_pattern, "regex",
|
||||
@@ -638,6 +687,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
"Specify disassembler style (e.g. -M intel for intel syntax)"),
|
||||
OPT_BOOLEAN(0, "show-total-period", &symbol_conf.show_total_period,
|
||||
"Show a column with the sum of periods"),
|
||||
OPT_BOOLEAN(0, "group", &symbol_conf.event_group,
|
||||
"Show event group information together"),
|
||||
OPT_CALLBACK_NOOPT('b', "branch-stack", &sort__branch_mode, "",
|
||||
"use branch records for histogram filling", parse_branch_mode),
|
||||
OPT_STRING(0, "objdump", &objdump_path, "path",
|
||||
@@ -645,6 +696,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
perf_config(perf_report_config, NULL);
|
||||
|
||||
argc = parse_options(argc, argv, options, report_usage, 0);
|
||||
|
||||
if (report.use_stdio)
|
||||
@@ -663,6 +716,16 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
else
|
||||
input_name = "perf.data";
|
||||
}
|
||||
|
||||
if (strcmp(input_name, "-") != 0)
|
||||
setup_browser(true);
|
||||
else {
|
||||
use_browser = 0;
|
||||
perf_hpp__column_enable(PERF_HPP__OVERHEAD);
|
||||
perf_hpp__init();
|
||||
}
|
||||
|
||||
repeat:
|
||||
session = perf_session__new(input_name, O_RDONLY,
|
||||
report.force, false, &report.tool);
|
||||
if (session == NULL)
|
||||
@@ -688,14 +751,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
|
||||
}
|
||||
|
||||
if (strcmp(input_name, "-") != 0)
|
||||
setup_browser(true);
|
||||
else {
|
||||
use_browser = 0;
|
||||
perf_hpp__init();
|
||||
}
|
||||
|
||||
setup_sorting(report_usage, options);
|
||||
if (setup_sorting() < 0)
|
||||
usage_with_options(report_usage, options);
|
||||
|
||||
/*
|
||||
* Only in the newt browser we are doing integrated annotation,
|
||||
@@ -763,6 +820,12 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
}
|
||||
|
||||
ret = __cmd_report(&report);
|
||||
if (ret == K_SWITCH_INPUT_DATA) {
|
||||
perf_session__delete(session);
|
||||
goto repeat;
|
||||
} else
|
||||
ret = 0;
|
||||
|
||||
error:
|
||||
perf_session__delete(session);
|
||||
return ret;
|
||||
|
@@ -1475,9 +1475,9 @@ static int perf_sched__read_events(struct perf_sched *sched, bool destroy,
|
||||
goto out_delete;
|
||||
}
|
||||
|
||||
sched->nr_events = session->hists.stats.nr_events[0];
|
||||
sched->nr_lost_events = session->hists.stats.total_lost;
|
||||
sched->nr_lost_chunks = session->hists.stats.nr_events[PERF_RECORD_LOST];
|
||||
sched->nr_events = session->stats.nr_events[0];
|
||||
sched->nr_lost_events = session->stats.total_lost;
|
||||
sched->nr_lost_chunks = session->stats.nr_events[PERF_RECORD_LOST];
|
||||
}
|
||||
|
||||
if (destroy)
|
||||
|
@@ -692,7 +692,7 @@ static int parse_output_fields(const struct option *opt __maybe_unused,
|
||||
const char *arg, int unset __maybe_unused)
|
||||
{
|
||||
char *tok;
|
||||
int i, imax = sizeof(all_output_options) / sizeof(struct output_option);
|
||||
int i, imax = ARRAY_SIZE(all_output_options);
|
||||
int j;
|
||||
int rc = 0;
|
||||
char *str = strdup(arg);
|
||||
@@ -909,18 +909,6 @@ static const char *ends_with(const char *str, const char *suffix)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char *ltrim(char *str)
|
||||
{
|
||||
int len = strlen(str);
|
||||
|
||||
while (len && isspace(*str)) {
|
||||
len--;
|
||||
str++;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
static int read_script_info(struct script_desc *desc, const char *filename)
|
||||
{
|
||||
char line[BUFSIZ], *p;
|
||||
@@ -1487,7 +1475,8 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
return -1;
|
||||
}
|
||||
|
||||
perf_session__fprintf_info(session, stdout, show_full_info);
|
||||
if (!script_name && !generate_script_lang)
|
||||
perf_session__fprintf_info(session, stdout, show_full_info);
|
||||
|
||||
if (!no_callchain)
|
||||
symbol_conf.use_callchain = true;
|
||||
|
@@ -65,6 +65,11 @@
|
||||
#define CNTR_NOT_SUPPORTED "<not supported>"
|
||||
#define CNTR_NOT_COUNTED "<not counted>"
|
||||
|
||||
static void print_stat(int argc, const char **argv);
|
||||
static void print_counter_aggr(struct perf_evsel *counter, char *prefix);
|
||||
static void print_counter(struct perf_evsel *counter, char *prefix);
|
||||
static void print_aggr_socket(char *prefix);
|
||||
|
||||
static struct perf_evlist *evsel_list;
|
||||
|
||||
static struct perf_target target = {
|
||||
@@ -75,6 +80,7 @@ static int run_count = 1;
|
||||
static bool no_inherit = false;
|
||||
static bool scale = true;
|
||||
static bool no_aggr = false;
|
||||
static bool aggr_socket = false;
|
||||
static pid_t child_pid = -1;
|
||||
static bool null_run = false;
|
||||
static int detailed_run = 0;
|
||||
@@ -87,6 +93,9 @@ static FILE *output = NULL;
|
||||
static const char *pre_cmd = NULL;
|
||||
static const char *post_cmd = NULL;
|
||||
static bool sync_run = false;
|
||||
static unsigned int interval = 0;
|
||||
static struct timespec ref_time;
|
||||
static struct cpu_map *sock_map;
|
||||
|
||||
static volatile int done = 0;
|
||||
|
||||
@@ -94,6 +103,28 @@ struct perf_stat {
|
||||
struct stats res_stats[3];
|
||||
};
|
||||
|
||||
static inline void diff_timespec(struct timespec *r, struct timespec *a,
|
||||
struct timespec *b)
|
||||
{
|
||||
r->tv_sec = a->tv_sec - b->tv_sec;
|
||||
if (a->tv_nsec < b->tv_nsec) {
|
||||
r->tv_nsec = a->tv_nsec + 1000000000L - b->tv_nsec;
|
||||
r->tv_sec--;
|
||||
} else {
|
||||
r->tv_nsec = a->tv_nsec - b->tv_nsec ;
|
||||
}
|
||||
}
|
||||
|
||||
static inline struct cpu_map *perf_evsel__cpus(struct perf_evsel *evsel)
|
||||
{
|
||||
return (evsel->cpus && !target.cpu_list) ? evsel->cpus : evsel_list->cpus;
|
||||
}
|
||||
|
||||
static inline int perf_evsel__nr_cpus(struct perf_evsel *evsel)
|
||||
{
|
||||
return perf_evsel__cpus(evsel)->nr;
|
||||
}
|
||||
|
||||
static int perf_evsel__alloc_stat_priv(struct perf_evsel *evsel)
|
||||
{
|
||||
evsel->priv = zalloc(sizeof(struct perf_stat));
|
||||
@@ -106,14 +137,27 @@ static void perf_evsel__free_stat_priv(struct perf_evsel *evsel)
|
||||
evsel->priv = NULL;
|
||||
}
|
||||
|
||||
static inline struct cpu_map *perf_evsel__cpus(struct perf_evsel *evsel)
|
||||
static int perf_evsel__alloc_prev_raw_counts(struct perf_evsel *evsel)
|
||||
{
|
||||
return (evsel->cpus && !target.cpu_list) ? evsel->cpus : evsel_list->cpus;
|
||||
void *addr;
|
||||
size_t sz;
|
||||
|
||||
sz = sizeof(*evsel->counts) +
|
||||
(perf_evsel__nr_cpus(evsel) * sizeof(struct perf_counts_values));
|
||||
|
||||
addr = zalloc(sz);
|
||||
if (!addr)
|
||||
return -ENOMEM;
|
||||
|
||||
evsel->prev_raw_counts = addr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int perf_evsel__nr_cpus(struct perf_evsel *evsel)
|
||||
static void perf_evsel__free_prev_raw_counts(struct perf_evsel *evsel)
|
||||
{
|
||||
return perf_evsel__cpus(evsel)->nr;
|
||||
free(evsel->prev_raw_counts);
|
||||
evsel->prev_raw_counts = NULL;
|
||||
}
|
||||
|
||||
static struct stats runtime_nsecs_stats[MAX_NR_CPUS];
|
||||
@@ -132,8 +176,6 @@ static struct stats walltime_nsecs_stats;
|
||||
static int create_perf_stat_counter(struct perf_evsel *evsel)
|
||||
{
|
||||
struct perf_event_attr *attr = &evsel->attr;
|
||||
bool exclude_guest_missing = false;
|
||||
int ret;
|
||||
|
||||
if (scale)
|
||||
attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED |
|
||||
@@ -141,38 +183,16 @@ static int create_perf_stat_counter(struct perf_evsel *evsel)
|
||||
|
||||
attr->inherit = !no_inherit;
|
||||
|
||||
retry:
|
||||
if (exclude_guest_missing)
|
||||
evsel->attr.exclude_guest = evsel->attr.exclude_host = 0;
|
||||
|
||||
if (perf_target__has_cpu(&target)) {
|
||||
ret = perf_evsel__open_per_cpu(evsel, perf_evsel__cpus(evsel));
|
||||
if (ret)
|
||||
goto check_ret;
|
||||
return 0;
|
||||
}
|
||||
if (perf_target__has_cpu(&target))
|
||||
return perf_evsel__open_per_cpu(evsel, perf_evsel__cpus(evsel));
|
||||
|
||||
if (!perf_target__has_task(&target) &&
|
||||
!perf_evsel__is_group_member(evsel)) {
|
||||
perf_evsel__is_group_leader(evsel)) {
|
||||
attr->disabled = 1;
|
||||
attr->enable_on_exec = 1;
|
||||
}
|
||||
|
||||
ret = perf_evsel__open_per_thread(evsel, evsel_list->threads);
|
||||
if (!ret)
|
||||
return 0;
|
||||
/* fall through */
|
||||
check_ret:
|
||||
if (ret && errno == EINVAL) {
|
||||
if (!exclude_guest_missing &&
|
||||
(evsel->attr.exclude_guest || evsel->attr.exclude_host)) {
|
||||
pr_debug("Old kernel, cannot exclude "
|
||||
"guest or host samples.\n");
|
||||
exclude_guest_missing = true;
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
return perf_evsel__open_per_thread(evsel, evsel_list->threads);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -269,15 +289,79 @@ static int read_counter(struct perf_evsel *counter)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void print_interval(void)
|
||||
{
|
||||
static int num_print_interval;
|
||||
struct perf_evsel *counter;
|
||||
struct perf_stat *ps;
|
||||
struct timespec ts, rs;
|
||||
char prefix[64];
|
||||
|
||||
if (no_aggr) {
|
||||
list_for_each_entry(counter, &evsel_list->entries, node) {
|
||||
ps = counter->priv;
|
||||
memset(ps->res_stats, 0, sizeof(ps->res_stats));
|
||||
read_counter(counter);
|
||||
}
|
||||
} else {
|
||||
list_for_each_entry(counter, &evsel_list->entries, node) {
|
||||
ps = counter->priv;
|
||||
memset(ps->res_stats, 0, sizeof(ps->res_stats));
|
||||
read_counter_aggr(counter);
|
||||
}
|
||||
}
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
diff_timespec(&rs, &ts, &ref_time);
|
||||
sprintf(prefix, "%6lu.%09lu%s", rs.tv_sec, rs.tv_nsec, csv_sep);
|
||||
|
||||
if (num_print_interval == 0 && !csv_output) {
|
||||
if (aggr_socket)
|
||||
fprintf(output, "# time socket cpus counts events\n");
|
||||
else if (no_aggr)
|
||||
fprintf(output, "# time CPU counts events\n");
|
||||
else
|
||||
fprintf(output, "# time counts events\n");
|
||||
}
|
||||
|
||||
if (++num_print_interval == 25)
|
||||
num_print_interval = 0;
|
||||
|
||||
if (aggr_socket)
|
||||
print_aggr_socket(prefix);
|
||||
else if (no_aggr) {
|
||||
list_for_each_entry(counter, &evsel_list->entries, node)
|
||||
print_counter(counter, prefix);
|
||||
} else {
|
||||
list_for_each_entry(counter, &evsel_list->entries, node)
|
||||
print_counter_aggr(counter, prefix);
|
||||
}
|
||||
}
|
||||
|
||||
static int __run_perf_stat(int argc __maybe_unused, const char **argv)
|
||||
{
|
||||
char msg[512];
|
||||
unsigned long long t0, t1;
|
||||
struct perf_evsel *counter;
|
||||
struct timespec ts;
|
||||
int status = 0;
|
||||
int child_ready_pipe[2], go_pipe[2];
|
||||
const bool forks = (argc > 0);
|
||||
char buf;
|
||||
|
||||
if (interval) {
|
||||
ts.tv_sec = interval / 1000;
|
||||
ts.tv_nsec = (interval % 1000) * 1000000;
|
||||
} else {
|
||||
ts.tv_sec = 1;
|
||||
ts.tv_nsec = 0;
|
||||
}
|
||||
|
||||
if (aggr_socket
|
||||
&& cpu_map__build_socket_map(evsel_list->cpus, &sock_map)) {
|
||||
perror("cannot build socket map");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (forks && (pipe(child_ready_pipe) < 0 || pipe(go_pipe) < 0)) {
|
||||
perror("failed to create pipes");
|
||||
return -1;
|
||||
@@ -348,20 +432,13 @@ static int __run_perf_stat(int argc __maybe_unused, const char **argv)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (errno == EPERM || errno == EACCES) {
|
||||
error("You may not have permission to collect %sstats.\n"
|
||||
"\t Consider tweaking"
|
||||
" /proc/sys/kernel/perf_event_paranoid or running as root.",
|
||||
target.system_wide ? "system-wide " : "");
|
||||
} else {
|
||||
error("open_counter returned with %d (%s). "
|
||||
"/bin/dmesg may provide additional information.\n",
|
||||
errno, strerror(errno));
|
||||
}
|
||||
perf_evsel__open_strerror(counter, &target,
|
||||
errno, msg, sizeof(msg));
|
||||
ui__error("%s\n", msg);
|
||||
|
||||
if (child_pid != -1)
|
||||
kill(child_pid, SIGTERM);
|
||||
|
||||
pr_err("Not all events could be opened.\n");
|
||||
return -1;
|
||||
}
|
||||
counter->supported = true;
|
||||
@@ -377,14 +454,25 @@ static int __run_perf_stat(int argc __maybe_unused, const char **argv)
|
||||
* Enable counters and exec the command:
|
||||
*/
|
||||
t0 = rdclock();
|
||||
clock_gettime(CLOCK_MONOTONIC, &ref_time);
|
||||
|
||||
if (forks) {
|
||||
close(go_pipe[1]);
|
||||
if (interval) {
|
||||
while (!waitpid(child_pid, &status, WNOHANG)) {
|
||||
nanosleep(&ts, NULL);
|
||||
print_interval();
|
||||
}
|
||||
}
|
||||
wait(&status);
|
||||
if (WIFSIGNALED(status))
|
||||
psignal(WTERMSIG(status), argv[0]);
|
||||
} else {
|
||||
while(!done) sleep(1);
|
||||
while (!done) {
|
||||
nanosleep(&ts, NULL);
|
||||
if (interval)
|
||||
print_interval();
|
||||
}
|
||||
}
|
||||
|
||||
t1 = rdclock();
|
||||
@@ -454,13 +542,21 @@ static void print_noise(struct perf_evsel *evsel, double avg)
|
||||
print_noise_pct(stddev_stats(&ps->res_stats[0]), avg);
|
||||
}
|
||||
|
||||
static void nsec_printout(int cpu, struct perf_evsel *evsel, double avg)
|
||||
static void nsec_printout(int cpu, int nr, struct perf_evsel *evsel, double avg)
|
||||
{
|
||||
double msecs = avg / 1e6;
|
||||
char cpustr[16] = { '\0', };
|
||||
const char *fmt = csv_output ? "%s%.6f%s%s" : "%s%18.6f%s%-25s";
|
||||
|
||||
if (no_aggr)
|
||||
if (aggr_socket)
|
||||
sprintf(cpustr, "S%*d%s%*d%s",
|
||||
csv_output ? 0 : -5,
|
||||
cpu,
|
||||
csv_sep,
|
||||
csv_output ? 0 : 4,
|
||||
nr,
|
||||
csv_sep);
|
||||
else if (no_aggr)
|
||||
sprintf(cpustr, "CPU%*d%s",
|
||||
csv_output ? 0 : -4,
|
||||
perf_evsel__cpus(evsel)->map[cpu], csv_sep);
|
||||
@@ -470,7 +566,7 @@ static void nsec_printout(int cpu, struct perf_evsel *evsel, double avg)
|
||||
if (evsel->cgrp)
|
||||
fprintf(output, "%s%s", csv_sep, evsel->cgrp->name);
|
||||
|
||||
if (csv_output)
|
||||
if (csv_output || interval)
|
||||
return;
|
||||
|
||||
if (perf_evsel__match(evsel, SOFTWARE, SW_TASK_CLOCK))
|
||||
@@ -659,7 +755,7 @@ static void print_ll_cache_misses(int cpu,
|
||||
fprintf(output, " of all LL-cache hits ");
|
||||
}
|
||||
|
||||
static void abs_printout(int cpu, struct perf_evsel *evsel, double avg)
|
||||
static void abs_printout(int cpu, int nr, struct perf_evsel *evsel, double avg)
|
||||
{
|
||||
double total, ratio = 0.0;
|
||||
char cpustr[16] = { '\0', };
|
||||
@@ -672,7 +768,15 @@ static void abs_printout(int cpu, struct perf_evsel *evsel, double avg)
|
||||
else
|
||||
fmt = "%s%18.0f%s%-25s";
|
||||
|
||||
if (no_aggr)
|
||||
if (aggr_socket)
|
||||
sprintf(cpustr, "S%*d%s%*d%s",
|
||||
csv_output ? 0 : -5,
|
||||
cpu,
|
||||
csv_sep,
|
||||
csv_output ? 0 : 4,
|
||||
nr,
|
||||
csv_sep);
|
||||
else if (no_aggr)
|
||||
sprintf(cpustr, "CPU%*d%s",
|
||||
csv_output ? 0 : -4,
|
||||
perf_evsel__cpus(evsel)->map[cpu], csv_sep);
|
||||
@@ -684,12 +788,11 @@ static void abs_printout(int cpu, struct perf_evsel *evsel, double avg)
|
||||
if (evsel->cgrp)
|
||||
fprintf(output, "%s%s", csv_sep, evsel->cgrp->name);
|
||||
|
||||
if (csv_output)
|
||||
if (csv_output || interval)
|
||||
return;
|
||||
|
||||
if (perf_evsel__match(evsel, HARDWARE, HW_INSTRUCTIONS)) {
|
||||
total = avg_stats(&runtime_cycles_stats[cpu]);
|
||||
|
||||
if (total)
|
||||
ratio = avg / total;
|
||||
|
||||
@@ -779,16 +882,83 @@ static void abs_printout(int cpu, struct perf_evsel *evsel, double avg)
|
||||
}
|
||||
}
|
||||
|
||||
static void print_aggr_socket(char *prefix)
|
||||
{
|
||||
struct perf_evsel *counter;
|
||||
u64 ena, run, val;
|
||||
int cpu, s, s2, sock, nr;
|
||||
|
||||
if (!sock_map)
|
||||
return;
|
||||
|
||||
for (s = 0; s < sock_map->nr; s++) {
|
||||
sock = cpu_map__socket(sock_map, s);
|
||||
list_for_each_entry(counter, &evsel_list->entries, node) {
|
||||
val = ena = run = 0;
|
||||
nr = 0;
|
||||
for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) {
|
||||
s2 = cpu_map__get_socket(evsel_list->cpus, cpu);
|
||||
if (s2 != sock)
|
||||
continue;
|
||||
val += counter->counts->cpu[cpu].val;
|
||||
ena += counter->counts->cpu[cpu].ena;
|
||||
run += counter->counts->cpu[cpu].run;
|
||||
nr++;
|
||||
}
|
||||
if (prefix)
|
||||
fprintf(output, "%s", prefix);
|
||||
|
||||
if (run == 0 || ena == 0) {
|
||||
fprintf(output, "S%*d%s%*d%s%*s%s%*s",
|
||||
csv_output ? 0 : -5,
|
||||
s,
|
||||
csv_sep,
|
||||
csv_output ? 0 : 4,
|
||||
nr,
|
||||
csv_sep,
|
||||
csv_output ? 0 : 18,
|
||||
counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED,
|
||||
csv_sep,
|
||||
csv_output ? 0 : -24,
|
||||
perf_evsel__name(counter));
|
||||
if (counter->cgrp)
|
||||
fprintf(output, "%s%s",
|
||||
csv_sep, counter->cgrp->name);
|
||||
|
||||
fputc('\n', output);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nsec_counter(counter))
|
||||
nsec_printout(sock, nr, counter, val);
|
||||
else
|
||||
abs_printout(sock, nr, counter, val);
|
||||
|
||||
if (!csv_output) {
|
||||
print_noise(counter, 1.0);
|
||||
|
||||
if (run != ena)
|
||||
fprintf(output, " (%.2f%%)",
|
||||
100.0 * run / ena);
|
||||
}
|
||||
fputc('\n', output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Print out the results of a single counter:
|
||||
* aggregated counts in system-wide mode
|
||||
*/
|
||||
static void print_counter_aggr(struct perf_evsel *counter)
|
||||
static void print_counter_aggr(struct perf_evsel *counter, char *prefix)
|
||||
{
|
||||
struct perf_stat *ps = counter->priv;
|
||||
double avg = avg_stats(&ps->res_stats[0]);
|
||||
int scaled = counter->counts->scaled;
|
||||
|
||||
if (prefix)
|
||||
fprintf(output, "%s", prefix);
|
||||
|
||||
if (scaled == -1) {
|
||||
fprintf(output, "%*s%s%*s",
|
||||
csv_output ? 0 : 18,
|
||||
@@ -805,9 +975,9 @@ static void print_counter_aggr(struct perf_evsel *counter)
|
||||
}
|
||||
|
||||
if (nsec_counter(counter))
|
||||
nsec_printout(-1, counter, avg);
|
||||
nsec_printout(-1, 0, counter, avg);
|
||||
else
|
||||
abs_printout(-1, counter, avg);
|
||||
abs_printout(-1, 0, counter, avg);
|
||||
|
||||
print_noise(counter, avg);
|
||||
|
||||
@@ -831,7 +1001,7 @@ static void print_counter_aggr(struct perf_evsel *counter)
|
||||
* Print out the results of a single counter:
|
||||
* does not use aggregated count in system-wide
|
||||
*/
|
||||
static void print_counter(struct perf_evsel *counter)
|
||||
static void print_counter(struct perf_evsel *counter, char *prefix)
|
||||
{
|
||||
u64 ena, run, val;
|
||||
int cpu;
|
||||
@@ -840,6 +1010,10 @@ static void print_counter(struct perf_evsel *counter)
|
||||
val = counter->counts->cpu[cpu].val;
|
||||
ena = counter->counts->cpu[cpu].ena;
|
||||
run = counter->counts->cpu[cpu].run;
|
||||
|
||||
if (prefix)
|
||||
fprintf(output, "%s", prefix);
|
||||
|
||||
if (run == 0 || ena == 0) {
|
||||
fprintf(output, "CPU%*d%s%*s%s%*s",
|
||||
csv_output ? 0 : -4,
|
||||
@@ -859,9 +1033,9 @@ static void print_counter(struct perf_evsel *counter)
|
||||
}
|
||||
|
||||
if (nsec_counter(counter))
|
||||
nsec_printout(cpu, counter, val);
|
||||
nsec_printout(cpu, 0, counter, val);
|
||||
else
|
||||
abs_printout(cpu, counter, val);
|
||||
abs_printout(cpu, 0, counter, val);
|
||||
|
||||
if (!csv_output) {
|
||||
print_noise(counter, 1.0);
|
||||
@@ -899,12 +1073,14 @@ static void print_stat(int argc, const char **argv)
|
||||
fprintf(output, ":\n\n");
|
||||
}
|
||||
|
||||
if (no_aggr) {
|
||||
if (aggr_socket)
|
||||
print_aggr_socket(NULL);
|
||||
else if (no_aggr) {
|
||||
list_for_each_entry(counter, &evsel_list->entries, node)
|
||||
print_counter(counter);
|
||||
print_counter(counter, NULL);
|
||||
} else {
|
||||
list_for_each_entry(counter, &evsel_list->entries, node)
|
||||
print_counter_aggr(counter);
|
||||
print_counter_aggr(counter, NULL);
|
||||
}
|
||||
|
||||
if (!csv_output) {
|
||||
@@ -925,7 +1101,7 @@ static volatile int signr = -1;
|
||||
|
||||
static void skip_signal(int signo)
|
||||
{
|
||||
if(child_pid == -1)
|
||||
if ((child_pid == -1) || interval)
|
||||
done = 1;
|
||||
|
||||
signr = signo;
|
||||
@@ -1145,6 +1321,9 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
"command to run prior to the measured command"),
|
||||
OPT_STRING(0, "post", &post_cmd, "command",
|
||||
"command to run after to the measured command"),
|
||||
OPT_UINTEGER('I', "interval-print", &interval,
|
||||
"print counts at regular interval in ms (>= 100)"),
|
||||
OPT_BOOLEAN(0, "aggr-socket", &aggr_socket, "aggregate counts per processor socket"),
|
||||
OPT_END()
|
||||
};
|
||||
const char * const stat_usage[] = {
|
||||
@@ -1231,6 +1410,14 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
usage_with_options(stat_usage, options);
|
||||
}
|
||||
|
||||
if (aggr_socket) {
|
||||
if (!perf_target__has_cpu(&target)) {
|
||||
fprintf(stderr, "--aggr-socket only available in system-wide mode (-a)\n");
|
||||
usage_with_options(stat_usage, options);
|
||||
}
|
||||
no_aggr = true;
|
||||
}
|
||||
|
||||
if (add_default_attributes())
|
||||
goto out;
|
||||
|
||||
@@ -1245,12 +1432,23 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
usage_with_options(stat_usage, options);
|
||||
return -1;
|
||||
}
|
||||
if (interval && interval < 100) {
|
||||
pr_err("print interval must be >= 100ms\n");
|
||||
usage_with_options(stat_usage, options);
|
||||
return -1;
|
||||
}
|
||||
|
||||
list_for_each_entry(pos, &evsel_list->entries, node) {
|
||||
if (perf_evsel__alloc_stat_priv(pos) < 0 ||
|
||||
perf_evsel__alloc_counts(pos, perf_evsel__nr_cpus(pos)) < 0)
|
||||
goto out_free_fd;
|
||||
}
|
||||
if (interval) {
|
||||
list_for_each_entry(pos, &evsel_list->entries, node) {
|
||||
if (perf_evsel__alloc_prev_raw_counts(pos) < 0)
|
||||
goto out_free_fd;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We dont want to block the signals - that would cause
|
||||
@@ -1260,6 +1458,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
*/
|
||||
atexit(sig_atexit);
|
||||
signal(SIGINT, skip_signal);
|
||||
signal(SIGCHLD, skip_signal);
|
||||
signal(SIGALRM, skip_signal);
|
||||
signal(SIGABRT, skip_signal);
|
||||
|
||||
@@ -1272,11 +1471,14 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
status = run_perf_stat(argc, argv);
|
||||
}
|
||||
|
||||
if (status != -1)
|
||||
if (status != -1 && !interval)
|
||||
print_stat(argc, argv);
|
||||
out_free_fd:
|
||||
list_for_each_entry(pos, &evsel_list->entries, node)
|
||||
list_for_each_entry(pos, &evsel_list->entries, node) {
|
||||
perf_evsel__free_stat_priv(pos);
|
||||
perf_evsel__free_counts(pos);
|
||||
perf_evsel__free_prev_raw_counts(pos);
|
||||
}
|
||||
perf_evlist__delete_maps(evsel_list);
|
||||
out:
|
||||
perf_evlist__delete(evsel_list);
|
||||
|
@@ -68,27 +68,7 @@
|
||||
#include <linux/unistd.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
void get_term_dimensions(struct winsize *ws)
|
||||
{
|
||||
char *s = getenv("LINES");
|
||||
|
||||
if (s != NULL) {
|
||||
ws->ws_row = atoi(s);
|
||||
s = getenv("COLUMNS");
|
||||
if (s != NULL) {
|
||||
ws->ws_col = atoi(s);
|
||||
if (ws->ws_row && ws->ws_col)
|
||||
return;
|
||||
}
|
||||
}
|
||||
#ifdef TIOCGWINSZ
|
||||
if (ioctl(1, TIOCGWINSZ, ws) == 0 &&
|
||||
ws->ws_row && ws->ws_col)
|
||||
return;
|
||||
#endif
|
||||
ws->ws_row = 25;
|
||||
ws->ws_col = 80;
|
||||
}
|
||||
static volatile int done;
|
||||
|
||||
static void perf_top__update_print_entries(struct perf_top *top)
|
||||
{
|
||||
@@ -453,8 +433,10 @@ static int perf_top__key_mapped(struct perf_top *top, int c)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void perf_top__handle_keypress(struct perf_top *top, int c)
|
||||
static bool perf_top__handle_keypress(struct perf_top *top, int c)
|
||||
{
|
||||
bool ret = true;
|
||||
|
||||
if (!perf_top__key_mapped(top, c)) {
|
||||
struct pollfd stdin_poll = { .fd = 0, .events = POLLIN };
|
||||
struct termios tc, save;
|
||||
@@ -475,7 +457,7 @@ static void perf_top__handle_keypress(struct perf_top *top, int c)
|
||||
|
||||
tcsetattr(0, TCSAFLUSH, &save);
|
||||
if (!perf_top__key_mapped(top, c))
|
||||
return;
|
||||
return ret;
|
||||
}
|
||||
|
||||
switch (c) {
|
||||
@@ -537,7 +519,8 @@ static void perf_top__handle_keypress(struct perf_top *top, int c)
|
||||
printf("exiting.\n");
|
||||
if (top->dump_symtab)
|
||||
perf_session__fprintf_dsos(top->session, stderr);
|
||||
exit(0);
|
||||
ret = false;
|
||||
break;
|
||||
case 's':
|
||||
perf_top__prompt_symbol(top, "Enter details symbol");
|
||||
break;
|
||||
@@ -560,6 +543,8 @@ static void perf_top__handle_keypress(struct perf_top *top, int c)
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void perf_top__sort_new_samples(void *arg)
|
||||
@@ -596,13 +581,12 @@ static void *display_thread_tui(void *arg)
|
||||
* via --uid.
|
||||
*/
|
||||
list_for_each_entry(pos, &top->evlist->entries, node)
|
||||
pos->hists.uid_filter_str = top->target.uid_str;
|
||||
pos->hists.uid_filter_str = top->record_opts.target.uid_str;
|
||||
|
||||
perf_evlist__tui_browse_hists(top->evlist, help, &hbt,
|
||||
&top->session->header.env);
|
||||
|
||||
exit_browser(0);
|
||||
exit(0);
|
||||
done = 1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -626,7 +610,7 @@ repeat:
|
||||
/* trash return*/
|
||||
getc(stdin);
|
||||
|
||||
while (1) {
|
||||
while (!done) {
|
||||
perf_top__print_sym_table(top);
|
||||
/*
|
||||
* Either timeout expired or we got an EINTR due to SIGWINCH,
|
||||
@@ -640,15 +624,14 @@ repeat:
|
||||
continue;
|
||||
/* Fall trhu */
|
||||
default:
|
||||
goto process_hotkey;
|
||||
c = getc(stdin);
|
||||
tcsetattr(0, TCSAFLUSH, &save);
|
||||
|
||||
if (perf_top__handle_keypress(top, c))
|
||||
goto repeat;
|
||||
done = 1;
|
||||
}
|
||||
}
|
||||
process_hotkey:
|
||||
c = getc(stdin);
|
||||
tcsetattr(0, TCSAFLUSH, &save);
|
||||
|
||||
perf_top__handle_keypress(top, c);
|
||||
goto repeat;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -716,7 +699,7 @@ static void perf_event__process_sample(struct perf_tool *tool,
|
||||
static struct intlist *seen;
|
||||
|
||||
if (!seen)
|
||||
seen = intlist__new();
|
||||
seen = intlist__new(NULL);
|
||||
|
||||
if (!intlist__has_entry(seen, event->ip.pid)) {
|
||||
pr_err("Can't find guest [%d]'s kernel information\n",
|
||||
@@ -727,8 +710,8 @@ static void perf_event__process_sample(struct perf_tool *tool,
|
||||
}
|
||||
|
||||
if (!machine) {
|
||||
pr_err("%u unprocessable samples recorded.",
|
||||
top->session->hists.stats.nr_unprocessable_samples++);
|
||||
pr_err("%u unprocessable samples recorded.\r",
|
||||
top->session->stats.nr_unprocessable_samples++);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -847,13 +830,13 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx)
|
||||
++top->us_samples;
|
||||
if (top->hide_user_symbols)
|
||||
continue;
|
||||
machine = perf_session__find_host_machine(session);
|
||||
machine = &session->machines.host;
|
||||
break;
|
||||
case PERF_RECORD_MISC_KERNEL:
|
||||
++top->kernel_samples;
|
||||
if (top->hide_kernel_symbols)
|
||||
continue;
|
||||
machine = perf_session__find_host_machine(session);
|
||||
machine = &session->machines.host;
|
||||
break;
|
||||
case PERF_RECORD_MISC_GUEST_KERNEL:
|
||||
++top->guest_kernel_samples;
|
||||
@@ -878,7 +861,7 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx)
|
||||
hists__inc_nr_events(&evsel->hists, event->header.type);
|
||||
machine__process_event(machine, event);
|
||||
} else
|
||||
++session->hists.stats.nr_unknown_events;
|
||||
++session->stats.nr_unknown_events;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -890,123 +873,42 @@ static void perf_top__mmap_read(struct perf_top *top)
|
||||
perf_top__mmap_read_idx(top, i);
|
||||
}
|
||||
|
||||
static void perf_top__start_counters(struct perf_top *top)
|
||||
static int perf_top__start_counters(struct perf_top *top)
|
||||
{
|
||||
char msg[512];
|
||||
struct perf_evsel *counter;
|
||||
struct perf_evlist *evlist = top->evlist;
|
||||
struct perf_record_opts *opts = &top->record_opts;
|
||||
|
||||
if (top->group)
|
||||
perf_evlist__set_leader(evlist);
|
||||
perf_evlist__config(evlist, opts);
|
||||
|
||||
list_for_each_entry(counter, &evlist->entries, node) {
|
||||
struct perf_event_attr *attr = &counter->attr;
|
||||
|
||||
attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID;
|
||||
|
||||
if (top->freq) {
|
||||
attr->sample_type |= PERF_SAMPLE_PERIOD;
|
||||
attr->freq = 1;
|
||||
attr->sample_freq = top->freq;
|
||||
}
|
||||
|
||||
if (evlist->nr_entries > 1) {
|
||||
attr->sample_type |= PERF_SAMPLE_ID;
|
||||
attr->read_format |= PERF_FORMAT_ID;
|
||||
}
|
||||
|
||||
if (perf_target__has_cpu(&top->target))
|
||||
attr->sample_type |= PERF_SAMPLE_CPU;
|
||||
|
||||
if (symbol_conf.use_callchain)
|
||||
attr->sample_type |= PERF_SAMPLE_CALLCHAIN;
|
||||
|
||||
attr->mmap = 1;
|
||||
attr->comm = 1;
|
||||
attr->inherit = top->inherit;
|
||||
fallback_missing_features:
|
||||
if (top->exclude_guest_missing)
|
||||
attr->exclude_guest = attr->exclude_host = 0;
|
||||
retry_sample_id:
|
||||
attr->sample_id_all = top->sample_id_all_missing ? 0 : 1;
|
||||
try_again:
|
||||
if (perf_evsel__open(counter, top->evlist->cpus,
|
||||
top->evlist->threads) < 0) {
|
||||
int err = errno;
|
||||
|
||||
if (err == EPERM || err == EACCES) {
|
||||
ui__error_paranoid();
|
||||
goto out_err;
|
||||
} else if (err == EINVAL) {
|
||||
if (!top->exclude_guest_missing &&
|
||||
(attr->exclude_guest || attr->exclude_host)) {
|
||||
pr_debug("Old kernel, cannot exclude "
|
||||
"guest or host samples.\n");
|
||||
top->exclude_guest_missing = true;
|
||||
goto fallback_missing_features;
|
||||
} else if (!top->sample_id_all_missing) {
|
||||
/*
|
||||
* Old kernel, no attr->sample_id_type_all field
|
||||
*/
|
||||
top->sample_id_all_missing = true;
|
||||
goto retry_sample_id;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* If it's cycles then fall back to hrtimer
|
||||
* based cpu-clock-tick sw counter, which
|
||||
* is always available even if no PMU support:
|
||||
*/
|
||||
if ((err == ENOENT || err == ENXIO) &&
|
||||
(attr->type == PERF_TYPE_HARDWARE) &&
|
||||
(attr->config == PERF_COUNT_HW_CPU_CYCLES)) {
|
||||
|
||||
if (perf_evsel__fallback(counter, errno, msg, sizeof(msg))) {
|
||||
if (verbose)
|
||||
ui__warning("Cycles event not supported,\n"
|
||||
"trying to fall back to cpu-clock-ticks\n");
|
||||
|
||||
attr->type = PERF_TYPE_SOFTWARE;
|
||||
attr->config = PERF_COUNT_SW_CPU_CLOCK;
|
||||
if (counter->name) {
|
||||
free(counter->name);
|
||||
counter->name = NULL;
|
||||
}
|
||||
ui__warning("%s\n", msg);
|
||||
goto try_again;
|
||||
}
|
||||
|
||||
if (err == ENOENT) {
|
||||
ui__error("The %s event is not supported.\n",
|
||||
perf_evsel__name(counter));
|
||||
goto out_err;
|
||||
} else if (err == EMFILE) {
|
||||
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 "
|
||||
"returned with %d (%s). /bin/dmesg "
|
||||
"may provide additional information.\n"
|
||||
"No CONFIG_PERF_EVENTS=y kernel support "
|
||||
"configured?\n", err, strerror(err));
|
||||
perf_evsel__open_strerror(counter, &opts->target,
|
||||
errno, msg, sizeof(msg));
|
||||
ui__error("%s\n", msg);
|
||||
goto out_err;
|
||||
}
|
||||
}
|
||||
|
||||
if (perf_evlist__mmap(evlist, top->mmap_pages, false) < 0) {
|
||||
if (perf_evlist__mmap(evlist, opts->mmap_pages, false) < 0) {
|
||||
ui__error("Failed to mmap with %d (%s)\n",
|
||||
errno, strerror(errno));
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
return;
|
||||
return 0;
|
||||
|
||||
out_err:
|
||||
exit_browser(0);
|
||||
exit(0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int perf_top__setup_sample_type(struct perf_top *top)
|
||||
@@ -1016,7 +918,7 @@ static int perf_top__setup_sample_type(struct perf_top *top)
|
||||
ui__error("Selected -g but \"sym\" not present in --sort/-s.");
|
||||
return -EINVAL;
|
||||
}
|
||||
} else if (!top->dont_use_callchains && callchain_param.mode != CHAIN_NONE) {
|
||||
} else if (callchain_param.mode != CHAIN_NONE) {
|
||||
if (callchain_register_param(&callchain_param) < 0) {
|
||||
ui__error("Can't register callchain params.\n");
|
||||
return -EINVAL;
|
||||
@@ -1028,6 +930,7 @@ static int perf_top__setup_sample_type(struct perf_top *top)
|
||||
|
||||
static int __cmd_top(struct perf_top *top)
|
||||
{
|
||||
struct perf_record_opts *opts = &top->record_opts;
|
||||
pthread_t thread;
|
||||
int ret;
|
||||
/*
|
||||
@@ -1042,26 +945,42 @@ static int __cmd_top(struct perf_top *top)
|
||||
if (ret)
|
||||
goto out_delete;
|
||||
|
||||
if (perf_target__has_task(&top->target))
|
||||
if (perf_target__has_task(&opts->target))
|
||||
perf_event__synthesize_thread_map(&top->tool, top->evlist->threads,
|
||||
perf_event__process,
|
||||
&top->session->host_machine);
|
||||
&top->session->machines.host);
|
||||
else
|
||||
perf_event__synthesize_threads(&top->tool, perf_event__process,
|
||||
&top->session->host_machine);
|
||||
perf_top__start_counters(top);
|
||||
&top->session->machines.host);
|
||||
|
||||
ret = perf_top__start_counters(top);
|
||||
if (ret)
|
||||
goto out_delete;
|
||||
|
||||
top->session->evlist = top->evlist;
|
||||
perf_session__set_id_hdr_size(top->session);
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* XXX 'top' still doesn't start workloads like record, trace, but should,
|
||||
* so leave the check here.
|
||||
*/
|
||||
if (!perf_target__none(&opts->target))
|
||||
perf_evlist__enable(top->evlist);
|
||||
|
||||
/* Wait for a minimal set of events before starting the snapshot */
|
||||
poll(top->evlist->pollfd, top->evlist->nr_fds, 100);
|
||||
|
||||
perf_top__mmap_read(top);
|
||||
|
||||
ret = -1;
|
||||
if (pthread_create(&thread, NULL, (use_browser > 0 ? display_thread_tui :
|
||||
display_thread), top)) {
|
||||
ui__error("Could not create display thread.\n");
|
||||
exit(-1);
|
||||
goto out_delete;
|
||||
}
|
||||
|
||||
if (top->realtime_prio) {
|
||||
@@ -1070,11 +989,11 @@ static int __cmd_top(struct perf_top *top)
|
||||
param.sched_priority = top->realtime_prio;
|
||||
if (sched_setscheduler(0, SCHED_FIFO, ¶m)) {
|
||||
ui__error("Could not set realtime priority.\n");
|
||||
exit(-1);
|
||||
goto out_delete;
|
||||
}
|
||||
}
|
||||
|
||||
while (1) {
|
||||
while (!done) {
|
||||
u64 hits = top->samples;
|
||||
|
||||
perf_top__mmap_read(top);
|
||||
@@ -1083,126 +1002,67 @@ static int __cmd_top(struct perf_top *top)
|
||||
ret = poll(top->evlist->pollfd, top->evlist->nr_fds, 100);
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
out_delete:
|
||||
perf_session__delete(top->session);
|
||||
top->session = NULL;
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
parse_callchain_opt(const struct option *opt, const char *arg, int unset)
|
||||
{
|
||||
struct perf_top *top = (struct perf_top *)opt->value;
|
||||
char *tok, *tok2;
|
||||
char *endptr;
|
||||
|
||||
/*
|
||||
* --no-call-graph
|
||||
*/
|
||||
if (unset) {
|
||||
top->dont_use_callchains = true;
|
||||
if (unset)
|
||||
return 0;
|
||||
}
|
||||
|
||||
symbol_conf.use_callchain = true;
|
||||
|
||||
if (!arg)
|
||||
return 0;
|
||||
|
||||
tok = strtok((char *)arg, ",");
|
||||
if (!tok)
|
||||
return -1;
|
||||
|
||||
/* get the output mode */
|
||||
if (!strncmp(tok, "graph", strlen(arg)))
|
||||
callchain_param.mode = CHAIN_GRAPH_ABS;
|
||||
|
||||
else if (!strncmp(tok, "flat", strlen(arg)))
|
||||
callchain_param.mode = CHAIN_FLAT;
|
||||
|
||||
else if (!strncmp(tok, "fractal", strlen(arg)))
|
||||
callchain_param.mode = CHAIN_GRAPH_REL;
|
||||
|
||||
else if (!strncmp(tok, "none", strlen(arg))) {
|
||||
callchain_param.mode = CHAIN_NONE;
|
||||
symbol_conf.use_callchain = false;
|
||||
|
||||
return 0;
|
||||
} else
|
||||
return -1;
|
||||
|
||||
/* get the min percentage */
|
||||
tok = strtok(NULL, ",");
|
||||
if (!tok)
|
||||
goto setup;
|
||||
|
||||
callchain_param.min_percent = strtod(tok, &endptr);
|
||||
if (tok == endptr)
|
||||
return -1;
|
||||
|
||||
/* get the print limit */
|
||||
tok2 = strtok(NULL, ",");
|
||||
if (!tok2)
|
||||
goto setup;
|
||||
|
||||
if (tok2[0] != 'c') {
|
||||
callchain_param.print_limit = strtod(tok2, &endptr);
|
||||
tok2 = strtok(NULL, ",");
|
||||
if (!tok2)
|
||||
goto setup;
|
||||
}
|
||||
|
||||
/* get the call chain order */
|
||||
if (!strcmp(tok2, "caller"))
|
||||
callchain_param.order = ORDER_CALLER;
|
||||
else if (!strcmp(tok2, "callee"))
|
||||
callchain_param.order = ORDER_CALLEE;
|
||||
else
|
||||
return -1;
|
||||
setup:
|
||||
if (callchain_register_param(&callchain_param) < 0) {
|
||||
fprintf(stderr, "Can't register callchain params\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
return record_parse_callchain_opt(opt, arg, unset);
|
||||
}
|
||||
|
||||
int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
{
|
||||
struct perf_evsel *pos;
|
||||
int status;
|
||||
char errbuf[BUFSIZ];
|
||||
struct perf_top top = {
|
||||
.count_filter = 5,
|
||||
.delay_secs = 2,
|
||||
.freq = 4000, /* 4 KHz */
|
||||
.mmap_pages = 128,
|
||||
.sym_pcnt_filter = 5,
|
||||
.target = {
|
||||
.uses_mmap = true,
|
||||
.record_opts = {
|
||||
.mmap_pages = UINT_MAX,
|
||||
.user_freq = UINT_MAX,
|
||||
.user_interval = ULLONG_MAX,
|
||||
.freq = 4000, /* 4 KHz */
|
||||
.target = {
|
||||
.uses_mmap = true,
|
||||
},
|
||||
},
|
||||
.sym_pcnt_filter = 5,
|
||||
};
|
||||
char callchain_default_opt[] = "fractal,0.5,callee";
|
||||
struct perf_record_opts *opts = &top.record_opts;
|
||||
struct perf_target *target = &opts->target;
|
||||
const struct option options[] = {
|
||||
OPT_CALLBACK('e', "event", &top.evlist, "event",
|
||||
"event selector. use 'perf list' to list available events",
|
||||
parse_events_option),
|
||||
OPT_INTEGER('c', "count", &top.default_interval,
|
||||
"event period to sample"),
|
||||
OPT_STRING('p', "pid", &top.target.pid, "pid",
|
||||
OPT_U64('c', "count", &opts->user_interval, "event period to sample"),
|
||||
OPT_STRING('p', "pid", &target->pid, "pid",
|
||||
"profile events on existing process id"),
|
||||
OPT_STRING('t', "tid", &top.target.tid, "tid",
|
||||
OPT_STRING('t', "tid", &target->tid, "tid",
|
||||
"profile events on existing thread id"),
|
||||
OPT_BOOLEAN('a', "all-cpus", &top.target.system_wide,
|
||||
OPT_BOOLEAN('a', "all-cpus", &target->system_wide,
|
||||
"system-wide collection from all CPUs"),
|
||||
OPT_STRING('C', "cpu", &top.target.cpu_list, "cpu",
|
||||
OPT_STRING('C', "cpu", &target->cpu_list, "cpu",
|
||||
"list of cpus to monitor"),
|
||||
OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
|
||||
"file", "vmlinux pathname"),
|
||||
OPT_BOOLEAN('K', "hide_kernel_symbols", &top.hide_kernel_symbols,
|
||||
"hide kernel symbols"),
|
||||
OPT_UINTEGER('m', "mmap-pages", &top.mmap_pages, "number of mmap data pages"),
|
||||
OPT_UINTEGER('m', "mmap-pages", &opts->mmap_pages,
|
||||
"number of mmap data pages"),
|
||||
OPT_INTEGER('r', "realtime", &top.realtime_prio,
|
||||
"collect data with this RT SCHED_FIFO priority"),
|
||||
OPT_INTEGER('d', "delay", &top.delay_secs,
|
||||
@@ -1211,16 +1071,14 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
"dump the symbol table used for profiling"),
|
||||
OPT_INTEGER('f', "count-filter", &top.count_filter,
|
||||
"only display functions with more events than this"),
|
||||
OPT_BOOLEAN('g', "group", &top.group,
|
||||
OPT_BOOLEAN('g', "group", &opts->group,
|
||||
"put the counters into a counter group"),
|
||||
OPT_BOOLEAN('i', "inherit", &top.inherit,
|
||||
"child tasks inherit counters"),
|
||||
OPT_BOOLEAN('i', "no-inherit", &opts->no_inherit,
|
||||
"child tasks do not inherit counters"),
|
||||
OPT_STRING(0, "sym-annotate", &top.sym_filter, "symbol name",
|
||||
"symbol to annotate"),
|
||||
OPT_BOOLEAN('z', "zero", &top.zero,
|
||||
"zero history across updates"),
|
||||
OPT_INTEGER('F', "freq", &top.freq,
|
||||
"profile at this frequency"),
|
||||
OPT_BOOLEAN('z', "zero", &top.zero, "zero history across updates"),
|
||||
OPT_UINTEGER('F', "freq", &opts->user_freq, "profile at this frequency"),
|
||||
OPT_INTEGER('E', "entries", &top.print_entries,
|
||||
"display this many functions"),
|
||||
OPT_BOOLEAN('U', "hide_user_symbols", &top.hide_user_symbols,
|
||||
@@ -1233,10 +1091,9 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
"sort by key(s): pid, comm, dso, symbol, parent"),
|
||||
OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples,
|
||||
"Show a column with the number of samples"),
|
||||
OPT_CALLBACK_DEFAULT('G', "call-graph", &top, "output_type,min_percent, call_order",
|
||||
"Display callchains using output_type (graph, flat, fractal, or none), min percent threshold and callchain order. "
|
||||
"Default: fractal,0.5,callee", &parse_callchain_opt,
|
||||
callchain_default_opt),
|
||||
OPT_CALLBACK_DEFAULT('G', "call-graph", &top.record_opts,
|
||||
"mode[,dump_size]", record_callchain_help,
|
||||
&parse_callchain_opt, "fp"),
|
||||
OPT_BOOLEAN(0, "show-total-period", &symbol_conf.show_total_period,
|
||||
"Show a column with the sum of periods"),
|
||||
OPT_STRING(0, "dsos", &symbol_conf.dso_list_str, "dso[,dso...]",
|
||||
@@ -1251,7 +1108,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
"Display raw encoding of assembly instructions (default)"),
|
||||
OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style",
|
||||
"Specify disassembler style (e.g. -M intel for intel syntax)"),
|
||||
OPT_STRING('u', "uid", &top.target.uid_str, "user", "user to profile"),
|
||||
OPT_STRING('u', "uid", &target->uid_str, "user", "user to profile"),
|
||||
OPT_END()
|
||||
};
|
||||
const char * const top_usage[] = {
|
||||
@@ -1272,7 +1129,8 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
if (sort_order == default_sort_order)
|
||||
sort_order = "dso,symbol";
|
||||
|
||||
setup_sorting(top_usage, options);
|
||||
if (setup_sorting() < 0)
|
||||
usage_with_options(top_usage, options);
|
||||
|
||||
if (top.use_stdio)
|
||||
use_browser = 0;
|
||||
@@ -1281,33 +1139,33 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
|
||||
setup_browser(false);
|
||||
|
||||
status = perf_target__validate(&top.target);
|
||||
status = perf_target__validate(target);
|
||||
if (status) {
|
||||
perf_target__strerror(&top.target, status, errbuf, BUFSIZ);
|
||||
perf_target__strerror(target, status, errbuf, BUFSIZ);
|
||||
ui__warning("%s", errbuf);
|
||||
}
|
||||
|
||||
status = perf_target__parse_uid(&top.target);
|
||||
status = perf_target__parse_uid(target);
|
||||
if (status) {
|
||||
int saved_errno = errno;
|
||||
|
||||
perf_target__strerror(&top.target, status, errbuf, BUFSIZ);
|
||||
perf_target__strerror(target, status, errbuf, BUFSIZ);
|
||||
ui__error("%s", errbuf);
|
||||
|
||||
status = -saved_errno;
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
if (perf_target__none(&top.target))
|
||||
top.target.system_wide = true;
|
||||
if (perf_target__none(target))
|
||||
target->system_wide = true;
|
||||
|
||||
if (perf_evlist__create_maps(top.evlist, &top.target) < 0)
|
||||
if (perf_evlist__create_maps(top.evlist, target) < 0)
|
||||
usage_with_options(top_usage, options);
|
||||
|
||||
if (!top.evlist->nr_entries &&
|
||||
perf_evlist__add_default(top.evlist) < 0) {
|
||||
ui__error("Not enough memory for event selector list\n");
|
||||
return -ENOMEM;
|
||||
goto out_delete_maps;
|
||||
}
|
||||
|
||||
symbol_conf.nr_events = top.evlist->nr_entries;
|
||||
@@ -1315,24 +1173,22 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
if (top.delay_secs < 1)
|
||||
top.delay_secs = 1;
|
||||
|
||||
if (opts->user_interval != ULLONG_MAX)
|
||||
opts->default_interval = opts->user_interval;
|
||||
if (opts->user_freq != UINT_MAX)
|
||||
opts->freq = opts->user_freq;
|
||||
|
||||
/*
|
||||
* User specified count overrides default frequency.
|
||||
*/
|
||||
if (top.default_interval)
|
||||
top.freq = 0;
|
||||
else if (top.freq) {
|
||||
top.default_interval = top.freq;
|
||||
if (opts->default_interval)
|
||||
opts->freq = 0;
|
||||
else if (opts->freq) {
|
||||
opts->default_interval = opts->freq;
|
||||
} else {
|
||||
ui__error("frequency and count are zero, aborting\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
list_for_each_entry(pos, &top.evlist->entries, node) {
|
||||
/*
|
||||
* Fill in the ones not specifically initialized via -c:
|
||||
*/
|
||||
if (!pos->attr.sample_period)
|
||||
pos->attr.sample_period = top.default_interval;
|
||||
status = -EINVAL;
|
||||
goto out_delete_maps;
|
||||
}
|
||||
|
||||
top.sym_evsel = perf_evlist__first(top.evlist);
|
||||
@@ -1365,6 +1221,8 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
|
||||
status = __cmd_top(&top);
|
||||
|
||||
out_delete_maps:
|
||||
perf_evlist__delete_maps(top.evlist);
|
||||
out_delete_evlist:
|
||||
perf_evlist__delete(top.evlist);
|
||||
|
||||
|
@@ -455,7 +455,7 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
perf_evlist__config_attrs(evlist, &trace->opts);
|
||||
perf_evlist__config(evlist, &trace->opts);
|
||||
|
||||
signal(SIGCHLD, sig_handler);
|
||||
signal(SIGINT, sig_handler);
|
||||
|
@@ -225,3 +225,14 @@ int main(void)
|
||||
return on_exit(NULL, NULL);
|
||||
}
|
||||
endef
|
||||
|
||||
define SOURCE_LIBNUMA
|
||||
#include <numa.h>
|
||||
#include <numaif.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
numa_available();
|
||||
return 0;
|
||||
}
|
||||
endef
|
@@ -13,7 +13,7 @@ newline := $(newline)
|
||||
# what should replace a newline when escaping
|
||||
# newlines; the default is a bizarre string.
|
||||
#
|
||||
nl-escape = $(or $(1),m822df3020w6a44id34bt574ctac44eb9f4n)
|
||||
nl-escape = $(if $(1),$(1),m822df3020w6a44id34bt574ctac44eb9f4n)
|
||||
|
||||
# escape-nl
|
||||
#
|
||||
@@ -173,9 +173,9 @@ _ge-abspath = $(if $(is-executable),$(1))
|
||||
# Usage: absolute-executable-path-or-empty = $(call get-executable-or-default,variable,default)
|
||||
#
|
||||
define get-executable-or-default
|
||||
$(if $($(1)),$(call _ge_attempt,$($(1)),$(1)),$(call _ge_attempt,$(2)))
|
||||
$(if $($(1)),$(call _ge_attempt,$($(1)),$(1)),$(call _ge_attempt,$(2),$(1)))
|
||||
endef
|
||||
_ge_attempt = $(or $(get-executable),$(_gea_warn),$(call _gea_err,$(2)))
|
||||
_ge_attempt = $(if $(get-executable),$(get-executable),$(_gea_warn)$(call _gea_err,$(2)))
|
||||
_gea_warn = $(warning The path '$(1)' is not executable.)
|
||||
_gea_err = $(if $(1),$(error Please set '$(1)' appropriately))
|
||||
|
||||
|
@@ -328,14 +328,23 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
|
||||
if (S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode))
|
||||
return 0;
|
||||
|
||||
status = 1;
|
||||
/* Check for ENOSPC and EIO errors.. */
|
||||
if (fflush(stdout))
|
||||
die("write failure on standard output: %s", strerror(errno));
|
||||
if (ferror(stdout))
|
||||
die("unknown write failure on standard output");
|
||||
if (fclose(stdout))
|
||||
die("close failed on standard output: %s", strerror(errno));
|
||||
return 0;
|
||||
if (fflush(stdout)) {
|
||||
fprintf(stderr, "write failure on standard output: %s", strerror(errno));
|
||||
goto out;
|
||||
}
|
||||
if (ferror(stdout)) {
|
||||
fprintf(stderr, "unknown write failure on standard output");
|
||||
goto out;
|
||||
}
|
||||
if (fclose(stdout)) {
|
||||
fprintf(stderr, "close failed on standard output: %s", strerror(errno));
|
||||
goto out;
|
||||
}
|
||||
status = 0;
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
static void handle_internal_command(int argc, const char **argv)
|
||||
@@ -467,7 +476,8 @@ int main(int argc, const char **argv)
|
||||
cmd += 5;
|
||||
argv[0] = cmd;
|
||||
handle_internal_command(argc, argv);
|
||||
die("cannot handle %s internally", cmd);
|
||||
fprintf(stderr, "cannot handle %s internally", cmd);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Look for flags.. */
|
||||
@@ -485,7 +495,7 @@ int main(int argc, const char **argv)
|
||||
printf("\n usage: %s\n\n", perf_usage_string);
|
||||
list_common_cmds_help();
|
||||
printf("\n %s\n\n", perf_more_info_string);
|
||||
exit(1);
|
||||
goto out;
|
||||
}
|
||||
cmd = argv[0];
|
||||
|
||||
@@ -517,7 +527,7 @@ int main(int argc, const char **argv)
|
||||
fprintf(stderr, "Expansion of alias '%s' failed; "
|
||||
"'%s' is not a perf-command\n",
|
||||
cmd, argv[0]);
|
||||
exit(1);
|
||||
goto out;
|
||||
}
|
||||
if (!done_help) {
|
||||
cmd = argv[0] = help_unknown_cmd(cmd);
|
||||
@@ -528,6 +538,6 @@ int main(int argc, const char **argv)
|
||||
|
||||
fprintf(stderr, "Failed to run command '%s': %s\n",
|
||||
cmd, strerror(errno));
|
||||
|
||||
out:
|
||||
return 1;
|
||||
}
|
||||
|
@@ -1,10 +1,6 @@
|
||||
#ifndef _PERF_PERF_H
|
||||
#define _PERF_PERF_H
|
||||
|
||||
struct winsize;
|
||||
|
||||
void get_term_dimensions(struct winsize *ws);
|
||||
|
||||
#include <asm/unistd.h>
|
||||
|
||||
#if defined(__i386__)
|
||||
@@ -107,32 +103,6 @@ void get_term_dimensions(struct winsize *ws);
|
||||
#include "util/types.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
struct perf_mmap {
|
||||
void *base;
|
||||
int mask;
|
||||
unsigned int prev;
|
||||
};
|
||||
|
||||
static inline unsigned int perf_mmap__read_head(struct perf_mmap *mm)
|
||||
{
|
||||
struct perf_event_mmap_page *pc = mm->base;
|
||||
int head = pc->data_head;
|
||||
rmb();
|
||||
return head;
|
||||
}
|
||||
|
||||
static inline void perf_mmap__write_tail(struct perf_mmap *md,
|
||||
unsigned long tail)
|
||||
{
|
||||
struct perf_event_mmap_page *pc = md->base;
|
||||
|
||||
/*
|
||||
* ensure all reads are done before we write the tail out.
|
||||
*/
|
||||
/* mb(); */
|
||||
pc->data_tail = tail;
|
||||
}
|
||||
|
||||
/*
|
||||
* prctl(PR_TASK_PERF_EVENTS_DISABLE) will (cheaply) disable all
|
||||
* counters in the current task.
|
||||
@@ -237,8 +207,6 @@ struct perf_record_opts {
|
||||
bool raw_samples;
|
||||
bool sample_address;
|
||||
bool sample_time;
|
||||
bool sample_id_all_missing;
|
||||
bool exclude_guest_missing;
|
||||
bool period;
|
||||
unsigned int freq;
|
||||
unsigned int mmap_pages;
|
||||
|
@@ -1,2 +0,0 @@
|
||||
#!/bin/bash
|
||||
perf record -e workqueue:workqueue_creation -e workqueue:workqueue_destruction -e workqueue:workqueue_execution -e workqueue:workqueue_insertion $@
|
@@ -1,3 +0,0 @@
|
||||
#!/bin/bash
|
||||
# description: workqueue stats (ins/exe/create/destroy)
|
||||
perf script $@ -s "$PERF_EXEC_PATH"/scripts/perl/workqueue-stats.pl
|
@@ -17,6 +17,7 @@ use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib";
|
||||
use lib "./Perf-Trace-Util/lib";
|
||||
use Perf::Trace::Core;
|
||||
use Perf::Trace::Util;
|
||||
use POSIX qw/SIGALRM SA_RESTART/;
|
||||
|
||||
my $default_interval = 3;
|
||||
my $nlines = 20;
|
||||
@@ -90,7 +91,10 @@ sub syscalls::sys_enter_write
|
||||
|
||||
sub trace_begin
|
||||
{
|
||||
$SIG{ALRM} = \&set_print_pending;
|
||||
my $sa = POSIX::SigAction->new(\&set_print_pending);
|
||||
$sa->flags(SA_RESTART);
|
||||
$sa->safe(1);
|
||||
POSIX::sigaction(SIGALRM, $sa) or die "Can't set SIGALRM handler: $!\n";
|
||||
alarm 1;
|
||||
}
|
||||
|
||||
|
@@ -1,129 +0,0 @@
|
||||
#!/usr/bin/perl -w
|
||||
# (c) 2009, Tom Zanussi <tzanussi@gmail.com>
|
||||
# Licensed under the terms of the GNU GPL License version 2
|
||||
|
||||
# Displays workqueue stats
|
||||
#
|
||||
# Usage:
|
||||
#
|
||||
# perf record -c 1 -f -a -R -e workqueue:workqueue_creation -e
|
||||
# workqueue:workqueue_destruction -e workqueue:workqueue_execution
|
||||
# -e workqueue:workqueue_insertion
|
||||
#
|
||||
# perf script -p -s tools/perf/scripts/perl/workqueue-stats.pl
|
||||
|
||||
use 5.010000;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib";
|
||||
use lib "./Perf-Trace-Util/lib";
|
||||
use Perf::Trace::Core;
|
||||
use Perf::Trace::Util;
|
||||
|
||||
my @cpus;
|
||||
|
||||
sub workqueue::workqueue_destruction
|
||||
{
|
||||
my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
|
||||
$common_pid, $common_comm,
|
||||
$thread_comm, $thread_pid) = @_;
|
||||
|
||||
$cpus[$common_cpu]{$thread_pid}{destroyed}++;
|
||||
$cpus[$common_cpu]{$thread_pid}{comm} = $thread_comm;
|
||||
}
|
||||
|
||||
sub workqueue::workqueue_creation
|
||||
{
|
||||
my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
|
||||
$common_pid, $common_comm,
|
||||
$thread_comm, $thread_pid, $cpu) = @_;
|
||||
|
||||
$cpus[$common_cpu]{$thread_pid}{created}++;
|
||||
$cpus[$common_cpu]{$thread_pid}{comm} = $thread_comm;
|
||||
}
|
||||
|
||||
sub workqueue::workqueue_execution
|
||||
{
|
||||
my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
|
||||
$common_pid, $common_comm,
|
||||
$thread_comm, $thread_pid, $func) = @_;
|
||||
|
||||
$cpus[$common_cpu]{$thread_pid}{executed}++;
|
||||
$cpus[$common_cpu]{$thread_pid}{comm} = $thread_comm;
|
||||
}
|
||||
|
||||
sub workqueue::workqueue_insertion
|
||||
{
|
||||
my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
|
||||
$common_pid, $common_comm,
|
||||
$thread_comm, $thread_pid, $func) = @_;
|
||||
|
||||
$cpus[$common_cpu]{$thread_pid}{inserted}++;
|
||||
$cpus[$common_cpu]{$thread_pid}{comm} = $thread_comm;
|
||||
}
|
||||
|
||||
sub trace_end
|
||||
{
|
||||
print "workqueue work stats:\n\n";
|
||||
my $cpu = 0;
|
||||
printf("%3s %6s %6s\t%-20s\n", "cpu", "ins", "exec", "name");
|
||||
printf("%3s %6s %6s\t%-20s\n", "---", "---", "----", "----");
|
||||
foreach my $pidhash (@cpus) {
|
||||
while ((my $pid, my $wqhash) = each %$pidhash) {
|
||||
my $ins = $$wqhash{'inserted'} || 0;
|
||||
my $exe = $$wqhash{'executed'} || 0;
|
||||
my $comm = $$wqhash{'comm'} || "";
|
||||
if ($ins || $exe) {
|
||||
printf("%3u %6u %6u\t%-20s\n", $cpu, $ins, $exe, $comm);
|
||||
}
|
||||
}
|
||||
$cpu++;
|
||||
}
|
||||
|
||||
$cpu = 0;
|
||||
print "\nworkqueue lifecycle stats:\n\n";
|
||||
printf("%3s %6s %6s\t%-20s\n", "cpu", "created", "destroyed", "name");
|
||||
printf("%3s %6s %6s\t%-20s\n", "---", "-------", "---------", "----");
|
||||
foreach my $pidhash (@cpus) {
|
||||
while ((my $pid, my $wqhash) = each %$pidhash) {
|
||||
my $created = $$wqhash{'created'} || 0;
|
||||
my $destroyed = $$wqhash{'destroyed'} || 0;
|
||||
my $comm = $$wqhash{'comm'} || "";
|
||||
if ($created || $destroyed) {
|
||||
printf("%3u %6u %6u\t%-20s\n", $cpu, $created, $destroyed,
|
||||
$comm);
|
||||
}
|
||||
}
|
||||
$cpu++;
|
||||
}
|
||||
|
||||
print_unhandled();
|
||||
}
|
||||
|
||||
my %unhandled;
|
||||
|
||||
sub print_unhandled
|
||||
{
|
||||
if ((scalar keys %unhandled) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
print "\nunhandled events:\n\n";
|
||||
|
||||
printf("%-40s %10s\n", "event", "count");
|
||||
printf("%-40s %10s\n", "----------------------------------------",
|
||||
"-----------");
|
||||
|
||||
foreach my $event_name (keys %unhandled) {
|
||||
printf("%-40s %10d\n", $event_name, $unhandled{$event_name});
|
||||
}
|
||||
}
|
||||
|
||||
sub trace_unhandled
|
||||
{
|
||||
my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
|
||||
$common_pid, $common_comm) = @_;
|
||||
|
||||
$unhandled{$event_name}++;
|
||||
}
|
@@ -19,6 +19,11 @@
|
||||
* permissions. All the event text files are stored there.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Powerpc needs __SANE_USERSPACE_TYPES__ before <linux/types.h> to select
|
||||
* 'int-ll64.h' and avoid compile warnings when printing __u64 with %llu.
|
||||
*/
|
||||
#define __SANE_USERSPACE_TYPES__
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
@@ -33,8 +38,6 @@
|
||||
|
||||
extern int verbose;
|
||||
|
||||
bool test_attr__enabled;
|
||||
|
||||
static char *dir;
|
||||
|
||||
void test_attr__init(void)
|
||||
@@ -146,7 +149,7 @@ 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",
|
||||
snprintf(cmd, 3*PATH_MAX, PYTHON " %s/attr.py -d %s/attr/ -p %s %s",
|
||||
d, d, perf, verbose ? "-v" : "");
|
||||
|
||||
return system(cmd);
|
||||
|
@@ -68,7 +68,7 @@ class Event(dict):
|
||||
self[key] = val
|
||||
|
||||
def __init__(self, name, data, base):
|
||||
log.info(" Event %s" % name);
|
||||
log.debug(" Event %s" % name);
|
||||
self.name = name;
|
||||
self.group = ''
|
||||
self.add(base)
|
||||
@@ -97,6 +97,14 @@ class Event(dict):
|
||||
return False
|
||||
return True
|
||||
|
||||
def diff(self, other):
|
||||
for t in Event.terms:
|
||||
if not self.has_key(t) or not other.has_key(t):
|
||||
continue
|
||||
if not self.compare_data(self[t], other[t]):
|
||||
log.warning("expected %s=%s, got %s" % (t, self[t], other[t]))
|
||||
|
||||
|
||||
# Test file description needs to have following sections:
|
||||
# [config]
|
||||
# - just single instance in file
|
||||
@@ -113,7 +121,7 @@ class Test(object):
|
||||
parser = ConfigParser.SafeConfigParser()
|
||||
parser.read(path)
|
||||
|
||||
log.warning("running '%s'" % path)
|
||||
log.debug("running '%s'" % path)
|
||||
|
||||
self.path = path
|
||||
self.test_dir = options.test_dir
|
||||
@@ -128,7 +136,7 @@ class Test(object):
|
||||
|
||||
self.expect = {}
|
||||
self.result = {}
|
||||
log.info(" loading expected events");
|
||||
log.debug(" loading expected events");
|
||||
self.load_events(path, self.expect)
|
||||
|
||||
def is_event(self, name):
|
||||
@@ -164,7 +172,7 @@ class Test(object):
|
||||
self.perf, self.command, tempdir, self.args)
|
||||
ret = os.WEXITSTATUS(os.system(cmd))
|
||||
|
||||
log.info(" running '%s' ret %d " % (cmd, ret))
|
||||
log.warning(" running '%s' ret %d " % (cmd, ret))
|
||||
|
||||
if ret != int(self.ret):
|
||||
raise Unsup(self)
|
||||
@@ -172,7 +180,7 @@ class Test(object):
|
||||
def compare(self, expect, result):
|
||||
match = {}
|
||||
|
||||
log.info(" compare");
|
||||
log.debug(" compare");
|
||||
|
||||
# For each expected event find all matching
|
||||
# events in result. Fail if there's not any.
|
||||
@@ -187,10 +195,11 @@ class Test(object):
|
||||
else:
|
||||
log.debug(" ->FAIL");
|
||||
|
||||
log.info(" match: [%s] matches %s" % (exp_name, str(exp_list)))
|
||||
log.debug(" match: [%s] matches %s" % (exp_name, str(exp_list)))
|
||||
|
||||
# we did not any matching event - fail
|
||||
if (not exp_list):
|
||||
exp_event.diff(res_event)
|
||||
raise Fail(self, 'match failure');
|
||||
|
||||
match[exp_name] = exp_list
|
||||
@@ -208,10 +217,10 @@ class Test(object):
|
||||
if res_group not in match[group]:
|
||||
raise Fail(self, 'group failure')
|
||||
|
||||
log.info(" group: [%s] matches group leader %s" %
|
||||
log.debug(" group: [%s] matches group leader %s" %
|
||||
(exp_name, str(match[group])))
|
||||
|
||||
log.info(" matched")
|
||||
log.debug(" matched")
|
||||
|
||||
def resolve_groups(self, events):
|
||||
for name, event in events.items():
|
||||
@@ -233,7 +242,7 @@ class Test(object):
|
||||
self.run_cmd(tempdir);
|
||||
|
||||
# load events expectation for the test
|
||||
log.info(" loading result events");
|
||||
log.debug(" loading result events");
|
||||
for f in glob.glob(tempdir + '/event*'):
|
||||
self.load_events(f, self.result);
|
||||
|
||||
|
@@ -7,7 +7,7 @@ size=96
|
||||
config=0
|
||||
sample_period=4000
|
||||
sample_type=263
|
||||
read_format=7
|
||||
read_format=0
|
||||
disabled=1
|
||||
inherit=1
|
||||
pinned=0
|
||||
|
@@ -6,12 +6,14 @@ args = --group -e cycles,instructions kill >/dev/null 2>&1
|
||||
fd=1
|
||||
group_fd=-1
|
||||
sample_type=327
|
||||
read_format=4
|
||||
|
||||
[event-2:base-record]
|
||||
fd=2
|
||||
group_fd=1
|
||||
config=1
|
||||
sample_type=327
|
||||
read_format=4
|
||||
mmap=0
|
||||
comm=0
|
||||
enable_on_exec=0
|
||||
|
@@ -1,11 +1,12 @@
|
||||
[config]
|
||||
command = record
|
||||
args = -e '{cycles,instructions}' kill >/tmp/krava 2>&1
|
||||
args = -e '{cycles,instructions}' kill >/dev/null 2>&1
|
||||
|
||||
[event-1:base-record]
|
||||
fd=1
|
||||
group_fd=-1
|
||||
sample_type=327
|
||||
read_format=4
|
||||
|
||||
[event-2:base-record]
|
||||
fd=2
|
||||
@@ -13,6 +14,7 @@ group_fd=1
|
||||
type=0
|
||||
config=1
|
||||
sample_type=327
|
||||
read_format=4
|
||||
mmap=0
|
||||
comm=0
|
||||
enable_on_exec=0
|
||||
|
@@ -4,6 +4,7 @@
|
||||
* Builtin regression testing command: ever growing number of sanity tests
|
||||
*/
|
||||
#include "builtin.h"
|
||||
#include "intlist.h"
|
||||
#include "tests.h"
|
||||
#include "debug.h"
|
||||
#include "color.h"
|
||||
@@ -68,6 +69,14 @@ static struct test {
|
||||
.desc = "struct perf_event_attr setup",
|
||||
.func = test__attr,
|
||||
},
|
||||
{
|
||||
.desc = "Test matching and linking mutliple hists",
|
||||
.func = test__hists_link,
|
||||
},
|
||||
{
|
||||
.desc = "Try 'use perf' in python, checking link problems",
|
||||
.func = test__python_use,
|
||||
},
|
||||
{
|
||||
.func = NULL,
|
||||
},
|
||||
@@ -97,7 +106,7 @@ static bool perf_test__matches(int curr, int argc, const char *argv[])
|
||||
return false;
|
||||
}
|
||||
|
||||
static int __cmd_test(int argc, const char *argv[])
|
||||
static int __cmd_test(int argc, const char *argv[], struct intlist *skiplist)
|
||||
{
|
||||
int i = 0;
|
||||
int width = 0;
|
||||
@@ -118,13 +127,28 @@ static int __cmd_test(int argc, const char *argv[])
|
||||
continue;
|
||||
|
||||
pr_info("%2d: %-*s:", i, width, tests[curr].desc);
|
||||
|
||||
if (intlist__find(skiplist, i)) {
|
||||
color_fprintf(stderr, PERF_COLOR_YELLOW, " Skip (user override)\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
switch (err) {
|
||||
case TEST_OK:
|
||||
pr_info(" Ok\n");
|
||||
break;
|
||||
case TEST_SKIP:
|
||||
color_fprintf(stderr, PERF_COLOR_YELLOW, " Skip\n");
|
||||
break;
|
||||
case TEST_FAIL:
|
||||
default:
|
||||
color_fprintf(stderr, PERF_COLOR_RED, " FAILED!\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -152,11 +176,14 @@ int cmd_test(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
"perf test [<options>] [{list <test-name-fragment>|[<test-name-fragments>|<test-numbers>]}]",
|
||||
NULL,
|
||||
};
|
||||
const char *skip = NULL;
|
||||
const struct option test_options[] = {
|
||||
OPT_STRING('s', "skip", &skip, "tests", "tests to skip"),
|
||||
OPT_INCR('v', "verbose", &verbose,
|
||||
"be more verbose (show symbol address, etc)"),
|
||||
OPT_END()
|
||||
};
|
||||
struct intlist *skiplist = NULL;
|
||||
|
||||
argc = parse_options(argc, argv, test_options, test_usage, 0);
|
||||
if (argc >= 1 && !strcmp(argv[0], "list"))
|
||||
@@ -169,5 +196,8 @@ int cmd_test(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
if (symbol__init() < 0)
|
||||
return -1;
|
||||
|
||||
return __cmd_test(argc, argv);
|
||||
if (skip != NULL)
|
||||
skiplist = intlist__new(skip);
|
||||
|
||||
return __cmd_test(argc, argv, skiplist);
|
||||
}
|
||||
|
@@ -22,7 +22,7 @@ static int perf_evsel__roundtrip_cache_name_test(void)
|
||||
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);
|
||||
err = parse_events(evlist, name);
|
||||
if (err)
|
||||
ret = err;
|
||||
}
|
||||
@@ -70,7 +70,7 @@ static int __perf_evsel__name_array_test(const char *names[], int nr_names)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < nr_names; ++i) {
|
||||
err = parse_events(evlist, names[i], 0);
|
||||
err = parse_events(evlist, names[i]);
|
||||
if (err) {
|
||||
pr_debug("failed to parse event '%s', err %d\n",
|
||||
names[i], err);
|
||||
|
500
tools/perf/tests/hists_link.c
Normal file
500
tools/perf/tests/hists_link.c
Normal file
@@ -0,0 +1,500 @@
|
||||
#include "perf.h"
|
||||
#include "tests.h"
|
||||
#include "debug.h"
|
||||
#include "symbol.h"
|
||||
#include "sort.h"
|
||||
#include "evsel.h"
|
||||
#include "evlist.h"
|
||||
#include "machine.h"
|
||||
#include "thread.h"
|
||||
#include "parse-events.h"
|
||||
|
||||
static struct {
|
||||
u32 pid;
|
||||
const char *comm;
|
||||
} fake_threads[] = {
|
||||
{ 100, "perf" },
|
||||
{ 200, "perf" },
|
||||
{ 300, "bash" },
|
||||
};
|
||||
|
||||
static struct {
|
||||
u32 pid;
|
||||
u64 start;
|
||||
const char *filename;
|
||||
} fake_mmap_info[] = {
|
||||
{ 100, 0x40000, "perf" },
|
||||
{ 100, 0x50000, "libc" },
|
||||
{ 100, 0xf0000, "[kernel]" },
|
||||
{ 200, 0x40000, "perf" },
|
||||
{ 200, 0x50000, "libc" },
|
||||
{ 200, 0xf0000, "[kernel]" },
|
||||
{ 300, 0x40000, "bash" },
|
||||
{ 300, 0x50000, "libc" },
|
||||
{ 300, 0xf0000, "[kernel]" },
|
||||
};
|
||||
|
||||
struct fake_sym {
|
||||
u64 start;
|
||||
u64 length;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
static struct fake_sym perf_syms[] = {
|
||||
{ 700, 100, "main" },
|
||||
{ 800, 100, "run_command" },
|
||||
{ 900, 100, "cmd_record" },
|
||||
};
|
||||
|
||||
static struct fake_sym bash_syms[] = {
|
||||
{ 700, 100, "main" },
|
||||
{ 800, 100, "xmalloc" },
|
||||
{ 900, 100, "xfree" },
|
||||
};
|
||||
|
||||
static struct fake_sym libc_syms[] = {
|
||||
{ 700, 100, "malloc" },
|
||||
{ 800, 100, "free" },
|
||||
{ 900, 100, "realloc" },
|
||||
};
|
||||
|
||||
static struct fake_sym kernel_syms[] = {
|
||||
{ 700, 100, "schedule" },
|
||||
{ 800, 100, "page_fault" },
|
||||
{ 900, 100, "sys_perf_event_open" },
|
||||
};
|
||||
|
||||
static struct {
|
||||
const char *dso_name;
|
||||
struct fake_sym *syms;
|
||||
size_t nr_syms;
|
||||
} fake_symbols[] = {
|
||||
{ "perf", perf_syms, ARRAY_SIZE(perf_syms) },
|
||||
{ "bash", bash_syms, ARRAY_SIZE(bash_syms) },
|
||||
{ "libc", libc_syms, ARRAY_SIZE(libc_syms) },
|
||||
{ "[kernel]", kernel_syms, ARRAY_SIZE(kernel_syms) },
|
||||
};
|
||||
|
||||
static struct machine *setup_fake_machine(struct machines *machines)
|
||||
{
|
||||
struct machine *machine = machines__find(machines, HOST_KERNEL_ID);
|
||||
size_t i;
|
||||
|
||||
if (machine == NULL) {
|
||||
pr_debug("Not enough memory for machine setup\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(fake_threads); i++) {
|
||||
struct thread *thread;
|
||||
|
||||
thread = machine__findnew_thread(machine, fake_threads[i].pid);
|
||||
if (thread == NULL)
|
||||
goto out;
|
||||
|
||||
thread__set_comm(thread, fake_threads[i].comm);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(fake_mmap_info); i++) {
|
||||
union perf_event fake_mmap_event = {
|
||||
.mmap = {
|
||||
.header = { .misc = PERF_RECORD_MISC_USER, },
|
||||
.pid = fake_mmap_info[i].pid,
|
||||
.start = fake_mmap_info[i].start,
|
||||
.len = 0x1000ULL,
|
||||
.pgoff = 0ULL,
|
||||
},
|
||||
};
|
||||
|
||||
strcpy(fake_mmap_event.mmap.filename,
|
||||
fake_mmap_info[i].filename);
|
||||
|
||||
machine__process_mmap_event(machine, &fake_mmap_event);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(fake_symbols); i++) {
|
||||
size_t k;
|
||||
struct dso *dso;
|
||||
|
||||
dso = __dsos__findnew(&machine->user_dsos,
|
||||
fake_symbols[i].dso_name);
|
||||
if (dso == NULL)
|
||||
goto out;
|
||||
|
||||
/* emulate dso__load() */
|
||||
dso__set_loaded(dso, MAP__FUNCTION);
|
||||
|
||||
for (k = 0; k < fake_symbols[i].nr_syms; k++) {
|
||||
struct symbol *sym;
|
||||
struct fake_sym *fsym = &fake_symbols[i].syms[k];
|
||||
|
||||
sym = symbol__new(fsym->start, fsym->length,
|
||||
STB_GLOBAL, fsym->name);
|
||||
if (sym == NULL)
|
||||
goto out;
|
||||
|
||||
symbols__insert(&dso->symbols[MAP__FUNCTION], sym);
|
||||
}
|
||||
}
|
||||
|
||||
return machine;
|
||||
|
||||
out:
|
||||
pr_debug("Not enough memory for machine setup\n");
|
||||
machine__delete_threads(machine);
|
||||
machine__delete(machine);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct sample {
|
||||
u32 pid;
|
||||
u64 ip;
|
||||
struct thread *thread;
|
||||
struct map *map;
|
||||
struct symbol *sym;
|
||||
};
|
||||
|
||||
static struct sample fake_common_samples[] = {
|
||||
/* perf [kernel] schedule() */
|
||||
{ .pid = 100, .ip = 0xf0000 + 700, },
|
||||
/* perf [perf] main() */
|
||||
{ .pid = 200, .ip = 0x40000 + 700, },
|
||||
/* perf [perf] cmd_record() */
|
||||
{ .pid = 200, .ip = 0x40000 + 900, },
|
||||
/* bash [bash] xmalloc() */
|
||||
{ .pid = 300, .ip = 0x40000 + 800, },
|
||||
/* bash [libc] malloc() */
|
||||
{ .pid = 300, .ip = 0x50000 + 700, },
|
||||
};
|
||||
|
||||
static struct sample fake_samples[][5] = {
|
||||
{
|
||||
/* perf [perf] run_command() */
|
||||
{ .pid = 100, .ip = 0x40000 + 800, },
|
||||
/* perf [libc] malloc() */
|
||||
{ .pid = 100, .ip = 0x50000 + 700, },
|
||||
/* perf [kernel] page_fault() */
|
||||
{ .pid = 100, .ip = 0xf0000 + 800, },
|
||||
/* perf [kernel] sys_perf_event_open() */
|
||||
{ .pid = 200, .ip = 0xf0000 + 900, },
|
||||
/* bash [libc] free() */
|
||||
{ .pid = 300, .ip = 0x50000 + 800, },
|
||||
},
|
||||
{
|
||||
/* perf [libc] free() */
|
||||
{ .pid = 200, .ip = 0x50000 + 800, },
|
||||
/* bash [libc] malloc() */
|
||||
{ .pid = 300, .ip = 0x50000 + 700, }, /* will be merged */
|
||||
/* bash [bash] xfee() */
|
||||
{ .pid = 300, .ip = 0x40000 + 900, },
|
||||
/* bash [libc] realloc() */
|
||||
{ .pid = 300, .ip = 0x50000 + 900, },
|
||||
/* bash [kernel] page_fault() */
|
||||
{ .pid = 300, .ip = 0xf0000 + 800, },
|
||||
},
|
||||
};
|
||||
|
||||
static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine)
|
||||
{
|
||||
struct perf_evsel *evsel;
|
||||
struct addr_location al;
|
||||
struct hist_entry *he;
|
||||
struct perf_sample sample = { .cpu = 0, };
|
||||
size_t i = 0, k;
|
||||
|
||||
/*
|
||||
* each evsel will have 10 samples - 5 common and 5 distinct.
|
||||
* However the second evsel also has a collapsed entry for
|
||||
* "bash [libc] malloc" so total 9 entries will be in the tree.
|
||||
*/
|
||||
list_for_each_entry(evsel, &evlist->entries, node) {
|
||||
for (k = 0; k < ARRAY_SIZE(fake_common_samples); k++) {
|
||||
const union perf_event event = {
|
||||
.ip = {
|
||||
.header = {
|
||||
.misc = PERF_RECORD_MISC_USER,
|
||||
},
|
||||
.pid = fake_common_samples[k].pid,
|
||||
.ip = fake_common_samples[k].ip,
|
||||
},
|
||||
};
|
||||
|
||||
if (perf_event__preprocess_sample(&event, machine, &al,
|
||||
&sample, 0) < 0)
|
||||
goto out;
|
||||
|
||||
he = __hists__add_entry(&evsel->hists, &al, NULL, 1);
|
||||
if (he == NULL)
|
||||
goto out;
|
||||
|
||||
fake_common_samples[k].thread = al.thread;
|
||||
fake_common_samples[k].map = al.map;
|
||||
fake_common_samples[k].sym = al.sym;
|
||||
}
|
||||
|
||||
for (k = 0; k < ARRAY_SIZE(fake_samples[i]); k++) {
|
||||
const union perf_event event = {
|
||||
.ip = {
|
||||
.header = {
|
||||
.misc = PERF_RECORD_MISC_USER,
|
||||
},
|
||||
.pid = fake_samples[i][k].pid,
|
||||
.ip = fake_samples[i][k].ip,
|
||||
},
|
||||
};
|
||||
|
||||
if (perf_event__preprocess_sample(&event, machine, &al,
|
||||
&sample, 0) < 0)
|
||||
goto out;
|
||||
|
||||
he = __hists__add_entry(&evsel->hists, &al, NULL, 1);
|
||||
if (he == NULL)
|
||||
goto out;
|
||||
|
||||
fake_samples[i][k].thread = al.thread;
|
||||
fake_samples[i][k].map = al.map;
|
||||
fake_samples[i][k].sym = al.sym;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
pr_debug("Not enough memory for adding a hist entry\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int find_sample(struct sample *samples, size_t nr_samples,
|
||||
struct thread *t, struct map *m, struct symbol *s)
|
||||
{
|
||||
while (nr_samples--) {
|
||||
if (samples->thread == t && samples->map == m &&
|
||||
samples->sym == s)
|
||||
return 1;
|
||||
samples++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __validate_match(struct hists *hists)
|
||||
{
|
||||
size_t count = 0;
|
||||
struct rb_root *root;
|
||||
struct rb_node *node;
|
||||
|
||||
/*
|
||||
* Only entries from fake_common_samples should have a pair.
|
||||
*/
|
||||
if (sort__need_collapse)
|
||||
root = &hists->entries_collapsed;
|
||||
else
|
||||
root = hists->entries_in;
|
||||
|
||||
node = rb_first(root);
|
||||
while (node) {
|
||||
struct hist_entry *he;
|
||||
|
||||
he = rb_entry(node, struct hist_entry, rb_node_in);
|
||||
|
||||
if (hist_entry__has_pairs(he)) {
|
||||
if (find_sample(fake_common_samples,
|
||||
ARRAY_SIZE(fake_common_samples),
|
||||
he->thread, he->ms.map, he->ms.sym)) {
|
||||
count++;
|
||||
} else {
|
||||
pr_debug("Can't find the matched entry\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
node = rb_next(node);
|
||||
}
|
||||
|
||||
if (count != ARRAY_SIZE(fake_common_samples)) {
|
||||
pr_debug("Invalid count for matched entries: %zd of %zd\n",
|
||||
count, ARRAY_SIZE(fake_common_samples));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int validate_match(struct hists *leader, struct hists *other)
|
||||
{
|
||||
return __validate_match(leader) || __validate_match(other);
|
||||
}
|
||||
|
||||
static int __validate_link(struct hists *hists, int idx)
|
||||
{
|
||||
size_t count = 0;
|
||||
size_t count_pair = 0;
|
||||
size_t count_dummy = 0;
|
||||
struct rb_root *root;
|
||||
struct rb_node *node;
|
||||
|
||||
/*
|
||||
* Leader hists (idx = 0) will have dummy entries from other,
|
||||
* and some entries will have no pair. However every entry
|
||||
* in other hists should have (dummy) pair.
|
||||
*/
|
||||
if (sort__need_collapse)
|
||||
root = &hists->entries_collapsed;
|
||||
else
|
||||
root = hists->entries_in;
|
||||
|
||||
node = rb_first(root);
|
||||
while (node) {
|
||||
struct hist_entry *he;
|
||||
|
||||
he = rb_entry(node, struct hist_entry, rb_node_in);
|
||||
|
||||
if (hist_entry__has_pairs(he)) {
|
||||
if (!find_sample(fake_common_samples,
|
||||
ARRAY_SIZE(fake_common_samples),
|
||||
he->thread, he->ms.map, he->ms.sym) &&
|
||||
!find_sample(fake_samples[idx],
|
||||
ARRAY_SIZE(fake_samples[idx]),
|
||||
he->thread, he->ms.map, he->ms.sym)) {
|
||||
count_dummy++;
|
||||
}
|
||||
count_pair++;
|
||||
} else if (idx) {
|
||||
pr_debug("A entry from the other hists should have pair\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
count++;
|
||||
node = rb_next(node);
|
||||
}
|
||||
|
||||
/*
|
||||
* Note that we have a entry collapsed in the other (idx = 1) hists.
|
||||
*/
|
||||
if (idx == 0) {
|
||||
if (count_dummy != ARRAY_SIZE(fake_samples[1]) - 1) {
|
||||
pr_debug("Invalid count of dummy entries: %zd of %zd\n",
|
||||
count_dummy, ARRAY_SIZE(fake_samples[1]) - 1);
|
||||
return -1;
|
||||
}
|
||||
if (count != count_pair + ARRAY_SIZE(fake_samples[0])) {
|
||||
pr_debug("Invalid count of total leader entries: %zd of %zd\n",
|
||||
count, count_pair + ARRAY_SIZE(fake_samples[0]));
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
if (count != count_pair) {
|
||||
pr_debug("Invalid count of total other entries: %zd of %zd\n",
|
||||
count, count_pair);
|
||||
return -1;
|
||||
}
|
||||
if (count_dummy > 0) {
|
||||
pr_debug("Other hists should not have dummy entries: %zd\n",
|
||||
count_dummy);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int validate_link(struct hists *leader, struct hists *other)
|
||||
{
|
||||
return __validate_link(leader, 0) || __validate_link(other, 1);
|
||||
}
|
||||
|
||||
static void print_hists(struct hists *hists)
|
||||
{
|
||||
int i = 0;
|
||||
struct rb_root *root;
|
||||
struct rb_node *node;
|
||||
|
||||
if (sort__need_collapse)
|
||||
root = &hists->entries_collapsed;
|
||||
else
|
||||
root = hists->entries_in;
|
||||
|
||||
pr_info("----- %s --------\n", __func__);
|
||||
node = rb_first(root);
|
||||
while (node) {
|
||||
struct hist_entry *he;
|
||||
|
||||
he = rb_entry(node, struct hist_entry, rb_node_in);
|
||||
|
||||
pr_info("%2d: entry: %-8s [%-8s] %20s: period = %"PRIu64"\n",
|
||||
i, he->thread->comm, he->ms.map->dso->short_name,
|
||||
he->ms.sym->name, he->stat.period);
|
||||
|
||||
i++;
|
||||
node = rb_next(node);
|
||||
}
|
||||
}
|
||||
|
||||
int test__hists_link(void)
|
||||
{
|
||||
int err = -1;
|
||||
struct machines machines;
|
||||
struct machine *machine = NULL;
|
||||
struct perf_evsel *evsel, *first;
|
||||
struct perf_evlist *evlist = perf_evlist__new(NULL, NULL);
|
||||
|
||||
if (evlist == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
err = parse_events(evlist, "cpu-clock");
|
||||
if (err)
|
||||
goto out;
|
||||
err = parse_events(evlist, "task-clock");
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
/* default sort order (comm,dso,sym) will be used */
|
||||
if (setup_sorting() < 0)
|
||||
goto out;
|
||||
|
||||
machines__init(&machines);
|
||||
|
||||
/* setup threads/dso/map/symbols also */
|
||||
machine = setup_fake_machine(&machines);
|
||||
if (!machine)
|
||||
goto out;
|
||||
|
||||
if (verbose > 1)
|
||||
machine__fprintf(machine, stderr);
|
||||
|
||||
/* process sample events */
|
||||
err = add_hist_entries(evlist, machine);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
list_for_each_entry(evsel, &evlist->entries, node) {
|
||||
hists__collapse_resort(&evsel->hists);
|
||||
|
||||
if (verbose > 2)
|
||||
print_hists(&evsel->hists);
|
||||
}
|
||||
|
||||
first = perf_evlist__first(evlist);
|
||||
evsel = perf_evlist__last(evlist);
|
||||
|
||||
/* match common entries */
|
||||
hists__match(&first->hists, &evsel->hists);
|
||||
err = validate_match(&first->hists, &evsel->hists);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
/* link common and/or dummy entries */
|
||||
hists__link(&first->hists, &evsel->hists);
|
||||
err = validate_link(&first->hists, &evsel->hists);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = 0;
|
||||
|
||||
out:
|
||||
/* tear down everything */
|
||||
perf_evlist__delete(evlist);
|
||||
machines__exit(&machines);
|
||||
|
||||
return err;
|
||||
}
|
@@ -22,36 +22,16 @@ int test__basic_mmap(void)
|
||||
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");
|
||||
@@ -79,18 +59,19 @@ int test__basic_mmap(void)
|
||||
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);
|
||||
char name[64];
|
||||
|
||||
snprintf(name, sizeof(name), "sys_enter_%s", syscall_names[i]);
|
||||
evsels[i] = perf_evsel__newtp("syscalls", name, i);
|
||||
if (evsels[i] == NULL) {
|
||||
pr_debug("perf_evsel__new\n");
|
||||
goto out_free_evlist;
|
||||
}
|
||||
|
||||
evsels[i]->attr.wakeup_events = 1;
|
||||
perf_evsel__set_sample_id(evsels[i]);
|
||||
|
||||
perf_evlist__add(evlist, evsels[i]);
|
||||
|
||||
if (perf_evsel__open(evsels[i], cpus, threads) < 0) {
|
||||
@@ -99,6 +80,9 @@ int test__basic_mmap(void)
|
||||
strerror(errno));
|
||||
goto out_close_fd;
|
||||
}
|
||||
|
||||
nr_events[i] = 0;
|
||||
expected_nr_events[i] = 1 + rand() % 127;
|
||||
}
|
||||
|
||||
if (perf_evlist__mmap(evlist, 128, true) < 0) {
|
||||
@@ -128,6 +112,7 @@ int test__basic_mmap(void)
|
||||
goto out_munmap;
|
||||
}
|
||||
|
||||
err = -1;
|
||||
evsel = perf_evlist__id2evsel(evlist, sample.id);
|
||||
if (evsel == NULL) {
|
||||
pr_debug("event with id %" PRIu64
|
||||
@@ -137,16 +122,17 @@ int test__basic_mmap(void)
|
||||
nr_events[evsel->idx]++;
|
||||
}
|
||||
|
||||
err = 0;
|
||||
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]);
|
||||
err = -1;
|
||||
goto out_munmap;
|
||||
}
|
||||
}
|
||||
|
||||
err = 0;
|
||||
out_munmap:
|
||||
perf_evlist__munmap(evlist);
|
||||
out_close_fd:
|
||||
|
@@ -7,20 +7,12 @@
|
||||
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");
|
||||
struct thread_map *threads = thread_map__new(-1, getpid(), UINT_MAX);
|
||||
|
||||
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;
|
||||
@@ -32,15 +24,11 @@ int test__open_syscall_event_on_all_cpus(void)
|
||||
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);
|
||||
evsel = perf_evsel__newtp("syscalls", "sys_enter_open", 0);
|
||||
if (evsel == NULL) {
|
||||
pr_debug("perf_evsel__new\n");
|
||||
pr_debug("is debugfs mounted on /sys/kernel/debug?\n");
|
||||
goto out_thread_map_delete;
|
||||
}
|
||||
|
||||
@@ -110,6 +98,7 @@ int test__open_syscall_event_on_all_cpus(void)
|
||||
}
|
||||
}
|
||||
|
||||
perf_evsel__free_counts(evsel);
|
||||
out_close_fd:
|
||||
perf_evsel__close_fd(evsel, 1, threads->nr);
|
||||
out_evsel_delete:
|
||||
|
@@ -6,29 +6,18 @@
|
||||
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");
|
||||
struct thread_map *threads = thread_map__new(-1, getpid(), UINT_MAX);
|
||||
|
||||
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);
|
||||
evsel = perf_evsel__newtp("syscalls", "sys_enter_open", 0);
|
||||
if (evsel == NULL) {
|
||||
pr_debug("perf_evsel__new\n");
|
||||
pr_debug("is debugfs mounted on /sys/kernel/debug?\n");
|
||||
goto out_thread_map_delete;
|
||||
}
|
||||
|
||||
|
@@ -3,6 +3,7 @@
|
||||
#include "evsel.h"
|
||||
#include "evlist.h"
|
||||
#include "sysfs.h"
|
||||
#include "debugfs.h"
|
||||
#include "tests.h"
|
||||
#include <linux/hw_breakpoint.h>
|
||||
|
||||
@@ -22,6 +23,7 @@ static int test__checkevent_tracepoint(struct perf_evlist *evlist)
|
||||
struct perf_evsel *evsel = perf_evlist__first(evlist);
|
||||
|
||||
TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
|
||||
TEST_ASSERT_VAL("wrong number of groups", 0 == evlist->nr_groups);
|
||||
TEST_ASSERT_VAL("wrong type", PERF_TYPE_TRACEPOINT == evsel->attr.type);
|
||||
TEST_ASSERT_VAL("wrong sample_type",
|
||||
PERF_TP_SAMPLE_TYPE == evsel->attr.sample_type);
|
||||
@@ -34,6 +36,7 @@ static int test__checkevent_tracepoint_multi(struct perf_evlist *evlist)
|
||||
struct perf_evsel *evsel;
|
||||
|
||||
TEST_ASSERT_VAL("wrong number of entries", evlist->nr_entries > 1);
|
||||
TEST_ASSERT_VAL("wrong number of groups", 0 == evlist->nr_groups);
|
||||
|
||||
list_for_each_entry(evsel, &evlist->entries, node) {
|
||||
TEST_ASSERT_VAL("wrong type",
|
||||
@@ -463,10 +466,10 @@ static int test__checkevent_pmu_events(struct perf_evlist *evlist)
|
||||
|
||||
static int test__checkterms_simple(struct list_head *terms)
|
||||
{
|
||||
struct parse_events__term *term;
|
||||
struct parse_events_term *term;
|
||||
|
||||
/* config=10 */
|
||||
term = list_entry(terms->next, struct parse_events__term, list);
|
||||
term = list_entry(terms->next, struct parse_events_term, list);
|
||||
TEST_ASSERT_VAL("wrong type term",
|
||||
term->type_term == PARSE_EVENTS__TERM_TYPE_CONFIG);
|
||||
TEST_ASSERT_VAL("wrong type val",
|
||||
@@ -475,7 +478,7 @@ static int test__checkterms_simple(struct list_head *terms)
|
||||
TEST_ASSERT_VAL("wrong config", !term->config);
|
||||
|
||||
/* config1 */
|
||||
term = list_entry(term->list.next, struct parse_events__term, list);
|
||||
term = list_entry(term->list.next, struct parse_events_term, list);
|
||||
TEST_ASSERT_VAL("wrong type term",
|
||||
term->type_term == PARSE_EVENTS__TERM_TYPE_CONFIG1);
|
||||
TEST_ASSERT_VAL("wrong type val",
|
||||
@@ -484,7 +487,7 @@ static int test__checkterms_simple(struct list_head *terms)
|
||||
TEST_ASSERT_VAL("wrong config", !term->config);
|
||||
|
||||
/* config2=3 */
|
||||
term = list_entry(term->list.next, struct parse_events__term, list);
|
||||
term = list_entry(term->list.next, struct parse_events_term, list);
|
||||
TEST_ASSERT_VAL("wrong type term",
|
||||
term->type_term == PARSE_EVENTS__TERM_TYPE_CONFIG2);
|
||||
TEST_ASSERT_VAL("wrong type val",
|
||||
@@ -493,7 +496,7 @@ static int test__checkterms_simple(struct list_head *terms)
|
||||
TEST_ASSERT_VAL("wrong config", !term->config);
|
||||
|
||||
/* umask=1*/
|
||||
term = list_entry(term->list.next, struct parse_events__term, list);
|
||||
term = list_entry(term->list.next, struct parse_events_term, list);
|
||||
TEST_ASSERT_VAL("wrong type term",
|
||||
term->type_term == PARSE_EVENTS__TERM_TYPE_USER);
|
||||
TEST_ASSERT_VAL("wrong type val",
|
||||
@@ -509,6 +512,7 @@ static int test__group1(struct perf_evlist *evlist)
|
||||
struct perf_evsel *evsel, *leader;
|
||||
|
||||
TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries);
|
||||
TEST_ASSERT_VAL("wrong number of groups", 1 == evlist->nr_groups);
|
||||
|
||||
/* instructions:k */
|
||||
evsel = leader = perf_evlist__first(evlist);
|
||||
@@ -521,7 +525,9 @@ 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", !perf_evsel__is_group_member(evsel));
|
||||
TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
|
||||
TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2);
|
||||
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
|
||||
|
||||
/* cycles:upp */
|
||||
evsel = perf_evsel__next(evsel);
|
||||
@@ -536,6 +542,7 @@ static int test__group1(struct perf_evlist *evlist)
|
||||
TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
|
||||
TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 2);
|
||||
TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
|
||||
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -545,6 +552,7 @@ static int test__group2(struct perf_evlist *evlist)
|
||||
struct perf_evsel *evsel, *leader;
|
||||
|
||||
TEST_ASSERT_VAL("wrong number of entries", 3 == evlist->nr_entries);
|
||||
TEST_ASSERT_VAL("wrong number of groups", 1 == evlist->nr_groups);
|
||||
|
||||
/* faults + :ku modifier */
|
||||
evsel = leader = perf_evlist__first(evlist);
|
||||
@@ -557,7 +565,9 @@ 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", !perf_evsel__is_group_member(evsel));
|
||||
TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
|
||||
TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2);
|
||||
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
|
||||
|
||||
/* cache-references + :u modifier */
|
||||
evsel = perf_evsel__next(evsel);
|
||||
@@ -567,10 +577,11 @@ static int test__group2(struct perf_evlist *evlist)
|
||||
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 exclude guest", !evsel->attr.exclude_guest);
|
||||
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 == leader);
|
||||
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
|
||||
|
||||
/* cycles:k */
|
||||
evsel = perf_evsel__next(evsel);
|
||||
@@ -583,7 +594,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", !perf_evsel__is_group_member(evsel));
|
||||
TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -593,6 +604,7 @@ static int test__group3(struct perf_evlist *evlist __maybe_unused)
|
||||
struct perf_evsel *evsel, *leader;
|
||||
|
||||
TEST_ASSERT_VAL("wrong number of entries", 5 == evlist->nr_entries);
|
||||
TEST_ASSERT_VAL("wrong number of groups", 2 == evlist->nr_groups);
|
||||
|
||||
/* group1 syscalls:sys_enter_open:H */
|
||||
evsel = leader = perf_evlist__first(evlist);
|
||||
@@ -606,9 +618,11 @@ 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", !perf_evsel__is_group_member(evsel));
|
||||
TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
|
||||
TEST_ASSERT_VAL("wrong group name",
|
||||
!strcmp(leader->group_name, "group1"));
|
||||
TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2);
|
||||
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
|
||||
|
||||
/* group1 cycles:kppp */
|
||||
evsel = perf_evsel__next(evsel);
|
||||
@@ -624,6 +638,7 @@ static int test__group3(struct perf_evlist *evlist __maybe_unused)
|
||||
TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 3);
|
||||
TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
|
||||
TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
|
||||
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
|
||||
|
||||
/* group2 cycles + G modifier */
|
||||
evsel = leader = perf_evsel__next(evsel);
|
||||
@@ -636,9 +651,11 @@ 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", !perf_evsel__is_group_member(evsel));
|
||||
TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
|
||||
TEST_ASSERT_VAL("wrong group name",
|
||||
!strcmp(leader->group_name, "group2"));
|
||||
TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2);
|
||||
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
|
||||
|
||||
/* group2 1:3 + G modifier */
|
||||
evsel = perf_evsel__next(evsel);
|
||||
@@ -651,6 +668,7 @@ static int test__group3(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 leader", evsel->leader == leader);
|
||||
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
|
||||
|
||||
/* instructions:u */
|
||||
evsel = perf_evsel__next(evsel);
|
||||
@@ -663,7 +681,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", !perf_evsel__is_group_member(evsel));
|
||||
TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -673,6 +691,7 @@ static int test__group4(struct perf_evlist *evlist __maybe_unused)
|
||||
struct perf_evsel *evsel, *leader;
|
||||
|
||||
TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries);
|
||||
TEST_ASSERT_VAL("wrong number of groups", 1 == evlist->nr_groups);
|
||||
|
||||
/* cycles:u + p */
|
||||
evsel = leader = perf_evlist__first(evlist);
|
||||
@@ -687,7 +706,9 @@ 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", !perf_evsel__is_group_member(evsel));
|
||||
TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
|
||||
TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2);
|
||||
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
|
||||
|
||||
/* instructions:kp + p */
|
||||
evsel = perf_evsel__next(evsel);
|
||||
@@ -702,6 +723,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 == 2);
|
||||
TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
|
||||
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -711,6 +733,7 @@ static int test__group5(struct perf_evlist *evlist __maybe_unused)
|
||||
struct perf_evsel *evsel, *leader;
|
||||
|
||||
TEST_ASSERT_VAL("wrong number of entries", 5 == evlist->nr_entries);
|
||||
TEST_ASSERT_VAL("wrong number of groups", 2 == evlist->nr_groups);
|
||||
|
||||
/* cycles + G */
|
||||
evsel = leader = perf_evlist__first(evlist);
|
||||
@@ -724,7 +747,9 @@ 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", !perf_evsel__is_group_member(evsel));
|
||||
TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
|
||||
TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2);
|
||||
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
|
||||
|
||||
/* instructions + G */
|
||||
evsel = perf_evsel__next(evsel);
|
||||
@@ -738,6 +763,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 leader", evsel->leader == leader);
|
||||
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
|
||||
|
||||
/* cycles:G */
|
||||
evsel = leader = perf_evsel__next(evsel);
|
||||
@@ -751,7 +777,9 @@ 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", !perf_evsel__is_group_member(evsel));
|
||||
TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
|
||||
TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2);
|
||||
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
|
||||
|
||||
/* instructions:G */
|
||||
evsel = perf_evsel__next(evsel);
|
||||
@@ -765,6 +793,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 leader", evsel->leader == leader);
|
||||
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
|
||||
|
||||
/* cycles */
|
||||
evsel = perf_evsel__next(evsel);
|
||||
@@ -777,18 +806,235 @@ 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", !perf_evsel__is_group_member(evsel));
|
||||
TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct test__event_st {
|
||||
static int test__group_gh1(struct perf_evlist *evlist)
|
||||
{
|
||||
struct perf_evsel *evsel, *leader;
|
||||
|
||||
TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries);
|
||||
TEST_ASSERT_VAL("wrong number of groups", 1 == evlist->nr_groups);
|
||||
|
||||
/* cycles + :H group modifier */
|
||||
evsel = leader = perf_evlist__first(evlist);
|
||||
TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
|
||||
TEST_ASSERT_VAL("wrong config",
|
||||
PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config);
|
||||
TEST_ASSERT_VAL("wrong 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 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 group name", !evsel->group_name);
|
||||
TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
|
||||
TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2);
|
||||
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
|
||||
|
||||
/* cache-misses:G + :H group modifier */
|
||||
evsel = perf_evsel__next(evsel);
|
||||
TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
|
||||
TEST_ASSERT_VAL("wrong config",
|
||||
PERF_COUNT_HW_CACHE_MISSES == evsel->attr.config);
|
||||
TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
|
||||
TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
|
||||
TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
|
||||
TEST_ASSERT_VAL("wrong 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 == leader);
|
||||
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test__group_gh2(struct perf_evlist *evlist)
|
||||
{
|
||||
struct perf_evsel *evsel, *leader;
|
||||
|
||||
TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries);
|
||||
TEST_ASSERT_VAL("wrong number of groups", 1 == evlist->nr_groups);
|
||||
|
||||
/* cycles + :G group modifier */
|
||||
evsel = leader = perf_evlist__first(evlist);
|
||||
TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
|
||||
TEST_ASSERT_VAL("wrong config",
|
||||
PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config);
|
||||
TEST_ASSERT_VAL("wrong 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 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 group name", !evsel->group_name);
|
||||
TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
|
||||
TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2);
|
||||
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
|
||||
|
||||
/* cache-misses:H + :G group modifier */
|
||||
evsel = perf_evsel__next(evsel);
|
||||
TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
|
||||
TEST_ASSERT_VAL("wrong config",
|
||||
PERF_COUNT_HW_CACHE_MISSES == evsel->attr.config);
|
||||
TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
|
||||
TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
|
||||
TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
|
||||
TEST_ASSERT_VAL("wrong 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 == leader);
|
||||
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test__group_gh3(struct perf_evlist *evlist)
|
||||
{
|
||||
struct perf_evsel *evsel, *leader;
|
||||
|
||||
TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries);
|
||||
TEST_ASSERT_VAL("wrong number of groups", 1 == evlist->nr_groups);
|
||||
|
||||
/* cycles:G + :u group modifier */
|
||||
evsel = leader = perf_evlist__first(evlist);
|
||||
TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
|
||||
TEST_ASSERT_VAL("wrong config",
|
||||
PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config);
|
||||
TEST_ASSERT_VAL("wrong 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 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 group name", !evsel->group_name);
|
||||
TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
|
||||
TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2);
|
||||
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
|
||||
|
||||
/* cache-misses:H + :u group modifier */
|
||||
evsel = perf_evsel__next(evsel);
|
||||
TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
|
||||
TEST_ASSERT_VAL("wrong config",
|
||||
PERF_COUNT_HW_CACHE_MISSES == evsel->attr.config);
|
||||
TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
|
||||
TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
|
||||
TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
|
||||
TEST_ASSERT_VAL("wrong 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 == leader);
|
||||
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test__group_gh4(struct perf_evlist *evlist)
|
||||
{
|
||||
struct perf_evsel *evsel, *leader;
|
||||
|
||||
TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries);
|
||||
TEST_ASSERT_VAL("wrong number of groups", 1 == evlist->nr_groups);
|
||||
|
||||
/* cycles:G + :uG group modifier */
|
||||
evsel = leader = perf_evlist__first(evlist);
|
||||
TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
|
||||
TEST_ASSERT_VAL("wrong config",
|
||||
PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config);
|
||||
TEST_ASSERT_VAL("wrong 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 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 group name", !evsel->group_name);
|
||||
TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
|
||||
TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2);
|
||||
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
|
||||
|
||||
/* cache-misses:H + :uG group modifier */
|
||||
evsel = perf_evsel__next(evsel);
|
||||
TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
|
||||
TEST_ASSERT_VAL("wrong config",
|
||||
PERF_COUNT_HW_CACHE_MISSES == evsel->attr.config);
|
||||
TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
|
||||
TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
|
||||
TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
|
||||
TEST_ASSERT_VAL("wrong 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 == leader);
|
||||
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int count_tracepoints(void)
|
||||
{
|
||||
char events_path[PATH_MAX];
|
||||
struct dirent *events_ent;
|
||||
DIR *events_dir;
|
||||
int cnt = 0;
|
||||
|
||||
scnprintf(events_path, PATH_MAX, "%s/tracing/events",
|
||||
debugfs_find_mountpoint());
|
||||
|
||||
events_dir = opendir(events_path);
|
||||
|
||||
TEST_ASSERT_VAL("Can't open events dir", events_dir);
|
||||
|
||||
while ((events_ent = readdir(events_dir))) {
|
||||
char sys_path[PATH_MAX];
|
||||
struct dirent *sys_ent;
|
||||
DIR *sys_dir;
|
||||
|
||||
if (!strcmp(events_ent->d_name, ".")
|
||||
|| !strcmp(events_ent->d_name, "..")
|
||||
|| !strcmp(events_ent->d_name, "enable")
|
||||
|| !strcmp(events_ent->d_name, "header_event")
|
||||
|| !strcmp(events_ent->d_name, "header_page"))
|
||||
continue;
|
||||
|
||||
scnprintf(sys_path, PATH_MAX, "%s/%s",
|
||||
events_path, events_ent->d_name);
|
||||
|
||||
sys_dir = opendir(sys_path);
|
||||
TEST_ASSERT_VAL("Can't open sys dir", sys_dir);
|
||||
|
||||
while ((sys_ent = readdir(sys_dir))) {
|
||||
if (!strcmp(sys_ent->d_name, ".")
|
||||
|| !strcmp(sys_ent->d_name, "..")
|
||||
|| !strcmp(sys_ent->d_name, "enable")
|
||||
|| !strcmp(sys_ent->d_name, "filter"))
|
||||
continue;
|
||||
|
||||
cnt++;
|
||||
}
|
||||
|
||||
closedir(sys_dir);
|
||||
}
|
||||
|
||||
closedir(events_dir);
|
||||
return cnt;
|
||||
}
|
||||
|
||||
static int test__all_tracepoints(struct perf_evlist *evlist)
|
||||
{
|
||||
TEST_ASSERT_VAL("wrong events count",
|
||||
count_tracepoints() == evlist->nr_entries);
|
||||
|
||||
return test__checkevent_tracepoint_multi(evlist);
|
||||
}
|
||||
|
||||
struct evlist_test {
|
||||
const char *name;
|
||||
__u32 type;
|
||||
int (*check)(struct perf_evlist *evlist);
|
||||
};
|
||||
|
||||
static struct test__event_st test__events[] = {
|
||||
static struct evlist_test test__events[] = {
|
||||
[0] = {
|
||||
.name = "syscalls:sys_enter_open",
|
||||
.check = test__checkevent_tracepoint,
|
||||
@@ -921,9 +1167,29 @@ static struct test__event_st test__events[] = {
|
||||
.name = "{cycles,instructions}:G,{cycles:G,instructions:G},cycles",
|
||||
.check = test__group5,
|
||||
},
|
||||
[33] = {
|
||||
.name = "*:*",
|
||||
.check = test__all_tracepoints,
|
||||
},
|
||||
[34] = {
|
||||
.name = "{cycles,cache-misses:G}:H",
|
||||
.check = test__group_gh1,
|
||||
},
|
||||
[35] = {
|
||||
.name = "{cycles,cache-misses:H}:G",
|
||||
.check = test__group_gh2,
|
||||
},
|
||||
[36] = {
|
||||
.name = "{cycles:G,cache-misses:H}:u",
|
||||
.check = test__group_gh3,
|
||||
},
|
||||
[37] = {
|
||||
.name = "{cycles:G,cache-misses:H}:uG",
|
||||
.check = test__group_gh4,
|
||||
},
|
||||
};
|
||||
|
||||
static struct test__event_st test__events_pmu[] = {
|
||||
static struct evlist_test test__events_pmu[] = {
|
||||
[0] = {
|
||||
.name = "cpu/config=10,config1,config2=3,period=1000/u",
|
||||
.check = test__checkevent_pmu,
|
||||
@@ -934,20 +1200,20 @@ static struct test__event_st test__events_pmu[] = {
|
||||
},
|
||||
};
|
||||
|
||||
struct test__term {
|
||||
struct terms_test {
|
||||
const char *str;
|
||||
__u32 type;
|
||||
int (*check)(struct list_head *terms);
|
||||
};
|
||||
|
||||
static struct test__term test__terms[] = {
|
||||
static struct terms_test test__terms[] = {
|
||||
[0] = {
|
||||
.str = "config=10,config1,config2=3,umask=1",
|
||||
.check = test__checkterms_simple,
|
||||
},
|
||||
};
|
||||
|
||||
static int test_event(struct test__event_st *e)
|
||||
static int test_event(struct evlist_test *e)
|
||||
{
|
||||
struct perf_evlist *evlist;
|
||||
int ret;
|
||||
@@ -956,7 +1222,7 @@ static int test_event(struct test__event_st *e)
|
||||
if (evlist == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = parse_events(evlist, e->name, 0);
|
||||
ret = parse_events(evlist, e->name);
|
||||
if (ret) {
|
||||
pr_debug("failed to parse event '%s', err %d\n",
|
||||
e->name, ret);
|
||||
@@ -969,13 +1235,13 @@ static int test_event(struct test__event_st *e)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int test_events(struct test__event_st *events, unsigned cnt)
|
||||
static int test_events(struct evlist_test *events, unsigned cnt)
|
||||
{
|
||||
int ret1, ret2 = 0;
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < cnt; i++) {
|
||||
struct test__event_st *e = &events[i];
|
||||
struct evlist_test *e = &events[i];
|
||||
|
||||
pr_debug("running test %d '%s'\n", i, e->name);
|
||||
ret1 = test_event(e);
|
||||
@@ -986,7 +1252,7 @@ static int test_events(struct test__event_st *events, unsigned cnt)
|
||||
return ret2;
|
||||
}
|
||||
|
||||
static int test_term(struct test__term *t)
|
||||
static int test_term(struct terms_test *t)
|
||||
{
|
||||
struct list_head *terms;
|
||||
int ret;
|
||||
@@ -1010,13 +1276,13 @@ static int test_term(struct test__term *t)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int test_terms(struct test__term *terms, unsigned cnt)
|
||||
static int test_terms(struct terms_test *terms, unsigned cnt)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < cnt; i++) {
|
||||
struct test__term *t = &terms[i];
|
||||
struct terms_test *t = &terms[i];
|
||||
|
||||
pr_debug("running test %d '%s'\n", i, t->str);
|
||||
ret = test_term(t);
|
||||
@@ -1067,7 +1333,7 @@ static int test_pmu_events(void)
|
||||
|
||||
while (!ret && (ent = readdir(dir))) {
|
||||
#define MAX_NAME 100
|
||||
struct test__event_st e;
|
||||
struct evlist_test e;
|
||||
char name[MAX_NAME];
|
||||
|
||||
if (!strcmp(ent->d_name, ".") ||
|
||||
|
@@ -96,22 +96,22 @@ int test__PERF_RECORD(void)
|
||||
err = perf_evlist__prepare_workload(evlist, &opts, argv);
|
||||
if (err < 0) {
|
||||
pr_debug("Couldn't run the workload!\n");
|
||||
goto out_delete_evlist;
|
||||
goto out_delete_maps;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
perf_evsel__set_sample_bit(evsel, CPU);
|
||||
perf_evsel__set_sample_bit(evsel, TID);
|
||||
perf_evsel__set_sample_bit(evsel, TIME);
|
||||
perf_evlist__config(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;
|
||||
goto out_delete_maps;
|
||||
}
|
||||
|
||||
cpu = err;
|
||||
@@ -121,7 +121,7 @@ int test__PERF_RECORD(void)
|
||||
*/
|
||||
if (sched_setaffinity(evlist->workload.pid, cpu_mask_size, &cpu_mask) < 0) {
|
||||
pr_debug("sched_setaffinity: %s\n", strerror(errno));
|
||||
goto out_delete_evlist;
|
||||
goto out_delete_maps;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -131,7 +131,7 @@ int test__PERF_RECORD(void)
|
||||
err = perf_evlist__open(evlist);
|
||||
if (err < 0) {
|
||||
pr_debug("perf_evlist__open: %s\n", strerror(errno));
|
||||
goto out_delete_evlist;
|
||||
goto out_delete_maps;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -142,7 +142,7 @@ int test__PERF_RECORD(void)
|
||||
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;
|
||||
goto out_delete_maps;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -305,6 +305,8 @@ found_exit:
|
||||
}
|
||||
out_err:
|
||||
perf_evlist__munmap(evlist);
|
||||
out_delete_maps:
|
||||
perf_evlist__delete_maps(evlist);
|
||||
out_delete_evlist:
|
||||
perf_evlist__delete(evlist);
|
||||
out:
|
||||
|
@@ -19,10 +19,8 @@ static struct test_format {
|
||||
{ "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[] = {
|
||||
static struct parse_events_term test_terms[] = {
|
||||
{
|
||||
.config = (char *) "krava01",
|
||||
.val.num = 15,
|
||||
@@ -78,7 +76,6 @@ static struct parse_events__term test_terms[] = {
|
||||
.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
|
||||
@@ -93,7 +90,7 @@ static char *test_format_dir_get(void)
|
||||
if (!mkdtemp(dir))
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < TEST_FORMATS_CNT; i++) {
|
||||
for (i = 0; i < ARRAY_SIZE(test_formats); i++) {
|
||||
static char name[PATH_MAX];
|
||||
struct test_format *format = &test_formats[i];
|
||||
FILE *file;
|
||||
@@ -130,14 +127,12 @@ static struct list_head *test_terms_list(void)
|
||||
static LIST_HEAD(terms);
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < TERMS_CNT; i++)
|
||||
for (i = 0; i < ARRAY_SIZE(test_terms); i++)
|
||||
list_add_tail(&test_terms[i].list, &terms);
|
||||
|
||||
return &terms;
|
||||
}
|
||||
|
||||
#undef TERMS_CNT
|
||||
|
||||
int test__pmu(void)
|
||||
{
|
||||
char *format = test_format_dir_get();
|
||||
|
23
tools/perf/tests/python-use.c
Normal file
23
tools/perf/tests/python-use.c
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Just test if we can load the python binding.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "tests.h"
|
||||
|
||||
extern int verbose;
|
||||
|
||||
int test__python_use(void)
|
||||
{
|
||||
char *cmd;
|
||||
int ret;
|
||||
|
||||
if (asprintf(&cmd, "echo \"import sys ; sys.path.append('%s'); import perf\" | %s %s",
|
||||
PYTHONPATH, PYTHON, verbose ? "" : "2> /dev/null") < 0)
|
||||
return -1;
|
||||
|
||||
ret = system(cmd) ? -1 : 0;
|
||||
free(cmd);
|
||||
return ret;
|
||||
}
|
@@ -1,6 +1,12 @@
|
||||
#ifndef TESTS_H
|
||||
#define TESTS_H
|
||||
|
||||
enum {
|
||||
TEST_OK = 0,
|
||||
TEST_FAIL = -1,
|
||||
TEST_SKIP = -2,
|
||||
};
|
||||
|
||||
/* Tests */
|
||||
int test__vmlinux_matches_kallsyms(void);
|
||||
int test__open_syscall_event(void);
|
||||
@@ -15,8 +21,7 @@ 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);
|
||||
int test__hists_link(void);
|
||||
int test__python_use(void);
|
||||
|
||||
#endif /* TESTS_H */
|
||||
|
@@ -1,30 +0,0 @@
|
||||
#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;
|
||||
}
|
@@ -44,7 +44,7 @@ int test__vmlinux_matches_kallsyms(void)
|
||||
*/
|
||||
if (machine__create_kernel_maps(&kallsyms) < 0) {
|
||||
pr_debug("machine__create_kernel_maps ");
|
||||
return -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -101,7 +101,8 @@ int test__vmlinux_matches_kallsyms(void)
|
||||
*/
|
||||
if (machine__load_vmlinux_path(&vmlinux, type,
|
||||
vmlinux_matches_kallsyms_filter) <= 0) {
|
||||
pr_debug("machine__load_vmlinux_path ");
|
||||
pr_debug("Couldn't find a vmlinux that matches the kernel running on this machine, skipping test\n");
|
||||
err = TEST_SKIP;
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -226,5 +227,7 @@ detour:
|
||||
map__fprintf(pos, stderr);
|
||||
}
|
||||
out:
|
||||
machine__exit(&kallsyms);
|
||||
machine__exit(&vmlinux);
|
||||
return err;
|
||||
}
|
||||
|
@@ -273,6 +273,8 @@ void ui_browser__hide(struct ui_browser *browser __maybe_unused)
|
||||
{
|
||||
pthread_mutex_lock(&ui__lock);
|
||||
ui_helpline__pop();
|
||||
free(browser->helpline);
|
||||
browser->helpline = NULL;
|
||||
pthread_mutex_unlock(&ui__lock);
|
||||
}
|
||||
|
||||
@@ -471,7 +473,7 @@ unsigned int ui_browser__list_head_refresh(struct ui_browser *browser)
|
||||
return row;
|
||||
}
|
||||
|
||||
static struct ui_browser__colorset {
|
||||
static struct ui_browser_colorset {
|
||||
const char *name, *fg, *bg;
|
||||
int colorset;
|
||||
} ui_browser__colorsets[] = {
|
||||
@@ -706,7 +708,7 @@ void ui_browser__init(void)
|
||||
perf_config(ui_browser__color_config, NULL);
|
||||
|
||||
while (ui_browser__colorsets[i].name) {
|
||||
struct ui_browser__colorset *c = &ui_browser__colorsets[i++];
|
||||
struct ui_browser_colorset *c = &ui_browser__colorsets[i++];
|
||||
sltt_set_color(c->colorset, c->name, c->fg, c->bg);
|
||||
}
|
||||
|
||||
|
@@ -182,6 +182,16 @@ static void annotate_browser__write(struct ui_browser *browser, void *entry, int
|
||||
ab->selection = dl;
|
||||
}
|
||||
|
||||
static bool disasm_line__is_valid_jump(struct disasm_line *dl, struct symbol *sym)
|
||||
{
|
||||
if (!dl || !dl->ins || !ins__is_jump(dl->ins)
|
||||
|| !disasm_line__has_offset(dl)
|
||||
|| dl->ops.target.offset >= symbol__size(sym))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void annotate_browser__draw_current_jump(struct ui_browser *browser)
|
||||
{
|
||||
struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
|
||||
@@ -195,8 +205,7 @@ static void annotate_browser__draw_current_jump(struct ui_browser *browser)
|
||||
if (strstr(sym->name, "@plt"))
|
||||
return;
|
||||
|
||||
if (!cursor || !cursor->ins || !ins__is_jump(cursor->ins) ||
|
||||
!disasm_line__has_offset(cursor))
|
||||
if (!disasm_line__is_valid_jump(cursor, sym))
|
||||
return;
|
||||
|
||||
target = ab->offsets[cursor->ops.target.offset];
|
||||
@@ -788,17 +797,9 @@ static void annotate_browser__mark_jump_targets(struct annotate_browser *browser
|
||||
struct disasm_line *dl = browser->offsets[offset], *dlt;
|
||||
struct browser_disasm_line *bdlt;
|
||||
|
||||
if (!dl || !dl->ins || !ins__is_jump(dl->ins) ||
|
||||
!disasm_line__has_offset(dl))
|
||||
if (!disasm_line__is_valid_jump(dl, sym))
|
||||
continue;
|
||||
|
||||
if (dl->ops.target.offset >= size) {
|
||||
ui__error("jump to after symbol!\n"
|
||||
"size: %zx, jump target: %" PRIx64,
|
||||
size, dl->ops.target.offset);
|
||||
continue;
|
||||
}
|
||||
|
||||
dlt = browser->offsets[dl->ops.target.offset];
|
||||
/*
|
||||
* FIXME: Oops, no jump target? Buggy disassembler? Or do we
|
||||
@@ -921,11 +922,11 @@ out_free_offsets:
|
||||
|
||||
#define ANNOTATE_CFG(n) \
|
||||
{ .name = #n, .value = &annotate_browser__opts.n, }
|
||||
|
||||
|
||||
/*
|
||||
* Keep the entries sorted, they are bsearch'ed
|
||||
*/
|
||||
static struct annotate__config {
|
||||
static struct annotate_config {
|
||||
const char *name;
|
||||
bool *value;
|
||||
} annotate__configs[] = {
|
||||
@@ -939,7 +940,7 @@ static struct annotate__config {
|
||||
|
||||
static int annotate_config__cmp(const void *name, const void *cfgp)
|
||||
{
|
||||
const struct annotate__config *cfg = cfgp;
|
||||
const struct annotate_config *cfg = cfgp;
|
||||
|
||||
return strcmp(name, cfg->name);
|
||||
}
|
||||
@@ -947,7 +948,7 @@ static int annotate_config__cmp(const void *name, const void *cfgp)
|
||||
static int annotate__config(const char *var, const char *value,
|
||||
void *data __maybe_unused)
|
||||
{
|
||||
struct annotate__config *cfg;
|
||||
struct annotate_config *cfg;
|
||||
const char *name;
|
||||
|
||||
if (prefixcmp(var, "annotate.") != 0)
|
||||
@@ -955,7 +956,7 @@ static int annotate__config(const char *var, const char *value,
|
||||
|
||||
name = var + 9;
|
||||
cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
|
||||
sizeof(struct annotate__config), annotate_config__cmp);
|
||||
sizeof(struct annotate_config), annotate_config__cmp);
|
||||
|
||||
if (cfg == NULL)
|
||||
return -1;
|
||||
|
@@ -567,26 +567,128 @@ static int hist_browser__show_callchain(struct hist_browser *browser,
|
||||
return row - first_row;
|
||||
}
|
||||
|
||||
#define HPP__COLOR_FN(_name, _field) \
|
||||
static int hist_browser__hpp_color_ ## _name(struct perf_hpp *hpp, \
|
||||
struct hist_entry *he) \
|
||||
{ \
|
||||
struct hists *hists = he->hists; \
|
||||
double percent = 100.0 * he->stat._field / hists->stats.total_period; \
|
||||
*(double *)hpp->ptr = percent; \
|
||||
return scnprintf(hpp->buf, hpp->size, "%6.2f%%", percent); \
|
||||
struct hpp_arg {
|
||||
struct ui_browser *b;
|
||||
char folded_sign;
|
||||
bool current_entry;
|
||||
};
|
||||
|
||||
static int __hpp__color_callchain(struct hpp_arg *arg)
|
||||
{
|
||||
if (!symbol_conf.use_callchain)
|
||||
return 0;
|
||||
|
||||
slsmg_printf("%c ", arg->folded_sign);
|
||||
return 2;
|
||||
}
|
||||
|
||||
HPP__COLOR_FN(overhead, period)
|
||||
HPP__COLOR_FN(overhead_sys, period_sys)
|
||||
HPP__COLOR_FN(overhead_us, period_us)
|
||||
HPP__COLOR_FN(overhead_guest_sys, period_guest_sys)
|
||||
HPP__COLOR_FN(overhead_guest_us, period_guest_us)
|
||||
static int __hpp__color_fmt(struct perf_hpp *hpp, struct hist_entry *he,
|
||||
u64 (*get_field)(struct hist_entry *),
|
||||
int (*callchain_cb)(struct hpp_arg *))
|
||||
{
|
||||
int ret = 0;
|
||||
double percent = 0.0;
|
||||
struct hists *hists = he->hists;
|
||||
struct hpp_arg *arg = hpp->ptr;
|
||||
|
||||
#undef HPP__COLOR_FN
|
||||
if (hists->stats.total_period)
|
||||
percent = 100.0 * get_field(he) / hists->stats.total_period;
|
||||
|
||||
ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
|
||||
|
||||
if (callchain_cb)
|
||||
ret += callchain_cb(arg);
|
||||
|
||||
ret += scnprintf(hpp->buf, hpp->size, "%6.2f%%", percent);
|
||||
slsmg_printf("%s", hpp->buf);
|
||||
|
||||
if (symbol_conf.event_group) {
|
||||
int prev_idx, idx_delta;
|
||||
struct perf_evsel *evsel = hists_to_evsel(hists);
|
||||
struct hist_entry *pair;
|
||||
int nr_members = evsel->nr_members;
|
||||
|
||||
if (nr_members <= 1)
|
||||
goto out;
|
||||
|
||||
prev_idx = perf_evsel__group_idx(evsel);
|
||||
|
||||
list_for_each_entry(pair, &he->pairs.head, pairs.node) {
|
||||
u64 period = get_field(pair);
|
||||
u64 total = pair->hists->stats.total_period;
|
||||
|
||||
if (!total)
|
||||
continue;
|
||||
|
||||
evsel = hists_to_evsel(pair->hists);
|
||||
idx_delta = perf_evsel__group_idx(evsel) - prev_idx - 1;
|
||||
|
||||
while (idx_delta--) {
|
||||
/*
|
||||
* zero-fill group members in the middle which
|
||||
* have no sample
|
||||
*/
|
||||
ui_browser__set_percent_color(arg->b, 0.0,
|
||||
arg->current_entry);
|
||||
ret += scnprintf(hpp->buf, hpp->size,
|
||||
" %6.2f%%", 0.0);
|
||||
slsmg_printf("%s", hpp->buf);
|
||||
}
|
||||
|
||||
percent = 100.0 * period / total;
|
||||
ui_browser__set_percent_color(arg->b, percent,
|
||||
arg->current_entry);
|
||||
ret += scnprintf(hpp->buf, hpp->size,
|
||||
" %6.2f%%", percent);
|
||||
slsmg_printf("%s", hpp->buf);
|
||||
|
||||
prev_idx = perf_evsel__group_idx(evsel);
|
||||
}
|
||||
|
||||
idx_delta = nr_members - prev_idx - 1;
|
||||
|
||||
while (idx_delta--) {
|
||||
/*
|
||||
* zero-fill group members at last which have no sample
|
||||
*/
|
||||
ui_browser__set_percent_color(arg->b, 0.0,
|
||||
arg->current_entry);
|
||||
ret += scnprintf(hpp->buf, hpp->size,
|
||||
" %6.2f%%", 0.0);
|
||||
slsmg_printf("%s", hpp->buf);
|
||||
}
|
||||
}
|
||||
out:
|
||||
if (!arg->current_entry || !arg->b->navkeypressed)
|
||||
ui_browser__set_color(arg->b, HE_COLORSET_NORMAL);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define __HPP_COLOR_PERCENT_FN(_type, _field, _cb) \
|
||||
static u64 __hpp_get_##_field(struct hist_entry *he) \
|
||||
{ \
|
||||
return he->stat._field; \
|
||||
} \
|
||||
\
|
||||
static int hist_browser__hpp_color_##_type(struct perf_hpp *hpp, \
|
||||
struct hist_entry *he) \
|
||||
{ \
|
||||
return __hpp__color_fmt(hpp, he, __hpp_get_##_field, _cb); \
|
||||
}
|
||||
|
||||
__HPP_COLOR_PERCENT_FN(overhead, period, __hpp__color_callchain)
|
||||
__HPP_COLOR_PERCENT_FN(overhead_sys, period_sys, NULL)
|
||||
__HPP_COLOR_PERCENT_FN(overhead_us, period_us, NULL)
|
||||
__HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys, NULL)
|
||||
__HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us, NULL)
|
||||
|
||||
#undef __HPP_COLOR_PERCENT_FN
|
||||
|
||||
void hist_browser__init_hpp(void)
|
||||
{
|
||||
perf_hpp__column_enable(PERF_HPP__OVERHEAD);
|
||||
|
||||
perf_hpp__init();
|
||||
|
||||
perf_hpp__format[PERF_HPP__OVERHEAD].color =
|
||||
@@ -606,13 +708,13 @@ static int hist_browser__show_entry(struct hist_browser *browser,
|
||||
unsigned short row)
|
||||
{
|
||||
char s[256];
|
||||
double percent;
|
||||
int i, printed = 0;
|
||||
int printed = 0;
|
||||
int width = browser->b.width;
|
||||
char folded_sign = ' ';
|
||||
bool current_entry = ui_browser__is_current_entry(&browser->b, row);
|
||||
off_t row_offset = entry->row_offset;
|
||||
bool first = true;
|
||||
struct perf_hpp_fmt *fmt;
|
||||
|
||||
if (current_entry) {
|
||||
browser->he_selection = entry;
|
||||
@@ -625,41 +727,30 @@ static int hist_browser__show_entry(struct hist_browser *browser,
|
||||
}
|
||||
|
||||
if (row_offset == 0) {
|
||||
struct hpp_arg arg = {
|
||||
.b = &browser->b,
|
||||
.folded_sign = folded_sign,
|
||||
.current_entry = current_entry,
|
||||
};
|
||||
struct perf_hpp hpp = {
|
||||
.buf = s,
|
||||
.size = sizeof(s),
|
||||
.ptr = &arg,
|
||||
};
|
||||
|
||||
ui_browser__gotorc(&browser->b, row, 0);
|
||||
|
||||
for (i = 0; i < PERF_HPP__MAX_INDEX; i++) {
|
||||
if (!perf_hpp__format[i].cond)
|
||||
continue;
|
||||
|
||||
perf_hpp__for_each_format(fmt) {
|
||||
if (!first) {
|
||||
slsmg_printf(" ");
|
||||
width -= 2;
|
||||
}
|
||||
first = false;
|
||||
|
||||
if (perf_hpp__format[i].color) {
|
||||
hpp.ptr = &percent;
|
||||
/* It will set percent for us. See HPP__COLOR_FN above. */
|
||||
width -= perf_hpp__format[i].color(&hpp, entry);
|
||||
|
||||
ui_browser__set_percent_color(&browser->b, percent, current_entry);
|
||||
|
||||
if (i == PERF_HPP__OVERHEAD && symbol_conf.use_callchain) {
|
||||
slsmg_printf("%c ", folded_sign);
|
||||
width -= 2;
|
||||
}
|
||||
|
||||
slsmg_printf("%s", s);
|
||||
|
||||
if (!current_entry || !browser->b.navkeypressed)
|
||||
ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
|
||||
if (fmt->color) {
|
||||
width -= fmt->color(&hpp, entry);
|
||||
} else {
|
||||
width -= perf_hpp__format[i].entry(&hpp, entry);
|
||||
width -= fmt->entry(&hpp, entry);
|
||||
slsmg_printf("%s", s);
|
||||
}
|
||||
}
|
||||
@@ -1098,6 +1189,21 @@ static int hists__browser_title(struct hists *hists, char *bf, size_t size,
|
||||
const struct thread *thread = hists->thread_filter;
|
||||
unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
|
||||
u64 nr_events = hists->stats.total_period;
|
||||
struct perf_evsel *evsel = hists_to_evsel(hists);
|
||||
char buf[512];
|
||||
size_t buflen = sizeof(buf);
|
||||
|
||||
if (symbol_conf.event_group && evsel->nr_members > 1) {
|
||||
struct perf_evsel *pos;
|
||||
|
||||
perf_evsel__group_desc(evsel, buf, buflen);
|
||||
ev_name = buf;
|
||||
|
||||
for_each_group_member(pos, evsel) {
|
||||
nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
|
||||
nr_events += pos->hists.stats.total_period;
|
||||
}
|
||||
}
|
||||
|
||||
nr_samples = convert_unit(nr_samples, &unit);
|
||||
printed = scnprintf(bf, size,
|
||||
@@ -1135,6 +1241,96 @@ static inline bool is_report_browser(void *timer)
|
||||
return timer == NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Only runtime switching of perf data file will make "input_name" point
|
||||
* to a malloced buffer. So add "is_input_name_malloced" flag to decide
|
||||
* whether we need to call free() for current "input_name" during the switch.
|
||||
*/
|
||||
static bool is_input_name_malloced = false;
|
||||
|
||||
static int switch_data_file(void)
|
||||
{
|
||||
char *pwd, *options[32], *abs_path[32], *tmp;
|
||||
DIR *pwd_dir;
|
||||
int nr_options = 0, choice = -1, ret = -1;
|
||||
struct dirent *dent;
|
||||
|
||||
pwd = getenv("PWD");
|
||||
if (!pwd)
|
||||
return ret;
|
||||
|
||||
pwd_dir = opendir(pwd);
|
||||
if (!pwd_dir)
|
||||
return ret;
|
||||
|
||||
memset(options, 0, sizeof(options));
|
||||
memset(options, 0, sizeof(abs_path));
|
||||
|
||||
while ((dent = readdir(pwd_dir))) {
|
||||
char path[PATH_MAX];
|
||||
u64 magic;
|
||||
char *name = dent->d_name;
|
||||
FILE *file;
|
||||
|
||||
if (!(dent->d_type == DT_REG))
|
||||
continue;
|
||||
|
||||
snprintf(path, sizeof(path), "%s/%s", pwd, name);
|
||||
|
||||
file = fopen(path, "r");
|
||||
if (!file)
|
||||
continue;
|
||||
|
||||
if (fread(&magic, 1, 8, file) < 8)
|
||||
goto close_file_and_continue;
|
||||
|
||||
if (is_perf_magic(magic)) {
|
||||
options[nr_options] = strdup(name);
|
||||
if (!options[nr_options])
|
||||
goto close_file_and_continue;
|
||||
|
||||
abs_path[nr_options] = strdup(path);
|
||||
if (!abs_path[nr_options]) {
|
||||
free(options[nr_options]);
|
||||
ui__warning("Can't search all data files due to memory shortage.\n");
|
||||
fclose(file);
|
||||
break;
|
||||
}
|
||||
|
||||
nr_options++;
|
||||
}
|
||||
|
||||
close_file_and_continue:
|
||||
fclose(file);
|
||||
if (nr_options >= 32) {
|
||||
ui__warning("Too many perf data files in PWD!\n"
|
||||
"Only the first 32 files will be listed.\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
closedir(pwd_dir);
|
||||
|
||||
if (nr_options) {
|
||||
choice = ui__popup_menu(nr_options, options);
|
||||
if (choice < nr_options && choice >= 0) {
|
||||
tmp = strdup(abs_path[choice]);
|
||||
if (tmp) {
|
||||
if (is_input_name_malloced)
|
||||
free((void *)input_name);
|
||||
input_name = tmp;
|
||||
is_input_name_malloced = true;
|
||||
ret = 0;
|
||||
} else
|
||||
ui__warning("Data switch failed due to memory shortage!\n");
|
||||
}
|
||||
}
|
||||
|
||||
free_popup_options(options, nr_options);
|
||||
free_popup_options(abs_path, nr_options);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
|
||||
const char *helpline, const char *ev_name,
|
||||
bool left_exits,
|
||||
@@ -1169,7 +1365,8 @@ 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;
|
||||
int scripts_comm = -2, scripts_symbol = -2,
|
||||
scripts_all = -2, switch_data = -2;
|
||||
|
||||
nr_options = 0;
|
||||
|
||||
@@ -1226,6 +1423,10 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
|
||||
if (is_report_browser(hbt))
|
||||
goto do_scripts;
|
||||
continue;
|
||||
case 's':
|
||||
if (is_report_browser(hbt))
|
||||
goto do_data_switch;
|
||||
continue;
|
||||
case K_F1:
|
||||
case 'h':
|
||||
case '?':
|
||||
@@ -1245,6 +1446,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
|
||||
"d Zoom into current DSO\n"
|
||||
"t Zoom into current Thread\n"
|
||||
"r Run available scripts('perf report' only)\n"
|
||||
"s Switch to another data file in PWD ('perf report' only)\n"
|
||||
"P Print histograms to perf.hist.N\n"
|
||||
"V Verbose (DSO names in callchains, etc)\n"
|
||||
"/ Filter symbol by name");
|
||||
@@ -1352,6 +1554,9 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
|
||||
if (asprintf(&options[nr_options], "Run scripts for all samples") > 0)
|
||||
scripts_all = nr_options++;
|
||||
|
||||
if (is_report_browser(hbt) && asprintf(&options[nr_options],
|
||||
"Switch to another data file in PWD") > 0)
|
||||
switch_data = nr_options++;
|
||||
add_exit_option:
|
||||
options[nr_options++] = (char *)"Exit";
|
||||
retry_popup_menu:
|
||||
@@ -1462,6 +1667,16 @@ do_scripts:
|
||||
|
||||
script_browse(script_opt);
|
||||
}
|
||||
/* Switch to another data file */
|
||||
else if (choice == switch_data) {
|
||||
do_data_switch:
|
||||
if (!switch_data_file()) {
|
||||
key = K_SWITCH_INPUT_DATA;
|
||||
break;
|
||||
} else
|
||||
ui__warning("Won't switch the data files due to\n"
|
||||
"no valid data file get selected!\n");
|
||||
}
|
||||
}
|
||||
out_free_stack:
|
||||
pstack__delete(fstack);
|
||||
@@ -1494,6 +1709,16 @@ static void perf_evsel_menu__write(struct ui_browser *browser,
|
||||
ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
|
||||
HE_COLORSET_NORMAL);
|
||||
|
||||
if (symbol_conf.event_group && evsel->nr_members > 1) {
|
||||
struct perf_evsel *pos;
|
||||
|
||||
ev_name = perf_evsel__group_name(evsel);
|
||||
|
||||
for_each_group_member(pos, evsel) {
|
||||
nr_events += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
|
||||
}
|
||||
}
|
||||
|
||||
nr_events = convert_unit(nr_events, &unit);
|
||||
printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
|
||||
unit, unit == ' ' ? "" : " ", ev_name);
|
||||
@@ -1578,6 +1803,7 @@ browse_hists:
|
||||
"Do you really want to exit?"))
|
||||
continue;
|
||||
/* Fall thru */
|
||||
case K_SWITCH_INPUT_DATA:
|
||||
case 'q':
|
||||
case CTRL('c'):
|
||||
goto out;
|
||||
@@ -1604,8 +1830,19 @@ out:
|
||||
return key;
|
||||
}
|
||||
|
||||
static bool filter_group_entries(struct ui_browser *self __maybe_unused,
|
||||
void *entry)
|
||||
{
|
||||
struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
|
||||
|
||||
if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
|
||||
const char *help,
|
||||
int nr_entries, const char *help,
|
||||
struct hist_browser_timer *hbt,
|
||||
struct perf_session_env *env)
|
||||
{
|
||||
@@ -1616,7 +1853,8 @@ static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
|
||||
.refresh = ui_browser__list_head_refresh,
|
||||
.seek = ui_browser__list_head_seek,
|
||||
.write = perf_evsel_menu__write,
|
||||
.nr_entries = evlist->nr_entries,
|
||||
.filter = filter_group_entries,
|
||||
.nr_entries = nr_entries,
|
||||
.priv = evlist,
|
||||
},
|
||||
.env = env,
|
||||
@@ -1632,20 +1870,37 @@ 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, hbt);
|
||||
return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
|
||||
}
|
||||
|
||||
int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
|
||||
struct hist_browser_timer *hbt,
|
||||
struct perf_session_env *env)
|
||||
{
|
||||
if (evlist->nr_entries == 1) {
|
||||
int nr_entries = evlist->nr_entries;
|
||||
|
||||
single_entry:
|
||||
if (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,
|
||||
|
||||
return perf_evsel__hists_browse(first, nr_entries, help,
|
||||
ev_name, false, hbt, env);
|
||||
}
|
||||
|
||||
return __perf_evlist__tui_browse_hists(evlist, help, hbt, env);
|
||||
if (symbol_conf.event_group) {
|
||||
struct perf_evsel *pos;
|
||||
|
||||
nr_entries = 0;
|
||||
list_for_each_entry(pos, &evlist->entries, node)
|
||||
if (perf_evsel__is_group_leader(pos))
|
||||
nr_entries++;
|
||||
|
||||
if (nr_entries == 1)
|
||||
goto single_entry;
|
||||
}
|
||||
|
||||
return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
|
||||
hbt, env);
|
||||
}
|
||||
|
229
tools/perf/ui/gtk/annotate.c
Normal file
229
tools/perf/ui/gtk/annotate.c
Normal file
@@ -0,0 +1,229 @@
|
||||
#include "gtk.h"
|
||||
#include "util/debug.h"
|
||||
#include "util/annotate.h"
|
||||
#include "ui/helpline.h"
|
||||
|
||||
|
||||
enum {
|
||||
ANN_COL__PERCENT,
|
||||
ANN_COL__OFFSET,
|
||||
ANN_COL__LINE,
|
||||
|
||||
MAX_ANN_COLS
|
||||
};
|
||||
|
||||
static const char *const col_names[] = {
|
||||
"Overhead",
|
||||
"Offset",
|
||||
"Line"
|
||||
};
|
||||
|
||||
static int perf_gtk__get_percent(char *buf, size_t size, struct symbol *sym,
|
||||
struct disasm_line *dl, int evidx)
|
||||
{
|
||||
struct sym_hist *symhist;
|
||||
double percent = 0.0;
|
||||
const char *markup;
|
||||
int ret = 0;
|
||||
|
||||
strcpy(buf, "");
|
||||
|
||||
if (dl->offset == (s64) -1)
|
||||
return 0;
|
||||
|
||||
symhist = annotation__histogram(symbol__annotation(sym), evidx);
|
||||
if (!symhist->addr[dl->offset])
|
||||
return 0;
|
||||
|
||||
percent = 100.0 * symhist->addr[dl->offset] / symhist->sum;
|
||||
|
||||
markup = perf_gtk__get_percent_color(percent);
|
||||
if (markup)
|
||||
ret += scnprintf(buf, size, "%s", markup);
|
||||
ret += scnprintf(buf + ret, size - ret, "%6.2f%%", percent);
|
||||
if (markup)
|
||||
ret += scnprintf(buf + ret, size - ret, "</span>");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int perf_gtk__get_offset(char *buf, size_t size, struct symbol *sym,
|
||||
struct map *map, struct disasm_line *dl)
|
||||
{
|
||||
u64 start = map__rip_2objdump(map, sym->start);
|
||||
|
||||
strcpy(buf, "");
|
||||
|
||||
if (dl->offset == (s64) -1)
|
||||
return 0;
|
||||
|
||||
return scnprintf(buf, size, "%"PRIx64, start + dl->offset);
|
||||
}
|
||||
|
||||
static int perf_gtk__get_line(char *buf, size_t size, struct disasm_line *dl)
|
||||
{
|
||||
int ret = 0;
|
||||
char *line = g_markup_escape_text(dl->line, -1);
|
||||
const char *markup = "<span fgcolor='gray'>";
|
||||
|
||||
strcpy(buf, "");
|
||||
|
||||
if (!line)
|
||||
return 0;
|
||||
|
||||
if (dl->offset != (s64) -1)
|
||||
markup = NULL;
|
||||
|
||||
if (markup)
|
||||
ret += scnprintf(buf, size, "%s", markup);
|
||||
ret += scnprintf(buf + ret, size - ret, "%s", line);
|
||||
if (markup)
|
||||
ret += scnprintf(buf + ret, size - ret, "</span>");
|
||||
|
||||
g_free(line);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int perf_gtk__annotate_symbol(GtkWidget *window, struct symbol *sym,
|
||||
struct map *map, int evidx,
|
||||
struct hist_browser_timer *hbt __maybe_unused)
|
||||
{
|
||||
struct disasm_line *pos, *n;
|
||||
struct annotation *notes;
|
||||
GType col_types[MAX_ANN_COLS];
|
||||
GtkCellRenderer *renderer;
|
||||
GtkListStore *store;
|
||||
GtkWidget *view;
|
||||
int i;
|
||||
char s[512];
|
||||
|
||||
notes = symbol__annotation(sym);
|
||||
|
||||
for (i = 0; i < MAX_ANN_COLS; i++) {
|
||||
col_types[i] = G_TYPE_STRING;
|
||||
}
|
||||
store = gtk_list_store_newv(MAX_ANN_COLS, col_types);
|
||||
|
||||
view = gtk_tree_view_new();
|
||||
renderer = gtk_cell_renderer_text_new();
|
||||
|
||||
for (i = 0; i < MAX_ANN_COLS; i++) {
|
||||
gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
|
||||
-1, col_names[i], renderer, "markup",
|
||||
i, NULL);
|
||||
}
|
||||
|
||||
gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store));
|
||||
g_object_unref(GTK_TREE_MODEL(store));
|
||||
|
||||
list_for_each_entry(pos, ¬es->src->source, node) {
|
||||
GtkTreeIter iter;
|
||||
|
||||
gtk_list_store_append(store, &iter);
|
||||
|
||||
if (perf_gtk__get_percent(s, sizeof(s), sym, pos, evidx))
|
||||
gtk_list_store_set(store, &iter, ANN_COL__PERCENT, s, -1);
|
||||
if (perf_gtk__get_offset(s, sizeof(s), sym, map, pos))
|
||||
gtk_list_store_set(store, &iter, ANN_COL__OFFSET, s, -1);
|
||||
if (perf_gtk__get_line(s, sizeof(s), pos))
|
||||
gtk_list_store_set(store, &iter, ANN_COL__LINE, s, -1);
|
||||
}
|
||||
|
||||
gtk_container_add(GTK_CONTAINER(window), view);
|
||||
|
||||
list_for_each_entry_safe(pos, n, ¬es->src->source, node) {
|
||||
list_del(&pos->node);
|
||||
disasm_line__free(pos);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int symbol__gtk_annotate(struct symbol *sym, struct map *map, int evidx,
|
||||
struct hist_browser_timer *hbt)
|
||||
{
|
||||
GtkWidget *window;
|
||||
GtkWidget *notebook;
|
||||
GtkWidget *scrolled_window;
|
||||
GtkWidget *tab_label;
|
||||
|
||||
if (map->dso->annotate_warned)
|
||||
return -1;
|
||||
|
||||
if (symbol__annotate(sym, map, 0) < 0) {
|
||||
ui__error("%s", ui_helpline__current);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (perf_gtk__is_active_context(pgctx)) {
|
||||
window = pgctx->main_window;
|
||||
notebook = pgctx->notebook;
|
||||
} else {
|
||||
GtkWidget *vbox;
|
||||
GtkWidget *infobar;
|
||||
GtkWidget *statbar;
|
||||
|
||||
signal(SIGSEGV, perf_gtk__signal);
|
||||
signal(SIGFPE, perf_gtk__signal);
|
||||
signal(SIGINT, perf_gtk__signal);
|
||||
signal(SIGQUIT, perf_gtk__signal);
|
||||
signal(SIGTERM, perf_gtk__signal);
|
||||
|
||||
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
||||
gtk_window_set_title(GTK_WINDOW(window), "perf annotate");
|
||||
|
||||
g_signal_connect(window, "delete_event", gtk_main_quit, NULL);
|
||||
|
||||
pgctx = perf_gtk__activate_context(window);
|
||||
if (!pgctx)
|
||||
return -1;
|
||||
|
||||
vbox = gtk_vbox_new(FALSE, 0);
|
||||
notebook = gtk_notebook_new();
|
||||
pgctx->notebook = notebook;
|
||||
|
||||
gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0);
|
||||
|
||||
infobar = perf_gtk__setup_info_bar();
|
||||
if (infobar) {
|
||||
gtk_box_pack_start(GTK_BOX(vbox), infobar,
|
||||
FALSE, FALSE, 0);
|
||||
}
|
||||
|
||||
statbar = perf_gtk__setup_statusbar();
|
||||
gtk_box_pack_start(GTK_BOX(vbox), statbar, FALSE, FALSE, 0);
|
||||
|
||||
gtk_container_add(GTK_CONTAINER(window), vbox);
|
||||
}
|
||||
|
||||
scrolled_window = gtk_scrolled_window_new(NULL, NULL);
|
||||
tab_label = gtk_label_new(sym->name);
|
||||
|
||||
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
|
||||
GTK_POLICY_AUTOMATIC,
|
||||
GTK_POLICY_AUTOMATIC);
|
||||
|
||||
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled_window,
|
||||
tab_label);
|
||||
|
||||
perf_gtk__annotate_symbol(scrolled_window, sym, map, evidx, hbt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void perf_gtk__show_annotations(void)
|
||||
{
|
||||
GtkWidget *window;
|
||||
|
||||
if (!perf_gtk__is_active_context(pgctx))
|
||||
return;
|
||||
|
||||
window = pgctx->main_window;
|
||||
gtk_widget_show_all(window);
|
||||
|
||||
perf_gtk__resize_window(window);
|
||||
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
|
||||
|
||||
gtk_main();
|
||||
|
||||
perf_gtk__deactivate_context(&pgctx);
|
||||
}
|
@@ -8,15 +8,13 @@
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#define MAX_COLUMNS 32
|
||||
|
||||
static void perf_gtk__signal(int sig)
|
||||
void perf_gtk__signal(int sig)
|
||||
{
|
||||
perf_gtk__exit(false);
|
||||
psignal(sig, "perf");
|
||||
}
|
||||
|
||||
static void perf_gtk__resize_window(GtkWidget *window)
|
||||
void perf_gtk__resize_window(GtkWidget *window)
|
||||
{
|
||||
GdkRectangle rect;
|
||||
GdkScreen *screen;
|
||||
@@ -36,7 +34,7 @@ static void perf_gtk__resize_window(GtkWidget *window)
|
||||
gtk_window_resize(GTK_WINDOW(window), width, height);
|
||||
}
|
||||
|
||||
static const char *perf_gtk__get_percent_color(double percent)
|
||||
const char *perf_gtk__get_percent_color(double percent)
|
||||
{
|
||||
if (percent >= MIN_RED)
|
||||
return "<span fgcolor='red'>";
|
||||
@@ -45,155 +43,8 @@ static const char *perf_gtk__get_percent_color(double percent)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define HPP__COLOR_FN(_name, _field) \
|
||||
static int perf_gtk__hpp_color_ ## _name(struct perf_hpp *hpp, \
|
||||
struct hist_entry *he) \
|
||||
{ \
|
||||
struct hists *hists = he->hists; \
|
||||
double percent = 100.0 * he->stat._field / hists->stats.total_period; \
|
||||
const char *markup; \
|
||||
int ret = 0; \
|
||||
\
|
||||
markup = perf_gtk__get_percent_color(percent); \
|
||||
if (markup) \
|
||||
ret += scnprintf(hpp->buf, hpp->size, "%s", markup); \
|
||||
ret += scnprintf(hpp->buf + ret, hpp->size - ret, "%6.2f%%", percent); \
|
||||
if (markup) \
|
||||
ret += scnprintf(hpp->buf + ret, hpp->size - ret, "</span>"); \
|
||||
\
|
||||
return ret; \
|
||||
}
|
||||
|
||||
HPP__COLOR_FN(overhead, period)
|
||||
HPP__COLOR_FN(overhead_sys, period_sys)
|
||||
HPP__COLOR_FN(overhead_us, period_us)
|
||||
HPP__COLOR_FN(overhead_guest_sys, period_guest_sys)
|
||||
HPP__COLOR_FN(overhead_guest_us, period_guest_us)
|
||||
|
||||
#undef HPP__COLOR_FN
|
||||
|
||||
void perf_gtk__init_hpp(void)
|
||||
{
|
||||
perf_hpp__init();
|
||||
|
||||
perf_hpp__format[PERF_HPP__OVERHEAD].color =
|
||||
perf_gtk__hpp_color_overhead;
|
||||
perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
|
||||
perf_gtk__hpp_color_overhead_sys;
|
||||
perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
|
||||
perf_gtk__hpp_color_overhead_us;
|
||||
perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
|
||||
perf_gtk__hpp_color_overhead_guest_sys;
|
||||
perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
|
||||
perf_gtk__hpp_color_overhead_guest_us;
|
||||
}
|
||||
|
||||
static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists)
|
||||
{
|
||||
GType col_types[MAX_COLUMNS];
|
||||
GtkCellRenderer *renderer;
|
||||
struct sort_entry *se;
|
||||
GtkListStore *store;
|
||||
struct rb_node *nd;
|
||||
GtkWidget *view;
|
||||
int i, col_idx;
|
||||
int nr_cols;
|
||||
char s[512];
|
||||
|
||||
struct perf_hpp hpp = {
|
||||
.buf = s,
|
||||
.size = sizeof(s),
|
||||
};
|
||||
|
||||
nr_cols = 0;
|
||||
|
||||
for (i = 0; i < PERF_HPP__MAX_INDEX; i++) {
|
||||
if (!perf_hpp__format[i].cond)
|
||||
continue;
|
||||
|
||||
col_types[nr_cols++] = G_TYPE_STRING;
|
||||
}
|
||||
|
||||
list_for_each_entry(se, &hist_entry__sort_list, list) {
|
||||
if (se->elide)
|
||||
continue;
|
||||
|
||||
col_types[nr_cols++] = G_TYPE_STRING;
|
||||
}
|
||||
|
||||
store = gtk_list_store_newv(nr_cols, col_types);
|
||||
|
||||
view = gtk_tree_view_new();
|
||||
|
||||
renderer = gtk_cell_renderer_text_new();
|
||||
|
||||
col_idx = 0;
|
||||
|
||||
for (i = 0; i < PERF_HPP__MAX_INDEX; i++) {
|
||||
if (!perf_hpp__format[i].cond)
|
||||
continue;
|
||||
|
||||
perf_hpp__format[i].header(&hpp);
|
||||
|
||||
gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
|
||||
-1, s,
|
||||
renderer, "markup",
|
||||
col_idx++, NULL);
|
||||
}
|
||||
|
||||
list_for_each_entry(se, &hist_entry__sort_list, list) {
|
||||
if (se->elide)
|
||||
continue;
|
||||
|
||||
gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
|
||||
-1, se->se_header,
|
||||
renderer, "text",
|
||||
col_idx++, NULL);
|
||||
}
|
||||
|
||||
gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store));
|
||||
|
||||
g_object_unref(GTK_TREE_MODEL(store));
|
||||
|
||||
for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
|
||||
struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
|
||||
GtkTreeIter iter;
|
||||
|
||||
if (h->filtered)
|
||||
continue;
|
||||
|
||||
gtk_list_store_append(store, &iter);
|
||||
|
||||
col_idx = 0;
|
||||
|
||||
for (i = 0; i < PERF_HPP__MAX_INDEX; i++) {
|
||||
if (!perf_hpp__format[i].cond)
|
||||
continue;
|
||||
|
||||
if (perf_hpp__format[i].color)
|
||||
perf_hpp__format[i].color(&hpp, h);
|
||||
else
|
||||
perf_hpp__format[i].entry(&hpp, h);
|
||||
|
||||
gtk_list_store_set(store, &iter, col_idx++, s, -1);
|
||||
}
|
||||
|
||||
list_for_each_entry(se, &hist_entry__sort_list, list) {
|
||||
if (se->elide)
|
||||
continue;
|
||||
|
||||
se->se_snprintf(h, s, ARRAY_SIZE(s),
|
||||
hists__col_len(hists, se->se_width_idx));
|
||||
|
||||
gtk_list_store_set(store, &iter, col_idx++, s, -1);
|
||||
}
|
||||
}
|
||||
|
||||
gtk_container_add(GTK_CONTAINER(window), view);
|
||||
}
|
||||
|
||||
#ifdef HAVE_GTK_INFO_BAR
|
||||
static GtkWidget *perf_gtk__setup_info_bar(void)
|
||||
GtkWidget *perf_gtk__setup_info_bar(void)
|
||||
{
|
||||
GtkWidget *info_bar;
|
||||
GtkWidget *label;
|
||||
@@ -220,7 +71,7 @@ static GtkWidget *perf_gtk__setup_info_bar(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
static GtkWidget *perf_gtk__setup_statusbar(void)
|
||||
GtkWidget *perf_gtk__setup_statusbar(void)
|
||||
{
|
||||
GtkWidget *stbar;
|
||||
unsigned ctxid;
|
||||
@@ -234,79 +85,3 @@ static GtkWidget *perf_gtk__setup_statusbar(void)
|
||||
|
||||
return stbar;
|
||||
}
|
||||
|
||||
int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist,
|
||||
const char *help,
|
||||
struct hist_browser_timer *hbt __maybe_unused)
|
||||
{
|
||||
struct perf_evsel *pos;
|
||||
GtkWidget *vbox;
|
||||
GtkWidget *notebook;
|
||||
GtkWidget *info_bar;
|
||||
GtkWidget *statbar;
|
||||
GtkWidget *window;
|
||||
|
||||
signal(SIGSEGV, perf_gtk__signal);
|
||||
signal(SIGFPE, perf_gtk__signal);
|
||||
signal(SIGINT, perf_gtk__signal);
|
||||
signal(SIGQUIT, perf_gtk__signal);
|
||||
signal(SIGTERM, perf_gtk__signal);
|
||||
|
||||
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
||||
|
||||
gtk_window_set_title(GTK_WINDOW(window), "perf report");
|
||||
|
||||
g_signal_connect(window, "delete_event", gtk_main_quit, NULL);
|
||||
|
||||
pgctx = perf_gtk__activate_context(window);
|
||||
if (!pgctx)
|
||||
return -1;
|
||||
|
||||
vbox = gtk_vbox_new(FALSE, 0);
|
||||
|
||||
notebook = gtk_notebook_new();
|
||||
|
||||
list_for_each_entry(pos, &evlist->entries, node) {
|
||||
struct hists *hists = &pos->hists;
|
||||
const char *evname = perf_evsel__name(pos);
|
||||
GtkWidget *scrolled_window;
|
||||
GtkWidget *tab_label;
|
||||
|
||||
scrolled_window = gtk_scrolled_window_new(NULL, NULL);
|
||||
|
||||
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
|
||||
GTK_POLICY_AUTOMATIC,
|
||||
GTK_POLICY_AUTOMATIC);
|
||||
|
||||
perf_gtk__show_hists(scrolled_window, hists);
|
||||
|
||||
tab_label = gtk_label_new(evname);
|
||||
|
||||
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled_window, tab_label);
|
||||
}
|
||||
|
||||
gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0);
|
||||
|
||||
info_bar = perf_gtk__setup_info_bar();
|
||||
if (info_bar)
|
||||
gtk_box_pack_start(GTK_BOX(vbox), info_bar, FALSE, FALSE, 0);
|
||||
|
||||
statbar = perf_gtk__setup_statusbar();
|
||||
gtk_box_pack_start(GTK_BOX(vbox), statbar, FALSE, FALSE, 0);
|
||||
|
||||
gtk_container_add(GTK_CONTAINER(window), vbox);
|
||||
|
||||
gtk_widget_show_all(window);
|
||||
|
||||
perf_gtk__resize_window(window);
|
||||
|
||||
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
|
||||
|
||||
ui_helpline__push(help);
|
||||
|
||||
gtk_main();
|
||||
|
||||
perf_gtk__deactivate_context(&pgctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@@ -10,6 +10,7 @@
|
||||
|
||||
struct perf_gtk_context {
|
||||
GtkWidget *main_window;
|
||||
GtkWidget *notebook;
|
||||
|
||||
#ifdef HAVE_GTK_INFO_BAR
|
||||
GtkWidget *info_bar;
|
||||
@@ -33,7 +34,14 @@ void perf_gtk__init_helpline(void);
|
||||
void perf_gtk__init_progress(void);
|
||||
void perf_gtk__init_hpp(void);
|
||||
|
||||
#ifndef HAVE_GTK_INFO_BAR
|
||||
void perf_gtk__signal(int sig);
|
||||
void perf_gtk__resize_window(GtkWidget *window);
|
||||
const char *perf_gtk__get_percent_color(double percent);
|
||||
GtkWidget *perf_gtk__setup_statusbar(void);
|
||||
|
||||
#ifdef HAVE_GTK_INFO_BAR
|
||||
GtkWidget *perf_gtk__setup_info_bar(void);
|
||||
#else
|
||||
static inline GtkWidget *perf_gtk__setup_info_bar(void)
|
||||
{
|
||||
return NULL;
|
||||
|
@@ -24,17 +24,7 @@ static void gtk_helpline_push(const char *msg)
|
||||
pgctx->statbar_ctx_id, msg);
|
||||
}
|
||||
|
||||
static struct ui_helpline gtk_helpline_fns = {
|
||||
.pop = gtk_helpline_pop,
|
||||
.push = gtk_helpline_push,
|
||||
};
|
||||
|
||||
void perf_gtk__init_helpline(void)
|
||||
{
|
||||
helpline_fns = >k_helpline_fns;
|
||||
}
|
||||
|
||||
int perf_gtk__show_helpline(const char *fmt, va_list ap)
|
||||
static int gtk_helpline_show(const char *fmt, va_list ap)
|
||||
{
|
||||
int ret;
|
||||
char *ptr;
|
||||
@@ -54,3 +44,14 @@ int perf_gtk__show_helpline(const char *fmt, va_list ap)
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct ui_helpline gtk_helpline_fns = {
|
||||
.pop = gtk_helpline_pop,
|
||||
.push = gtk_helpline_push,
|
||||
.show = gtk_helpline_show,
|
||||
};
|
||||
|
||||
void perf_gtk__init_helpline(void)
|
||||
{
|
||||
helpline_fns = >k_helpline_fns;
|
||||
}
|
||||
|
312
tools/perf/ui/gtk/hists.c
Normal file
312
tools/perf/ui/gtk/hists.c
Normal file
@@ -0,0 +1,312 @@
|
||||
#include "../evlist.h"
|
||||
#include "../cache.h"
|
||||
#include "../evsel.h"
|
||||
#include "../sort.h"
|
||||
#include "../hist.h"
|
||||
#include "../helpline.h"
|
||||
#include "gtk.h"
|
||||
|
||||
#define MAX_COLUMNS 32
|
||||
|
||||
static int __percent_color_snprintf(char *buf, size_t size, double percent)
|
||||
{
|
||||
int ret = 0;
|
||||
const char *markup;
|
||||
|
||||
markup = perf_gtk__get_percent_color(percent);
|
||||
if (markup)
|
||||
ret += scnprintf(buf, size, markup);
|
||||
|
||||
ret += scnprintf(buf + ret, size - ret, " %6.2f%%", percent);
|
||||
|
||||
if (markup)
|
||||
ret += scnprintf(buf + ret, size - ret, "</span>");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int __hpp__color_fmt(struct perf_hpp *hpp, struct hist_entry *he,
|
||||
u64 (*get_field)(struct hist_entry *))
|
||||
{
|
||||
int ret;
|
||||
double percent = 0.0;
|
||||
struct hists *hists = he->hists;
|
||||
|
||||
if (hists->stats.total_period)
|
||||
percent = 100.0 * get_field(he) / hists->stats.total_period;
|
||||
|
||||
ret = __percent_color_snprintf(hpp->buf, hpp->size, percent);
|
||||
|
||||
if (symbol_conf.event_group) {
|
||||
int prev_idx, idx_delta;
|
||||
struct perf_evsel *evsel = hists_to_evsel(hists);
|
||||
struct hist_entry *pair;
|
||||
int nr_members = evsel->nr_members;
|
||||
|
||||
if (nr_members <= 1)
|
||||
return ret;
|
||||
|
||||
prev_idx = perf_evsel__group_idx(evsel);
|
||||
|
||||
list_for_each_entry(pair, &he->pairs.head, pairs.node) {
|
||||
u64 period = get_field(pair);
|
||||
u64 total = pair->hists->stats.total_period;
|
||||
|
||||
evsel = hists_to_evsel(pair->hists);
|
||||
idx_delta = perf_evsel__group_idx(evsel) - prev_idx - 1;
|
||||
|
||||
while (idx_delta--) {
|
||||
/*
|
||||
* zero-fill group members in the middle which
|
||||
* have no sample
|
||||
*/
|
||||
ret += __percent_color_snprintf(hpp->buf + ret,
|
||||
hpp->size - ret,
|
||||
0.0);
|
||||
}
|
||||
|
||||
percent = 100.0 * period / total;
|
||||
ret += __percent_color_snprintf(hpp->buf + ret,
|
||||
hpp->size - ret,
|
||||
percent);
|
||||
|
||||
prev_idx = perf_evsel__group_idx(evsel);
|
||||
}
|
||||
|
||||
idx_delta = nr_members - prev_idx - 1;
|
||||
|
||||
while (idx_delta--) {
|
||||
/*
|
||||
* zero-fill group members at last which have no sample
|
||||
*/
|
||||
ret += __percent_color_snprintf(hpp->buf + ret,
|
||||
hpp->size - ret,
|
||||
0.0);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define __HPP_COLOR_PERCENT_FN(_type, _field) \
|
||||
static u64 he_get_##_field(struct hist_entry *he) \
|
||||
{ \
|
||||
return he->stat._field; \
|
||||
} \
|
||||
\
|
||||
static int perf_gtk__hpp_color_##_type(struct perf_hpp *hpp, \
|
||||
struct hist_entry *he) \
|
||||
{ \
|
||||
return __hpp__color_fmt(hpp, he, he_get_##_field); \
|
||||
}
|
||||
|
||||
__HPP_COLOR_PERCENT_FN(overhead, period)
|
||||
__HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
|
||||
__HPP_COLOR_PERCENT_FN(overhead_us, period_us)
|
||||
__HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
|
||||
__HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
|
||||
|
||||
#undef __HPP_COLOR_PERCENT_FN
|
||||
|
||||
|
||||
void perf_gtk__init_hpp(void)
|
||||
{
|
||||
perf_hpp__column_enable(PERF_HPP__OVERHEAD);
|
||||
|
||||
perf_hpp__init();
|
||||
|
||||
perf_hpp__format[PERF_HPP__OVERHEAD].color =
|
||||
perf_gtk__hpp_color_overhead;
|
||||
perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
|
||||
perf_gtk__hpp_color_overhead_sys;
|
||||
perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
|
||||
perf_gtk__hpp_color_overhead_us;
|
||||
perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
|
||||
perf_gtk__hpp_color_overhead_guest_sys;
|
||||
perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
|
||||
perf_gtk__hpp_color_overhead_guest_us;
|
||||
}
|
||||
|
||||
static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists)
|
||||
{
|
||||
struct perf_hpp_fmt *fmt;
|
||||
GType col_types[MAX_COLUMNS];
|
||||
GtkCellRenderer *renderer;
|
||||
struct sort_entry *se;
|
||||
GtkListStore *store;
|
||||
struct rb_node *nd;
|
||||
GtkWidget *view;
|
||||
int col_idx;
|
||||
int nr_cols;
|
||||
char s[512];
|
||||
|
||||
struct perf_hpp hpp = {
|
||||
.buf = s,
|
||||
.size = sizeof(s),
|
||||
.ptr = hists_to_evsel(hists),
|
||||
};
|
||||
|
||||
nr_cols = 0;
|
||||
|
||||
perf_hpp__for_each_format(fmt)
|
||||
col_types[nr_cols++] = G_TYPE_STRING;
|
||||
|
||||
list_for_each_entry(se, &hist_entry__sort_list, list) {
|
||||
if (se->elide)
|
||||
continue;
|
||||
|
||||
col_types[nr_cols++] = G_TYPE_STRING;
|
||||
}
|
||||
|
||||
store = gtk_list_store_newv(nr_cols, col_types);
|
||||
|
||||
view = gtk_tree_view_new();
|
||||
|
||||
renderer = gtk_cell_renderer_text_new();
|
||||
|
||||
col_idx = 0;
|
||||
|
||||
perf_hpp__for_each_format(fmt) {
|
||||
fmt->header(&hpp);
|
||||
|
||||
gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
|
||||
-1, ltrim(s),
|
||||
renderer, "markup",
|
||||
col_idx++, NULL);
|
||||
}
|
||||
|
||||
list_for_each_entry(se, &hist_entry__sort_list, list) {
|
||||
if (se->elide)
|
||||
continue;
|
||||
|
||||
gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
|
||||
-1, se->se_header,
|
||||
renderer, "text",
|
||||
col_idx++, NULL);
|
||||
}
|
||||
|
||||
gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store));
|
||||
|
||||
g_object_unref(GTK_TREE_MODEL(store));
|
||||
|
||||
for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
|
||||
struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
|
||||
GtkTreeIter iter;
|
||||
|
||||
if (h->filtered)
|
||||
continue;
|
||||
|
||||
gtk_list_store_append(store, &iter);
|
||||
|
||||
col_idx = 0;
|
||||
|
||||
perf_hpp__for_each_format(fmt) {
|
||||
if (fmt->color)
|
||||
fmt->color(&hpp, h);
|
||||
else
|
||||
fmt->entry(&hpp, h);
|
||||
|
||||
gtk_list_store_set(store, &iter, col_idx++, s, -1);
|
||||
}
|
||||
|
||||
list_for_each_entry(se, &hist_entry__sort_list, list) {
|
||||
if (se->elide)
|
||||
continue;
|
||||
|
||||
se->se_snprintf(h, s, ARRAY_SIZE(s),
|
||||
hists__col_len(hists, se->se_width_idx));
|
||||
|
||||
gtk_list_store_set(store, &iter, col_idx++, s, -1);
|
||||
}
|
||||
}
|
||||
|
||||
gtk_container_add(GTK_CONTAINER(window), view);
|
||||
}
|
||||
|
||||
int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist,
|
||||
const char *help,
|
||||
struct hist_browser_timer *hbt __maybe_unused)
|
||||
{
|
||||
struct perf_evsel *pos;
|
||||
GtkWidget *vbox;
|
||||
GtkWidget *notebook;
|
||||
GtkWidget *info_bar;
|
||||
GtkWidget *statbar;
|
||||
GtkWidget *window;
|
||||
|
||||
signal(SIGSEGV, perf_gtk__signal);
|
||||
signal(SIGFPE, perf_gtk__signal);
|
||||
signal(SIGINT, perf_gtk__signal);
|
||||
signal(SIGQUIT, perf_gtk__signal);
|
||||
signal(SIGTERM, perf_gtk__signal);
|
||||
|
||||
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
||||
|
||||
gtk_window_set_title(GTK_WINDOW(window), "perf report");
|
||||
|
||||
g_signal_connect(window, "delete_event", gtk_main_quit, NULL);
|
||||
|
||||
pgctx = perf_gtk__activate_context(window);
|
||||
if (!pgctx)
|
||||
return -1;
|
||||
|
||||
vbox = gtk_vbox_new(FALSE, 0);
|
||||
|
||||
notebook = gtk_notebook_new();
|
||||
|
||||
gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0);
|
||||
|
||||
info_bar = perf_gtk__setup_info_bar();
|
||||
if (info_bar)
|
||||
gtk_box_pack_start(GTK_BOX(vbox), info_bar, FALSE, FALSE, 0);
|
||||
|
||||
statbar = perf_gtk__setup_statusbar();
|
||||
gtk_box_pack_start(GTK_BOX(vbox), statbar, FALSE, FALSE, 0);
|
||||
|
||||
gtk_container_add(GTK_CONTAINER(window), vbox);
|
||||
|
||||
list_for_each_entry(pos, &evlist->entries, node) {
|
||||
struct hists *hists = &pos->hists;
|
||||
const char *evname = perf_evsel__name(pos);
|
||||
GtkWidget *scrolled_window;
|
||||
GtkWidget *tab_label;
|
||||
char buf[512];
|
||||
size_t size = sizeof(buf);
|
||||
|
||||
if (symbol_conf.event_group) {
|
||||
if (!perf_evsel__is_group_leader(pos))
|
||||
continue;
|
||||
|
||||
if (pos->nr_members > 1) {
|
||||
perf_evsel__group_desc(pos, buf, size);
|
||||
evname = buf;
|
||||
}
|
||||
}
|
||||
|
||||
scrolled_window = gtk_scrolled_window_new(NULL, NULL);
|
||||
|
||||
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
|
||||
GTK_POLICY_AUTOMATIC,
|
||||
GTK_POLICY_AUTOMATIC);
|
||||
|
||||
perf_gtk__show_hists(scrolled_window, hists);
|
||||
|
||||
tab_label = gtk_label_new(evname);
|
||||
|
||||
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled_window, tab_label);
|
||||
}
|
||||
|
||||
gtk_widget_show_all(window);
|
||||
|
||||
perf_gtk__resize_window(window);
|
||||
|
||||
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
|
||||
|
||||
ui_helpline__push(help);
|
||||
|
||||
gtk_main();
|
||||
|
||||
perf_gtk__deactivate_context(&pgctx);
|
||||
|
||||
return 0;
|
||||
}
|
@@ -16,9 +16,16 @@ static void nop_helpline__push(const char *msg __maybe_unused)
|
||||
{
|
||||
}
|
||||
|
||||
static int nop_helpline__show(const char *fmt __maybe_unused,
|
||||
va_list ap __maybe_unused)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ui_helpline default_helpline_fns = {
|
||||
.pop = nop_helpline__pop,
|
||||
.push = nop_helpline__push,
|
||||
.show = nop_helpline__show,
|
||||
};
|
||||
|
||||
struct ui_helpline *helpline_fns = &default_helpline_fns;
|
||||
@@ -59,3 +66,8 @@ void ui_helpline__puts(const char *msg)
|
||||
ui_helpline__pop();
|
||||
ui_helpline__push(msg);
|
||||
}
|
||||
|
||||
int ui_helpline__vshow(const char *fmt, va_list ap)
|
||||
{
|
||||
return helpline_fns->show(fmt, ap);
|
||||
}
|
||||
|
@@ -9,6 +9,7 @@
|
||||
struct ui_helpline {
|
||||
void (*pop)(void);
|
||||
void (*push)(const char *msg);
|
||||
int (*show)(const char *fmt, va_list ap);
|
||||
};
|
||||
|
||||
extern struct ui_helpline *helpline_fns;
|
||||
@@ -20,28 +21,9 @@ void ui_helpline__push(const char *msg);
|
||||
void ui_helpline__vpush(const char *fmt, va_list ap);
|
||||
void ui_helpline__fpush(const char *fmt, ...);
|
||||
void ui_helpline__puts(const char *msg);
|
||||
int ui_helpline__vshow(const char *fmt, va_list ap);
|
||||
|
||||
extern char ui_helpline__current[512];
|
||||
|
||||
#ifdef NEWT_SUPPORT
|
||||
extern char ui_helpline__last_msg[];
|
||||
int ui_helpline__show_help(const char *format, va_list ap);
|
||||
#else
|
||||
static inline int ui_helpline__show_help(const char *format __maybe_unused,
|
||||
va_list ap __maybe_unused)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* NEWT_SUPPORT */
|
||||
|
||||
#ifdef GTK2_SUPPORT
|
||||
int perf_gtk__show_helpline(const char *format, va_list ap);
|
||||
#else
|
||||
static inline int perf_gtk__show_helpline(const char *format __maybe_unused,
|
||||
va_list ap __maybe_unused)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* GTK2_SUPPORT */
|
||||
|
||||
#endif /* _PERF_UI_HELPLINE_H_ */
|
||||
|
@@ -3,151 +3,163 @@
|
||||
#include "../util/hist.h"
|
||||
#include "../util/util.h"
|
||||
#include "../util/sort.h"
|
||||
|
||||
#include "../util/evsel.h"
|
||||
|
||||
/* hist period print (hpp) functions */
|
||||
static int hpp__header_overhead(struct perf_hpp *hpp)
|
||||
{
|
||||
return scnprintf(hpp->buf, hpp->size, "Overhead");
|
||||
}
|
||||
|
||||
static int hpp__width_overhead(struct perf_hpp *hpp __maybe_unused)
|
||||
{
|
||||
return 8;
|
||||
}
|
||||
typedef int (*hpp_snprint_fn)(char *buf, size_t size, const char *fmt, ...);
|
||||
|
||||
static int hpp__color_overhead(struct perf_hpp *hpp, struct hist_entry *he)
|
||||
static int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
|
||||
u64 (*get_field)(struct hist_entry *),
|
||||
const char *fmt, hpp_snprint_fn print_fn,
|
||||
bool fmt_percent)
|
||||
{
|
||||
int ret;
|
||||
struct hists *hists = he->hists;
|
||||
double percent = 100.0 * he->stat.period / hists->stats.total_period;
|
||||
|
||||
return percent_color_snprintf(hpp->buf, hpp->size, " %6.2f%%", percent);
|
||||
if (fmt_percent) {
|
||||
double percent = 0.0;
|
||||
|
||||
if (hists->stats.total_period)
|
||||
percent = 100.0 * get_field(he) /
|
||||
hists->stats.total_period;
|
||||
|
||||
ret = print_fn(hpp->buf, hpp->size, fmt, percent);
|
||||
} else
|
||||
ret = print_fn(hpp->buf, hpp->size, fmt, get_field(he));
|
||||
|
||||
if (symbol_conf.event_group) {
|
||||
int prev_idx, idx_delta;
|
||||
struct perf_evsel *evsel = hists_to_evsel(hists);
|
||||
struct hist_entry *pair;
|
||||
int nr_members = evsel->nr_members;
|
||||
|
||||
if (nr_members <= 1)
|
||||
return ret;
|
||||
|
||||
prev_idx = perf_evsel__group_idx(evsel);
|
||||
|
||||
list_for_each_entry(pair, &he->pairs.head, pairs.node) {
|
||||
u64 period = get_field(pair);
|
||||
u64 total = pair->hists->stats.total_period;
|
||||
|
||||
if (!total)
|
||||
continue;
|
||||
|
||||
evsel = hists_to_evsel(pair->hists);
|
||||
idx_delta = perf_evsel__group_idx(evsel) - prev_idx - 1;
|
||||
|
||||
while (idx_delta--) {
|
||||
/*
|
||||
* zero-fill group members in the middle which
|
||||
* have no sample
|
||||
*/
|
||||
ret += print_fn(hpp->buf + ret, hpp->size - ret,
|
||||
fmt, 0);
|
||||
}
|
||||
|
||||
if (fmt_percent)
|
||||
ret += print_fn(hpp->buf + ret, hpp->size - ret,
|
||||
fmt, 100.0 * period / total);
|
||||
else
|
||||
ret += print_fn(hpp->buf + ret, hpp->size - ret,
|
||||
fmt, period);
|
||||
|
||||
prev_idx = perf_evsel__group_idx(evsel);
|
||||
}
|
||||
|
||||
idx_delta = nr_members - prev_idx - 1;
|
||||
|
||||
while (idx_delta--) {
|
||||
/*
|
||||
* zero-fill group members at last which have no sample
|
||||
*/
|
||||
ret += print_fn(hpp->buf + ret, hpp->size - ret,
|
||||
fmt, 0);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hpp__entry_overhead(struct perf_hpp *hpp, struct hist_entry *he)
|
||||
{
|
||||
struct hists *hists = he->hists;
|
||||
double percent = 100.0 * he->stat.period / hists->stats.total_period;
|
||||
const char *fmt = symbol_conf.field_sep ? "%.2f" : " %6.2f%%";
|
||||
|
||||
return scnprintf(hpp->buf, hpp->size, fmt, percent);
|
||||
#define __HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \
|
||||
static int hpp__header_##_type(struct perf_hpp *hpp) \
|
||||
{ \
|
||||
int len = _min_width; \
|
||||
\
|
||||
if (symbol_conf.event_group) { \
|
||||
struct perf_evsel *evsel = hpp->ptr; \
|
||||
\
|
||||
len = max(len, evsel->nr_members * _unit_width); \
|
||||
} \
|
||||
return scnprintf(hpp->buf, hpp->size, "%*s", len, _str); \
|
||||
}
|
||||
|
||||
static int hpp__header_overhead_sys(struct perf_hpp *hpp)
|
||||
{
|
||||
const char *fmt = symbol_conf.field_sep ? "%s" : "%7s";
|
||||
|
||||
return scnprintf(hpp->buf, hpp->size, fmt, "sys");
|
||||
#define __HPP_WIDTH_FN(_type, _min_width, _unit_width) \
|
||||
static int hpp__width_##_type(struct perf_hpp *hpp __maybe_unused) \
|
||||
{ \
|
||||
int len = _min_width; \
|
||||
\
|
||||
if (symbol_conf.event_group) { \
|
||||
struct perf_evsel *evsel = hpp->ptr; \
|
||||
\
|
||||
len = max(len, evsel->nr_members * _unit_width); \
|
||||
} \
|
||||
return len; \
|
||||
}
|
||||
|
||||
static int hpp__width_overhead_sys(struct perf_hpp *hpp __maybe_unused)
|
||||
{
|
||||
return 7;
|
||||
#define __HPP_COLOR_PERCENT_FN(_type, _field) \
|
||||
static u64 he_get_##_field(struct hist_entry *he) \
|
||||
{ \
|
||||
return he->stat._field; \
|
||||
} \
|
||||
\
|
||||
static int hpp__color_##_type(struct perf_hpp *hpp, struct hist_entry *he) \
|
||||
{ \
|
||||
return __hpp__fmt(hpp, he, he_get_##_field, " %6.2f%%", \
|
||||
(hpp_snprint_fn)percent_color_snprintf, true); \
|
||||
}
|
||||
|
||||
static int hpp__color_overhead_sys(struct perf_hpp *hpp, struct hist_entry *he)
|
||||
{
|
||||
struct hists *hists = he->hists;
|
||||
double percent = 100.0 * he->stat.period_sys / hists->stats.total_period;
|
||||
|
||||
return percent_color_snprintf(hpp->buf, hpp->size, "%6.2f%%", percent);
|
||||
#define __HPP_ENTRY_PERCENT_FN(_type, _field) \
|
||||
static int hpp__entry_##_type(struct perf_hpp *hpp, struct hist_entry *he) \
|
||||
{ \
|
||||
const char *fmt = symbol_conf.field_sep ? " %.2f" : " %6.2f%%"; \
|
||||
return __hpp__fmt(hpp, he, he_get_##_field, fmt, \
|
||||
scnprintf, true); \
|
||||
}
|
||||
|
||||
static int hpp__entry_overhead_sys(struct perf_hpp *hpp, struct hist_entry *he)
|
||||
{
|
||||
struct hists *hists = he->hists;
|
||||
double percent = 100.0 * he->stat.period_sys / hists->stats.total_period;
|
||||
const char *fmt = symbol_conf.field_sep ? "%.2f" : "%6.2f%%";
|
||||
|
||||
return scnprintf(hpp->buf, hpp->size, fmt, percent);
|
||||
#define __HPP_ENTRY_RAW_FN(_type, _field) \
|
||||
static u64 he_get_raw_##_field(struct hist_entry *he) \
|
||||
{ \
|
||||
return he->stat._field; \
|
||||
} \
|
||||
\
|
||||
static int hpp__entry_##_type(struct perf_hpp *hpp, struct hist_entry *he) \
|
||||
{ \
|
||||
const char *fmt = symbol_conf.field_sep ? " %"PRIu64 : " %11"PRIu64; \
|
||||
return __hpp__fmt(hpp, he, he_get_raw_##_field, fmt, scnprintf, false); \
|
||||
}
|
||||
|
||||
static int hpp__header_overhead_us(struct perf_hpp *hpp)
|
||||
{
|
||||
const char *fmt = symbol_conf.field_sep ? "%s" : "%7s";
|
||||
#define HPP_PERCENT_FNS(_type, _str, _field, _min_width, _unit_width) \
|
||||
__HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \
|
||||
__HPP_WIDTH_FN(_type, _min_width, _unit_width) \
|
||||
__HPP_COLOR_PERCENT_FN(_type, _field) \
|
||||
__HPP_ENTRY_PERCENT_FN(_type, _field)
|
||||
|
||||
return scnprintf(hpp->buf, hpp->size, fmt, "user");
|
||||
}
|
||||
#define HPP_RAW_FNS(_type, _str, _field, _min_width, _unit_width) \
|
||||
__HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \
|
||||
__HPP_WIDTH_FN(_type, _min_width, _unit_width) \
|
||||
__HPP_ENTRY_RAW_FN(_type, _field)
|
||||
|
||||
static int hpp__width_overhead_us(struct perf_hpp *hpp __maybe_unused)
|
||||
{
|
||||
return 7;
|
||||
}
|
||||
|
||||
static int hpp__color_overhead_us(struct perf_hpp *hpp, struct hist_entry *he)
|
||||
{
|
||||
struct hists *hists = he->hists;
|
||||
double percent = 100.0 * he->stat.period_us / hists->stats.total_period;
|
||||
HPP_PERCENT_FNS(overhead, "Overhead", period, 8, 8)
|
||||
HPP_PERCENT_FNS(overhead_sys, "sys", period_sys, 8, 8)
|
||||
HPP_PERCENT_FNS(overhead_us, "usr", period_us, 8, 8)
|
||||
HPP_PERCENT_FNS(overhead_guest_sys, "guest sys", period_guest_sys, 9, 8)
|
||||
HPP_PERCENT_FNS(overhead_guest_us, "guest usr", period_guest_us, 9, 8)
|
||||
|
||||
return percent_color_snprintf(hpp->buf, hpp->size, "%6.2f%%", percent);
|
||||
}
|
||||
HPP_RAW_FNS(samples, "Samples", nr_events, 12, 12)
|
||||
HPP_RAW_FNS(period, "Period", period, 12, 12)
|
||||
|
||||
static int hpp__entry_overhead_us(struct perf_hpp *hpp, struct hist_entry *he)
|
||||
{
|
||||
struct hists *hists = he->hists;
|
||||
double percent = 100.0 * he->stat.period_us / hists->stats.total_period;
|
||||
const char *fmt = symbol_conf.field_sep ? "%.2f" : "%6.2f%%";
|
||||
|
||||
return scnprintf(hpp->buf, hpp->size, fmt, percent);
|
||||
}
|
||||
|
||||
static int hpp__header_overhead_guest_sys(struct perf_hpp *hpp)
|
||||
{
|
||||
return scnprintf(hpp->buf, hpp->size, "guest sys");
|
||||
}
|
||||
|
||||
static int hpp__width_overhead_guest_sys(struct perf_hpp *hpp __maybe_unused)
|
||||
{
|
||||
return 9;
|
||||
}
|
||||
|
||||
static int hpp__color_overhead_guest_sys(struct perf_hpp *hpp,
|
||||
struct hist_entry *he)
|
||||
{
|
||||
struct hists *hists = he->hists;
|
||||
double percent = 100.0 * he->stat.period_guest_sys / hists->stats.total_period;
|
||||
|
||||
return percent_color_snprintf(hpp->buf, hpp->size, " %6.2f%% ", percent);
|
||||
}
|
||||
|
||||
static int hpp__entry_overhead_guest_sys(struct perf_hpp *hpp,
|
||||
struct hist_entry *he)
|
||||
{
|
||||
struct hists *hists = he->hists;
|
||||
double percent = 100.0 * he->stat.period_guest_sys / hists->stats.total_period;
|
||||
const char *fmt = symbol_conf.field_sep ? "%.2f" : " %6.2f%% ";
|
||||
|
||||
return scnprintf(hpp->buf, hpp->size, fmt, percent);
|
||||
}
|
||||
|
||||
static int hpp__header_overhead_guest_us(struct perf_hpp *hpp)
|
||||
{
|
||||
return scnprintf(hpp->buf, hpp->size, "guest usr");
|
||||
}
|
||||
|
||||
static int hpp__width_overhead_guest_us(struct perf_hpp *hpp __maybe_unused)
|
||||
{
|
||||
return 9;
|
||||
}
|
||||
|
||||
static int hpp__color_overhead_guest_us(struct perf_hpp *hpp,
|
||||
struct hist_entry *he)
|
||||
{
|
||||
struct hists *hists = he->hists;
|
||||
double percent = 100.0 * he->stat.period_guest_us / hists->stats.total_period;
|
||||
|
||||
return percent_color_snprintf(hpp->buf, hpp->size, " %6.2f%% ", percent);
|
||||
}
|
||||
|
||||
static int hpp__entry_overhead_guest_us(struct perf_hpp *hpp,
|
||||
struct hist_entry *he)
|
||||
{
|
||||
struct hists *hists = he->hists;
|
||||
double percent = 100.0 * he->stat.period_guest_us / hists->stats.total_period;
|
||||
const char *fmt = symbol_conf.field_sep ? "%.2f" : " %6.2f%% ";
|
||||
|
||||
return scnprintf(hpp->buf, hpp->size, fmt, percent);
|
||||
}
|
||||
|
||||
static int hpp__header_baseline(struct perf_hpp *hpp)
|
||||
{
|
||||
@@ -179,7 +191,7 @@ static int hpp__color_baseline(struct perf_hpp *hpp, struct hist_entry *he)
|
||||
{
|
||||
double percent = baseline_percent(he);
|
||||
|
||||
if (hist_entry__has_pairs(he))
|
||||
if (hist_entry__has_pairs(he) || symbol_conf.field_sep)
|
||||
return percent_color_snprintf(hpp->buf, hpp->size, " %6.2f%%", percent);
|
||||
else
|
||||
return scnprintf(hpp->buf, hpp->size, " ");
|
||||
@@ -196,44 +208,6 @@ static int hpp__entry_baseline(struct perf_hpp *hpp, struct hist_entry *he)
|
||||
return scnprintf(hpp->buf, hpp->size, " ");
|
||||
}
|
||||
|
||||
static int hpp__header_samples(struct perf_hpp *hpp)
|
||||
{
|
||||
const char *fmt = symbol_conf.field_sep ? "%s" : "%11s";
|
||||
|
||||
return scnprintf(hpp->buf, hpp->size, fmt, "Samples");
|
||||
}
|
||||
|
||||
static int hpp__width_samples(struct perf_hpp *hpp __maybe_unused)
|
||||
{
|
||||
return 11;
|
||||
}
|
||||
|
||||
static int hpp__entry_samples(struct perf_hpp *hpp, struct hist_entry *he)
|
||||
{
|
||||
const char *fmt = symbol_conf.field_sep ? "%" PRIu64 : "%11" PRIu64;
|
||||
|
||||
return scnprintf(hpp->buf, hpp->size, fmt, he->stat.nr_events);
|
||||
}
|
||||
|
||||
static int hpp__header_period(struct perf_hpp *hpp)
|
||||
{
|
||||
const char *fmt = symbol_conf.field_sep ? "%s" : "%12s";
|
||||
|
||||
return scnprintf(hpp->buf, hpp->size, fmt, "Period");
|
||||
}
|
||||
|
||||
static int hpp__width_period(struct perf_hpp *hpp __maybe_unused)
|
||||
{
|
||||
return 12;
|
||||
}
|
||||
|
||||
static int hpp__entry_period(struct perf_hpp *hpp, struct hist_entry *he)
|
||||
{
|
||||
const char *fmt = symbol_conf.field_sep ? "%" PRIu64 : "%12" PRIu64;
|
||||
|
||||
return scnprintf(hpp->buf, hpp->size, fmt, he->stat.period);
|
||||
}
|
||||
|
||||
static int hpp__header_period_baseline(struct perf_hpp *hpp)
|
||||
{
|
||||
const char *fmt = symbol_conf.field_sep ? "%s" : "%12s";
|
||||
@@ -254,6 +228,7 @@ static int hpp__entry_period_baseline(struct perf_hpp *hpp, struct hist_entry *h
|
||||
|
||||
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";
|
||||
@@ -268,14 +243,18 @@ 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 = hist_entry__next_pair(he);
|
||||
const char *fmt = symbol_conf.field_sep ? "%s" : "%7.7s";
|
||||
char buf[32] = " ";
|
||||
double diff;
|
||||
double diff = 0.0;
|
||||
|
||||
if (he->diff.computed)
|
||||
diff = he->diff.period_ratio_delta;
|
||||
else
|
||||
diff = perf_diff__compute_delta(he);
|
||||
if (pair) {
|
||||
if (he->diff.computed)
|
||||
diff = he->diff.period_ratio_delta;
|
||||
else
|
||||
diff = perf_diff__compute_delta(he, pair);
|
||||
} else
|
||||
diff = perf_diff__period_percent(he, he->stat.period);
|
||||
|
||||
if (fabs(diff) >= 0.01)
|
||||
scnprintf(buf, sizeof(buf), "%+4.2F%%", diff);
|
||||
@@ -297,14 +276,17 @@ static int hpp__width_ratio(struct perf_hpp *hpp __maybe_unused)
|
||||
|
||||
static int hpp__entry_ratio(struct perf_hpp *hpp, struct hist_entry *he)
|
||||
{
|
||||
struct hist_entry *pair = hist_entry__next_pair(he);
|
||||
const char *fmt = symbol_conf.field_sep ? "%s" : "%14s";
|
||||
char buf[32] = " ";
|
||||
double ratio;
|
||||
double ratio = 0.0;
|
||||
|
||||
if (he->diff.computed)
|
||||
ratio = he->diff.period_ratio;
|
||||
else
|
||||
ratio = perf_diff__compute_ratio(he);
|
||||
if (pair) {
|
||||
if (he->diff.computed)
|
||||
ratio = he->diff.period_ratio;
|
||||
else
|
||||
ratio = perf_diff__compute_ratio(he, pair);
|
||||
}
|
||||
|
||||
if (ratio > 0.0)
|
||||
scnprintf(buf, sizeof(buf), "%+14.6F", ratio);
|
||||
@@ -326,14 +308,17 @@ static int hpp__width_wdiff(struct perf_hpp *hpp __maybe_unused)
|
||||
|
||||
static int hpp__entry_wdiff(struct perf_hpp *hpp, struct hist_entry *he)
|
||||
{
|
||||
struct hist_entry *pair = hist_entry__next_pair(he);
|
||||
const char *fmt = symbol_conf.field_sep ? "%s" : "%14s";
|
||||
char buf[32] = " ";
|
||||
s64 wdiff;
|
||||
s64 wdiff = 0;
|
||||
|
||||
if (he->diff.computed)
|
||||
wdiff = he->diff.wdiff;
|
||||
else
|
||||
wdiff = perf_diff__compute_wdiff(he);
|
||||
if (pair) {
|
||||
if (he->diff.computed)
|
||||
wdiff = he->diff.wdiff;
|
||||
else
|
||||
wdiff = perf_diff__compute_wdiff(he, pair);
|
||||
}
|
||||
|
||||
if (wdiff != 0)
|
||||
scnprintf(buf, sizeof(buf), "%14ld", wdiff);
|
||||
@@ -341,30 +326,6 @@ static int hpp__entry_wdiff(struct perf_hpp *hpp, struct hist_entry *he)
|
||||
return scnprintf(hpp->buf, hpp->size, fmt, buf);
|
||||
}
|
||||
|
||||
static int hpp__header_displ(struct perf_hpp *hpp)
|
||||
{
|
||||
return scnprintf(hpp->buf, hpp->size, "Displ.");
|
||||
}
|
||||
|
||||
static int hpp__width_displ(struct perf_hpp *hpp __maybe_unused)
|
||||
{
|
||||
return 6;
|
||||
}
|
||||
|
||||
static int hpp__entry_displ(struct perf_hpp *hpp,
|
||||
struct hist_entry *he)
|
||||
{
|
||||
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] = " ";
|
||||
|
||||
if (displacement)
|
||||
scnprintf(buf, sizeof(buf), "%+4ld", displacement);
|
||||
|
||||
return scnprintf(hpp->buf, hpp->size, fmt, buf);
|
||||
}
|
||||
|
||||
static int hpp__header_formula(struct perf_hpp *hpp)
|
||||
{
|
||||
const char *fmt = symbol_conf.field_sep ? "%s" : "%70s";
|
||||
@@ -379,67 +340,91 @@ static int hpp__width_formula(struct perf_hpp *hpp __maybe_unused)
|
||||
|
||||
static int hpp__entry_formula(struct perf_hpp *hpp, struct hist_entry *he)
|
||||
{
|
||||
struct hist_entry *pair = hist_entry__next_pair(he);
|
||||
const char *fmt = symbol_conf.field_sep ? "%s" : "%-70s";
|
||||
char buf[96] = " ";
|
||||
|
||||
perf_diff__formula(buf, sizeof(buf), he);
|
||||
if (pair)
|
||||
perf_diff__formula(he, pair, buf, sizeof(buf));
|
||||
|
||||
return scnprintf(hpp->buf, hpp->size, fmt, buf);
|
||||
}
|
||||
|
||||
#define HPP__COLOR_PRINT_FNS(_name) \
|
||||
.header = hpp__header_ ## _name, \
|
||||
.width = hpp__width_ ## _name, \
|
||||
.color = hpp__color_ ## _name, \
|
||||
.entry = hpp__entry_ ## _name
|
||||
#define HPP__COLOR_PRINT_FNS(_name) \
|
||||
{ \
|
||||
.header = hpp__header_ ## _name, \
|
||||
.width = hpp__width_ ## _name, \
|
||||
.color = hpp__color_ ## _name, \
|
||||
.entry = hpp__entry_ ## _name \
|
||||
}
|
||||
|
||||
#define HPP__PRINT_FNS(_name) \
|
||||
.header = hpp__header_ ## _name, \
|
||||
.width = hpp__width_ ## _name, \
|
||||
.entry = hpp__entry_ ## _name
|
||||
#define HPP__PRINT_FNS(_name) \
|
||||
{ \
|
||||
.header = hpp__header_ ## _name, \
|
||||
.width = hpp__width_ ## _name, \
|
||||
.entry = hpp__entry_ ## _name \
|
||||
}
|
||||
|
||||
struct perf_hpp_fmt perf_hpp__format[] = {
|
||||
{ .cond = false, HPP__COLOR_PRINT_FNS(baseline) },
|
||||
{ .cond = true, HPP__COLOR_PRINT_FNS(overhead) },
|
||||
{ .cond = false, HPP__COLOR_PRINT_FNS(overhead_sys) },
|
||||
{ .cond = false, HPP__COLOR_PRINT_FNS(overhead_us) },
|
||||
{ .cond = false, HPP__COLOR_PRINT_FNS(overhead_guest_sys) },
|
||||
{ .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(ratio) },
|
||||
{ .cond = false, HPP__PRINT_FNS(wdiff) },
|
||||
{ .cond = false, HPP__PRINT_FNS(displ) },
|
||||
{ .cond = false, HPP__PRINT_FNS(formula) }
|
||||
HPP__COLOR_PRINT_FNS(baseline),
|
||||
HPP__COLOR_PRINT_FNS(overhead),
|
||||
HPP__COLOR_PRINT_FNS(overhead_sys),
|
||||
HPP__COLOR_PRINT_FNS(overhead_us),
|
||||
HPP__COLOR_PRINT_FNS(overhead_guest_sys),
|
||||
HPP__COLOR_PRINT_FNS(overhead_guest_us),
|
||||
HPP__PRINT_FNS(samples),
|
||||
HPP__PRINT_FNS(period),
|
||||
HPP__PRINT_FNS(period_baseline),
|
||||
HPP__PRINT_FNS(delta),
|
||||
HPP__PRINT_FNS(ratio),
|
||||
HPP__PRINT_FNS(wdiff),
|
||||
HPP__PRINT_FNS(formula)
|
||||
};
|
||||
|
||||
LIST_HEAD(perf_hpp__list);
|
||||
|
||||
|
||||
#undef HPP__COLOR_PRINT_FNS
|
||||
#undef HPP__PRINT_FNS
|
||||
|
||||
#undef HPP_PERCENT_FNS
|
||||
#undef HPP_RAW_FNS
|
||||
|
||||
#undef __HPP_HEADER_FN
|
||||
#undef __HPP_WIDTH_FN
|
||||
#undef __HPP_COLOR_PERCENT_FN
|
||||
#undef __HPP_ENTRY_PERCENT_FN
|
||||
#undef __HPP_ENTRY_RAW_FN
|
||||
|
||||
|
||||
void perf_hpp__init(void)
|
||||
{
|
||||
if (symbol_conf.show_cpu_utilization) {
|
||||
perf_hpp__format[PERF_HPP__OVERHEAD_SYS].cond = true;
|
||||
perf_hpp__format[PERF_HPP__OVERHEAD_US].cond = true;
|
||||
perf_hpp__column_enable(PERF_HPP__OVERHEAD_SYS);
|
||||
perf_hpp__column_enable(PERF_HPP__OVERHEAD_US);
|
||||
|
||||
if (perf_guest) {
|
||||
perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].cond = true;
|
||||
perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].cond = true;
|
||||
perf_hpp__column_enable(PERF_HPP__OVERHEAD_GUEST_SYS);
|
||||
perf_hpp__column_enable(PERF_HPP__OVERHEAD_GUEST_US);
|
||||
}
|
||||
}
|
||||
|
||||
if (symbol_conf.show_nr_samples)
|
||||
perf_hpp__format[PERF_HPP__SAMPLES].cond = true;
|
||||
perf_hpp__column_enable(PERF_HPP__SAMPLES);
|
||||
|
||||
if (symbol_conf.show_total_period)
|
||||
perf_hpp__format[PERF_HPP__PERIOD].cond = true;
|
||||
perf_hpp__column_enable(PERF_HPP__PERIOD);
|
||||
}
|
||||
|
||||
void perf_hpp__column_enable(unsigned col, bool enable)
|
||||
void perf_hpp__column_register(struct perf_hpp_fmt *format)
|
||||
{
|
||||
list_add_tail(&format->list, &perf_hpp__list);
|
||||
}
|
||||
|
||||
void perf_hpp__column_enable(unsigned col)
|
||||
{
|
||||
BUG_ON(col >= PERF_HPP__MAX_INDEX);
|
||||
perf_hpp__format[col].cond = enable;
|
||||
perf_hpp__column_register(&perf_hpp__format[col]);
|
||||
}
|
||||
|
||||
static inline void advance_hpp(struct perf_hpp *hpp, int inc)
|
||||
@@ -452,27 +437,29 @@ int hist_entry__period_snprintf(struct perf_hpp *hpp, struct hist_entry *he,
|
||||
bool color)
|
||||
{
|
||||
const char *sep = symbol_conf.field_sep;
|
||||
struct perf_hpp_fmt *fmt;
|
||||
char *start = hpp->buf;
|
||||
int i, ret;
|
||||
int ret;
|
||||
bool first = true;
|
||||
|
||||
if (symbol_conf.exclude_other && !he->parent)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < PERF_HPP__MAX_INDEX; i++) {
|
||||
if (!perf_hpp__format[i].cond)
|
||||
continue;
|
||||
|
||||
perf_hpp__for_each_format(fmt) {
|
||||
/*
|
||||
* If there's no field_sep, we still need
|
||||
* to display initial ' '.
|
||||
*/
|
||||
if (!sep || !first) {
|
||||
ret = scnprintf(hpp->buf, hpp->size, "%s", sep ?: " ");
|
||||
advance_hpp(hpp, ret);
|
||||
} else
|
||||
first = false;
|
||||
}
|
||||
|
||||
if (color && perf_hpp__format[i].color)
|
||||
ret = perf_hpp__format[i].color(hpp, he);
|
||||
if (color && fmt->color)
|
||||
ret = fmt->color(hpp, he);
|
||||
else
|
||||
ret = perf_hpp__format[i].entry(hpp, he);
|
||||
ret = fmt->entry(hpp, he);
|
||||
|
||||
advance_hpp(hpp, ret);
|
||||
}
|
||||
@@ -504,16 +491,18 @@ int hist_entry__sort_snprintf(struct hist_entry *he, char *s, size_t size,
|
||||
*/
|
||||
unsigned int hists__sort_list_width(struct hists *hists)
|
||||
{
|
||||
struct perf_hpp_fmt *fmt;
|
||||
struct sort_entry *se;
|
||||
int i, ret = 0;
|
||||
int i = 0, ret = 0;
|
||||
struct perf_hpp dummy_hpp = {
|
||||
.ptr = hists_to_evsel(hists),
|
||||
};
|
||||
|
||||
for (i = 0; i < PERF_HPP__MAX_INDEX; i++) {
|
||||
if (!perf_hpp__format[i].cond)
|
||||
continue;
|
||||
perf_hpp__for_each_format(fmt) {
|
||||
if (i)
|
||||
ret += 2;
|
||||
|
||||
ret += perf_hpp__format[i].width(NULL);
|
||||
ret += fmt->width(&dummy_hpp);
|
||||
}
|
||||
|
||||
list_for_each_entry(se, &hist_entry__sort_list, list)
|
||||
|
@@ -23,5 +23,6 @@
|
||||
#define K_TIMER -1
|
||||
#define K_ERROR -2
|
||||
#define K_RESIZE -3
|
||||
#define K_SWITCH_INPUT_DATA -4
|
||||
|
||||
#endif /* _PERF_KEYSYMS_H_ */
|
||||
|
@@ -8,7 +8,7 @@ pthread_mutex_t ui__lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
void setup_browser(bool fallback_to_pager)
|
||||
{
|
||||
if (!isatty(1) || dump_trace)
|
||||
if (use_browser < 2 && (!isatty(1) || dump_trace))
|
||||
use_browser = 0;
|
||||
|
||||
/* default to TUI */
|
||||
@@ -30,6 +30,7 @@ void setup_browser(bool fallback_to_pager)
|
||||
if (fallback_to_pager)
|
||||
setup_pager();
|
||||
|
||||
perf_hpp__column_enable(PERF_HPP__OVERHEAD);
|
||||
perf_hpp__init();
|
||||
break;
|
||||
}
|
||||
|
@@ -3,6 +3,7 @@
|
||||
#include "../../util/util.h"
|
||||
#include "../../util/hist.h"
|
||||
#include "../../util/sort.h"
|
||||
#include "../../util/evsel.h"
|
||||
|
||||
|
||||
static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin)
|
||||
@@ -335,17 +336,19 @@ static int hist_entry__fprintf(struct hist_entry *he, size_t size,
|
||||
size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
|
||||
int max_cols, FILE *fp)
|
||||
{
|
||||
struct perf_hpp_fmt *fmt;
|
||||
struct sort_entry *se;
|
||||
struct rb_node *nd;
|
||||
size_t ret = 0;
|
||||
unsigned int width;
|
||||
const char *sep = symbol_conf.field_sep;
|
||||
const char *col_width = symbol_conf.col_width_list_str;
|
||||
int idx, nr_rows = 0;
|
||||
int nr_rows = 0;
|
||||
char bf[96];
|
||||
struct perf_hpp dummy_hpp = {
|
||||
.buf = bf,
|
||||
.size = sizeof(bf),
|
||||
.ptr = hists_to_evsel(hists),
|
||||
};
|
||||
bool first = true;
|
||||
|
||||
@@ -355,16 +358,14 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
|
||||
goto print_entries;
|
||||
|
||||
fprintf(fp, "# ");
|
||||
for (idx = 0; idx < PERF_HPP__MAX_INDEX; idx++) {
|
||||
if (!perf_hpp__format[idx].cond)
|
||||
continue;
|
||||
|
||||
perf_hpp__for_each_format(fmt) {
|
||||
if (!first)
|
||||
fprintf(fp, "%s", sep ?: " ");
|
||||
else
|
||||
first = false;
|
||||
|
||||
perf_hpp__format[idx].header(&dummy_hpp);
|
||||
fmt->header(&dummy_hpp);
|
||||
fprintf(fp, "%s", bf);
|
||||
}
|
||||
|
||||
@@ -400,18 +401,16 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
|
||||
first = true;
|
||||
|
||||
fprintf(fp, "# ");
|
||||
for (idx = 0; idx < PERF_HPP__MAX_INDEX; idx++) {
|
||||
unsigned int i;
|
||||
|
||||
if (!perf_hpp__format[idx].cond)
|
||||
continue;
|
||||
perf_hpp__for_each_format(fmt) {
|
||||
unsigned int i;
|
||||
|
||||
if (!first)
|
||||
fprintf(fp, "%s", sep ?: " ");
|
||||
else
|
||||
first = false;
|
||||
|
||||
width = perf_hpp__format[idx].width(&dummy_hpp);
|
||||
width = fmt->width(&dummy_hpp);
|
||||
for (i = 0; i < width; i++)
|
||||
fprintf(fp, ".");
|
||||
}
|
||||
@@ -462,7 +461,7 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t hists__fprintf_nr_events(struct hists *hists, FILE *fp)
|
||||
size_t events_stats__fprintf(struct events_stats *stats, FILE *fp)
|
||||
{
|
||||
int i;
|
||||
size_t ret = 0;
|
||||
@@ -470,7 +469,7 @@ size_t hists__fprintf_nr_events(struct hists *hists, FILE *fp)
|
||||
for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) {
|
||||
const char *name;
|
||||
|
||||
if (hists->stats.nr_events[i] == 0)
|
||||
if (stats->nr_events[i] == 0)
|
||||
continue;
|
||||
|
||||
name = perf_event__name(i);
|
||||
@@ -478,7 +477,7 @@ size_t hists__fprintf_nr_events(struct hists *hists, FILE *fp)
|
||||
continue;
|
||||
|
||||
ret += fprintf(fp, "%16s events: %10d\n", name,
|
||||
hists->stats.nr_events[i]);
|
||||
stats->nr_events[i]);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@@ -8,6 +8,8 @@
|
||||
#include "../ui.h"
|
||||
#include "../libslang.h"
|
||||
|
||||
char ui_helpline__last_msg[1024];
|
||||
|
||||
static void tui_helpline__pop(void)
|
||||
{
|
||||
}
|
||||
@@ -23,20 +25,7 @@ static void tui_helpline__push(const char *msg)
|
||||
strncpy(ui_helpline__current, msg, sz)[sz - 1] = '\0';
|
||||
}
|
||||
|
||||
struct ui_helpline tui_helpline_fns = {
|
||||
.pop = tui_helpline__pop,
|
||||
.push = tui_helpline__push,
|
||||
};
|
||||
|
||||
void ui_helpline__init(void)
|
||||
{
|
||||
helpline_fns = &tui_helpline_fns;
|
||||
ui_helpline__puts(" ");
|
||||
}
|
||||
|
||||
char ui_helpline__last_msg[1024];
|
||||
|
||||
int ui_helpline__show_help(const char *format, va_list ap)
|
||||
static int tui_helpline__show(const char *format, va_list ap)
|
||||
{
|
||||
int ret;
|
||||
static int backlog;
|
||||
@@ -55,3 +44,15 @@ int ui_helpline__show_help(const char *format, va_list ap)
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct ui_helpline tui_helpline_fns = {
|
||||
.pop = tui_helpline__pop,
|
||||
.push = tui_helpline__push,
|
||||
.show = tui_helpline__show,
|
||||
};
|
||||
|
||||
void ui_helpline__init(void)
|
||||
{
|
||||
helpline_fns = &tui_helpline_fns;
|
||||
ui_helpline__puts(" ");
|
||||
}
|
||||
|
@@ -52,7 +52,6 @@ int ui__warning(const char *format, ...)
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* perf_error__register - Register error logging functions
|
||||
* @eops: The pointer to error logging function struct
|
||||
|
@@ -26,13 +26,13 @@ VN=$(expr "$VN" : v*'\(.*\)')
|
||||
|
||||
if test -r $GVF
|
||||
then
|
||||
VC=$(sed -e 's/^PERF_VERSION = //' <$GVF)
|
||||
VC=$(sed -e 's/^#define PERF_VERSION "\(.*\)"/\1/' <$GVF)
|
||||
else
|
||||
VC=unset
|
||||
fi
|
||||
test "$VN" = "$VC" || {
|
||||
echo >&2 "PERF_VERSION = $VN"
|
||||
echo "PERF_VERSION = $VN" >$GVF
|
||||
echo "#define PERF_VERSION \"$VN\"" >$GVF
|
||||
}
|
||||
|
||||
|
||||
|
@@ -809,7 +809,7 @@ fallback:
|
||||
pr_err("Can't annotate %s:\n\n"
|
||||
"No vmlinux file%s\nwas found in the path.\n\n"
|
||||
"Please use:\n\n"
|
||||
" perf buildid-cache -av vmlinux\n\n"
|
||||
" perf buildid-cache -vu vmlinux\n\n"
|
||||
"or:\n\n"
|
||||
" --vmlinux vmlinux\n",
|
||||
sym->name, build_id_msg ?: "");
|
||||
|
@@ -6,6 +6,7 @@
|
||||
#include "types.h"
|
||||
#include "symbol.h"
|
||||
#include "hist.h"
|
||||
#include "sort.h"
|
||||
#include <linux/list.h>
|
||||
#include <linux/rbtree.h>
|
||||
#include <pthread.h>
|
||||
@@ -154,6 +155,29 @@ static inline int symbol__tui_annotate(struct symbol *sym __maybe_unused,
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef GTK2_SUPPORT
|
||||
int symbol__gtk_annotate(struct symbol *sym, struct map *map, int evidx,
|
||||
struct hist_browser_timer *hbt);
|
||||
|
||||
static inline int hist_entry__gtk_annotate(struct hist_entry *he, int evidx,
|
||||
struct hist_browser_timer *hbt)
|
||||
{
|
||||
return symbol__gtk_annotate(he->ms.sym, he->ms.map, evidx, hbt);
|
||||
}
|
||||
|
||||
void perf_gtk__show_annotations(void);
|
||||
#else
|
||||
static inline int hist_entry__gtk_annotate(struct hist_entry *he __maybe_unused,
|
||||
int evidx __maybe_unused,
|
||||
struct hist_browser_timer *hbt
|
||||
__maybe_unused)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void perf_gtk__show_annotations(void) {}
|
||||
#endif
|
||||
|
||||
extern const char *disassembler_style;
|
||||
|
||||
#endif /* __PERF_ANNOTATE_H */
|
||||
|
@@ -444,7 +444,7 @@ int callchain_cursor_append(struct callchain_cursor *cursor,
|
||||
struct callchain_cursor_node *node = *cursor->last;
|
||||
|
||||
if (!node) {
|
||||
node = calloc(sizeof(*node), 1);
|
||||
node = calloc(1, sizeof(*node));
|
||||
if (!node)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@@ -143,4 +143,9 @@ static inline void callchain_cursor_advance(struct callchain_cursor *cursor)
|
||||
cursor->curr = cursor->curr->next;
|
||||
cursor->pos++;
|
||||
}
|
||||
|
||||
struct option;
|
||||
|
||||
int record_parse_callchain_opt(const struct option *opt, const char *arg, int unset);
|
||||
extern const char record_callchain_help[];
|
||||
#endif /* __PERF_CALLCHAIN_H */
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#include "util.h"
|
||||
#include "sysfs.h"
|
||||
#include "../perf.h"
|
||||
#include "cpumap.h"
|
||||
#include <assert.h>
|
||||
@@ -201,3 +202,56 @@ void cpu_map__delete(struct cpu_map *map)
|
||||
{
|
||||
free(map);
|
||||
}
|
||||
|
||||
int cpu_map__get_socket(struct cpu_map *map, int idx)
|
||||
{
|
||||
FILE *fp;
|
||||
const char *mnt;
|
||||
char path[PATH_MAX];
|
||||
int cpu, ret;
|
||||
|
||||
if (idx > map->nr)
|
||||
return -1;
|
||||
|
||||
cpu = map->map[idx];
|
||||
|
||||
mnt = sysfs_find_mountpoint();
|
||||
if (!mnt)
|
||||
return -1;
|
||||
|
||||
sprintf(path,
|
||||
"%s/devices/system/cpu/cpu%d/topology/physical_package_id",
|
||||
mnt, cpu);
|
||||
|
||||
fp = fopen(path, "r");
|
||||
if (!fp)
|
||||
return -1;
|
||||
ret = fscanf(fp, "%d", &cpu);
|
||||
fclose(fp);
|
||||
return ret == 1 ? cpu : -1;
|
||||
}
|
||||
|
||||
int cpu_map__build_socket_map(struct cpu_map *cpus, struct cpu_map **sockp)
|
||||
{
|
||||
struct cpu_map *sock;
|
||||
int nr = cpus->nr;
|
||||
int cpu, s1, s2;
|
||||
|
||||
sock = calloc(1, sizeof(*sock) + nr * sizeof(int));
|
||||
if (!sock)
|
||||
return -1;
|
||||
|
||||
for (cpu = 0; cpu < nr; cpu++) {
|
||||
s1 = cpu_map__get_socket(cpus, cpu);
|
||||
for (s2 = 0; s2 < sock->nr; s2++) {
|
||||
if (s1 == sock->map[s2])
|
||||
break;
|
||||
}
|
||||
if (s2 == sock->nr) {
|
||||
sock->map[sock->nr] = s1;
|
||||
sock->nr++;
|
||||
}
|
||||
}
|
||||
*sockp = sock;
|
||||
return 0;
|
||||
}
|
||||
|
@@ -14,6 +14,15 @@ struct cpu_map *cpu_map__dummy_new(void);
|
||||
void cpu_map__delete(struct cpu_map *map);
|
||||
struct cpu_map *cpu_map__read(FILE *file);
|
||||
size_t cpu_map__fprintf(struct cpu_map *map, FILE *fp);
|
||||
int cpu_map__get_socket(struct cpu_map *map, int idx);
|
||||
int cpu_map__build_socket_map(struct cpu_map *cpus, struct cpu_map **sockp);
|
||||
|
||||
static inline int cpu_map__socket(struct cpu_map *sock, int s)
|
||||
{
|
||||
if (!sock || s > sock->nr || s < 0)
|
||||
return 0;
|
||||
return sock->map[s];
|
||||
}
|
||||
|
||||
static inline int cpu_map__nr(const struct cpu_map *map)
|
||||
{
|
||||
|
@@ -23,10 +23,8 @@ int eprintf(int level, const char *fmt, ...)
|
||||
|
||||
if (verbose >= level) {
|
||||
va_start(args, fmt);
|
||||
if (use_browser == 1)
|
||||
ret = ui_helpline__show_help(fmt, args);
|
||||
else if (use_browser == 2)
|
||||
ret = perf_gtk__show_helpline(fmt, args);
|
||||
if (use_browser >= 1)
|
||||
ui_helpline__vshow(fmt, args);
|
||||
else
|
||||
ret = vfprintf(stderr, fmt, args);
|
||||
va_end(args);
|
||||
@@ -49,28 +47,6 @@ int dump_printf(const char *fmt, ...)
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if !defined(NEWT_SUPPORT) && !defined(GTK2_SUPPORT)
|
||||
int ui__warning(const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, format);
|
||||
vfprintf(stderr, format, args);
|
||||
va_end(args);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int ui__error_paranoid(void)
|
||||
{
|
||||
return ui__error("Permission error - are you root?\n"
|
||||
"Consider tweaking /proc/sys/kernel/perf_event_paranoid:\n"
|
||||
" -1 - Not paranoid at all\n"
|
||||
" 0 - Disallow raw tracepoint access for unpriv\n"
|
||||
" 1 - Disallow cpu events for unpriv\n"
|
||||
" 2 - Disallow kernel profiling for unpriv\n");
|
||||
}
|
||||
|
||||
void trace_event(union perf_event *event)
|
||||
{
|
||||
unsigned char *raw_event = (void *)event;
|
||||
|
@@ -5,6 +5,8 @@
|
||||
#include <stdbool.h>
|
||||
#include "event.h"
|
||||
#include "../ui/helpline.h"
|
||||
#include "../ui/progress.h"
|
||||
#include "../ui/util.h"
|
||||
|
||||
extern int verbose;
|
||||
extern bool quiet, dump_trace;
|
||||
@@ -12,39 +14,7 @@ extern bool quiet, dump_trace;
|
||||
int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
|
||||
void trace_event(union perf_event *event);
|
||||
|
||||
struct ui_progress;
|
||||
struct perf_error_ops;
|
||||
|
||||
#if defined(NEWT_SUPPORT) || defined(GTK2_SUPPORT)
|
||||
|
||||
#include "../ui/progress.h"
|
||||
int ui__error(const char *format, ...) __attribute__((format(printf, 1, 2)));
|
||||
#include "../ui/util.h"
|
||||
|
||||
#else
|
||||
|
||||
static inline void ui_progress__update(u64 curr __maybe_unused,
|
||||
u64 total __maybe_unused,
|
||||
const char *title __maybe_unused) {}
|
||||
static inline void ui_progress__finish(void) {}
|
||||
|
||||
#define ui__error(format, arg...) ui__warning(format, ##arg)
|
||||
|
||||
static inline int
|
||||
perf_error__register(struct perf_error_ops *eops __maybe_unused)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
perf_error__unregister(struct perf_error_ops *eops __maybe_unused)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* NEWT_SUPPORT || GTK2_SUPPORT */
|
||||
|
||||
int ui__warning(const char *format, ...) __attribute__((format(printf, 1, 2)));
|
||||
int ui__error_paranoid(void);
|
||||
|
||||
#endif /* __PERF_DEBUG_H */
|
||||
|
@@ -539,13 +539,13 @@ struct dso *__dsos__findnew(struct list_head *head, const char *name)
|
||||
}
|
||||
|
||||
size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp,
|
||||
bool with_hits)
|
||||
bool (skip)(struct dso *dso, int parm), int parm)
|
||||
{
|
||||
struct dso *pos;
|
||||
size_t ret = 0;
|
||||
|
||||
list_for_each_entry(pos, head, node) {
|
||||
if (with_hits && !pos->hit)
|
||||
if (skip && skip(pos, parm))
|
||||
continue;
|
||||
ret += dso__fprintf_buildid(pos, fp);
|
||||
ret += fprintf(fp, " %s\n", pos->long_name);
|
||||
@@ -583,7 +583,7 @@ size_t dso__fprintf(struct dso *dso, enum map_type type, FILE *fp)
|
||||
if (dso->short_name != dso->long_name)
|
||||
ret += fprintf(fp, "%s, ", dso->long_name);
|
||||
ret += fprintf(fp, "%s, %sloaded, ", map_type__name[type],
|
||||
dso->loaded ? "" : "NOT ");
|
||||
dso__loaded(dso, type) ? "" : "NOT ");
|
||||
ret += dso__fprintf_buildid(dso, fp);
|
||||
ret += fprintf(fp, ")\n");
|
||||
for (nd = rb_first(&dso->symbols[type]); nd; nd = rb_next(nd)) {
|
||||
|
@@ -138,7 +138,7 @@ struct dso *__dsos__findnew(struct list_head *head, const char *name);
|
||||
bool __dsos__read_build_ids(struct list_head *head, bool with_hits);
|
||||
|
||||
size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp,
|
||||
bool with_hits);
|
||||
bool (skip)(struct dso *dso, int parm), int parm);
|
||||
size_t __dsos__fprintf(struct list_head *head, FILE *fp);
|
||||
|
||||
size_t dso__fprintf_buildid(struct dso *dso, FILE *fp);
|
||||
|
@@ -476,8 +476,10 @@ int perf_event__synthesize_kernel_mmap(struct perf_tool *tool,
|
||||
}
|
||||
}
|
||||
|
||||
if (kallsyms__parse(filename, &args, find_symbol_cb) <= 0)
|
||||
if (kallsyms__parse(filename, &args, find_symbol_cb) <= 0) {
|
||||
free(event);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
map = machine->vmlinux_maps[MAP__FUNCTION];
|
||||
size = snprintf(event->mmap.filename, sizeof(event->mmap.filename),
|
||||
|
@@ -49,10 +49,16 @@ struct perf_evlist *perf_evlist__new(struct cpu_map *cpus,
|
||||
return evlist;
|
||||
}
|
||||
|
||||
void perf_evlist__config_attrs(struct perf_evlist *evlist,
|
||||
struct perf_record_opts *opts)
|
||||
void perf_evlist__config(struct perf_evlist *evlist,
|
||||
struct perf_record_opts *opts)
|
||||
{
|
||||
struct perf_evsel *evsel;
|
||||
/*
|
||||
* Set the evsel leader links before we configure attributes,
|
||||
* since some might depend on this info.
|
||||
*/
|
||||
if (opts->group)
|
||||
perf_evlist__set_leader(evlist);
|
||||
|
||||
if (evlist->cpus->map[0] < 0)
|
||||
opts->no_inherit = true;
|
||||
@@ -61,7 +67,7 @@ void perf_evlist__config_attrs(struct perf_evlist *evlist,
|
||||
perf_evsel__config(evsel, opts);
|
||||
|
||||
if (evlist->nr_entries > 1)
|
||||
evsel->attr.sample_type |= PERF_SAMPLE_ID;
|
||||
perf_evsel__set_sample_id(evsel);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,18 +117,21 @@ void __perf_evlist__set_leader(struct list_head *list)
|
||||
struct perf_evsel *evsel, *leader;
|
||||
|
||||
leader = list_entry(list->next, struct perf_evsel, node);
|
||||
leader->leader = NULL;
|
||||
evsel = list_entry(list->prev, struct perf_evsel, node);
|
||||
|
||||
leader->nr_members = evsel->idx - leader->idx + 1;
|
||||
|
||||
list_for_each_entry(evsel, list, node) {
|
||||
if (evsel != leader)
|
||||
evsel->leader = leader;
|
||||
evsel->leader = leader;
|
||||
}
|
||||
}
|
||||
|
||||
void perf_evlist__set_leader(struct perf_evlist *evlist)
|
||||
{
|
||||
if (evlist->nr_entries)
|
||||
if (evlist->nr_entries) {
|
||||
evlist->nr_groups = evlist->nr_entries > 1 ? 1 : 0;
|
||||
__perf_evlist__set_leader(&evlist->entries);
|
||||
}
|
||||
}
|
||||
|
||||
int perf_evlist__add_default(struct perf_evlist *evlist)
|
||||
@@ -222,7 +231,7 @@ void perf_evlist__disable(struct perf_evlist *evlist)
|
||||
|
||||
for (cpu = 0; cpu < evlist->cpus->nr; cpu++) {
|
||||
list_for_each_entry(pos, &evlist->entries, node) {
|
||||
if (perf_evsel__is_group_member(pos))
|
||||
if (!perf_evsel__is_group_leader(pos))
|
||||
continue;
|
||||
for (thread = 0; thread < evlist->threads->nr; thread++)
|
||||
ioctl(FD(pos, cpu, thread),
|
||||
@@ -238,7 +247,7 @@ void perf_evlist__enable(struct perf_evlist *evlist)
|
||||
|
||||
for (cpu = 0; cpu < cpu_map__nr(evlist->cpus); cpu++) {
|
||||
list_for_each_entry(pos, &evlist->entries, node) {
|
||||
if (perf_evsel__is_group_member(pos))
|
||||
if (!perf_evsel__is_group_leader(pos))
|
||||
continue;
|
||||
for (thread = 0; thread < evlist->threads->nr; thread++)
|
||||
ioctl(FD(pos, cpu, thread),
|
||||
@@ -366,7 +375,7 @@ union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx)
|
||||
if ((old & md->mask) + size != ((old + size) & md->mask)) {
|
||||
unsigned int offset = old;
|
||||
unsigned int len = min(sizeof(*event), size), cpy;
|
||||
void *dst = &evlist->event_copy;
|
||||
void *dst = &md->event_copy;
|
||||
|
||||
do {
|
||||
cpy = min(md->mask + 1 - (offset & md->mask), len);
|
||||
@@ -376,7 +385,7 @@ union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx)
|
||||
len -= cpy;
|
||||
} while (len);
|
||||
|
||||
event = &evlist->event_copy;
|
||||
event = &md->event_copy;
|
||||
}
|
||||
|
||||
old += size;
|
||||
|
@@ -17,10 +17,18 @@ struct perf_record_opts;
|
||||
#define PERF_EVLIST__HLIST_BITS 8
|
||||
#define PERF_EVLIST__HLIST_SIZE (1 << PERF_EVLIST__HLIST_BITS)
|
||||
|
||||
struct perf_mmap {
|
||||
void *base;
|
||||
int mask;
|
||||
unsigned int prev;
|
||||
union perf_event event_copy;
|
||||
};
|
||||
|
||||
struct perf_evlist {
|
||||
struct list_head entries;
|
||||
struct hlist_head heads[PERF_EVLIST__HLIST_SIZE];
|
||||
int nr_entries;
|
||||
int nr_groups;
|
||||
int nr_fds;
|
||||
int nr_mmaps;
|
||||
int mmap_len;
|
||||
@@ -29,7 +37,6 @@ struct perf_evlist {
|
||||
pid_t pid;
|
||||
} workload;
|
||||
bool overwrite;
|
||||
union perf_event event_copy;
|
||||
struct perf_mmap *mmap;
|
||||
struct pollfd *pollfd;
|
||||
struct thread_map *threads;
|
||||
@@ -76,8 +83,8 @@ union perf_event *perf_evlist__mmap_read(struct perf_evlist *self, int idx);
|
||||
|
||||
int perf_evlist__open(struct perf_evlist *evlist);
|
||||
|
||||
void perf_evlist__config_attrs(struct perf_evlist *evlist,
|
||||
struct perf_record_opts *opts);
|
||||
void perf_evlist__config(struct perf_evlist *evlist,
|
||||
struct perf_record_opts *opts);
|
||||
|
||||
int perf_evlist__prepare_workload(struct perf_evlist *evlist,
|
||||
struct perf_record_opts *opts,
|
||||
@@ -135,4 +142,25 @@ static inline struct perf_evsel *perf_evlist__last(struct perf_evlist *evlist)
|
||||
}
|
||||
|
||||
size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp);
|
||||
|
||||
static inline unsigned int perf_mmap__read_head(struct perf_mmap *mm)
|
||||
{
|
||||
struct perf_event_mmap_page *pc = mm->base;
|
||||
int head = pc->data_head;
|
||||
rmb();
|
||||
return head;
|
||||
}
|
||||
|
||||
static inline void perf_mmap__write_tail(struct perf_mmap *md,
|
||||
unsigned long tail)
|
||||
{
|
||||
struct perf_event_mmap_page *pc = md->base;
|
||||
|
||||
/*
|
||||
* ensure all reads are done before we write the tail out.
|
||||
*/
|
||||
/* mb(); */
|
||||
pc->data_tail = tail;
|
||||
}
|
||||
|
||||
#endif /* __PERF_EVLIST_H */
|
||||
|
@@ -22,6 +22,11 @@
|
||||
#include <linux/perf_event.h>
|
||||
#include "perf_regs.h"
|
||||
|
||||
static struct {
|
||||
bool sample_id_all;
|
||||
bool exclude_guest;
|
||||
} perf_missing_features;
|
||||
|
||||
#define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y))
|
||||
|
||||
static int __perf_evsel__sample_size(u64 sample_type)
|
||||
@@ -50,11 +55,36 @@ void hists__init(struct hists *hists)
|
||||
pthread_mutex_init(&hists->lock, NULL);
|
||||
}
|
||||
|
||||
void __perf_evsel__set_sample_bit(struct perf_evsel *evsel,
|
||||
enum perf_event_sample_format bit)
|
||||
{
|
||||
if (!(evsel->attr.sample_type & bit)) {
|
||||
evsel->attr.sample_type |= bit;
|
||||
evsel->sample_size += sizeof(u64);
|
||||
}
|
||||
}
|
||||
|
||||
void __perf_evsel__reset_sample_bit(struct perf_evsel *evsel,
|
||||
enum perf_event_sample_format bit)
|
||||
{
|
||||
if (evsel->attr.sample_type & bit) {
|
||||
evsel->attr.sample_type &= ~bit;
|
||||
evsel->sample_size -= sizeof(u64);
|
||||
}
|
||||
}
|
||||
|
||||
void perf_evsel__set_sample_id(struct perf_evsel *evsel)
|
||||
{
|
||||
perf_evsel__set_sample_bit(evsel, ID);
|
||||
evsel->attr.read_format |= PERF_FORMAT_ID;
|
||||
}
|
||||
|
||||
void perf_evsel__init(struct perf_evsel *evsel,
|
||||
struct perf_event_attr *attr, int idx)
|
||||
{
|
||||
evsel->idx = idx;
|
||||
evsel->attr = *attr;
|
||||
evsel->leader = evsel;
|
||||
INIT_LIST_HEAD(&evsel->node);
|
||||
hists__init(&evsel->hists);
|
||||
evsel->sample_size = __perf_evsel__sample_size(attr->sample_type);
|
||||
@@ -404,6 +434,31 @@ const char *perf_evsel__name(struct perf_evsel *evsel)
|
||||
return evsel->name ?: "unknown";
|
||||
}
|
||||
|
||||
const char *perf_evsel__group_name(struct perf_evsel *evsel)
|
||||
{
|
||||
return evsel->group_name ?: "anon group";
|
||||
}
|
||||
|
||||
int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size)
|
||||
{
|
||||
int ret;
|
||||
struct perf_evsel *pos;
|
||||
const char *group_name = perf_evsel__group_name(evsel);
|
||||
|
||||
ret = scnprintf(buf, size, "%s", group_name);
|
||||
|
||||
ret += scnprintf(buf + ret, size - ret, " { %s",
|
||||
perf_evsel__name(evsel));
|
||||
|
||||
for_each_group_member(pos, evsel)
|
||||
ret += scnprintf(buf + ret, size - ret, ", %s",
|
||||
perf_evsel__name(pos));
|
||||
|
||||
ret += scnprintf(buf + ret, size - ret, " }");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* The enable_on_exec/disabled value strategy:
|
||||
*
|
||||
@@ -438,13 +493,11 @@ void perf_evsel__config(struct perf_evsel *evsel,
|
||||
struct perf_event_attr *attr = &evsel->attr;
|
||||
int track = !evsel->idx; /* only the first counter needs these */
|
||||
|
||||
attr->sample_id_all = opts->sample_id_all_missing ? 0 : 1;
|
||||
attr->sample_id_all = perf_missing_features.sample_id_all ? 0 : 1;
|
||||
attr->inherit = !opts->no_inherit;
|
||||
attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED |
|
||||
PERF_FORMAT_TOTAL_TIME_RUNNING |
|
||||
PERF_FORMAT_ID;
|
||||
|
||||
attr->sample_type |= PERF_SAMPLE_IP | PERF_SAMPLE_TID;
|
||||
perf_evsel__set_sample_bit(evsel, IP);
|
||||
perf_evsel__set_sample_bit(evsel, TID);
|
||||
|
||||
/*
|
||||
* We default some events to a 1 default interval. But keep
|
||||
@@ -453,7 +506,7 @@ void perf_evsel__config(struct perf_evsel *evsel,
|
||||
if (!attr->sample_period || (opts->user_freq != UINT_MAX &&
|
||||
opts->user_interval != ULLONG_MAX)) {
|
||||
if (opts->freq) {
|
||||
attr->sample_type |= PERF_SAMPLE_PERIOD;
|
||||
perf_evsel__set_sample_bit(evsel, PERIOD);
|
||||
attr->freq = 1;
|
||||
attr->sample_freq = opts->freq;
|
||||
} else {
|
||||
@@ -468,16 +521,16 @@ void perf_evsel__config(struct perf_evsel *evsel,
|
||||
attr->inherit_stat = 1;
|
||||
|
||||
if (opts->sample_address) {
|
||||
attr->sample_type |= PERF_SAMPLE_ADDR;
|
||||
perf_evsel__set_sample_bit(evsel, ADDR);
|
||||
attr->mmap_data = track;
|
||||
}
|
||||
|
||||
if (opts->call_graph) {
|
||||
attr->sample_type |= PERF_SAMPLE_CALLCHAIN;
|
||||
perf_evsel__set_sample_bit(evsel, CALLCHAIN);
|
||||
|
||||
if (opts->call_graph == CALLCHAIN_DWARF) {
|
||||
attr->sample_type |= PERF_SAMPLE_REGS_USER |
|
||||
PERF_SAMPLE_STACK_USER;
|
||||
perf_evsel__set_sample_bit(evsel, REGS_USER);
|
||||
perf_evsel__set_sample_bit(evsel, STACK_USER);
|
||||
attr->sample_regs_user = PERF_REGS_MASK;
|
||||
attr->sample_stack_user = opts->stack_dump_size;
|
||||
attr->exclude_callchain_user = 1;
|
||||
@@ -485,20 +538,20 @@ void perf_evsel__config(struct perf_evsel *evsel,
|
||||
}
|
||||
|
||||
if (perf_target__has_cpu(&opts->target))
|
||||
attr->sample_type |= PERF_SAMPLE_CPU;
|
||||
perf_evsel__set_sample_bit(evsel, CPU);
|
||||
|
||||
if (opts->period)
|
||||
attr->sample_type |= PERF_SAMPLE_PERIOD;
|
||||
perf_evsel__set_sample_bit(evsel, PERIOD);
|
||||
|
||||
if (!opts->sample_id_all_missing &&
|
||||
if (!perf_missing_features.sample_id_all &&
|
||||
(opts->sample_time || !opts->no_inherit ||
|
||||
perf_target__has_cpu(&opts->target)))
|
||||
attr->sample_type |= PERF_SAMPLE_TIME;
|
||||
perf_evsel__set_sample_bit(evsel, TIME);
|
||||
|
||||
if (opts->raw_samples) {
|
||||
attr->sample_type |= PERF_SAMPLE_TIME;
|
||||
attr->sample_type |= PERF_SAMPLE_RAW;
|
||||
attr->sample_type |= PERF_SAMPLE_CPU;
|
||||
perf_evsel__set_sample_bit(evsel, TIME);
|
||||
perf_evsel__set_sample_bit(evsel, RAW);
|
||||
perf_evsel__set_sample_bit(evsel, CPU);
|
||||
}
|
||||
|
||||
if (opts->no_delay) {
|
||||
@@ -506,7 +559,7 @@ void perf_evsel__config(struct perf_evsel *evsel,
|
||||
attr->wakeup_events = 1;
|
||||
}
|
||||
if (opts->branch_stack) {
|
||||
attr->sample_type |= PERF_SAMPLE_BRANCH_STACK;
|
||||
perf_evsel__set_sample_bit(evsel, BRANCH_STACK);
|
||||
attr->branch_sample_type = opts->branch_stack;
|
||||
}
|
||||
|
||||
@@ -519,14 +572,14 @@ void perf_evsel__config(struct perf_evsel *evsel,
|
||||
* Disabling only independent events or group leaders,
|
||||
* keeping group members enabled.
|
||||
*/
|
||||
if (!perf_evsel__is_group_member(evsel))
|
||||
if (perf_evsel__is_group_leader(evsel))
|
||||
attr->disabled = 1;
|
||||
|
||||
/*
|
||||
* Setting enable_on_exec for independent events and
|
||||
* group leaders for traced executed by perf.
|
||||
*/
|
||||
if (perf_target__none(&opts->target) && !perf_evsel__is_group_member(evsel))
|
||||
if (perf_target__none(&opts->target) && perf_evsel__is_group_leader(evsel))
|
||||
attr->enable_on_exec = 1;
|
||||
}
|
||||
|
||||
@@ -612,6 +665,11 @@ void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads)
|
||||
}
|
||||
}
|
||||
|
||||
void perf_evsel__free_counts(struct perf_evsel *evsel)
|
||||
{
|
||||
free(evsel->counts);
|
||||
}
|
||||
|
||||
void perf_evsel__exit(struct perf_evsel *evsel)
|
||||
{
|
||||
assert(list_empty(&evsel->node));
|
||||
@@ -631,6 +689,28 @@ void perf_evsel__delete(struct perf_evsel *evsel)
|
||||
free(evsel);
|
||||
}
|
||||
|
||||
static inline void compute_deltas(struct perf_evsel *evsel,
|
||||
int cpu,
|
||||
struct perf_counts_values *count)
|
||||
{
|
||||
struct perf_counts_values tmp;
|
||||
|
||||
if (!evsel->prev_raw_counts)
|
||||
return;
|
||||
|
||||
if (cpu == -1) {
|
||||
tmp = evsel->prev_raw_counts->aggr;
|
||||
evsel->prev_raw_counts->aggr = *count;
|
||||
} else {
|
||||
tmp = evsel->prev_raw_counts->cpu[cpu];
|
||||
evsel->prev_raw_counts->cpu[cpu] = *count;
|
||||
}
|
||||
|
||||
count->val = count->val - tmp.val;
|
||||
count->ena = count->ena - tmp.ena;
|
||||
count->run = count->run - tmp.run;
|
||||
}
|
||||
|
||||
int __perf_evsel__read_on_cpu(struct perf_evsel *evsel,
|
||||
int cpu, int thread, bool scale)
|
||||
{
|
||||
@@ -646,6 +726,8 @@ int __perf_evsel__read_on_cpu(struct perf_evsel *evsel,
|
||||
if (readn(FD(evsel, cpu, thread), &count, nv * sizeof(u64)) < 0)
|
||||
return -errno;
|
||||
|
||||
compute_deltas(evsel, cpu, &count);
|
||||
|
||||
if (scale) {
|
||||
if (count.run == 0)
|
||||
count.val = 0;
|
||||
@@ -684,6 +766,8 @@ int __perf_evsel__read(struct perf_evsel *evsel,
|
||||
}
|
||||
}
|
||||
|
||||
compute_deltas(evsel, -1, aggr);
|
||||
|
||||
evsel->counts->scaled = 0;
|
||||
if (scale) {
|
||||
if (aggr->run == 0) {
|
||||
@@ -707,7 +791,7 @@ static int get_group_fd(struct perf_evsel *evsel, int cpu, int thread)
|
||||
struct perf_evsel *leader = evsel->leader;
|
||||
int fd;
|
||||
|
||||
if (!perf_evsel__is_group_member(evsel))
|
||||
if (perf_evsel__is_group_leader(evsel))
|
||||
return -1;
|
||||
|
||||
/*
|
||||
@@ -738,6 +822,13 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus,
|
||||
pid = evsel->cgrp->fd;
|
||||
}
|
||||
|
||||
fallback_missing_features:
|
||||
if (perf_missing_features.exclude_guest)
|
||||
evsel->attr.exclude_guest = evsel->attr.exclude_host = 0;
|
||||
retry_sample_id:
|
||||
if (perf_missing_features.sample_id_all)
|
||||
evsel->attr.sample_id_all = 0;
|
||||
|
||||
for (cpu = 0; cpu < cpus->nr; cpu++) {
|
||||
|
||||
for (thread = 0; thread < threads->nr; thread++) {
|
||||
@@ -754,13 +845,26 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus,
|
||||
group_fd, flags);
|
||||
if (FD(evsel, cpu, thread) < 0) {
|
||||
err = -errno;
|
||||
goto out_close;
|
||||
goto try_fallback;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
try_fallback:
|
||||
if (err != -EINVAL || cpu > 0 || thread > 0)
|
||||
goto out_close;
|
||||
|
||||
if (!perf_missing_features.exclude_guest &&
|
||||
(evsel->attr.exclude_guest || evsel->attr.exclude_host)) {
|
||||
perf_missing_features.exclude_guest = true;
|
||||
goto fallback_missing_features;
|
||||
} else if (!perf_missing_features.sample_id_all) {
|
||||
perf_missing_features.sample_id_all = true;
|
||||
goto retry_sample_id;
|
||||
}
|
||||
|
||||
out_close:
|
||||
do {
|
||||
while (--thread >= 0) {
|
||||
@@ -1205,3 +1309,225 @@ u64 perf_evsel__intval(struct perf_evsel *evsel, struct perf_sample *sample,
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int comma_fprintf(FILE *fp, bool *first, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
int ret = 0;
|
||||
|
||||
if (!*first) {
|
||||
ret += fprintf(fp, ",");
|
||||
} else {
|
||||
ret += fprintf(fp, ":");
|
||||
*first = false;
|
||||
}
|
||||
|
||||
va_start(args, fmt);
|
||||
ret += vfprintf(fp, fmt, args);
|
||||
va_end(args);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __if_fprintf(FILE *fp, bool *first, const char *field, u64 value)
|
||||
{
|
||||
if (value == 0)
|
||||
return 0;
|
||||
|
||||
return comma_fprintf(fp, first, " %s: %" PRIu64, field, value);
|
||||
}
|
||||
|
||||
#define if_print(field) printed += __if_fprintf(fp, &first, #field, evsel->attr.field)
|
||||
|
||||
struct bit_names {
|
||||
int bit;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
static int bits__fprintf(FILE *fp, const char *field, u64 value,
|
||||
struct bit_names *bits, bool *first)
|
||||
{
|
||||
int i = 0, printed = comma_fprintf(fp, first, " %s: ", field);
|
||||
bool first_bit = true;
|
||||
|
||||
do {
|
||||
if (value & bits[i].bit) {
|
||||
printed += fprintf(fp, "%s%s", first_bit ? "" : "|", bits[i].name);
|
||||
first_bit = false;
|
||||
}
|
||||
} while (bits[++i].name != NULL);
|
||||
|
||||
return printed;
|
||||
}
|
||||
|
||||
static int sample_type__fprintf(FILE *fp, bool *first, u64 value)
|
||||
{
|
||||
#define bit_name(n) { PERF_SAMPLE_##n, #n }
|
||||
struct bit_names bits[] = {
|
||||
bit_name(IP), bit_name(TID), bit_name(TIME), bit_name(ADDR),
|
||||
bit_name(READ), bit_name(CALLCHAIN), bit_name(ID), bit_name(CPU),
|
||||
bit_name(PERIOD), bit_name(STREAM_ID), bit_name(RAW),
|
||||
bit_name(BRANCH_STACK), bit_name(REGS_USER), bit_name(STACK_USER),
|
||||
{ .name = NULL, }
|
||||
};
|
||||
#undef bit_name
|
||||
return bits__fprintf(fp, "sample_type", value, bits, first);
|
||||
}
|
||||
|
||||
static int read_format__fprintf(FILE *fp, bool *first, u64 value)
|
||||
{
|
||||
#define bit_name(n) { PERF_FORMAT_##n, #n }
|
||||
struct bit_names bits[] = {
|
||||
bit_name(TOTAL_TIME_ENABLED), bit_name(TOTAL_TIME_RUNNING),
|
||||
bit_name(ID), bit_name(GROUP),
|
||||
{ .name = NULL, }
|
||||
};
|
||||
#undef bit_name
|
||||
return bits__fprintf(fp, "read_format", value, bits, first);
|
||||
}
|
||||
|
||||
int perf_evsel__fprintf(struct perf_evsel *evsel,
|
||||
struct perf_attr_details *details, FILE *fp)
|
||||
{
|
||||
bool first = true;
|
||||
int printed = 0;
|
||||
|
||||
if (details->event_group) {
|
||||
struct perf_evsel *pos;
|
||||
|
||||
if (!perf_evsel__is_group_leader(evsel))
|
||||
return 0;
|
||||
|
||||
if (evsel->nr_members > 1)
|
||||
printed += fprintf(fp, "%s{", evsel->group_name ?: "");
|
||||
|
||||
printed += fprintf(fp, "%s", perf_evsel__name(evsel));
|
||||
for_each_group_member(pos, evsel)
|
||||
printed += fprintf(fp, ",%s", perf_evsel__name(pos));
|
||||
|
||||
if (evsel->nr_members > 1)
|
||||
printed += fprintf(fp, "}");
|
||||
goto out;
|
||||
}
|
||||
|
||||
printed += fprintf(fp, "%s", perf_evsel__name(evsel));
|
||||
|
||||
if (details->verbose || details->freq) {
|
||||
printed += comma_fprintf(fp, &first, " sample_freq=%" PRIu64,
|
||||
(u64)evsel->attr.sample_freq);
|
||||
}
|
||||
|
||||
if (details->verbose) {
|
||||
if_print(type);
|
||||
if_print(config);
|
||||
if_print(config1);
|
||||
if_print(config2);
|
||||
if_print(size);
|
||||
printed += sample_type__fprintf(fp, &first, evsel->attr.sample_type);
|
||||
if (evsel->attr.read_format)
|
||||
printed += read_format__fprintf(fp, &first, evsel->attr.read_format);
|
||||
if_print(disabled);
|
||||
if_print(inherit);
|
||||
if_print(pinned);
|
||||
if_print(exclusive);
|
||||
if_print(exclude_user);
|
||||
if_print(exclude_kernel);
|
||||
if_print(exclude_hv);
|
||||
if_print(exclude_idle);
|
||||
if_print(mmap);
|
||||
if_print(comm);
|
||||
if_print(freq);
|
||||
if_print(inherit_stat);
|
||||
if_print(enable_on_exec);
|
||||
if_print(task);
|
||||
if_print(watermark);
|
||||
if_print(precise_ip);
|
||||
if_print(mmap_data);
|
||||
if_print(sample_id_all);
|
||||
if_print(exclude_host);
|
||||
if_print(exclude_guest);
|
||||
if_print(__reserved_1);
|
||||
if_print(wakeup_events);
|
||||
if_print(bp_type);
|
||||
if_print(branch_sample_type);
|
||||
}
|
||||
out:
|
||||
fputc('\n', fp);
|
||||
return ++printed;
|
||||
}
|
||||
|
||||
bool perf_evsel__fallback(struct perf_evsel *evsel, int err,
|
||||
char *msg, size_t msgsize)
|
||||
{
|
||||
if ((err == ENOENT || err == ENXIO) &&
|
||||
evsel->attr.type == PERF_TYPE_HARDWARE &&
|
||||
evsel->attr.config == PERF_COUNT_HW_CPU_CYCLES) {
|
||||
/*
|
||||
* If it's cycles then fall back to hrtimer based
|
||||
* cpu-clock-tick sw counter, which is always available even if
|
||||
* no PMU support.
|
||||
*
|
||||
* PPC returns ENXIO until 2.6.37 (behavior changed with commit
|
||||
* b0a873e).
|
||||
*/
|
||||
scnprintf(msg, msgsize, "%s",
|
||||
"The cycles event is not supported, trying to fall back to cpu-clock-ticks");
|
||||
|
||||
evsel->attr.type = PERF_TYPE_SOFTWARE;
|
||||
evsel->attr.config = PERF_COUNT_SW_CPU_CLOCK;
|
||||
|
||||
free(evsel->name);
|
||||
evsel->name = NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int perf_evsel__open_strerror(struct perf_evsel *evsel,
|
||||
struct perf_target *target,
|
||||
int err, char *msg, size_t size)
|
||||
{
|
||||
switch (err) {
|
||||
case EPERM:
|
||||
case EACCES:
|
||||
return scnprintf(msg, size, "%s",
|
||||
"You may not have permission to collect %sstats.\n"
|
||||
"Consider tweaking /proc/sys/kernel/perf_event_paranoid:\n"
|
||||
" -1 - Not paranoid at all\n"
|
||||
" 0 - Disallow raw tracepoint access for unpriv\n"
|
||||
" 1 - Disallow cpu events for unpriv\n"
|
||||
" 2 - Disallow kernel profiling for unpriv",
|
||||
target->system_wide ? "system-wide " : "");
|
||||
case ENOENT:
|
||||
return scnprintf(msg, size, "The %s event is not supported.",
|
||||
perf_evsel__name(evsel));
|
||||
case EMFILE:
|
||||
return scnprintf(msg, size, "%s",
|
||||
"Too many events are opened.\n"
|
||||
"Try again after reducing the number of events.");
|
||||
case ENODEV:
|
||||
if (target->cpu_list)
|
||||
return scnprintf(msg, size, "%s",
|
||||
"No such device - did you specify an out-of-range profile CPU?\n");
|
||||
break;
|
||||
case EOPNOTSUPP:
|
||||
if (evsel->attr.precise_ip)
|
||||
return scnprintf(msg, size, "%s",
|
||||
"\'precise\' request may not be supported. Try removing 'p' modifier.");
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
if (evsel->attr.type == PERF_TYPE_HARDWARE)
|
||||
return scnprintf(msg, size, "%s",
|
||||
"No hardware sampling interrupt available.\n"
|
||||
"No APIC? If so then you can boot the kernel with the \"lapic\" boot parameter to force-enable it.");
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return scnprintf(msg, size,
|
||||
"The sys_perf_event_open() syscall returned with %d (%s) for event (%s). \n"
|
||||
"/bin/dmesg may provide additional information.\n"
|
||||
"No CONFIG_PERF_EVENTS=y kernel support configured?\n",
|
||||
err, strerror(err), perf_evsel__name(evsel));
|
||||
}
|
||||
|
@@ -53,6 +53,7 @@ struct perf_evsel {
|
||||
struct xyarray *sample_id;
|
||||
u64 *id;
|
||||
struct perf_counts *counts;
|
||||
struct perf_counts *prev_raw_counts;
|
||||
int idx;
|
||||
u32 ids;
|
||||
struct hists hists;
|
||||
@@ -73,10 +74,13 @@ struct perf_evsel {
|
||||
bool needs_swap;
|
||||
/* parse modifier helper */
|
||||
int exclude_GH;
|
||||
int nr_members;
|
||||
struct perf_evsel *leader;
|
||||
char *group_name;
|
||||
};
|
||||
|
||||
#define hists_to_evsel(h) container_of(h, struct perf_evsel, hists)
|
||||
|
||||
struct cpu_map;
|
||||
struct thread_map;
|
||||
struct perf_evlist;
|
||||
@@ -110,14 +114,30 @@ extern const char *perf_evsel__sw_names[PERF_COUNT_SW_MAX];
|
||||
int __perf_evsel__hw_cache_type_op_res_name(u8 type, u8 op, u8 result,
|
||||
char *bf, size_t size);
|
||||
const char *perf_evsel__name(struct perf_evsel *evsel);
|
||||
const char *perf_evsel__group_name(struct perf_evsel *evsel);
|
||||
int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size);
|
||||
|
||||
int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads);
|
||||
int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads);
|
||||
int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus);
|
||||
void perf_evsel__free_fd(struct perf_evsel *evsel);
|
||||
void perf_evsel__free_id(struct perf_evsel *evsel);
|
||||
void perf_evsel__free_counts(struct perf_evsel *evsel);
|
||||
void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads);
|
||||
|
||||
void __perf_evsel__set_sample_bit(struct perf_evsel *evsel,
|
||||
enum perf_event_sample_format bit);
|
||||
void __perf_evsel__reset_sample_bit(struct perf_evsel *evsel,
|
||||
enum perf_event_sample_format bit);
|
||||
|
||||
#define perf_evsel__set_sample_bit(evsel, bit) \
|
||||
__perf_evsel__set_sample_bit(evsel, PERF_SAMPLE_##bit)
|
||||
|
||||
#define perf_evsel__reset_sample_bit(evsel, bit) \
|
||||
__perf_evsel__reset_sample_bit(evsel, PERF_SAMPLE_##bit)
|
||||
|
||||
void perf_evsel__set_sample_id(struct perf_evsel *evsel);
|
||||
|
||||
int perf_evsel__set_filter(struct perf_evsel *evsel, int ncpus, int nthreads,
|
||||
const char *filter);
|
||||
|
||||
@@ -226,8 +246,34 @@ static inline struct perf_evsel *perf_evsel__next(struct perf_evsel *evsel)
|
||||
return list_entry(evsel->node.next, struct perf_evsel, node);
|
||||
}
|
||||
|
||||
static inline bool perf_evsel__is_group_member(const struct perf_evsel *evsel)
|
||||
static inline bool perf_evsel__is_group_leader(const struct perf_evsel *evsel)
|
||||
{
|
||||
return evsel->leader != NULL;
|
||||
return evsel->leader == evsel;
|
||||
}
|
||||
|
||||
struct perf_attr_details {
|
||||
bool freq;
|
||||
bool verbose;
|
||||
bool event_group;
|
||||
};
|
||||
|
||||
int perf_evsel__fprintf(struct perf_evsel *evsel,
|
||||
struct perf_attr_details *details, FILE *fp);
|
||||
|
||||
bool perf_evsel__fallback(struct perf_evsel *evsel, int err,
|
||||
char *msg, size_t msgsize);
|
||||
int perf_evsel__open_strerror(struct perf_evsel *evsel,
|
||||
struct perf_target *target,
|
||||
int err, char *msg, size_t size);
|
||||
|
||||
static inline int perf_evsel__group_idx(struct perf_evsel *evsel)
|
||||
{
|
||||
return evsel->idx - evsel->leader->idx;
|
||||
}
|
||||
|
||||
#define for_each_group_member(_evsel, _leader) \
|
||||
for ((_evsel) = list_entry((_leader)->node.next, struct perf_evsel, node); \
|
||||
(_evsel) && (_evsel)->leader == (_leader); \
|
||||
(_evsel) = list_entry((_evsel)->node.next, struct perf_evsel, node))
|
||||
|
||||
#endif /* __PERF_EVSEL_H */
|
||||
|
@@ -148,7 +148,7 @@ static char *do_read_string(int fd, struct perf_header *ph)
|
||||
u32 len;
|
||||
char *buf;
|
||||
|
||||
sz = read(fd, &len, sizeof(len));
|
||||
sz = readn(fd, &len, sizeof(len));
|
||||
if (sz < (ssize_t)sizeof(len))
|
||||
return NULL;
|
||||
|
||||
@@ -159,7 +159,7 @@ static char *do_read_string(int fd, struct perf_header *ph)
|
||||
if (!buf)
|
||||
return NULL;
|
||||
|
||||
ret = read(fd, buf, len);
|
||||
ret = readn(fd, buf, len);
|
||||
if (ret == (ssize_t)len) {
|
||||
/*
|
||||
* strings are padded by zeroes
|
||||
@@ -287,12 +287,12 @@ static int dsos__write_buildid_table(struct perf_header *header, int fd)
|
||||
struct perf_session *session = container_of(header,
|
||||
struct perf_session, header);
|
||||
struct rb_node *nd;
|
||||
int err = machine__write_buildid_table(&session->host_machine, fd);
|
||||
int err = machine__write_buildid_table(&session->machines.host, fd);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) {
|
||||
for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) {
|
||||
struct machine *pos = rb_entry(nd, struct machine, rb_node);
|
||||
err = machine__write_buildid_table(pos, fd);
|
||||
if (err)
|
||||
@@ -313,7 +313,8 @@ int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
|
||||
if (is_kallsyms) {
|
||||
if (symbol_conf.kptr_restrict) {
|
||||
pr_debug("Not caching a kptr_restrict'ed /proc/kallsyms\n");
|
||||
return 0;
|
||||
err = 0;
|
||||
goto out_free;
|
||||
}
|
||||
realname = (char *) name;
|
||||
} else
|
||||
@@ -448,9 +449,9 @@ static int perf_session__cache_build_ids(struct perf_session *session)
|
||||
if (mkdir(debugdir, 0755) != 0 && errno != EEXIST)
|
||||
return -1;
|
||||
|
||||
ret = machine__cache_build_ids(&session->host_machine, debugdir);
|
||||
ret = machine__cache_build_ids(&session->machines.host, debugdir);
|
||||
|
||||
for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) {
|
||||
for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) {
|
||||
struct machine *pos = rb_entry(nd, struct machine, rb_node);
|
||||
ret |= machine__cache_build_ids(pos, debugdir);
|
||||
}
|
||||
@@ -467,9 +468,9 @@ static bool machine__read_build_ids(struct machine *machine, bool with_hits)
|
||||
static bool perf_session__read_build_ids(struct perf_session *session, bool with_hits)
|
||||
{
|
||||
struct rb_node *nd;
|
||||
bool ret = machine__read_build_ids(&session->host_machine, with_hits);
|
||||
bool ret = machine__read_build_ids(&session->machines.host, with_hits);
|
||||
|
||||
for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) {
|
||||
for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) {
|
||||
struct machine *pos = rb_entry(nd, struct machine, rb_node);
|
||||
ret |= machine__read_build_ids(pos, with_hits);
|
||||
}
|
||||
@@ -954,6 +955,7 @@ static int write_topo_node(int fd, int node)
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
fp = NULL;
|
||||
|
||||
ret = do_write(fd, &mem_total, sizeof(u64));
|
||||
if (ret)
|
||||
@@ -980,7 +982,8 @@ static int write_topo_node(int fd, int node)
|
||||
ret = do_write_string(fd, buf);
|
||||
done:
|
||||
free(buf);
|
||||
fclose(fp);
|
||||
if (fp)
|
||||
fclose(fp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1051,16 +1054,25 @@ static int write_pmu_mappings(int fd, struct perf_header *h __maybe_unused,
|
||||
struct perf_pmu *pmu = NULL;
|
||||
off_t offset = lseek(fd, 0, SEEK_CUR);
|
||||
__u32 pmu_num = 0;
|
||||
int ret;
|
||||
|
||||
/* write real pmu_num later */
|
||||
do_write(fd, &pmu_num, sizeof(pmu_num));
|
||||
ret = do_write(fd, &pmu_num, sizeof(pmu_num));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
while ((pmu = perf_pmu__scan(pmu))) {
|
||||
if (!pmu->name)
|
||||
continue;
|
||||
pmu_num++;
|
||||
do_write(fd, &pmu->type, sizeof(pmu->type));
|
||||
do_write_string(fd, pmu->name);
|
||||
|
||||
ret = do_write(fd, &pmu->type, sizeof(pmu->type));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = do_write_string(fd, pmu->name);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (pwrite(fd, &pmu_num, sizeof(pmu_num), offset) != sizeof(pmu_num)) {
|
||||
@@ -1072,6 +1084,52 @@ static int write_pmu_mappings(int fd, struct perf_header *h __maybe_unused,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* File format:
|
||||
*
|
||||
* struct group_descs {
|
||||
* u32 nr_groups;
|
||||
* struct group_desc {
|
||||
* char name[];
|
||||
* u32 leader_idx;
|
||||
* u32 nr_members;
|
||||
* }[nr_groups];
|
||||
* };
|
||||
*/
|
||||
static int write_group_desc(int fd, struct perf_header *h __maybe_unused,
|
||||
struct perf_evlist *evlist)
|
||||
{
|
||||
u32 nr_groups = evlist->nr_groups;
|
||||
struct perf_evsel *evsel;
|
||||
int ret;
|
||||
|
||||
ret = do_write(fd, &nr_groups, sizeof(nr_groups));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
list_for_each_entry(evsel, &evlist->entries, node) {
|
||||
if (perf_evsel__is_group_leader(evsel) &&
|
||||
evsel->nr_members > 1) {
|
||||
const char *name = evsel->group_name ?: "{anon_group}";
|
||||
u32 leader_idx = evsel->idx;
|
||||
u32 nr_members = evsel->nr_members;
|
||||
|
||||
ret = do_write_string(fd, name);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = do_write(fd, &leader_idx, sizeof(leader_idx));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = do_write(fd, &nr_members, sizeof(nr_members));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* default get_cpuid(): nothing gets recorded
|
||||
* actual implementation must be in arch/$(ARCH)/util/header.c
|
||||
@@ -1209,14 +1267,14 @@ read_event_desc(struct perf_header *ph, int fd)
|
||||
size_t msz;
|
||||
|
||||
/* number of events */
|
||||
ret = read(fd, &nre, sizeof(nre));
|
||||
ret = readn(fd, &nre, sizeof(nre));
|
||||
if (ret != (ssize_t)sizeof(nre))
|
||||
goto error;
|
||||
|
||||
if (ph->needs_swap)
|
||||
nre = bswap_32(nre);
|
||||
|
||||
ret = read(fd, &sz, sizeof(sz));
|
||||
ret = readn(fd, &sz, sizeof(sz));
|
||||
if (ret != (ssize_t)sizeof(sz))
|
||||
goto error;
|
||||
|
||||
@@ -1244,7 +1302,7 @@ read_event_desc(struct perf_header *ph, int fd)
|
||||
* must read entire on-file attr struct to
|
||||
* sync up with layout.
|
||||
*/
|
||||
ret = read(fd, buf, sz);
|
||||
ret = readn(fd, buf, sz);
|
||||
if (ret != (ssize_t)sz)
|
||||
goto error;
|
||||
|
||||
@@ -1253,7 +1311,7 @@ read_event_desc(struct perf_header *ph, int fd)
|
||||
|
||||
memcpy(&evsel->attr, buf, msz);
|
||||
|
||||
ret = read(fd, &nr, sizeof(nr));
|
||||
ret = readn(fd, &nr, sizeof(nr));
|
||||
if (ret != (ssize_t)sizeof(nr))
|
||||
goto error;
|
||||
|
||||
@@ -1274,7 +1332,7 @@ read_event_desc(struct perf_header *ph, int fd)
|
||||
evsel->id = id;
|
||||
|
||||
for (j = 0 ; j < nr; j++) {
|
||||
ret = read(fd, id, sizeof(*id));
|
||||
ret = readn(fd, id, sizeof(*id));
|
||||
if (ret != (ssize_t)sizeof(*id))
|
||||
goto error;
|
||||
if (ph->needs_swap)
|
||||
@@ -1435,6 +1493,31 @@ error:
|
||||
fprintf(fp, "# pmu mappings: unable to read\n");
|
||||
}
|
||||
|
||||
static void print_group_desc(struct perf_header *ph, int fd __maybe_unused,
|
||||
FILE *fp)
|
||||
{
|
||||
struct perf_session *session;
|
||||
struct perf_evsel *evsel;
|
||||
u32 nr = 0;
|
||||
|
||||
session = container_of(ph, struct perf_session, header);
|
||||
|
||||
list_for_each_entry(evsel, &session->evlist->entries, node) {
|
||||
if (perf_evsel__is_group_leader(evsel) &&
|
||||
evsel->nr_members > 1) {
|
||||
fprintf(fp, "# group: %s{%s", evsel->group_name ?: "",
|
||||
perf_evsel__name(evsel));
|
||||
|
||||
nr = evsel->nr_members - 1;
|
||||
} else if (nr) {
|
||||
fprintf(fp, ",%s", perf_evsel__name(evsel));
|
||||
|
||||
if (--nr == 0)
|
||||
fprintf(fp, "}\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int __event_process_build_id(struct build_id_event *bev,
|
||||
char *filename,
|
||||
struct perf_session *session)
|
||||
@@ -1506,14 +1589,14 @@ static int perf_header__read_build_ids_abi_quirk(struct perf_header *header,
|
||||
while (offset < limit) {
|
||||
ssize_t len;
|
||||
|
||||
if (read(input, &old_bev, sizeof(old_bev)) != sizeof(old_bev))
|
||||
if (readn(input, &old_bev, sizeof(old_bev)) != sizeof(old_bev))
|
||||
return -1;
|
||||
|
||||
if (header->needs_swap)
|
||||
perf_event_header__bswap(&old_bev.header);
|
||||
|
||||
len = old_bev.header.size - sizeof(old_bev);
|
||||
if (read(input, filename, len) != len)
|
||||
if (readn(input, filename, len) != len)
|
||||
return -1;
|
||||
|
||||
bev.header = old_bev.header;
|
||||
@@ -1548,14 +1631,14 @@ static int perf_header__read_build_ids(struct perf_header *header,
|
||||
while (offset < limit) {
|
||||
ssize_t len;
|
||||
|
||||
if (read(input, &bev, sizeof(bev)) != sizeof(bev))
|
||||
if (readn(input, &bev, sizeof(bev)) != sizeof(bev))
|
||||
goto out;
|
||||
|
||||
if (header->needs_swap)
|
||||
perf_event_header__bswap(&bev.header);
|
||||
|
||||
len = bev.header.size - sizeof(bev);
|
||||
if (read(input, filename, len) != len)
|
||||
if (readn(input, filename, len) != len)
|
||||
goto out;
|
||||
/*
|
||||
* The a1645ce1 changeset:
|
||||
@@ -1641,7 +1724,7 @@ static int process_nrcpus(struct perf_file_section *section __maybe_unused,
|
||||
size_t ret;
|
||||
u32 nr;
|
||||
|
||||
ret = read(fd, &nr, sizeof(nr));
|
||||
ret = readn(fd, &nr, sizeof(nr));
|
||||
if (ret != sizeof(nr))
|
||||
return -1;
|
||||
|
||||
@@ -1650,7 +1733,7 @@ static int process_nrcpus(struct perf_file_section *section __maybe_unused,
|
||||
|
||||
ph->env.nr_cpus_online = nr;
|
||||
|
||||
ret = read(fd, &nr, sizeof(nr));
|
||||
ret = readn(fd, &nr, sizeof(nr));
|
||||
if (ret != sizeof(nr))
|
||||
return -1;
|
||||
|
||||
@@ -1684,7 +1767,7 @@ static int process_total_mem(struct perf_file_section *section __maybe_unused,
|
||||
uint64_t mem;
|
||||
size_t ret;
|
||||
|
||||
ret = read(fd, &mem, sizeof(mem));
|
||||
ret = readn(fd, &mem, sizeof(mem));
|
||||
if (ret != sizeof(mem))
|
||||
return -1;
|
||||
|
||||
@@ -1756,7 +1839,7 @@ static int process_cmdline(struct perf_file_section *section __maybe_unused,
|
||||
u32 nr, i;
|
||||
struct strbuf sb;
|
||||
|
||||
ret = read(fd, &nr, sizeof(nr));
|
||||
ret = readn(fd, &nr, sizeof(nr));
|
||||
if (ret != sizeof(nr))
|
||||
return -1;
|
||||
|
||||
@@ -1792,7 +1875,7 @@ static int process_cpu_topology(struct perf_file_section *section __maybe_unused
|
||||
char *str;
|
||||
struct strbuf sb;
|
||||
|
||||
ret = read(fd, &nr, sizeof(nr));
|
||||
ret = readn(fd, &nr, sizeof(nr));
|
||||
if (ret != sizeof(nr))
|
||||
return -1;
|
||||
|
||||
@@ -1813,7 +1896,7 @@ static int process_cpu_topology(struct perf_file_section *section __maybe_unused
|
||||
}
|
||||
ph->env.sibling_cores = strbuf_detach(&sb, NULL);
|
||||
|
||||
ret = read(fd, &nr, sizeof(nr));
|
||||
ret = readn(fd, &nr, sizeof(nr));
|
||||
if (ret != sizeof(nr))
|
||||
return -1;
|
||||
|
||||
@@ -1850,7 +1933,7 @@ static int process_numa_topology(struct perf_file_section *section __maybe_unuse
|
||||
struct strbuf sb;
|
||||
|
||||
/* nr nodes */
|
||||
ret = read(fd, &nr, sizeof(nr));
|
||||
ret = readn(fd, &nr, sizeof(nr));
|
||||
if (ret != sizeof(nr))
|
||||
goto error;
|
||||
|
||||
@@ -1862,15 +1945,15 @@ static int process_numa_topology(struct perf_file_section *section __maybe_unuse
|
||||
|
||||
for (i = 0; i < nr; i++) {
|
||||
/* node number */
|
||||
ret = read(fd, &node, sizeof(node));
|
||||
ret = readn(fd, &node, sizeof(node));
|
||||
if (ret != sizeof(node))
|
||||
goto error;
|
||||
|
||||
ret = read(fd, &mem_total, sizeof(u64));
|
||||
ret = readn(fd, &mem_total, sizeof(u64));
|
||||
if (ret != sizeof(u64))
|
||||
goto error;
|
||||
|
||||
ret = read(fd, &mem_free, sizeof(u64));
|
||||
ret = readn(fd, &mem_free, sizeof(u64));
|
||||
if (ret != sizeof(u64))
|
||||
goto error;
|
||||
|
||||
@@ -1909,7 +1992,7 @@ static int process_pmu_mappings(struct perf_file_section *section __maybe_unused
|
||||
u32 type;
|
||||
struct strbuf sb;
|
||||
|
||||
ret = read(fd, &pmu_num, sizeof(pmu_num));
|
||||
ret = readn(fd, &pmu_num, sizeof(pmu_num));
|
||||
if (ret != sizeof(pmu_num))
|
||||
return -1;
|
||||
|
||||
@@ -1925,7 +2008,7 @@ static int process_pmu_mappings(struct perf_file_section *section __maybe_unused
|
||||
strbuf_init(&sb, 128);
|
||||
|
||||
while (pmu_num) {
|
||||
if (read(fd, &type, sizeof(type)) != sizeof(type))
|
||||
if (readn(fd, &type, sizeof(type)) != sizeof(type))
|
||||
goto error;
|
||||
if (ph->needs_swap)
|
||||
type = bswap_32(type);
|
||||
@@ -1949,6 +2032,98 @@ error:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int process_group_desc(struct perf_file_section *section __maybe_unused,
|
||||
struct perf_header *ph, int fd,
|
||||
void *data __maybe_unused)
|
||||
{
|
||||
size_t ret = -1;
|
||||
u32 i, nr, nr_groups;
|
||||
struct perf_session *session;
|
||||
struct perf_evsel *evsel, *leader = NULL;
|
||||
struct group_desc {
|
||||
char *name;
|
||||
u32 leader_idx;
|
||||
u32 nr_members;
|
||||
} *desc;
|
||||
|
||||
if (readn(fd, &nr_groups, sizeof(nr_groups)) != sizeof(nr_groups))
|
||||
return -1;
|
||||
|
||||
if (ph->needs_swap)
|
||||
nr_groups = bswap_32(nr_groups);
|
||||
|
||||
ph->env.nr_groups = nr_groups;
|
||||
if (!nr_groups) {
|
||||
pr_debug("group desc not available\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
desc = calloc(nr_groups, sizeof(*desc));
|
||||
if (!desc)
|
||||
return -1;
|
||||
|
||||
for (i = 0; i < nr_groups; i++) {
|
||||
desc[i].name = do_read_string(fd, ph);
|
||||
if (!desc[i].name)
|
||||
goto out_free;
|
||||
|
||||
if (readn(fd, &desc[i].leader_idx, sizeof(u32)) != sizeof(u32))
|
||||
goto out_free;
|
||||
|
||||
if (readn(fd, &desc[i].nr_members, sizeof(u32)) != sizeof(u32))
|
||||
goto out_free;
|
||||
|
||||
if (ph->needs_swap) {
|
||||
desc[i].leader_idx = bswap_32(desc[i].leader_idx);
|
||||
desc[i].nr_members = bswap_32(desc[i].nr_members);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Rebuild group relationship based on the group_desc
|
||||
*/
|
||||
session = container_of(ph, struct perf_session, header);
|
||||
session->evlist->nr_groups = nr_groups;
|
||||
|
||||
i = nr = 0;
|
||||
list_for_each_entry(evsel, &session->evlist->entries, node) {
|
||||
if (evsel->idx == (int) desc[i].leader_idx) {
|
||||
evsel->leader = evsel;
|
||||
/* {anon_group} is a dummy name */
|
||||
if (strcmp(desc[i].name, "{anon_group}"))
|
||||
evsel->group_name = desc[i].name;
|
||||
evsel->nr_members = desc[i].nr_members;
|
||||
|
||||
if (i >= nr_groups || nr > 0) {
|
||||
pr_debug("invalid group desc\n");
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
leader = evsel;
|
||||
nr = evsel->nr_members - 1;
|
||||
i++;
|
||||
} else if (nr) {
|
||||
/* This is a group member */
|
||||
evsel->leader = leader;
|
||||
|
||||
nr--;
|
||||
}
|
||||
}
|
||||
|
||||
if (i != nr_groups || nr != 0) {
|
||||
pr_debug("invalid group desc\n");
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
out_free:
|
||||
while ((int) --i >= 0)
|
||||
free(desc[i].name);
|
||||
free(desc);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct feature_ops {
|
||||
int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist);
|
||||
void (*print)(struct perf_header *h, int fd, FILE *fp);
|
||||
@@ -1988,6 +2163,7 @@ static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = {
|
||||
FEAT_OPF(HEADER_NUMA_TOPOLOGY, numa_topology),
|
||||
FEAT_OPA(HEADER_BRANCH_STACK, branch_stack),
|
||||
FEAT_OPP(HEADER_PMU_MAPPINGS, pmu_mappings),
|
||||
FEAT_OPP(HEADER_GROUP_DESC, group_desc),
|
||||
};
|
||||
|
||||
struct header_print_data {
|
||||
@@ -2077,7 +2253,7 @@ static int perf_header__adds_write(struct perf_header *header,
|
||||
if (!nr_sections)
|
||||
return 0;
|
||||
|
||||
feat_sec = p = calloc(sizeof(*feat_sec), nr_sections);
|
||||
feat_sec = p = calloc(nr_sections, sizeof(*feat_sec));
|
||||
if (feat_sec == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -2249,7 +2425,7 @@ int perf_header__process_sections(struct perf_header *header, int fd,
|
||||
if (!nr_sections)
|
||||
return 0;
|
||||
|
||||
feat_sec = sec = calloc(sizeof(*feat_sec), nr_sections);
|
||||
feat_sec = sec = calloc(nr_sections, sizeof(*feat_sec));
|
||||
if (!feat_sec)
|
||||
return -1;
|
||||
|
||||
@@ -2912,16 +3088,22 @@ int perf_event__process_tracing_data(union perf_event *event,
|
||||
session->repipe);
|
||||
padding = PERF_ALIGN(size_read, sizeof(u64)) - size_read;
|
||||
|
||||
if (read(session->fd, buf, padding) < 0)
|
||||
die("reading input file");
|
||||
if (readn(session->fd, buf, padding) < 0) {
|
||||
pr_err("%s: reading input file", __func__);
|
||||
return -1;
|
||||
}
|
||||
if (session->repipe) {
|
||||
int retw = write(STDOUT_FILENO, buf, padding);
|
||||
if (retw <= 0 || retw != padding)
|
||||
die("repiping tracing data padding");
|
||||
if (retw <= 0 || retw != padding) {
|
||||
pr_err("%s: repiping tracing data padding", __func__);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (size_read + padding != size)
|
||||
die("tracing data size mismatch");
|
||||
if (size_read + padding != size) {
|
||||
pr_err("%s: tracing data size mismatch", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
perf_evlist__prepare_tracepoint_events(session->evlist,
|
||||
session->pevent);
|
||||
|
@@ -29,6 +29,7 @@ enum {
|
||||
HEADER_NUMA_TOPOLOGY,
|
||||
HEADER_BRANCH_STACK,
|
||||
HEADER_PMU_MAPPINGS,
|
||||
HEADER_GROUP_DESC,
|
||||
HEADER_LAST_FEATURE,
|
||||
HEADER_FEAT_BITS = 256,
|
||||
};
|
||||
@@ -79,6 +80,7 @@ struct perf_session_env {
|
||||
char *numa_nodes;
|
||||
int nr_pmu_mappings;
|
||||
char *pmu_mappings;
|
||||
int nr_groups;
|
||||
};
|
||||
|
||||
struct perf_header {
|
||||
|
@@ -4,6 +4,7 @@
|
||||
#include "hist.h"
|
||||
#include "session.h"
|
||||
#include "sort.h"
|
||||
#include "evsel.h"
|
||||
#include <math.h>
|
||||
|
||||
static bool hists__filter_entry_by_dso(struct hists *hists,
|
||||
@@ -82,6 +83,9 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
|
||||
hists__new_col_len(hists, HISTC_DSO, len);
|
||||
}
|
||||
|
||||
if (h->parent)
|
||||
hists__new_col_len(hists, HISTC_PARENT, h->parent->namelen);
|
||||
|
||||
if (h->branch_info) {
|
||||
int symlen;
|
||||
/*
|
||||
@@ -242,6 +246,14 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template)
|
||||
|
||||
if (he->ms.map)
|
||||
he->ms.map->referenced = true;
|
||||
|
||||
if (he->branch_info) {
|
||||
if (he->branch_info->from.map)
|
||||
he->branch_info->from.map->referenced = true;
|
||||
if (he->branch_info->to.map)
|
||||
he->branch_info->to.map->referenced = true;
|
||||
}
|
||||
|
||||
if (symbol_conf.use_callchain)
|
||||
callchain_init(he->callchain);
|
||||
|
||||
@@ -251,7 +263,7 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template)
|
||||
return he;
|
||||
}
|
||||
|
||||
static void hists__inc_nr_entries(struct hists *hists, struct hist_entry *h)
|
||||
void hists__inc_nr_entries(struct hists *hists, struct hist_entry *h)
|
||||
{
|
||||
if (!h->filtered) {
|
||||
hists__calc_col_len(hists, h);
|
||||
@@ -285,7 +297,13 @@ static struct hist_entry *add_hist_entry(struct hists *hists,
|
||||
parent = *p;
|
||||
he = rb_entry(parent, struct hist_entry, rb_node_in);
|
||||
|
||||
cmp = hist_entry__cmp(entry, he);
|
||||
/*
|
||||
* Make sure that it receives arguments in a same order as
|
||||
* hist_entry__collapse() so that we can use an appropriate
|
||||
* function when searching an entry regardless which sort
|
||||
* keys were used.
|
||||
*/
|
||||
cmp = hist_entry__cmp(he, entry);
|
||||
|
||||
if (!cmp) {
|
||||
he_stat__add_period(&he->stat, period);
|
||||
@@ -523,6 +541,62 @@ void hists__collapse_resort_threaded(struct hists *hists)
|
||||
* reverse the map, sort on period.
|
||||
*/
|
||||
|
||||
static int period_cmp(u64 period_a, u64 period_b)
|
||||
{
|
||||
if (period_a > period_b)
|
||||
return 1;
|
||||
if (period_a < period_b)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hist_entry__sort_on_period(struct hist_entry *a,
|
||||
struct hist_entry *b)
|
||||
{
|
||||
int ret;
|
||||
int i, nr_members;
|
||||
struct perf_evsel *evsel;
|
||||
struct hist_entry *pair;
|
||||
u64 *periods_a, *periods_b;
|
||||
|
||||
ret = period_cmp(a->stat.period, b->stat.period);
|
||||
if (ret || !symbol_conf.event_group)
|
||||
return ret;
|
||||
|
||||
evsel = hists_to_evsel(a->hists);
|
||||
nr_members = evsel->nr_members;
|
||||
if (nr_members <= 1)
|
||||
return ret;
|
||||
|
||||
periods_a = zalloc(sizeof(periods_a) * nr_members);
|
||||
periods_b = zalloc(sizeof(periods_b) * nr_members);
|
||||
|
||||
if (!periods_a || !periods_b)
|
||||
goto out;
|
||||
|
||||
list_for_each_entry(pair, &a->pairs.head, pairs.node) {
|
||||
evsel = hists_to_evsel(pair->hists);
|
||||
periods_a[perf_evsel__group_idx(evsel)] = pair->stat.period;
|
||||
}
|
||||
|
||||
list_for_each_entry(pair, &b->pairs.head, pairs.node) {
|
||||
evsel = hists_to_evsel(pair->hists);
|
||||
periods_b[perf_evsel__group_idx(evsel)] = pair->stat.period;
|
||||
}
|
||||
|
||||
for (i = 1; i < nr_members; i++) {
|
||||
ret = period_cmp(periods_a[i], periods_b[i]);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
free(periods_a);
|
||||
free(periods_b);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __hists__insert_output_entry(struct rb_root *entries,
|
||||
struct hist_entry *he,
|
||||
u64 min_callchain_hits)
|
||||
@@ -539,7 +613,7 @@ static void __hists__insert_output_entry(struct rb_root *entries,
|
||||
parent = *p;
|
||||
iter = rb_entry(parent, struct hist_entry, rb_node);
|
||||
|
||||
if (he->stat.period > iter->stat.period)
|
||||
if (hist_entry__sort_on_period(he, iter) > 0)
|
||||
p = &(*p)->rb_left;
|
||||
else
|
||||
p = &(*p)->rb_right;
|
||||
@@ -711,25 +785,38 @@ int hist_entry__annotate(struct hist_entry *he, size_t privsize)
|
||||
return symbol__annotate(he->ms.sym, he->ms.map, privsize);
|
||||
}
|
||||
|
||||
void events_stats__inc(struct events_stats *stats, u32 type)
|
||||
{
|
||||
++stats->nr_events[0];
|
||||
++stats->nr_events[type];
|
||||
}
|
||||
|
||||
void hists__inc_nr_events(struct hists *hists, u32 type)
|
||||
{
|
||||
++hists->stats.nr_events[0];
|
||||
++hists->stats.nr_events[type];
|
||||
events_stats__inc(&hists->stats, type);
|
||||
}
|
||||
|
||||
static struct hist_entry *hists__add_dummy_entry(struct hists *hists,
|
||||
struct hist_entry *pair)
|
||||
{
|
||||
struct rb_node **p = &hists->entries.rb_node;
|
||||
struct rb_root *root;
|
||||
struct rb_node **p;
|
||||
struct rb_node *parent = NULL;
|
||||
struct hist_entry *he;
|
||||
int cmp;
|
||||
|
||||
if (sort__need_collapse)
|
||||
root = &hists->entries_collapsed;
|
||||
else
|
||||
root = hists->entries_in;
|
||||
|
||||
p = &root->rb_node;
|
||||
|
||||
while (*p != NULL) {
|
||||
parent = *p;
|
||||
he = rb_entry(parent, struct hist_entry, rb_node);
|
||||
he = rb_entry(parent, struct hist_entry, rb_node_in);
|
||||
|
||||
cmp = hist_entry__cmp(pair, he);
|
||||
cmp = hist_entry__collapse(he, pair);
|
||||
|
||||
if (!cmp)
|
||||
goto out;
|
||||
@@ -744,8 +831,8 @@ static struct hist_entry *hists__add_dummy_entry(struct hists *hists,
|
||||
if (he) {
|
||||
memset(&he->stat, 0, sizeof(he->stat));
|
||||
he->hists = hists;
|
||||
rb_link_node(&he->rb_node, parent, p);
|
||||
rb_insert_color(&he->rb_node, &hists->entries);
|
||||
rb_link_node(&he->rb_node_in, parent, p);
|
||||
rb_insert_color(&he->rb_node_in, root);
|
||||
hists__inc_nr_entries(hists, he);
|
||||
}
|
||||
out:
|
||||
@@ -755,11 +842,16 @@ out:
|
||||
static struct hist_entry *hists__find_entry(struct hists *hists,
|
||||
struct hist_entry *he)
|
||||
{
|
||||
struct rb_node *n = hists->entries.rb_node;
|
||||
struct rb_node *n;
|
||||
|
||||
if (sort__need_collapse)
|
||||
n = hists->entries_collapsed.rb_node;
|
||||
else
|
||||
n = hists->entries_in->rb_node;
|
||||
|
||||
while (n) {
|
||||
struct hist_entry *iter = rb_entry(n, struct hist_entry, rb_node);
|
||||
int64_t cmp = hist_entry__cmp(he, iter);
|
||||
struct hist_entry *iter = rb_entry(n, struct hist_entry, rb_node_in);
|
||||
int64_t cmp = hist_entry__collapse(iter, he);
|
||||
|
||||
if (cmp < 0)
|
||||
n = n->rb_left;
|
||||
@@ -777,15 +869,21 @@ static struct hist_entry *hists__find_entry(struct hists *hists,
|
||||
*/
|
||||
void hists__match(struct hists *leader, struct hists *other)
|
||||
{
|
||||
struct rb_root *root;
|
||||
struct rb_node *nd;
|
||||
struct hist_entry *pos, *pair;
|
||||
|
||||
for (nd = rb_first(&leader->entries); nd; nd = rb_next(nd)) {
|
||||
pos = rb_entry(nd, struct hist_entry, rb_node);
|
||||
if (sort__need_collapse)
|
||||
root = &leader->entries_collapsed;
|
||||
else
|
||||
root = leader->entries_in;
|
||||
|
||||
for (nd = rb_first(root); nd; nd = rb_next(nd)) {
|
||||
pos = rb_entry(nd, struct hist_entry, rb_node_in);
|
||||
pair = hists__find_entry(other, pos);
|
||||
|
||||
if (pair)
|
||||
hist__entry_add_pair(pos, pair);
|
||||
hist_entry__add_pair(pair, pos);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -796,17 +894,23 @@ void hists__match(struct hists *leader, struct hists *other)
|
||||
*/
|
||||
int hists__link(struct hists *leader, struct hists *other)
|
||||
{
|
||||
struct rb_root *root;
|
||||
struct rb_node *nd;
|
||||
struct hist_entry *pos, *pair;
|
||||
|
||||
for (nd = rb_first(&other->entries); nd; nd = rb_next(nd)) {
|
||||
pos = rb_entry(nd, struct hist_entry, rb_node);
|
||||
if (sort__need_collapse)
|
||||
root = &other->entries_collapsed;
|
||||
else
|
||||
root = other->entries_in;
|
||||
|
||||
for (nd = rb_first(root); nd; nd = rb_next(nd)) {
|
||||
pos = rb_entry(nd, struct hist_entry, rb_node_in);
|
||||
|
||||
if (!hist_entry__has_pairs(pos)) {
|
||||
pair = hists__add_dummy_entry(leader, pos);
|
||||
if (pair == NULL)
|
||||
return -1;
|
||||
hist__entry_add_pair(pair, pos);
|
||||
hist_entry__add_pair(pos, pair);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -96,8 +96,10 @@ void hists__decay_entries_threaded(struct hists *hists, bool zap_user,
|
||||
bool zap_kernel);
|
||||
void hists__output_recalc_col_len(struct hists *hists, int max_rows);
|
||||
|
||||
void hists__inc_nr_entries(struct hists *hists, struct hist_entry *h);
|
||||
void hists__inc_nr_events(struct hists *self, u32 type);
|
||||
size_t hists__fprintf_nr_events(struct hists *self, FILE *fp);
|
||||
void events_stats__inc(struct events_stats *stats, u32 type);
|
||||
size_t events_stats__fprintf(struct events_stats *stats, FILE *fp);
|
||||
|
||||
size_t hists__fprintf(struct hists *self, bool show_header, int max_rows,
|
||||
int max_cols, FILE *fp);
|
||||
@@ -126,13 +128,19 @@ struct perf_hpp {
|
||||
};
|
||||
|
||||
struct perf_hpp_fmt {
|
||||
bool cond;
|
||||
int (*header)(struct perf_hpp *hpp);
|
||||
int (*width)(struct perf_hpp *hpp);
|
||||
int (*color)(struct perf_hpp *hpp, struct hist_entry *he);
|
||||
int (*entry)(struct perf_hpp *hpp, struct hist_entry *he);
|
||||
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
extern struct list_head perf_hpp__list;
|
||||
|
||||
#define perf_hpp__for_each_format(format) \
|
||||
list_for_each_entry(format, &perf_hpp__list, list)
|
||||
|
||||
extern struct perf_hpp_fmt perf_hpp__format[];
|
||||
|
||||
enum {
|
||||
@@ -148,14 +156,14 @@ enum {
|
||||
PERF_HPP__DELTA,
|
||||
PERF_HPP__RATIO,
|
||||
PERF_HPP__WEIGHTED_DIFF,
|
||||
PERF_HPP__DISPL,
|
||||
PERF_HPP__FORMULA,
|
||||
|
||||
PERF_HPP__MAX_INDEX
|
||||
};
|
||||
|
||||
void perf_hpp__init(void);
|
||||
void perf_hpp__column_enable(unsigned col, bool enable);
|
||||
void perf_hpp__column_register(struct perf_hpp_fmt *format);
|
||||
void perf_hpp__column_enable(unsigned col);
|
||||
int hist_entry__period_snprintf(struct perf_hpp *hpp, struct hist_entry *he,
|
||||
bool color);
|
||||
|
||||
@@ -219,8 +227,10 @@ int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist __maybe_unused,
|
||||
|
||||
unsigned int hists__sort_list_width(struct hists *self);
|
||||
|
||||
double perf_diff__compute_delta(struct hist_entry *he);
|
||||
double perf_diff__compute_ratio(struct hist_entry *he);
|
||||
s64 perf_diff__compute_wdiff(struct hist_entry *he);
|
||||
int perf_diff__formula(char *buf, size_t size, struct hist_entry *he);
|
||||
double perf_diff__compute_delta(struct hist_entry *he, struct hist_entry *pair);
|
||||
double perf_diff__compute_ratio(struct hist_entry *he, struct hist_entry *pair);
|
||||
s64 perf_diff__compute_wdiff(struct hist_entry *he, struct hist_entry *pair);
|
||||
int perf_diff__formula(struct hist_entry *he, struct hist_entry *pair,
|
||||
char *buf, size_t size);
|
||||
double perf_diff__period_percent(struct hist_entry *he, u64 period);
|
||||
#endif /* __PERF_HIST_H */
|
||||
|
@@ -14,6 +14,7 @@
|
||||
#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))
|
||||
#define BITS_TO_U64(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u64))
|
||||
#define BITS_TO_U32(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u32))
|
||||
#define BITS_TO_BYTES(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE)
|
||||
|
||||
#define for_each_set_bit(bit, addr, size) \
|
||||
for ((bit) = find_first_bit((addr), (size)); \
|
||||
|
@@ -59,16 +59,40 @@ void intlist__remove(struct intlist *ilist, struct int_node *node)
|
||||
|
||||
struct int_node *intlist__find(struct intlist *ilist, int i)
|
||||
{
|
||||
struct int_node *node = NULL;
|
||||
struct rb_node *rb_node = rblist__find(&ilist->rblist, (void *)((long)i));
|
||||
struct int_node *node;
|
||||
struct rb_node *rb_node;
|
||||
|
||||
if (ilist == NULL)
|
||||
return NULL;
|
||||
|
||||
node = NULL;
|
||||
rb_node = rblist__find(&ilist->rblist, (void *)((long)i));
|
||||
if (rb_node)
|
||||
node = container_of(rb_node, struct int_node, rb_node);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
struct intlist *intlist__new(void)
|
||||
static int intlist__parse_list(struct intlist *ilist, const char *s)
|
||||
{
|
||||
char *sep;
|
||||
int err;
|
||||
|
||||
do {
|
||||
long value = strtol(s, &sep, 10);
|
||||
err = -EINVAL;
|
||||
if (*sep != ',' && *sep != '\0')
|
||||
break;
|
||||
err = intlist__add(ilist, value);
|
||||
if (err)
|
||||
break;
|
||||
s = sep + 1;
|
||||
} while (*sep != '\0');
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
struct intlist *intlist__new(const char *slist)
|
||||
{
|
||||
struct intlist *ilist = malloc(sizeof(*ilist));
|
||||
|
||||
@@ -77,9 +101,15 @@ struct intlist *intlist__new(void)
|
||||
ilist->rblist.node_cmp = intlist__node_cmp;
|
||||
ilist->rblist.node_new = intlist__node_new;
|
||||
ilist->rblist.node_delete = intlist__node_delete;
|
||||
|
||||
if (slist && intlist__parse_list(ilist, slist))
|
||||
goto out_delete;
|
||||
}
|
||||
|
||||
return ilist;
|
||||
out_delete:
|
||||
intlist__delete(ilist);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void intlist__delete(struct intlist *ilist)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user