Merge branch 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull perf updates from Ingo Molnar: "Features: - Add "uretprobes" - an optimization to uprobes, like kretprobes are an optimization to kprobes. "perf probe -x file sym%return" now works like kretprobes. By Oleg Nesterov. - Introduce per core aggregation in 'perf stat', from Stephane Eranian. - Add memory profiling via PEBS, from Stephane Eranian. - Event group view for 'annotate' in --stdio, --tui and --gtk, from Namhyung Kim. - Add support for AMD NB and L2I "uncore" counters, by Jacob Shin. - Add Ivy Bridge-EP uncore support, by Zheng Yan - IBM zEnterprise EC12 oprofile support patchlet from Robert Richter. - Add perf test entries for checking breakpoint overflow signal handler issues, from Jiri Olsa. - Add perf test entry for for checking number of EXIT events, from Namhyung Kim. - Add perf test entries for checking --cpu in record and stat, from Jiri Olsa. - Introduce perf stat --repeat forever, from Frederik Deweerdt. - Add --no-demangle to report/top, from Namhyung Kim. - PowerPC fixes plus a couple of cleanups/optimizations in uprobes and trace_uprobes, by Oleg Nesterov. Various fixes and refactorings: - Fix dependency of the python binding wrt libtraceevent, from Naohiro Aota. - Simplify some perf_evlist methods and to allow 'stat' to share code with 'record' and 'trace', by Arnaldo Carvalho de Melo. - Remove dead code in related to libtraceevent integration, from Namhyung Kim. - Revert "perf sched: Handle PERF_RECORD_EXIT events" to get 'perf sched lat' back working, by Arnaldo Carvalho de Melo - We don't use Newt anymore, just plain libslang, by Arnaldo Carvalho de Melo. - Kill a bunch of die() calls, from Namhyung Kim. - Fix build on non-glibc systems due to libio.h absence, from Cody P Schafer. - Remove some perf_session and tracing dead code, from David Ahern. - Honor parallel jobs, fix from Borislav Petkov - Introduce tools/lib/lk library, initially just removing duplication among tools/perf and tools/vm. from Borislav Petkov ... and many more I missed to list, see the shortlog and git log for more details." * 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (136 commits) perf/x86/intel/P4: Robistify P4 PMU types perf/x86/amd: Fix AMD NB and L2I "uncore" support perf/x86/amd: Remove old-style NB counter support from perf_event_amd.c perf/x86: Check all MSRs before passing hw check perf/x86/amd: Add support for AMD NB and L2I "uncore" counters perf/x86/intel: Add Ivy Bridge-EP uncore support perf/x86/intel: Fix SNB-EP CBO and PCU uncore PMU filter management perf/x86: Avoid kfree() in CPU_{STARTING,DYING} uprobes/perf: Avoid perf_trace_buf_prepare/submit if ->perf_events is empty uprobes/tracing: Don't pass addr=ip to perf_trace_buf_submit() uprobes/tracing: Change create_trace_uprobe() to support uretprobes uprobes/tracing: Make seq_printf() code uretprobe-friendly uprobes/tracing: Make register_uprobe_event() paths uretprobe-friendly uprobes/tracing: Make uprobe_{trace,perf}_print() uretprobe-friendly uprobes/tracing: Introduce is_ret_probe() and uretprobe_dispatcher() uprobes/tracing: Introduce uprobe_{trace,perf}_print() helpers uprobes/tracing: Generalize struct uprobe_trace_entry_head uprobes/tracing: Kill the pointless local_save_flags/preempt_count calls uprobes/tracing: Kill the pointless seq_print_ip_sym() call uprobes/tracing: Kill the pointless task_pt_regs() calls ...
This commit is contained in:
commit
e0972916e8
130 changed files with 5678 additions and 1716 deletions
|
@ -34,7 +34,13 @@ help:
|
|||
cpupower: FORCE
|
||||
$(call descend,power/$@)
|
||||
|
||||
cgroup firewire lguest perf usb virtio vm: FORCE
|
||||
cgroup firewire guest usb virtio vm: FORCE
|
||||
$(call descend,$@)
|
||||
|
||||
liblk: FORCE
|
||||
$(call descend,lib/lk)
|
||||
|
||||
perf: liblk FORCE
|
||||
$(call descend,$@)
|
||||
|
||||
selftests: FORCE
|
||||
|
@ -62,7 +68,13 @@ install: cgroup_install cpupower_install firewire_install lguest_install \
|
|||
cpupower_clean:
|
||||
$(call descend,power/cpupower,clean)
|
||||
|
||||
cgroup_clean firewire_clean lguest_clean perf_clean usb_clean virtio_clean vm_clean:
|
||||
cgroup_clean firewire_clean lguest_clean usb_clean virtio_clean vm_clean:
|
||||
$(call descend,$(@:_clean=),clean)
|
||||
|
||||
liblk_clean:
|
||||
$(call descend,lib/lk,clean)
|
||||
|
||||
perf_clean: liblk_clean
|
||||
$(call descend,$(@:_clean=),clean)
|
||||
|
||||
selftests_clean:
|
||||
|
|
35
tools/lib/lk/Makefile
Normal file
35
tools/lib/lk/Makefile
Normal file
|
@ -0,0 +1,35 @@
|
|||
include ../../scripts/Makefile.include
|
||||
|
||||
# guard against environment variables
|
||||
LIB_H=
|
||||
LIB_OBJS=
|
||||
|
||||
LIB_H += debugfs.h
|
||||
|
||||
LIB_OBJS += $(OUTPUT)debugfs.o
|
||||
|
||||
LIBFILE = liblk.a
|
||||
|
||||
CFLAGS = -ggdb3 -Wall -Wextra -std=gnu99 -Werror -O6 -D_FORTIFY_SOURCE=2 $(EXTRA_WARNINGS) $(EXTRA_CFLAGS) -fPIC
|
||||
EXTLIBS = -lpthread -lrt -lelf -lm
|
||||
ALL_CFLAGS = $(CFLAGS) $(BASIC_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
|
||||
ALL_LDFLAGS = $(LDFLAGS)
|
||||
|
||||
RM = rm -f
|
||||
|
||||
$(LIBFILE): $(LIB_OBJS)
|
||||
$(QUIET_AR)$(RM) $@ && $(AR) rcs $(OUTPUT)$@ $(LIB_OBJS)
|
||||
|
||||
$(LIB_OBJS): $(LIB_H)
|
||||
|
||||
$(OUTPUT)%.o: %.c
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $<
|
||||
$(OUTPUT)%.s: %.c
|
||||
$(QUIET_CC)$(CC) -S $(ALL_CFLAGS) $<
|
||||
$(OUTPUT)%.o: %.S
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $<
|
||||
|
||||
clean:
|
||||
$(RM) $(LIB_OBJS) $(LIBFILE)
|
||||
|
||||
.PHONY: clean
|
|
@ -1,36 +1,39 @@
|
|||
#include "util.h"
|
||||
#include "debugfs.h"
|
||||
#include "cache.h"
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/vfs.h>
|
||||
#include <sys/mount.h>
|
||||
#include <linux/magic.h>
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#include "debugfs.h"
|
||||
|
||||
static int debugfs_premounted;
|
||||
char debugfs_mountpoint[PATH_MAX + 1] = "/sys/kernel/debug";
|
||||
char tracing_events_path[PATH_MAX + 1] = "/sys/kernel/debug/tracing/events";
|
||||
|
||||
static const char *debugfs_known_mountpoints[] = {
|
||||
static const char * const debugfs_known_mountpoints[] = {
|
||||
"/sys/kernel/debug/",
|
||||
"/debug/",
|
||||
0,
|
||||
};
|
||||
|
||||
static int debugfs_found;
|
||||
static bool debugfs_found;
|
||||
|
||||
/* find the path to the mounted debugfs */
|
||||
const char *debugfs_find_mountpoint(void)
|
||||
{
|
||||
const char **ptr;
|
||||
const char * const *ptr;
|
||||
char type[100];
|
||||
FILE *fp;
|
||||
|
||||
if (debugfs_found)
|
||||
return (const char *) debugfs_mountpoint;
|
||||
return (const char *)debugfs_mountpoint;
|
||||
|
||||
ptr = debugfs_known_mountpoints;
|
||||
while (*ptr) {
|
||||
if (debugfs_valid_mountpoint(*ptr) == 0) {
|
||||
debugfs_found = 1;
|
||||
debugfs_found = true;
|
||||
strcpy(debugfs_mountpoint, *ptr);
|
||||
return debugfs_mountpoint;
|
||||
}
|
||||
|
@ -52,7 +55,7 @@ const char *debugfs_find_mountpoint(void)
|
|||
if (strcmp(type, "debugfs") != 0)
|
||||
return NULL;
|
||||
|
||||
debugfs_found = 1;
|
||||
debugfs_found = true;
|
||||
|
||||
return debugfs_mountpoint;
|
||||
}
|
||||
|
@ -71,21 +74,12 @@ int debugfs_valid_mountpoint(const char *debugfs)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void debugfs_set_tracing_events_path(const char *mountpoint)
|
||||
{
|
||||
snprintf(tracing_events_path, sizeof(tracing_events_path), "%s/%s",
|
||||
mountpoint, "tracing/events");
|
||||
}
|
||||
|
||||
/* mount the debugfs somewhere if it's not mounted */
|
||||
|
||||
char *debugfs_mount(const char *mountpoint)
|
||||
{
|
||||
/* see if it's already mounted */
|
||||
if (debugfs_find_mountpoint()) {
|
||||
debugfs_premounted = 1;
|
||||
if (debugfs_find_mountpoint())
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* if not mounted and no argument */
|
||||
if (mountpoint == NULL) {
|
||||
|
@ -100,15 +94,8 @@ char *debugfs_mount(const char *mountpoint)
|
|||
return NULL;
|
||||
|
||||
/* save the mountpoint */
|
||||
debugfs_found = 1;
|
||||
debugfs_found = true;
|
||||
strncpy(debugfs_mountpoint, mountpoint, sizeof(debugfs_mountpoint));
|
||||
out:
|
||||
debugfs_set_tracing_events_path(debugfs_mountpoint);
|
||||
return debugfs_mountpoint;
|
||||
}
|
||||
|
||||
void debugfs_set_path(const char *mountpoint)
|
||||
{
|
||||
snprintf(debugfs_mountpoint, sizeof(debugfs_mountpoint), "%s", mountpoint);
|
||||
debugfs_set_tracing_events_path(mountpoint);
|
||||
}
|
29
tools/lib/lk/debugfs.h
Normal file
29
tools/lib/lk/debugfs.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
#ifndef __LK_DEBUGFS_H__
|
||||
#define __LK_DEBUGFS_H__
|
||||
|
||||
#define _STR(x) #x
|
||||
#define STR(x) _STR(x)
|
||||
|
||||
/*
|
||||
* On most systems <limits.h> would have given us this, but not on some systems
|
||||
* (e.g. GNU/Hurd).
|
||||
*/
|
||||
#ifndef PATH_MAX
|
||||
#define PATH_MAX 4096
|
||||
#endif
|
||||
|
||||
#ifndef DEBUGFS_MAGIC
|
||||
#define DEBUGFS_MAGIC 0x64626720
|
||||
#endif
|
||||
|
||||
#ifndef PERF_DEBUGFS_ENVIRONMENT
|
||||
#define PERF_DEBUGFS_ENVIRONMENT "PERF_DEBUGFS_DIR"
|
||||
#endif
|
||||
|
||||
const char *debugfs_find_mountpoint(void);
|
||||
int debugfs_valid_mountpoint(const char *debugfs);
|
||||
char *debugfs_mount(const char *mountpoint);
|
||||
|
||||
extern char debugfs_mountpoint[];
|
||||
|
||||
#endif /* __LK_DEBUGFS_H__ */
|
|
@ -93,6 +93,9 @@ OPTIONS
|
|||
--skip-missing::
|
||||
Skip symbols that cannot be annotated.
|
||||
|
||||
--group::
|
||||
Show event group information together
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-record[1], linkperf:perf-report[1]
|
||||
|
|
48
tools/perf/Documentation/perf-mem.txt
Normal file
48
tools/perf/Documentation/perf-mem.txt
Normal file
|
@ -0,0 +1,48 @@
|
|||
perf-mem(1)
|
||||
===========
|
||||
|
||||
NAME
|
||||
----
|
||||
perf-mem - Profile memory accesses
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'perf mem' [<options>] (record [<command>] | report)
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
"perf mem -t <TYPE> record" runs a command and gathers memory operation data
|
||||
from it, into perf.data. Perf record options are accepted and are passed through.
|
||||
|
||||
"perf mem -t <TYPE> report" displays the result. It invokes perf report with the
|
||||
right set of options to display a memory access profile.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
<command>...::
|
||||
Any command you can specify in a shell.
|
||||
|
||||
-t::
|
||||
--type=::
|
||||
Select the memory operation type: load or store (default: load)
|
||||
|
||||
-D::
|
||||
--dump-raw-samples=::
|
||||
Dump the raw decoded samples on the screen in a format that is easy to parse with
|
||||
one sample per line.
|
||||
|
||||
-x::
|
||||
--field-separator::
|
||||
Specify the field separator used when dump raw samples (-D option). By default,
|
||||
The separator is the space character.
|
||||
|
||||
-C::
|
||||
--cpu-list::
|
||||
Restrict dump of raw samples to those provided via this option. Note that the same
|
||||
option can be passed in record mode. It will be interpreted the same way as perf
|
||||
record.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-record[1], linkperf:perf-report[1]
|
|
@ -182,6 +182,12 @@ is enabled for all the sampling events. The sampled branch type is the same for
|
|||
The various filters must be specified as a comma separated list: --branch-filter any_ret,u,k
|
||||
Note that this feature may not be available on all processors.
|
||||
|
||||
-W::
|
||||
--weight::
|
||||
Enable weightened sampling. An additional weight is recorded per sample and can be
|
||||
displayed with the weight and local_weight sort keys. This currently works for TSX
|
||||
abort events and some memory events in precise mode on modern Intel CPUs.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-stat[1], linkperf:perf-list[1]
|
||||
|
|
|
@ -59,7 +59,7 @@ OPTIONS
|
|||
--sort=::
|
||||
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.
|
||||
pid, comm, dso, symbol, parent, cpu, srcline, weight, local_weight.
|
||||
|
||||
Each key has following meaning:
|
||||
|
||||
|
@ -206,6 +206,10 @@ OPTIONS
|
|||
--group::
|
||||
Show event group information together.
|
||||
|
||||
--demangle::
|
||||
Demangle symbol names to human readable form. It's enabled by default,
|
||||
disable with --no-demangle.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-stat[1], linkperf:perf-annotate[1]
|
||||
|
|
|
@ -52,7 +52,7 @@ OPTIONS
|
|||
|
||||
-r::
|
||||
--repeat=<n>::
|
||||
repeat command and print average + stddev (max: 100)
|
||||
repeat command and print average + stddev (max: 100). 0 means forever.
|
||||
|
||||
-B::
|
||||
--big-num::
|
||||
|
@ -119,13 +119,19 @@ perf stat --repeat 10 --null --sync --pre 'make -s O=defconfig-build/clean' -- m
|
|||
Print count deltas every N milliseconds (minimum: 100ms)
|
||||
example: perf stat -I 1000 -e cycles -a sleep 5
|
||||
|
||||
--aggr-socket::
|
||||
--per-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
|
||||
use --per-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.
|
||||
|
||||
--per-core::
|
||||
Aggregate counts per physical processor for system-wide mode measurements. This
|
||||
is a useful mode to detect imbalance between physical cores. To enable this mode,
|
||||
use --per-core in addition to -a. (system-wide). The output includes the
|
||||
core number and the number of online logical processors on that physical processor.
|
||||
|
||||
EXAMPLES
|
||||
--------
|
||||
|
||||
|
|
|
@ -112,7 +112,7 @@ Default is to monitor all CPUS.
|
|||
|
||||
-s::
|
||||
--sort::
|
||||
Sort by key(s): pid, comm, dso, symbol, parent, srcline.
|
||||
Sort by key(s): pid, comm, dso, symbol, parent, srcline, weight, local_weight.
|
||||
|
||||
-n::
|
||||
--show-nr-samples::
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
tools/perf
|
||||
tools/scripts
|
||||
tools/lib/traceevent
|
||||
tools/lib/lk
|
||||
include/linux/const.h
|
||||
include/linux/perf_event.h
|
||||
include/linux/rbtree.h
|
||||
|
|
|
@ -35,7 +35,9 @@ include config/utilities.mak
|
|||
#
|
||||
# Define WERROR=0 to disable treating any warnings as errors.
|
||||
#
|
||||
# Define NO_NEWT if you do not want TUI support.
|
||||
# Define NO_NEWT if you do not want TUI support. (deprecated)
|
||||
#
|
||||
# Define NO_SLANG if you do not want TUI support.
|
||||
#
|
||||
# Define NO_GTK2 if you do not want GTK+ GUI support.
|
||||
#
|
||||
|
@ -104,6 +106,10 @@ ifdef PARSER_DEBUG
|
|||
PARSER_DEBUG_CFLAGS := -DPARSER_DEBUG
|
||||
endif
|
||||
|
||||
ifdef NO_NEWT
|
||||
NO_SLANG=1
|
||||
endif
|
||||
|
||||
CFLAGS = -fno-omit-frame-pointer -ggdb3 -funwind-tables -Wall -Wextra -std=gnu99 $(CFLAGS_WERROR) $(CFLAGS_OPTIMIZE) $(EXTRA_WARNINGS) $(EXTRA_CFLAGS) $(PARSER_DEBUG_CFLAGS)
|
||||
EXTLIBS = -lpthread -lrt -lelf -lm
|
||||
ALL_CFLAGS = $(CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE
|
||||
|
@ -215,6 +221,7 @@ BASIC_CFLAGS = \
|
|||
-Iutil \
|
||||
-I. \
|
||||
-I$(TRACE_EVENT_DIR) \
|
||||
-I../lib/ \
|
||||
-D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE
|
||||
|
||||
BASIC_LDFLAGS =
|
||||
|
@ -240,19 +247,28 @@ SCRIPT_SH += perf-archive.sh
|
|||
grep-libs = $(filter -l%,$(1))
|
||||
strip-libs = $(filter-out -l%,$(1))
|
||||
|
||||
LK_DIR = ../lib/lk/
|
||||
TRACE_EVENT_DIR = ../lib/traceevent/
|
||||
|
||||
LK_PATH=$(LK_DIR)
|
||||
|
||||
ifneq ($(OUTPUT),)
|
||||
TE_PATH=$(OUTPUT)
|
||||
ifneq ($(subdir),)
|
||||
LK_PATH=$(OUTPUT)$(LK_DIR)
|
||||
else
|
||||
LK_PATH=$(OUTPUT)
|
||||
endif
|
||||
else
|
||||
TE_PATH=$(TRACE_EVENT_DIR)
|
||||
endif
|
||||
|
||||
LIBTRACEEVENT = $(TE_PATH)libtraceevent.a
|
||||
TE_LIB := -L$(TE_PATH) -ltraceevent
|
||||
|
||||
export LIBTRACEEVENT
|
||||
|
||||
LIBLK = $(LK_PATH)liblk.a
|
||||
export LIBLK
|
||||
|
||||
# python extension build directories
|
||||
PYTHON_EXTBUILD := $(OUTPUT)python_ext_build/
|
||||
PYTHON_EXTBUILD_LIB := $(PYTHON_EXTBUILD)lib/
|
||||
|
@ -262,7 +278,7 @@ 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
|
||||
PYTHON_EXT_DEPS := util/python-ext-sources util/setup.py $(LIBTRACEEVENT)
|
||||
|
||||
$(OUTPUT)python/perf.so: $(PYTHON_EXT_SRCS) $(PYTHON_EXT_DEPS)
|
||||
$(QUIET_GEN)CFLAGS='$(BASIC_CFLAGS)' $(PYTHON_WORD) util/setup.py \
|
||||
|
@ -355,7 +371,6 @@ LIB_H += util/cache.h
|
|||
LIB_H += util/callchain.h
|
||||
LIB_H += util/build-id.h
|
||||
LIB_H += util/debug.h
|
||||
LIB_H += util/debugfs.h
|
||||
LIB_H += util/sysfs.h
|
||||
LIB_H += util/pmu.h
|
||||
LIB_H += util/event.h
|
||||
|
@ -416,7 +431,6 @@ LIB_OBJS += $(OUTPUT)util/annotate.o
|
|||
LIB_OBJS += $(OUTPUT)util/build-id.o
|
||||
LIB_OBJS += $(OUTPUT)util/config.o
|
||||
LIB_OBJS += $(OUTPUT)util/ctype.o
|
||||
LIB_OBJS += $(OUTPUT)util/debugfs.o
|
||||
LIB_OBJS += $(OUTPUT)util/sysfs.o
|
||||
LIB_OBJS += $(OUTPUT)util/pmu.o
|
||||
LIB_OBJS += $(OUTPUT)util/environment.o
|
||||
|
@ -503,6 +517,10 @@ LIB_OBJS += $(OUTPUT)tests/evsel-tp-sched.o
|
|||
LIB_OBJS += $(OUTPUT)tests/pmu.o
|
||||
LIB_OBJS += $(OUTPUT)tests/hists_link.o
|
||||
LIB_OBJS += $(OUTPUT)tests/python-use.o
|
||||
LIB_OBJS += $(OUTPUT)tests/bp_signal.o
|
||||
LIB_OBJS += $(OUTPUT)tests/bp_signal_overflow.o
|
||||
LIB_OBJS += $(OUTPUT)tests/task-exit.o
|
||||
LIB_OBJS += $(OUTPUT)tests/sw-clock.o
|
||||
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-bench.o
|
||||
|
@ -535,8 +553,9 @@ BUILTIN_OBJS += $(OUTPUT)builtin-lock.o
|
|||
BUILTIN_OBJS += $(OUTPUT)builtin-kvm.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-inject.o
|
||||
BUILTIN_OBJS += $(OUTPUT)tests/builtin-test.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-mem.o
|
||||
|
||||
PERFLIBS = $(LIB_FILE) $(LIBTRACEEVENT)
|
||||
PERFLIBS = $(LIB_FILE) $(LIBLK) $(LIBTRACEEVENT)
|
||||
|
||||
#
|
||||
# Platform specific tweaks
|
||||
|
@ -667,15 +686,15 @@ ifndef NO_LIBAUDIT
|
|||
endif
|
||||
endif
|
||||
|
||||
ifndef NO_NEWT
|
||||
FLAGS_NEWT=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -lnewt
|
||||
ifneq ($(call try-cc,$(SOURCE_NEWT),$(FLAGS_NEWT),libnewt),y)
|
||||
msg := $(warning newt not found, disables TUI support. Please install newt-devel or libnewt-dev);
|
||||
ifndef NO_SLANG
|
||||
FLAGS_SLANG=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -I/usr/include/slang -lslang
|
||||
ifneq ($(call try-cc,$(SOURCE_SLANG),$(FLAGS_SLANG),libslang),y)
|
||||
msg := $(warning slang not found, disables TUI support. Please install slang-devel or libslang-dev);
|
||||
else
|
||||
# Fedora has /usr/include/slang/slang.h, but ubuntu /usr/include/slang.h
|
||||
BASIC_CFLAGS += -I/usr/include/slang
|
||||
BASIC_CFLAGS += -DNEWT_SUPPORT
|
||||
EXTLIBS += -lnewt -lslang
|
||||
BASIC_CFLAGS += -DSLANG_SUPPORT
|
||||
EXTLIBS += -lslang
|
||||
LIB_OBJS += $(OUTPUT)ui/browser.o
|
||||
LIB_OBJS += $(OUTPUT)ui/browsers/annotate.o
|
||||
LIB_OBJS += $(OUTPUT)ui/browsers/hists.o
|
||||
|
@ -1051,6 +1070,18 @@ $(LIBTRACEEVENT):
|
|||
$(LIBTRACEEVENT)-clean:
|
||||
$(QUIET_SUBDIR0)$(TRACE_EVENT_DIR) $(QUIET_SUBDIR1) O=$(OUTPUT) clean
|
||||
|
||||
# if subdir is set, we've been called from above so target has been built
|
||||
# already
|
||||
$(LIBLK):
|
||||
ifeq ($(subdir),)
|
||||
$(QUIET_SUBDIR0)$(LK_DIR) $(QUIET_SUBDIR1) O=$(OUTPUT) liblk.a
|
||||
endif
|
||||
|
||||
$(LIBLK)-clean:
|
||||
ifeq ($(subdir),)
|
||||
$(QUIET_SUBDIR0)$(LK_DIR) $(QUIET_SUBDIR1) O=$(OUTPUT) clean
|
||||
endif
|
||||
|
||||
help:
|
||||
@echo 'Perf make targets:'
|
||||
@echo ' doc - make *all* documentation (see below)'
|
||||
|
@ -1171,7 +1202,7 @@ $(INSTALL_DOC_TARGETS):
|
|||
|
||||
### Cleaning rules
|
||||
|
||||
clean: $(LIBTRACEEVENT)-clean
|
||||
clean: $(LIBTRACEEVENT)-clean $(LIBLK)-clean
|
||||
$(RM) $(LIB_OBJS) $(BUILTIN_OBJS) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf.o $(LANG_BINDINGS)
|
||||
$(RM) $(ALL_PROGRAMS) perf
|
||||
$(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope*
|
||||
|
@ -1181,6 +1212,6 @@ clean: $(LIBTRACEEVENT)-clean
|
|||
$(RM) $(OUTPUT)util/*-flex*
|
||||
$(python-clean)
|
||||
|
||||
.PHONY: all install clean strip $(LIBTRACEEVENT)
|
||||
.PHONY: all install clean strip $(LIBTRACEEVENT) $(LIBLK)
|
||||
.PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell
|
||||
.PHONY: .FORCE-PERF-VERSION-FILE TAGS tags cscope .FORCE-PERF-CFLAGS
|
||||
|
|
|
@ -8,10 +8,7 @@
|
|||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#ifndef __UCLIBC__
|
||||
#include <libio.h>
|
||||
#endif
|
||||
#include <stddef.h>
|
||||
#include <dwarf-regs.h>
|
||||
|
||||
struct pt_regs_dwarfnum {
|
||||
|
|
|
@ -9,10 +9,7 @@
|
|||
* 2 of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#ifndef __UCLIBC__
|
||||
#include <libio.h>
|
||||
#endif
|
||||
#include <stddef.h>
|
||||
#include <dwarf-regs.h>
|
||||
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include <libio.h>
|
||||
#include <stddef.h>
|
||||
#include <dwarf-regs.h>
|
||||
|
||||
#define NUM_GPRS 16
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include <libio.h>
|
||||
#include <stddef.h>
|
||||
#include <dwarf-regs.h>
|
||||
|
||||
/*
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
* 2 of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <libio.h>
|
||||
#include <stddef.h>
|
||||
#include <dwarf-regs.h>
|
||||
|
||||
#define SPARC_MAX_REGS 96
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include <libio.h>
|
||||
#include <stddef.h>
|
||||
#include <dwarf-regs.h>
|
||||
|
||||
/*
|
||||
|
|
|
@ -63,7 +63,7 @@ static int perf_evsel__add_sample(struct perf_evsel *evsel,
|
|||
return 0;
|
||||
}
|
||||
|
||||
he = __hists__add_entry(&evsel->hists, al, NULL, 1);
|
||||
he = __hists__add_entry(&evsel->hists, al, NULL, 1, 1);
|
||||
if (he == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -109,14 +109,16 @@ static int process_sample_event(struct perf_tool *tool,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int hist_entry__tty_annotate(struct hist_entry *he, int evidx,
|
||||
static int hist_entry__tty_annotate(struct hist_entry *he,
|
||||
struct perf_evsel *evsel,
|
||||
struct perf_annotate *ann)
|
||||
{
|
||||
return symbol__tty_annotate(he->ms.sym, he->ms.map, evidx,
|
||||
return symbol__tty_annotate(he->ms.sym, he->ms.map, evsel,
|
||||
ann->print_line, ann->full_paths, 0, 0);
|
||||
}
|
||||
|
||||
static void hists__find_annotations(struct hists *self, int evidx,
|
||||
static void hists__find_annotations(struct hists *self,
|
||||
struct perf_evsel *evsel,
|
||||
struct perf_annotate *ann)
|
||||
{
|
||||
struct rb_node *nd = rb_first(&self->entries), *next;
|
||||
|
@ -142,14 +144,14 @@ find_next:
|
|||
if (use_browser == 2) {
|
||||
int ret;
|
||||
|
||||
ret = hist_entry__gtk_annotate(he, evidx, NULL);
|
||||
ret = hist_entry__gtk_annotate(he, evsel, 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);
|
||||
key = hist_entry__tui_annotate(he, evsel, NULL);
|
||||
switch (key) {
|
||||
case -1:
|
||||
if (!ann->skip_missing)
|
||||
|
@ -168,7 +170,7 @@ find_next:
|
|||
if (next != NULL)
|
||||
nd = next;
|
||||
} else {
|
||||
hist_entry__tty_annotate(he, evidx, ann);
|
||||
hist_entry__tty_annotate(he, evsel, ann);
|
||||
nd = rb_next(nd);
|
||||
/*
|
||||
* Since we have a hist_entry per IP for the same
|
||||
|
@ -230,7 +232,12 @@ static int __cmd_annotate(struct perf_annotate *ann)
|
|||
total_nr_samples += nr_samples;
|
||||
hists__collapse_resort(hists);
|
||||
hists__output_resort(hists);
|
||||
hists__find_annotations(hists, pos->idx, ann);
|
||||
|
||||
if (symbol_conf.event_group &&
|
||||
!perf_evsel__is_group_leader(pos))
|
||||
continue;
|
||||
|
||||
hists__find_annotations(hists, pos, ann);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -312,6 +319,8 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
"Specify disassembler style (e.g. -M intel for intel syntax)"),
|
||||
OPT_STRING(0, "objdump", &objdump_path, "path",
|
||||
"objdump binary to use for disassembly and annotations"),
|
||||
OPT_BOOLEAN(0, "group", &symbol_conf.event_group,
|
||||
"Show event group information together"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
|
|
|
@ -231,9 +231,10 @@ int perf_diff__formula(struct hist_entry *he, struct hist_entry *pair,
|
|||
}
|
||||
|
||||
static int hists__add_entry(struct hists *self,
|
||||
struct addr_location *al, u64 period)
|
||||
struct addr_location *al, u64 period,
|
||||
u64 weight)
|
||||
{
|
||||
if (__hists__add_entry(self, al, NULL, period) != NULL)
|
||||
if (__hists__add_entry(self, al, NULL, period, weight) != NULL)
|
||||
return 0;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
@ -255,7 +256,7 @@ static int diff__process_sample_event(struct perf_tool *tool __maybe_unused,
|
|||
if (al.filtered)
|
||||
return 0;
|
||||
|
||||
if (hists__add_entry(&evsel->hists, &al, sample->period)) {
|
||||
if (hists__add_entry(&evsel->hists, &al, sample->period, sample->weight)) {
|
||||
pr_warning("problem incrementing symbol period, skipping event\n");
|
||||
return -1;
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
#include "util/parse-options.h"
|
||||
#include "util/trace-event.h"
|
||||
#include "util/debug.h"
|
||||
#include "util/debugfs.h"
|
||||
#include <lk/debugfs.h>
|
||||
#include "util/tool.h"
|
||||
#include "util/stat.h"
|
||||
|
||||
|
|
242
tools/perf/builtin-mem.c
Normal file
242
tools/perf/builtin-mem.c
Normal file
|
@ -0,0 +1,242 @@
|
|||
#include "builtin.h"
|
||||
#include "perf.h"
|
||||
|
||||
#include "util/parse-options.h"
|
||||
#include "util/trace-event.h"
|
||||
#include "util/tool.h"
|
||||
#include "util/session.h"
|
||||
|
||||
#define MEM_OPERATION_LOAD "load"
|
||||
#define MEM_OPERATION_STORE "store"
|
||||
|
||||
static const char *mem_operation = MEM_OPERATION_LOAD;
|
||||
|
||||
struct perf_mem {
|
||||
struct perf_tool tool;
|
||||
char const *input_name;
|
||||
symbol_filter_t annotate_init;
|
||||
bool hide_unresolved;
|
||||
bool dump_raw;
|
||||
const char *cpu_list;
|
||||
DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
|
||||
};
|
||||
|
||||
static const char * const mem_usage[] = {
|
||||
"perf mem [<options>] {record <command> |report}",
|
||||
NULL
|
||||
};
|
||||
|
||||
static int __cmd_record(int argc, const char **argv)
|
||||
{
|
||||
int rec_argc, i = 0, j;
|
||||
const char **rec_argv;
|
||||
char event[64];
|
||||
int ret;
|
||||
|
||||
rec_argc = argc + 4;
|
||||
rec_argv = calloc(rec_argc + 1, sizeof(char *));
|
||||
if (!rec_argv)
|
||||
return -1;
|
||||
|
||||
rec_argv[i++] = strdup("record");
|
||||
if (!strcmp(mem_operation, MEM_OPERATION_LOAD))
|
||||
rec_argv[i++] = strdup("-W");
|
||||
rec_argv[i++] = strdup("-d");
|
||||
rec_argv[i++] = strdup("-e");
|
||||
|
||||
if (strcmp(mem_operation, MEM_OPERATION_LOAD))
|
||||
sprintf(event, "cpu/mem-stores/pp");
|
||||
else
|
||||
sprintf(event, "cpu/mem-loads/pp");
|
||||
|
||||
rec_argv[i++] = strdup(event);
|
||||
for (j = 1; j < argc; j++, i++)
|
||||
rec_argv[i] = argv[j];
|
||||
|
||||
ret = cmd_record(i, rec_argv, NULL);
|
||||
free(rec_argv);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
dump_raw_samples(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct perf_evsel *evsel __maybe_unused,
|
||||
struct machine *machine)
|
||||
{
|
||||
struct perf_mem *mem = container_of(tool, struct perf_mem, tool);
|
||||
struct addr_location al;
|
||||
const char *fmt;
|
||||
|
||||
if (perf_event__preprocess_sample(event, machine, &al, sample,
|
||||
mem->annotate_init) < 0) {
|
||||
fprintf(stderr, "problem processing %d event, skipping it.\n",
|
||||
event->header.type);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (al.filtered || (mem->hide_unresolved && al.sym == NULL))
|
||||
return 0;
|
||||
|
||||
if (al.map != NULL)
|
||||
al.map->dso->hit = 1;
|
||||
|
||||
if (symbol_conf.field_sep) {
|
||||
fmt = "%d%s%d%s0x%"PRIx64"%s0x%"PRIx64"%s%"PRIu64
|
||||
"%s0x%"PRIx64"%s%s:%s\n";
|
||||
} else {
|
||||
fmt = "%5d%s%5d%s0x%016"PRIx64"%s0x016%"PRIx64
|
||||
"%s%5"PRIu64"%s0x%06"PRIx64"%s%s:%s\n";
|
||||
symbol_conf.field_sep = " ";
|
||||
}
|
||||
|
||||
printf(fmt,
|
||||
sample->pid,
|
||||
symbol_conf.field_sep,
|
||||
sample->tid,
|
||||
symbol_conf.field_sep,
|
||||
event->ip.ip,
|
||||
symbol_conf.field_sep,
|
||||
sample->addr,
|
||||
symbol_conf.field_sep,
|
||||
sample->weight,
|
||||
symbol_conf.field_sep,
|
||||
sample->data_src,
|
||||
symbol_conf.field_sep,
|
||||
al.map ? (al.map->dso ? al.map->dso->long_name : "???") : "???",
|
||||
al.sym ? al.sym->name : "???");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_sample_event(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct perf_evsel *evsel,
|
||||
struct machine *machine)
|
||||
{
|
||||
return dump_raw_samples(tool, event, sample, evsel, machine);
|
||||
}
|
||||
|
||||
static int report_raw_events(struct perf_mem *mem)
|
||||
{
|
||||
int err = -EINVAL;
|
||||
int ret;
|
||||
struct perf_session *session = perf_session__new(input_name, O_RDONLY,
|
||||
0, false, &mem->tool);
|
||||
|
||||
if (session == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
if (mem->cpu_list) {
|
||||
ret = perf_session__cpu_bitmap(session, mem->cpu_list,
|
||||
mem->cpu_bitmap);
|
||||
if (ret)
|
||||
goto out_delete;
|
||||
}
|
||||
|
||||
if (symbol__init() < 0)
|
||||
return -1;
|
||||
|
||||
printf("# PID, TID, IP, ADDR, LOCAL WEIGHT, DSRC, SYMBOL\n");
|
||||
|
||||
err = perf_session__process_events(session, &mem->tool);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
|
||||
out_delete:
|
||||
perf_session__delete(session);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int report_events(int argc, const char **argv, struct perf_mem *mem)
|
||||
{
|
||||
const char **rep_argv;
|
||||
int ret, i = 0, j, rep_argc;
|
||||
|
||||
if (mem->dump_raw)
|
||||
return report_raw_events(mem);
|
||||
|
||||
rep_argc = argc + 3;
|
||||
rep_argv = calloc(rep_argc + 1, sizeof(char *));
|
||||
if (!rep_argv)
|
||||
return -1;
|
||||
|
||||
rep_argv[i++] = strdup("report");
|
||||
rep_argv[i++] = strdup("--mem-mode");
|
||||
rep_argv[i++] = strdup("-n"); /* display number of samples */
|
||||
|
||||
/*
|
||||
* there is no weight (cost) associated with stores, so don't print
|
||||
* the column
|
||||
*/
|
||||
if (strcmp(mem_operation, MEM_OPERATION_LOAD))
|
||||
rep_argv[i++] = strdup("--sort=mem,sym,dso,symbol_daddr,"
|
||||
"dso_daddr,tlb,locked");
|
||||
|
||||
for (j = 1; j < argc; j++, i++)
|
||||
rep_argv[i] = argv[j];
|
||||
|
||||
ret = cmd_report(i, rep_argv, NULL);
|
||||
free(rep_argv);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cmd_mem(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
{
|
||||
struct stat st;
|
||||
struct perf_mem mem = {
|
||||
.tool = {
|
||||
.sample = process_sample_event,
|
||||
.mmap = perf_event__process_mmap,
|
||||
.comm = perf_event__process_comm,
|
||||
.lost = perf_event__process_lost,
|
||||
.fork = perf_event__process_fork,
|
||||
.build_id = perf_event__process_build_id,
|
||||
.ordered_samples = true,
|
||||
},
|
||||
.input_name = "perf.data",
|
||||
};
|
||||
const struct option mem_options[] = {
|
||||
OPT_STRING('t', "type", &mem_operation,
|
||||
"type", "memory operations(load/store)"),
|
||||
OPT_BOOLEAN('D', "dump-raw-samples", &mem.dump_raw,
|
||||
"dump raw samples in ASCII"),
|
||||
OPT_BOOLEAN('U', "hide-unresolved", &mem.hide_unresolved,
|
||||
"Only display entries resolved to a symbol"),
|
||||
OPT_STRING('i', "input", &input_name, "file",
|
||||
"input file name"),
|
||||
OPT_STRING('C', "cpu", &mem.cpu_list, "cpu",
|
||||
"list of cpus to profile"),
|
||||
OPT_STRING('x', "field-separator", &symbol_conf.field_sep,
|
||||
"separator",
|
||||
"separator for columns, no spaces will be added"
|
||||
" between columns '.' is reserved."),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
argc = parse_options(argc, argv, mem_options, mem_usage,
|
||||
PARSE_OPT_STOP_AT_NON_OPTION);
|
||||
|
||||
if (!argc || !(strncmp(argv[0], "rec", 3) || mem_operation))
|
||||
usage_with_options(mem_usage, mem_options);
|
||||
|
||||
if (!mem.input_name || !strlen(mem.input_name)) {
|
||||
if (!fstat(STDIN_FILENO, &st) && S_ISFIFO(st.st_mode))
|
||||
mem.input_name = "-";
|
||||
else
|
||||
mem.input_name = "perf.data";
|
||||
}
|
||||
|
||||
if (!strncmp(argv[0], "rec", 3))
|
||||
return __cmd_record(argc, argv);
|
||||
else if (!strncmp(argv[0], "rep", 3))
|
||||
return report_events(argc, argv, &mem);
|
||||
else
|
||||
usage_with_options(mem_usage, mem_options);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -37,7 +37,7 @@
|
|||
#include "util/strfilter.h"
|
||||
#include "util/symbol.h"
|
||||
#include "util/debug.h"
|
||||
#include "util/debugfs.h"
|
||||
#include <lk/debugfs.h>
|
||||
#include "util/parse-options.h"
|
||||
#include "util/probe-finder.h"
|
||||
#include "util/probe-event.h"
|
||||
|
|
|
@ -5,8 +5,6 @@
|
|||
* (or a CPU, or a PID) into the perf.data output file - for
|
||||
* later analysis via perf report.
|
||||
*/
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
|
||||
#include "builtin.h"
|
||||
|
||||
#include "perf.h"
|
||||
|
@ -474,7 +472,9 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
|
|||
}
|
||||
|
||||
if (forks) {
|
||||
err = perf_evlist__prepare_workload(evsel_list, opts, argv);
|
||||
err = perf_evlist__prepare_workload(evsel_list, &opts->target,
|
||||
argv, opts->pipe_output,
|
||||
true);
|
||||
if (err < 0) {
|
||||
pr_err("Couldn't run the workload!\n");
|
||||
goto out_delete_session;
|
||||
|
@ -953,6 +953,8 @@ const struct option record_options[] = {
|
|||
OPT_CALLBACK('j', "branch-filter", &record.opts.branch_stack,
|
||||
"branch filter mask", "branch stack filter modes",
|
||||
parse_branch_stack),
|
||||
OPT_BOOLEAN('W', "weight", &record.opts.sample_weight,
|
||||
"sample by weight (on special events only)"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
|
@ -964,7 +966,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
struct perf_record *rec = &record;
|
||||
char errbuf[BUFSIZ];
|
||||
|
||||
evsel_list = perf_evlist__new(NULL, NULL);
|
||||
evsel_list = perf_evlist__new();
|
||||
if (evsel_list == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -1026,7 +1028,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
ui__error("%s", errbuf);
|
||||
|
||||
err = -saved_errno;
|
||||
goto out_free_fd;
|
||||
goto out_symbol_exit;
|
||||
}
|
||||
|
||||
err = -ENOMEM;
|
||||
|
@ -1057,6 +1059,9 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
}
|
||||
|
||||
err = __cmd_record(&record, argc, argv);
|
||||
|
||||
perf_evlist__munmap(evsel_list);
|
||||
perf_evlist__close(evsel_list);
|
||||
out_free_fd:
|
||||
perf_evlist__delete_maps(evsel_list);
|
||||
out_symbol_exit:
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
#include "util/annotate.h"
|
||||
#include "util/color.h"
|
||||
#include <linux/list.h>
|
||||
#include "util/cache.h"
|
||||
#include <linux/rbtree.h>
|
||||
#include "util/symbol.h"
|
||||
#include "util/callchain.h"
|
||||
|
@ -47,6 +46,7 @@ struct perf_report {
|
|||
bool show_full_info;
|
||||
bool show_threads;
|
||||
bool inverted_callchain;
|
||||
bool mem_mode;
|
||||
struct perf_read_values show_threads_values;
|
||||
const char *pretty_printing_style;
|
||||
symbol_filter_t annotate_init;
|
||||
|
@ -65,6 +65,99 @@ static int perf_report_config(const char *var, const char *value, void *cb)
|
|||
return perf_default_config(var, value, cb);
|
||||
}
|
||||
|
||||
static int perf_report__add_mem_hist_entry(struct perf_tool *tool,
|
||||
struct addr_location *al,
|
||||
struct perf_sample *sample,
|
||||
struct perf_evsel *evsel,
|
||||
struct machine *machine,
|
||||
union perf_event *event)
|
||||
{
|
||||
struct perf_report *rep = container_of(tool, struct perf_report, tool);
|
||||
struct symbol *parent = NULL;
|
||||
u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
|
||||
int err = 0;
|
||||
struct hist_entry *he;
|
||||
struct mem_info *mi, *mx;
|
||||
uint64_t cost;
|
||||
|
||||
if ((sort__has_parent || symbol_conf.use_callchain) &&
|
||||
sample->callchain) {
|
||||
err = machine__resolve_callchain(machine, evsel, al->thread,
|
||||
sample, &parent);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
mi = machine__resolve_mem(machine, al->thread, sample, cpumode);
|
||||
if (!mi)
|
||||
return -ENOMEM;
|
||||
|
||||
if (rep->hide_unresolved && !al->sym)
|
||||
return 0;
|
||||
|
||||
cost = sample->weight;
|
||||
if (!cost)
|
||||
cost = 1;
|
||||
|
||||
/*
|
||||
* must pass period=weight in order to get the correct
|
||||
* sorting from hists__collapse_resort() which is solely
|
||||
* based on periods. We want sorting be done on nr_events * weight
|
||||
* and this is indirectly achieved by passing period=weight here
|
||||
* and the he_stat__add_period() function.
|
||||
*/
|
||||
he = __hists__add_mem_entry(&evsel->hists, al, parent, mi, cost, cost);
|
||||
if (!he)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* In the TUI browser, we are doing integrated annotation,
|
||||
* so we don't allocate the extra space needed because the stdio
|
||||
* code will not use it.
|
||||
*/
|
||||
if (sort__has_sym && he->ms.sym && use_browser > 0) {
|
||||
struct annotation *notes = symbol__annotation(he->ms.sym);
|
||||
|
||||
assert(evsel != NULL);
|
||||
|
||||
if (notes->src == NULL && symbol__alloc_hist(he->ms.sym) < 0)
|
||||
goto out;
|
||||
|
||||
err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (sort__has_sym && he->mem_info->daddr.sym && use_browser > 0) {
|
||||
struct annotation *notes;
|
||||
|
||||
mx = he->mem_info;
|
||||
|
||||
notes = symbol__annotation(mx->daddr.sym);
|
||||
if (notes->src == NULL && symbol__alloc_hist(mx->daddr.sym) < 0)
|
||||
goto out;
|
||||
|
||||
err = symbol__inc_addr_samples(mx->daddr.sym,
|
||||
mx->daddr.map,
|
||||
evsel->idx,
|
||||
mx->daddr.al_addr);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
|
||||
evsel->hists.stats.total_period += cost;
|
||||
hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE);
|
||||
err = 0;
|
||||
|
||||
if (symbol_conf.use_callchain) {
|
||||
err = callchain_append(he->callchain,
|
||||
&callchain_cursor,
|
||||
sample->period);
|
||||
}
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int perf_report__add_branch_hist_entry(struct perf_tool *tool,
|
||||
struct addr_location *al,
|
||||
struct perf_sample *sample,
|
||||
|
@ -99,7 +192,7 @@ static int perf_report__add_branch_hist_entry(struct perf_tool *tool,
|
|||
* and not events sampled. Thus we use a pseudo period of 1.
|
||||
*/
|
||||
he = __hists__add_branch_entry(&evsel->hists, al, parent,
|
||||
&bi[i], 1);
|
||||
&bi[i], 1, 1);
|
||||
if (he) {
|
||||
struct annotation *notes;
|
||||
err = -ENOMEM;
|
||||
|
@ -157,7 +250,8 @@ static int perf_evsel__add_hist_entry(struct perf_evsel *evsel,
|
|||
return err;
|
||||
}
|
||||
|
||||
he = __hists__add_entry(&evsel->hists, al, parent, sample->period);
|
||||
he = __hists__add_entry(&evsel->hists, al, parent, sample->period,
|
||||
sample->weight);
|
||||
if (he == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -169,7 +263,7 @@ static int perf_evsel__add_hist_entry(struct perf_evsel *evsel,
|
|||
return err;
|
||||
}
|
||||
/*
|
||||
* Only in the newt browser we are doing integrated annotation,
|
||||
* Only in the TUI browser we are doing integrated annotation,
|
||||
* so we don't allocated the extra space needed because the stdio
|
||||
* code will not use it.
|
||||
*/
|
||||
|
@ -220,6 +314,12 @@ static int process_sample_event(struct perf_tool *tool,
|
|||
pr_debug("problem adding lbr entry, skipping event\n");
|
||||
return -1;
|
||||
}
|
||||
} else if (rep->mem_mode == 1) {
|
||||
if (perf_report__add_mem_hist_entry(tool, &al, sample,
|
||||
evsel, machine, event)) {
|
||||
pr_debug("problem adding mem entry, skipping event\n");
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
if (al.map != NULL)
|
||||
al.map->dso->hit = 1;
|
||||
|
@ -303,7 +403,8 @@ static void sig_handler(int sig __maybe_unused)
|
|||
session_done = 1;
|
||||
}
|
||||
|
||||
static size_t hists__fprintf_nr_sample_events(struct hists *self,
|
||||
static size_t hists__fprintf_nr_sample_events(struct perf_report *rep,
|
||||
struct hists *self,
|
||||
const char *evname, FILE *fp)
|
||||
{
|
||||
size_t ret;
|
||||
|
@ -314,7 +415,7 @@ static size_t hists__fprintf_nr_sample_events(struct hists *self,
|
|||
char buf[512];
|
||||
size_t size = sizeof(buf);
|
||||
|
||||
if (symbol_conf.event_group && evsel->nr_members > 1) {
|
||||
if (perf_evsel__is_group_event(evsel)) {
|
||||
struct perf_evsel *pos;
|
||||
|
||||
perf_evsel__group_desc(evsel, buf, size);
|
||||
|
@ -331,7 +432,11 @@ static size_t hists__fprintf_nr_sample_events(struct hists *self,
|
|||
if (evname != NULL)
|
||||
ret += fprintf(fp, " of event '%s'", evname);
|
||||
|
||||
ret += fprintf(fp, "\n# Event count (approx.): %" PRIu64, nr_events);
|
||||
if (rep->mem_mode) {
|
||||
ret += fprintf(fp, "\n# Total weight : %" PRIu64, nr_events);
|
||||
ret += fprintf(fp, "\n# Sort order : %s", sort_order);
|
||||
} else
|
||||
ret += fprintf(fp, "\n# Event count (approx.): %" PRIu64, nr_events);
|
||||
return ret + fprintf(fp, "\n#\n");
|
||||
}
|
||||
|
||||
|
@ -349,7 +454,7 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist,
|
|||
!perf_evsel__is_group_leader(pos))
|
||||
continue;
|
||||
|
||||
hists__fprintf_nr_sample_events(hists, evname, stdout);
|
||||
hists__fprintf_nr_sample_events(rep, hists, evname, stdout);
|
||||
hists__fprintf(hists, true, 0, 0, stdout);
|
||||
fprintf(stdout, "\n\n");
|
||||
}
|
||||
|
@ -645,7 +750,9 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
"Use the stdio interface"),
|
||||
OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
|
||||
"sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline,"
|
||||
" dso_to, dso_from, symbol_to, symbol_from, mispredict"),
|
||||
" dso_to, dso_from, symbol_to, symbol_from, mispredict,"
|
||||
" weight, local_weight, mem, symbol_daddr, dso_daddr, tlb, "
|
||||
"snoop, locked"),
|
||||
OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization,
|
||||
"Show sample percentage for different cpu modes"),
|
||||
OPT_STRING('p', "parent", &parent_pattern, "regex",
|
||||
|
@ -693,6 +800,9 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
"use branch records for histogram filling", parse_branch_mode),
|
||||
OPT_STRING(0, "objdump", &objdump_path, "path",
|
||||
"objdump binary to use for disassembly and annotations"),
|
||||
OPT_BOOLEAN(0, "demangle", &symbol_conf.demangle,
|
||||
"Disable symbol demangling"),
|
||||
OPT_BOOLEAN(0, "mem-mode", &report.mem_mode, "mem access profile"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
|
@ -750,12 +860,24 @@ repeat:
|
|||
"dso_to,symbol_to";
|
||||
|
||||
}
|
||||
if (report.mem_mode) {
|
||||
if (sort__branch_mode == 1) {
|
||||
fprintf(stderr, "branch and mem mode incompatible\n");
|
||||
goto error;
|
||||
}
|
||||
/*
|
||||
* if no sort_order is provided, then specify
|
||||
* branch-mode specific order
|
||||
*/
|
||||
if (sort_order == default_sort_order)
|
||||
sort_order = "local_weight,mem,sym,dso,symbol_daddr,dso_daddr,snoop,tlb,locked";
|
||||
}
|
||||
|
||||
if (setup_sorting() < 0)
|
||||
usage_with_options(report_usage, options);
|
||||
|
||||
/*
|
||||
* Only in the newt browser we are doing integrated annotation,
|
||||
* Only in the TUI browser we are doing integrated annotation,
|
||||
* so don't allocate extra space that won't be used in the stdio
|
||||
* implementation.
|
||||
*/
|
||||
|
@ -815,6 +937,14 @@ repeat:
|
|||
sort_entry__setup_elide(&sort_sym_from, symbol_conf.sym_from_list, "sym_from", stdout);
|
||||
sort_entry__setup_elide(&sort_sym_to, symbol_conf.sym_to_list, "sym_to", stdout);
|
||||
} else {
|
||||
if (report.mem_mode) {
|
||||
sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "symbol_daddr", stdout);
|
||||
sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso_daddr", stdout);
|
||||
sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "mem", stdout);
|
||||
sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "local_weight", stdout);
|
||||
sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "tlb", stdout);
|
||||
sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "snoop", stdout);
|
||||
}
|
||||
sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso", stdout);
|
||||
sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list, "symbol", stdout);
|
||||
}
|
||||
|
|
|
@ -1671,7 +1671,6 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
.sample = perf_sched__process_tracepoint_sample,
|
||||
.comm = perf_event__process_comm,
|
||||
.lost = perf_event__process_lost,
|
||||
.exit = perf_event__process_exit,
|
||||
.fork = perf_event__process_fork,
|
||||
.ordered_samples = true,
|
||||
},
|
||||
|
|
|
@ -68,7 +68,7 @@
|
|||
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 void print_aggr(char *prefix);
|
||||
|
||||
static struct perf_evlist *evsel_list;
|
||||
|
||||
|
@ -76,11 +76,17 @@ static struct perf_target target = {
|
|||
.uid = UINT_MAX,
|
||||
};
|
||||
|
||||
enum aggr_mode {
|
||||
AGGR_NONE,
|
||||
AGGR_GLOBAL,
|
||||
AGGR_SOCKET,
|
||||
AGGR_CORE,
|
||||
};
|
||||
|
||||
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 enum aggr_mode aggr_mode = AGGR_GLOBAL;
|
||||
static pid_t child_pid = -1;
|
||||
static bool null_run = false;
|
||||
static int detailed_run = 0;
|
||||
|
@ -94,8 +100,10 @@ static const char *pre_cmd = NULL;
|
|||
static const char *post_cmd = NULL;
|
||||
static bool sync_run = false;
|
||||
static unsigned int interval = 0;
|
||||
static bool forever = false;
|
||||
static struct timespec ref_time;
|
||||
static struct cpu_map *sock_map;
|
||||
static struct cpu_map *aggr_map;
|
||||
static int (*aggr_get_id)(struct cpu_map *m, int cpu);
|
||||
|
||||
static volatile int done = 0;
|
||||
|
||||
|
@ -125,6 +133,11 @@ static inline int perf_evsel__nr_cpus(struct perf_evsel *evsel)
|
|||
return perf_evsel__cpus(evsel)->nr;
|
||||
}
|
||||
|
||||
static void perf_evsel__reset_stat_priv(struct perf_evsel *evsel)
|
||||
{
|
||||
memset(evsel->priv, 0, sizeof(struct perf_stat));
|
||||
}
|
||||
|
||||
static int perf_evsel__alloc_stat_priv(struct perf_evsel *evsel)
|
||||
{
|
||||
evsel->priv = zalloc(sizeof(struct perf_stat));
|
||||
|
@ -160,6 +173,35 @@ static void perf_evsel__free_prev_raw_counts(struct perf_evsel *evsel)
|
|||
evsel->prev_raw_counts = NULL;
|
||||
}
|
||||
|
||||
static void perf_evlist__free_stats(struct perf_evlist *evlist)
|
||||
{
|
||||
struct perf_evsel *evsel;
|
||||
|
||||
list_for_each_entry(evsel, &evlist->entries, node) {
|
||||
perf_evsel__free_stat_priv(evsel);
|
||||
perf_evsel__free_counts(evsel);
|
||||
perf_evsel__free_prev_raw_counts(evsel);
|
||||
}
|
||||
}
|
||||
|
||||
static int perf_evlist__alloc_stats(struct perf_evlist *evlist, bool alloc_raw)
|
||||
{
|
||||
struct perf_evsel *evsel;
|
||||
|
||||
list_for_each_entry(evsel, &evlist->entries, node) {
|
||||
if (perf_evsel__alloc_stat_priv(evsel) < 0 ||
|
||||
perf_evsel__alloc_counts(evsel, perf_evsel__nr_cpus(evsel)) < 0 ||
|
||||
(alloc_raw && perf_evsel__alloc_prev_raw_counts(evsel) < 0))
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_free:
|
||||
perf_evlist__free_stats(evlist);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static struct stats runtime_nsecs_stats[MAX_NR_CPUS];
|
||||
static struct stats runtime_cycles_stats[MAX_NR_CPUS];
|
||||
static struct stats runtime_stalled_cycles_front_stats[MAX_NR_CPUS];
|
||||
|
@ -173,6 +215,29 @@ static struct stats runtime_itlb_cache_stats[MAX_NR_CPUS];
|
|||
static struct stats runtime_dtlb_cache_stats[MAX_NR_CPUS];
|
||||
static struct stats walltime_nsecs_stats;
|
||||
|
||||
static void perf_stat__reset_stats(struct perf_evlist *evlist)
|
||||
{
|
||||
struct perf_evsel *evsel;
|
||||
|
||||
list_for_each_entry(evsel, &evlist->entries, node) {
|
||||
perf_evsel__reset_stat_priv(evsel);
|
||||
perf_evsel__reset_counts(evsel, perf_evsel__nr_cpus(evsel));
|
||||
}
|
||||
|
||||
memset(runtime_nsecs_stats, 0, sizeof(runtime_nsecs_stats));
|
||||
memset(runtime_cycles_stats, 0, sizeof(runtime_cycles_stats));
|
||||
memset(runtime_stalled_cycles_front_stats, 0, sizeof(runtime_stalled_cycles_front_stats));
|
||||
memset(runtime_stalled_cycles_back_stats, 0, sizeof(runtime_stalled_cycles_back_stats));
|
||||
memset(runtime_branches_stats, 0, sizeof(runtime_branches_stats));
|
||||
memset(runtime_cacherefs_stats, 0, sizeof(runtime_cacherefs_stats));
|
||||
memset(runtime_l1_dcache_stats, 0, sizeof(runtime_l1_dcache_stats));
|
||||
memset(runtime_l1_icache_stats, 0, sizeof(runtime_l1_icache_stats));
|
||||
memset(runtime_ll_cache_stats, 0, sizeof(runtime_ll_cache_stats));
|
||||
memset(runtime_itlb_cache_stats, 0, sizeof(runtime_itlb_cache_stats));
|
||||
memset(runtime_dtlb_cache_stats, 0, sizeof(runtime_dtlb_cache_stats));
|
||||
memset(&walltime_nsecs_stats, 0, sizeof(walltime_nsecs_stats));
|
||||
}
|
||||
|
||||
static int create_perf_stat_counter(struct perf_evsel *evsel)
|
||||
{
|
||||
struct perf_event_attr *attr = &evsel->attr;
|
||||
|
@ -249,7 +314,7 @@ static int read_counter_aggr(struct perf_evsel *counter)
|
|||
int i;
|
||||
|
||||
if (__perf_evsel__read(counter, perf_evsel__nr_cpus(counter),
|
||||
evsel_list->threads->nr, scale) < 0)
|
||||
thread_map__nr(evsel_list->threads), scale) < 0)
|
||||
return -1;
|
||||
|
||||
for (i = 0; i < 3; i++)
|
||||
|
@ -297,56 +362,68 @@ static void print_interval(void)
|
|||
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 {
|
||||
if (aggr_mode == AGGR_GLOBAL) {
|
||||
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);
|
||||
}
|
||||
} else {
|
||||
list_for_each_entry(counter, &evsel_list->entries, node) {
|
||||
ps = counter->priv;
|
||||
memset(ps->res_stats, 0, sizeof(ps->res_stats));
|
||||
read_counter(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)
|
||||
switch (aggr_mode) {
|
||||
case AGGR_SOCKET:
|
||||
fprintf(output, "# time socket cpus counts events\n");
|
||||
else if (no_aggr)
|
||||
break;
|
||||
case AGGR_CORE:
|
||||
fprintf(output, "# time core cpus counts events\n");
|
||||
break;
|
||||
case AGGR_NONE:
|
||||
fprintf(output, "# time CPU counts events\n");
|
||||
else
|
||||
break;
|
||||
case AGGR_GLOBAL:
|
||||
default:
|
||||
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) {
|
||||
switch (aggr_mode) {
|
||||
case AGGR_CORE:
|
||||
case AGGR_SOCKET:
|
||||
print_aggr(prefix);
|
||||
break;
|
||||
case AGGR_NONE:
|
||||
list_for_each_entry(counter, &evsel_list->entries, node)
|
||||
print_counter(counter, prefix);
|
||||
} else {
|
||||
break;
|
||||
case AGGR_GLOBAL:
|
||||
default:
|
||||
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)
|
||||
static int __run_perf_stat(int argc, 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;
|
||||
|
@ -356,61 +433,12 @@ static int __run_perf_stat(int argc __maybe_unused, const char **argv)
|
|||
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;
|
||||
}
|
||||
|
||||
if (forks) {
|
||||
if ((child_pid = fork()) < 0)
|
||||
perror("failed to fork");
|
||||
|
||||
if (!child_pid) {
|
||||
close(child_ready_pipe[0]);
|
||||
close(go_pipe[1]);
|
||||
fcntl(go_pipe[0], F_SETFD, FD_CLOEXEC);
|
||||
|
||||
/*
|
||||
* Do a dummy execvp to get the PLT entry resolved,
|
||||
* so we avoid the resolver overhead on the real
|
||||
* execvp call.
|
||||
*/
|
||||
execvp("", (char **)argv);
|
||||
|
||||
/*
|
||||
* Tell the parent we're ready to go
|
||||
*/
|
||||
close(child_ready_pipe[1]);
|
||||
|
||||
/*
|
||||
* Wait until the parent tells us to go.
|
||||
*/
|
||||
if (read(go_pipe[0], &buf, 1) == -1)
|
||||
perror("unable to read pipe");
|
||||
|
||||
execvp(argv[0], (char **)argv);
|
||||
|
||||
perror(argv[0]);
|
||||
exit(-1);
|
||||
if (perf_evlist__prepare_workload(evsel_list, &target, argv,
|
||||
false, false) < 0) {
|
||||
perror("failed to prepare workload");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (perf_target__none(&target))
|
||||
evsel_list->threads->map[0] = child_pid;
|
||||
|
||||
/*
|
||||
* Wait for the child to be ready to exec.
|
||||
*/
|
||||
close(child_ready_pipe[1]);
|
||||
close(go_pipe[0]);
|
||||
if (read(child_ready_pipe[0], &buf, 1) == -1)
|
||||
perror("unable to read pipe");
|
||||
close(child_ready_pipe[0]);
|
||||
}
|
||||
|
||||
if (group)
|
||||
|
@ -457,7 +485,8 @@ static int __run_perf_stat(int argc __maybe_unused, const char **argv)
|
|||
clock_gettime(CLOCK_MONOTONIC, &ref_time);
|
||||
|
||||
if (forks) {
|
||||
close(go_pipe[1]);
|
||||
perf_evlist__start_workload(evsel_list);
|
||||
|
||||
if (interval) {
|
||||
while (!waitpid(child_pid, &status, WNOHANG)) {
|
||||
nanosleep(&ts, NULL);
|
||||
|
@ -479,16 +508,16 @@ static int __run_perf_stat(int argc __maybe_unused, const char **argv)
|
|||
|
||||
update_stats(&walltime_nsecs_stats, t1 - t0);
|
||||
|
||||
if (no_aggr) {
|
||||
list_for_each_entry(counter, &evsel_list->entries, node) {
|
||||
read_counter(counter);
|
||||
perf_evsel__close_fd(counter, perf_evsel__nr_cpus(counter), 1);
|
||||
}
|
||||
} else {
|
||||
if (aggr_mode == AGGR_GLOBAL) {
|
||||
list_for_each_entry(counter, &evsel_list->entries, node) {
|
||||
read_counter_aggr(counter);
|
||||
perf_evsel__close_fd(counter, perf_evsel__nr_cpus(counter),
|
||||
evsel_list->threads->nr);
|
||||
thread_map__nr(evsel_list->threads));
|
||||
}
|
||||
} else {
|
||||
list_for_each_entry(counter, &evsel_list->entries, node) {
|
||||
read_counter(counter);
|
||||
perf_evsel__close_fd(counter, perf_evsel__nr_cpus(counter), 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -542,26 +571,47 @@ 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, int nr, struct perf_evsel *evsel, double avg)
|
||||
static void aggr_printout(struct perf_evsel *evsel, int id, int nr)
|
||||
{
|
||||
double msecs = avg / 1e6;
|
||||
char cpustr[16] = { '\0', };
|
||||
const char *fmt = csv_output ? "%s%.6f%s%s" : "%s%18.6f%s%-25s";
|
||||
|
||||
if (aggr_socket)
|
||||
sprintf(cpustr, "S%*d%s%*d%s",
|
||||
csv_output ? 0 : -5,
|
||||
cpu,
|
||||
switch (aggr_mode) {
|
||||
case AGGR_CORE:
|
||||
fprintf(output, "S%d-C%*d%s%*d%s",
|
||||
cpu_map__id_to_socket(id),
|
||||
csv_output ? 0 : -8,
|
||||
cpu_map__id_to_cpu(id),
|
||||
csv_sep,
|
||||
csv_output ? 0 : 4,
|
||||
nr,
|
||||
csv_sep);
|
||||
else if (no_aggr)
|
||||
sprintf(cpustr, "CPU%*d%s",
|
||||
break;
|
||||
case AGGR_SOCKET:
|
||||
fprintf(output, "S%*d%s%*d%s",
|
||||
csv_output ? 0 : -5,
|
||||
id,
|
||||
csv_sep,
|
||||
csv_output ? 0 : 4,
|
||||
nr,
|
||||
csv_sep);
|
||||
break;
|
||||
case AGGR_NONE:
|
||||
fprintf(output, "CPU%*d%s",
|
||||
csv_output ? 0 : -4,
|
||||
perf_evsel__cpus(evsel)->map[cpu], csv_sep);
|
||||
perf_evsel__cpus(evsel)->map[id], csv_sep);
|
||||
break;
|
||||
case AGGR_GLOBAL:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(output, fmt, cpustr, msecs, csv_sep, perf_evsel__name(evsel));
|
||||
static void nsec_printout(int cpu, int nr, struct perf_evsel *evsel, double avg)
|
||||
{
|
||||
double msecs = avg / 1e6;
|
||||
const char *fmt = csv_output ? "%.6f%s%s" : "%18.6f%s%-25s";
|
||||
|
||||
aggr_printout(evsel, cpu, nr);
|
||||
|
||||
fprintf(output, fmt, msecs, csv_sep, perf_evsel__name(evsel));
|
||||
|
||||
if (evsel->cgrp)
|
||||
fprintf(output, "%s%s", csv_sep, evsel->cgrp->name);
|
||||
|
@ -758,32 +808,21 @@ static void print_ll_cache_misses(int cpu,
|
|||
static void abs_printout(int cpu, int nr, struct perf_evsel *evsel, double avg)
|
||||
{
|
||||
double total, ratio = 0.0;
|
||||
char cpustr[16] = { '\0', };
|
||||
const char *fmt;
|
||||
|
||||
if (csv_output)
|
||||
fmt = "%s%.0f%s%s";
|
||||
fmt = "%.0f%s%s";
|
||||
else if (big_num)
|
||||
fmt = "%s%'18.0f%s%-25s";
|
||||
fmt = "%'18.0f%s%-25s";
|
||||
else
|
||||
fmt = "%s%18.0f%s%-25s";
|
||||
fmt = "%18.0f%s%-25s";
|
||||
|
||||
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);
|
||||
else
|
||||
aggr_printout(evsel, cpu, nr);
|
||||
|
||||
if (aggr_mode == AGGR_GLOBAL)
|
||||
cpu = 0;
|
||||
|
||||
fprintf(output, fmt, cpustr, avg, csv_sep, perf_evsel__name(evsel));
|
||||
fprintf(output, fmt, avg, csv_sep, perf_evsel__name(evsel));
|
||||
|
||||
if (evsel->cgrp)
|
||||
fprintf(output, "%s%s", csv_sep, evsel->cgrp->name);
|
||||
|
@ -882,23 +921,23 @@ static void abs_printout(int cpu, int nr, struct perf_evsel *evsel, double avg)
|
|||
}
|
||||
}
|
||||
|
||||
static void print_aggr_socket(char *prefix)
|
||||
static void print_aggr(char *prefix)
|
||||
{
|
||||
struct perf_evsel *counter;
|
||||
int cpu, s, s2, id, nr;
|
||||
u64 ena, run, val;
|
||||
int cpu, s, s2, sock, nr;
|
||||
|
||||
if (!sock_map)
|
||||
if (!(aggr_map || aggr_get_id))
|
||||
return;
|
||||
|
||||
for (s = 0; s < sock_map->nr; s++) {
|
||||
sock = cpu_map__socket(sock_map, s);
|
||||
for (s = 0; s < aggr_map->nr; s++) {
|
||||
id = aggr_map->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)
|
||||
s2 = aggr_get_id(evsel_list->cpus, cpu);
|
||||
if (s2 != id)
|
||||
continue;
|
||||
val += counter->counts->cpu[cpu].val;
|
||||
ena += counter->counts->cpu[cpu].ena;
|
||||
|
@ -909,18 +948,15 @@ static void print_aggr_socket(char *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,
|
||||
aggr_printout(counter, cpu, nr);
|
||||
|
||||
fprintf(output, "%*s%s%*s",
|
||||
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);
|
||||
|
@ -930,9 +966,9 @@ static void print_aggr_socket(char *prefix)
|
|||
}
|
||||
|
||||
if (nsec_counter(counter))
|
||||
nsec_printout(sock, nr, counter, val);
|
||||
nsec_printout(id, nr, counter, val);
|
||||
else
|
||||
abs_printout(sock, nr, counter, val);
|
||||
abs_printout(id, nr, counter, val);
|
||||
|
||||
if (!csv_output) {
|
||||
print_noise(counter, 1.0);
|
||||
|
@ -1073,14 +1109,21 @@ static void print_stat(int argc, const char **argv)
|
|||
fprintf(output, ":\n\n");
|
||||
}
|
||||
|
||||
if (aggr_socket)
|
||||
print_aggr_socket(NULL);
|
||||
else if (no_aggr) {
|
||||
list_for_each_entry(counter, &evsel_list->entries, node)
|
||||
print_counter(counter, NULL);
|
||||
} else {
|
||||
switch (aggr_mode) {
|
||||
case AGGR_CORE:
|
||||
case AGGR_SOCKET:
|
||||
print_aggr(NULL);
|
||||
break;
|
||||
case AGGR_GLOBAL:
|
||||
list_for_each_entry(counter, &evsel_list->entries, node)
|
||||
print_counter_aggr(counter, NULL);
|
||||
break;
|
||||
case AGGR_NONE:
|
||||
list_for_each_entry(counter, &evsel_list->entries, node)
|
||||
print_counter(counter, NULL);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!csv_output) {
|
||||
|
@ -1126,6 +1169,32 @@ static int stat__set_big_num(const struct option *opt __maybe_unused,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int perf_stat_init_aggr_mode(void)
|
||||
{
|
||||
switch (aggr_mode) {
|
||||
case AGGR_SOCKET:
|
||||
if (cpu_map__build_socket_map(evsel_list->cpus, &aggr_map)) {
|
||||
perror("cannot build socket map");
|
||||
return -1;
|
||||
}
|
||||
aggr_get_id = cpu_map__get_socket;
|
||||
break;
|
||||
case AGGR_CORE:
|
||||
if (cpu_map__build_core_map(evsel_list->cpus, &aggr_map)) {
|
||||
perror("cannot build core map");
|
||||
return -1;
|
||||
}
|
||||
aggr_get_id = cpu_map__get_core;
|
||||
break;
|
||||
case AGGR_NONE:
|
||||
case AGGR_GLOBAL:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Add default attributes, if there were no attributes specified or
|
||||
* if -d/--detailed, -d -d or -d -d -d is used:
|
||||
|
@ -1296,7 +1365,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
OPT_INCR('v', "verbose", &verbose,
|
||||
"be more verbose (show counter open errors, etc)"),
|
||||
OPT_INTEGER('r', "repeat", &run_count,
|
||||
"repeat command and print average + stddev (max: 100)"),
|
||||
"repeat command and print average + stddev (max: 100, forever: 0)"),
|
||||
OPT_BOOLEAN('n', "null", &null_run,
|
||||
"null run - dont start any counters"),
|
||||
OPT_INCR('d', "detailed", &detailed_run,
|
||||
|
@ -1308,7 +1377,8 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
stat__set_big_num),
|
||||
OPT_STRING('C', "cpu", &target.cpu_list, "cpu",
|
||||
"list of cpus to monitor in system-wide"),
|
||||
OPT_BOOLEAN('A', "no-aggr", &no_aggr, "disable CPU count aggregation"),
|
||||
OPT_SET_UINT('A', "no-aggr", &aggr_mode,
|
||||
"disable CPU count aggregation", AGGR_NONE),
|
||||
OPT_STRING('x', "field-separator", &csv_sep, "separator",
|
||||
"print counts with custom separator"),
|
||||
OPT_CALLBACK('G', "cgroup", &evsel_list, "name",
|
||||
|
@ -1323,20 +1393,22 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
"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_SET_UINT(0, "per-socket", &aggr_mode,
|
||||
"aggregate counts per processor socket", AGGR_SOCKET),
|
||||
OPT_SET_UINT(0, "per-core", &aggr_mode,
|
||||
"aggregate counts per physical processor core", AGGR_CORE),
|
||||
OPT_END()
|
||||
};
|
||||
const char * const stat_usage[] = {
|
||||
"perf stat [<options>] [<command>]",
|
||||
NULL
|
||||
};
|
||||
struct perf_evsel *pos;
|
||||
int status = -ENOMEM, run_idx;
|
||||
const char *mode;
|
||||
|
||||
setlocale(LC_ALL, "");
|
||||
|
||||
evsel_list = perf_evlist__new(NULL, NULL);
|
||||
evsel_list = perf_evlist__new();
|
||||
if (evsel_list == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -1399,23 +1471,21 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
|
||||
if (!argc && !perf_target__has_task(&target))
|
||||
usage_with_options(stat_usage, options);
|
||||
if (run_count <= 0)
|
||||
if (run_count < 0) {
|
||||
usage_with_options(stat_usage, options);
|
||||
} else if (run_count == 0) {
|
||||
forever = true;
|
||||
run_count = 1;
|
||||
}
|
||||
|
||||
/* no_aggr, cgroup are for system-wide only */
|
||||
if ((no_aggr || nr_cgroups) && !perf_target__has_cpu(&target)) {
|
||||
if ((aggr_mode != AGGR_GLOBAL || nr_cgroups)
|
||||
&& !perf_target__has_cpu(&target)) {
|
||||
fprintf(stderr, "both cgroup and no-aggregation "
|
||||
"modes only available in system-wide mode\n");
|
||||
|
||||
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;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (add_default_attributes())
|
||||
|
@ -1438,17 +1508,11 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
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;
|
||||
}
|
||||
}
|
||||
if (perf_evlist__alloc_stats(evsel_list, interval))
|
||||
goto out_free_maps;
|
||||
|
||||
if (perf_stat_init_aggr_mode())
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* We dont want to block the signals - that would cause
|
||||
|
@ -1457,28 +1521,30 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
* task, but being ignored by perf stat itself:
|
||||
*/
|
||||
atexit(sig_atexit);
|
||||
signal(SIGINT, skip_signal);
|
||||
if (!forever)
|
||||
signal(SIGINT, skip_signal);
|
||||
signal(SIGCHLD, skip_signal);
|
||||
signal(SIGALRM, skip_signal);
|
||||
signal(SIGABRT, skip_signal);
|
||||
|
||||
status = 0;
|
||||
for (run_idx = 0; run_idx < run_count; run_idx++) {
|
||||
for (run_idx = 0; forever || run_idx < run_count; run_idx++) {
|
||||
if (run_count != 1 && verbose)
|
||||
fprintf(output, "[ perf stat: executing run #%d ... ]\n",
|
||||
run_idx + 1);
|
||||
|
||||
status = run_perf_stat(argc, argv);
|
||||
if (forever && status != -1) {
|
||||
print_stat(argc, argv);
|
||||
perf_stat__reset_stats(evsel_list);
|
||||
}
|
||||
}
|
||||
|
||||
if (status != -1 && !interval)
|
||||
if (!forever && status != -1 && !interval)
|
||||
print_stat(argc, argv);
|
||||
out_free_fd:
|
||||
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__free_stats(evsel_list);
|
||||
out_free_maps:
|
||||
perf_evlist__delete_maps(evsel_list);
|
||||
out:
|
||||
perf_evlist__delete(evsel_list);
|
||||
|
|
|
@ -231,7 +231,7 @@ static void perf_top__show_details(struct perf_top *top)
|
|||
printf("Showing %s for %s\n", perf_evsel__name(top->sym_evsel), symbol->name);
|
||||
printf(" Events Pcnt (>=%d%%)\n", top->sym_pcnt_filter);
|
||||
|
||||
more = symbol__annotate_printf(symbol, he->ms.map, top->sym_evsel->idx,
|
||||
more = symbol__annotate_printf(symbol, he->ms.map, top->sym_evsel,
|
||||
0, top->sym_pcnt_filter, top->print_entries, 4);
|
||||
if (top->zero)
|
||||
symbol__annotate_zero_histogram(symbol, top->sym_evsel->idx);
|
||||
|
@ -251,7 +251,8 @@ static struct hist_entry *perf_evsel__add_hist_entry(struct perf_evsel *evsel,
|
|||
{
|
||||
struct hist_entry *he;
|
||||
|
||||
he = __hists__add_entry(&evsel->hists, al, NULL, sample->period);
|
||||
he = __hists__add_entry(&evsel->hists, al, NULL, sample->period,
|
||||
sample->weight);
|
||||
if (he == NULL)
|
||||
return NULL;
|
||||
|
||||
|
@ -1088,7 +1089,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
OPT_INCR('v', "verbose", &verbose,
|
||||
"be more verbose (show counter open errors, etc)"),
|
||||
OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
|
||||
"sort by key(s): pid, comm, dso, symbol, parent"),
|
||||
"sort by key(s): pid, comm, dso, symbol, parent, weight, local_weight"),
|
||||
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.record_opts,
|
||||
|
@ -1116,7 +1117,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
NULL
|
||||
};
|
||||
|
||||
top.evlist = perf_evlist__new(NULL, NULL);
|
||||
top.evlist = perf_evlist__new();
|
||||
if (top.evlist == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
|
|
|
@ -419,7 +419,7 @@ out_dump:
|
|||
|
||||
static int trace__run(struct trace *trace, int argc, const char **argv)
|
||||
{
|
||||
struct perf_evlist *evlist = perf_evlist__new(NULL, NULL);
|
||||
struct perf_evlist *evlist = perf_evlist__new();
|
||||
struct perf_evsel *evsel;
|
||||
int err = -1, i;
|
||||
unsigned long before;
|
||||
|
@ -452,7 +452,7 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
|
|||
err = trace__symbols_init(trace, evlist);
|
||||
if (err < 0) {
|
||||
printf("Problems initializing symbol libraries!\n");
|
||||
goto out_delete_evlist;
|
||||
goto out_delete_maps;
|
||||
}
|
||||
|
||||
perf_evlist__config(evlist, &trace->opts);
|
||||
|
@ -461,23 +461,24 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
|
|||
signal(SIGINT, sig_handler);
|
||||
|
||||
if (forks) {
|
||||
err = perf_evlist__prepare_workload(evlist, &trace->opts, argv);
|
||||
err = perf_evlist__prepare_workload(evlist, &trace->opts.target,
|
||||
argv, false, false);
|
||||
if (err < 0) {
|
||||
printf("Couldn't run the workload!\n");
|
||||
goto out_delete_evlist;
|
||||
goto out_delete_maps;
|
||||
}
|
||||
}
|
||||
|
||||
err = perf_evlist__open(evlist);
|
||||
if (err < 0) {
|
||||
printf("Couldn't create the events: %s\n", strerror(errno));
|
||||
goto out_delete_evlist;
|
||||
goto out_delete_maps;
|
||||
}
|
||||
|
||||
err = perf_evlist__mmap(evlist, UINT_MAX, false);
|
||||
if (err < 0) {
|
||||
printf("Couldn't mmap the events: %s\n", strerror(errno));
|
||||
goto out_delete_evlist;
|
||||
goto out_close_evlist;
|
||||
}
|
||||
|
||||
perf_evlist__enable(evlist);
|
||||
|
@ -526,13 +527,6 @@ again:
|
|||
continue;
|
||||
}
|
||||
|
||||
if (sample.raw_data == NULL) {
|
||||
printf("%s sample with no payload for tid: %d, cpu %d, raw_size=%d, skipping...\n",
|
||||
perf_evsel__name(evsel), sample.tid,
|
||||
sample.cpu, sample.raw_size);
|
||||
continue;
|
||||
}
|
||||
|
||||
handler = evsel->handler.func;
|
||||
handler(trace, evsel, &sample);
|
||||
}
|
||||
|
@ -540,7 +534,7 @@ again:
|
|||
|
||||
if (trace->nr_events == before) {
|
||||
if (done)
|
||||
goto out_delete_evlist;
|
||||
goto out_unmap_evlist;
|
||||
|
||||
poll(evlist->pollfd, evlist->nr_fds, -1);
|
||||
}
|
||||
|
@ -550,6 +544,12 @@ again:
|
|||
|
||||
goto again;
|
||||
|
||||
out_unmap_evlist:
|
||||
perf_evlist__munmap(evlist);
|
||||
out_close_evlist:
|
||||
perf_evlist__close(evlist);
|
||||
out_delete_maps:
|
||||
perf_evlist__delete_maps(evlist);
|
||||
out_delete_evlist:
|
||||
perf_evlist__delete(evlist);
|
||||
out:
|
||||
|
|
|
@ -36,6 +36,7 @@ extern int cmd_kvm(int argc, const char **argv, const char *prefix);
|
|||
extern int cmd_test(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_trace(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_inject(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_mem(int argc, const char **argv, const char *prefix);
|
||||
|
||||
extern int find_scripts(char **scripts_array, char **scripts_path_array);
|
||||
#endif
|
||||
|
|
|
@ -10,17 +10,18 @@ perf-buildid-list mainporcelain common
|
|||
perf-diff mainporcelain common
|
||||
perf-evlist mainporcelain common
|
||||
perf-inject mainporcelain common
|
||||
perf-kmem mainporcelain common
|
||||
perf-kvm mainporcelain common
|
||||
perf-list mainporcelain common
|
||||
perf-sched mainporcelain common
|
||||
perf-lock mainporcelain common
|
||||
perf-mem mainporcelain common
|
||||
perf-probe mainporcelain full
|
||||
perf-record mainporcelain common
|
||||
perf-report mainporcelain common
|
||||
perf-sched mainporcelain common
|
||||
perf-script mainporcelain common
|
||||
perf-stat mainporcelain common
|
||||
perf-test mainporcelain common
|
||||
perf-timechart mainporcelain common
|
||||
perf-top mainporcelain common
|
||||
perf-trace mainporcelain common
|
||||
perf-script mainporcelain common
|
||||
perf-probe mainporcelain full
|
||||
perf-kmem mainporcelain common
|
||||
perf-lock mainporcelain common
|
||||
perf-kvm mainporcelain common
|
||||
perf-test mainporcelain common
|
||||
|
|
|
@ -61,15 +61,13 @@ int main(void)
|
|||
}
|
||||
endef
|
||||
|
||||
ifndef NO_NEWT
|
||||
define SOURCE_NEWT
|
||||
#include <newt.h>
|
||||
ifndef NO_SLANG
|
||||
define SOURCE_SLANG
|
||||
#include <slang.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
newtInit();
|
||||
newtCls();
|
||||
return newtFinished();
|
||||
return SLsmg_init_smg();
|
||||
}
|
||||
endef
|
||||
endif
|
||||
|
@ -235,4 +233,4 @@ int main(void)
|
|||
numa_available();
|
||||
return 0;
|
||||
}
|
||||
endef
|
||||
endef
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
#include "util/quote.h"
|
||||
#include "util/run-command.h"
|
||||
#include "util/parse-events.h"
|
||||
#include "util/debugfs.h"
|
||||
#include <lk/debugfs.h>
|
||||
#include <pthread.h>
|
||||
|
||||
const char perf_usage_string[] =
|
||||
|
@ -60,6 +60,7 @@ static struct cmd_struct commands[] = {
|
|||
{ "trace", cmd_trace, 0 },
|
||||
#endif
|
||||
{ "inject", cmd_inject, 0 },
|
||||
{ "mem", cmd_mem, 0 },
|
||||
};
|
||||
|
||||
struct pager_config {
|
||||
|
@ -193,13 +194,13 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
|
|||
fprintf(stderr, "No directory given for --debugfs-dir.\n");
|
||||
usage(perf_usage_string);
|
||||
}
|
||||
debugfs_set_path((*argv)[1]);
|
||||
perf_debugfs_set_path((*argv)[1]);
|
||||
if (envchanged)
|
||||
*envchanged = 1;
|
||||
(*argv)++;
|
||||
(*argc)--;
|
||||
} else if (!prefixcmp(cmd, CMD_DEBUGFS_DIR)) {
|
||||
debugfs_set_path(cmd + strlen(CMD_DEBUGFS_DIR));
|
||||
perf_debugfs_set_path(cmd + strlen(CMD_DEBUGFS_DIR));
|
||||
fprintf(stderr, "dir: %s\n", debugfs_mountpoint);
|
||||
if (envchanged)
|
||||
*envchanged = 1;
|
||||
|
@ -461,7 +462,7 @@ int main(int argc, const char **argv)
|
|||
if (!cmd)
|
||||
cmd = "perf-help";
|
||||
/* get debugfs mount point from /proc/mounts */
|
||||
debugfs_mount(NULL);
|
||||
perf_debugfs_mount(NULL);
|
||||
/*
|
||||
* "perf-xxxx" is the same as "perf xxxx", but we obviously:
|
||||
*
|
||||
|
@ -517,9 +518,8 @@ int main(int argc, const char **argv)
|
|||
|
||||
while (1) {
|
||||
static int done_help;
|
||||
static int was_alias;
|
||||
int was_alias = run_argv(&argc, &argv);
|
||||
|
||||
was_alias = run_argv(&argc, &argv);
|
||||
if (errno != ENOENT)
|
||||
break;
|
||||
|
||||
|
|
|
@ -218,6 +218,7 @@ struct perf_record_opts {
|
|||
bool pipe_output;
|
||||
bool raw_samples;
|
||||
bool sample_address;
|
||||
bool sample_weight;
|
||||
bool sample_time;
|
||||
bool period;
|
||||
unsigned int freq;
|
||||
|
|
|
@ -147,10 +147,15 @@ void test_attr__open(struct perf_event_attr *attr, pid_t pid, int cpu,
|
|||
|
||||
static int run_dir(const char *d, const char *perf)
|
||||
{
|
||||
char v[] = "-vvvvv";
|
||||
int vcnt = min(verbose, (int) sizeof(v) - 1);
|
||||
char cmd[3*PATH_MAX];
|
||||
|
||||
snprintf(cmd, 3*PATH_MAX, PYTHON " %s/attr.py -d %s/attr/ -p %s %s",
|
||||
d, d, perf, verbose ? "-v" : "");
|
||||
if (verbose)
|
||||
vcnt++;
|
||||
|
||||
snprintf(cmd, 3*PATH_MAX, PYTHON " %s/attr.py -d %s/attr/ -p %s %.*s",
|
||||
d, d, perf, vcnt, v);
|
||||
|
||||
return system(cmd);
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ class Unsup(Exception):
|
|||
|
||||
class Event(dict):
|
||||
terms = [
|
||||
'cpu',
|
||||
'flags',
|
||||
'type',
|
||||
'size',
|
||||
|
@ -121,7 +122,7 @@ class Test(object):
|
|||
parser = ConfigParser.SafeConfigParser()
|
||||
parser.read(path)
|
||||
|
||||
log.debug("running '%s'" % path)
|
||||
log.warning("running '%s'" % path)
|
||||
|
||||
self.path = path
|
||||
self.test_dir = options.test_dir
|
||||
|
@ -172,7 +173,7 @@ class Test(object):
|
|||
self.perf, self.command, tempdir, self.args)
|
||||
ret = os.WEXITSTATUS(os.system(cmd))
|
||||
|
||||
log.warning(" running '%s' ret %d " % (cmd, ret))
|
||||
log.info(" '%s' ret %d " % (cmd, ret))
|
||||
|
||||
if ret != int(self.ret):
|
||||
raise Unsup(self)
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
fd=1
|
||||
group_fd=-1
|
||||
flags=0
|
||||
cpu=*
|
||||
type=0|1
|
||||
size=96
|
||||
config=0
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
fd=1
|
||||
group_fd=-1
|
||||
flags=0
|
||||
cpu=*
|
||||
type=0
|
||||
size=96
|
||||
config=0
|
||||
|
|
13
tools/perf/tests/attr/test-record-C0
Normal file
13
tools/perf/tests/attr/test-record-C0
Normal file
|
@ -0,0 +1,13 @@
|
|||
[config]
|
||||
command = record
|
||||
args = -C 0 kill >/dev/null 2>&1
|
||||
|
||||
[event:base-record]
|
||||
cpu=0
|
||||
|
||||
# no enable on exec for CPU attached
|
||||
enable_on_exec=0
|
||||
|
||||
# PERF_SAMPLE_IP | PERF_SAMPLE_TID PERF_SAMPLE_TIME | # PERF_SAMPLE_PERIOD
|
||||
# + PERF_SAMPLE_CPU added by -C 0
|
||||
sample_type=391
|
9
tools/perf/tests/attr/test-stat-C0
Normal file
9
tools/perf/tests/attr/test-stat-C0
Normal file
|
@ -0,0 +1,9 @@
|
|||
[config]
|
||||
command = stat
|
||||
args = -e cycles -C 0 kill >/dev/null 2>&1
|
||||
ret = 1
|
||||
|
||||
[event:base-stat]
|
||||
# events are enabled by default when attached to cpu
|
||||
disabled=0
|
||||
enable_on_exec=0
|
186
tools/perf/tests/bp_signal.c
Normal file
186
tools/perf/tests/bp_signal.c
Normal file
|
@ -0,0 +1,186 @@
|
|||
/*
|
||||
* Inspired by breakpoint overflow test done by
|
||||
* Vince Weaver <vincent.weaver@maine.edu> for perf_event_tests
|
||||
* (git://github.com/deater/perf_event_tests)
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <time.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <sys/mman.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/hw_breakpoint.h>
|
||||
|
||||
#include "tests.h"
|
||||
#include "debug.h"
|
||||
#include "perf.h"
|
||||
|
||||
static int fd1;
|
||||
static int fd2;
|
||||
static int overflows;
|
||||
|
||||
__attribute__ ((noinline))
|
||||
static int test_function(void)
|
||||
{
|
||||
return time(NULL);
|
||||
}
|
||||
|
||||
static void sig_handler(int signum __maybe_unused,
|
||||
siginfo_t *oh __maybe_unused,
|
||||
void *uc __maybe_unused)
|
||||
{
|
||||
overflows++;
|
||||
|
||||
if (overflows > 10) {
|
||||
/*
|
||||
* This should be executed only once during
|
||||
* this test, if we are here for the 10th
|
||||
* time, consider this the recursive issue.
|
||||
*
|
||||
* We can get out of here by disable events,
|
||||
* so no new SIGIO is delivered.
|
||||
*/
|
||||
ioctl(fd1, PERF_EVENT_IOC_DISABLE, 0);
|
||||
ioctl(fd2, PERF_EVENT_IOC_DISABLE, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static int bp_event(void *fn, int setup_signal)
|
||||
{
|
||||
struct perf_event_attr pe;
|
||||
int fd;
|
||||
|
||||
memset(&pe, 0, sizeof(struct perf_event_attr));
|
||||
pe.type = PERF_TYPE_BREAKPOINT;
|
||||
pe.size = sizeof(struct perf_event_attr);
|
||||
|
||||
pe.config = 0;
|
||||
pe.bp_type = HW_BREAKPOINT_X;
|
||||
pe.bp_addr = (unsigned long) fn;
|
||||
pe.bp_len = sizeof(long);
|
||||
|
||||
pe.sample_period = 1;
|
||||
pe.sample_type = PERF_SAMPLE_IP;
|
||||
pe.wakeup_events = 1;
|
||||
|
||||
pe.disabled = 1;
|
||||
pe.exclude_kernel = 1;
|
||||
pe.exclude_hv = 1;
|
||||
|
||||
fd = sys_perf_event_open(&pe, 0, -1, -1, 0);
|
||||
if (fd < 0) {
|
||||
pr_debug("failed opening event %llx\n", pe.config);
|
||||
return TEST_FAIL;
|
||||
}
|
||||
|
||||
if (setup_signal) {
|
||||
fcntl(fd, F_SETFL, O_RDWR|O_NONBLOCK|O_ASYNC);
|
||||
fcntl(fd, F_SETSIG, SIGIO);
|
||||
fcntl(fd, F_SETOWN, getpid());
|
||||
}
|
||||
|
||||
ioctl(fd, PERF_EVENT_IOC_RESET, 0);
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static long long bp_count(int fd)
|
||||
{
|
||||
long long count;
|
||||
int ret;
|
||||
|
||||
ret = read(fd, &count, sizeof(long long));
|
||||
if (ret != sizeof(long long)) {
|
||||
pr_debug("failed to read: %d\n", ret);
|
||||
return TEST_FAIL;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int test__bp_signal(void)
|
||||
{
|
||||
struct sigaction sa;
|
||||
long long count1, count2;
|
||||
|
||||
/* setup SIGIO signal handler */
|
||||
memset(&sa, 0, sizeof(struct sigaction));
|
||||
sa.sa_sigaction = (void *) sig_handler;
|
||||
sa.sa_flags = SA_SIGINFO;
|
||||
|
||||
if (sigaction(SIGIO, &sa, NULL) < 0) {
|
||||
pr_debug("failed setting up signal handler\n");
|
||||
return TEST_FAIL;
|
||||
}
|
||||
|
||||
/*
|
||||
* We create following events:
|
||||
*
|
||||
* fd1 - breakpoint event on test_function with SIGIO
|
||||
* signal configured. We should get signal
|
||||
* notification each time the breakpoint is hit
|
||||
*
|
||||
* fd2 - breakpoint event on sig_handler without SIGIO
|
||||
* configured.
|
||||
*
|
||||
* Following processing should happen:
|
||||
* - execute test_function
|
||||
* - fd1 event breakpoint hit -> count1 == 1
|
||||
* - SIGIO is delivered -> overflows == 1
|
||||
* - fd2 event breakpoint hit -> count2 == 1
|
||||
*
|
||||
* The test case check following error conditions:
|
||||
* - we get stuck in signal handler because of debug
|
||||
* exception being triggered receursively due to
|
||||
* the wrong RF EFLAG management
|
||||
*
|
||||
* - we never trigger the sig_handler breakpoint due
|
||||
* to the rong RF EFLAG management
|
||||
*
|
||||
*/
|
||||
|
||||
fd1 = bp_event(test_function, 1);
|
||||
fd2 = bp_event(sig_handler, 0);
|
||||
|
||||
ioctl(fd1, PERF_EVENT_IOC_ENABLE, 0);
|
||||
ioctl(fd2, PERF_EVENT_IOC_ENABLE, 0);
|
||||
|
||||
/*
|
||||
* Kick off the test by trigering 'fd1'
|
||||
* breakpoint.
|
||||
*/
|
||||
test_function();
|
||||
|
||||
ioctl(fd1, PERF_EVENT_IOC_DISABLE, 0);
|
||||
ioctl(fd2, PERF_EVENT_IOC_DISABLE, 0);
|
||||
|
||||
count1 = bp_count(fd1);
|
||||
count2 = bp_count(fd2);
|
||||
|
||||
close(fd1);
|
||||
close(fd2);
|
||||
|
||||
pr_debug("count1 %lld, count2 %lld, overflow %d\n",
|
||||
count1, count2, overflows);
|
||||
|
||||
if (count1 != 1) {
|
||||
if (count1 == 11)
|
||||
pr_debug("failed: RF EFLAG recursion issue detected\n");
|
||||
else
|
||||
pr_debug("failed: wrong count for bp1%lld\n", count1);
|
||||
}
|
||||
|
||||
if (overflows != 1)
|
||||
pr_debug("failed: wrong overflow hit\n");
|
||||
|
||||
if (count2 != 1)
|
||||
pr_debug("failed: wrong count for bp2\n");
|
||||
|
||||
return count1 == 1 && overflows == 1 && count2 == 1 ?
|
||||
TEST_OK : TEST_FAIL;
|
||||
}
|
126
tools/perf/tests/bp_signal_overflow.c
Normal file
126
tools/perf/tests/bp_signal_overflow.c
Normal file
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
* Originally done by Vince Weaver <vincent.weaver@maine.edu> for
|
||||
* perf_event_tests (git://github.com/deater/perf_event_tests)
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <time.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <sys/mman.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/hw_breakpoint.h>
|
||||
|
||||
#include "tests.h"
|
||||
#include "debug.h"
|
||||
#include "perf.h"
|
||||
|
||||
static int overflows;
|
||||
|
||||
__attribute__ ((noinline))
|
||||
static int test_function(void)
|
||||
{
|
||||
return time(NULL);
|
||||
}
|
||||
|
||||
static void sig_handler(int signum __maybe_unused,
|
||||
siginfo_t *oh __maybe_unused,
|
||||
void *uc __maybe_unused)
|
||||
{
|
||||
overflows++;
|
||||
}
|
||||
|
||||
static long long bp_count(int fd)
|
||||
{
|
||||
long long count;
|
||||
int ret;
|
||||
|
||||
ret = read(fd, &count, sizeof(long long));
|
||||
if (ret != sizeof(long long)) {
|
||||
pr_debug("failed to read: %d\n", ret);
|
||||
return TEST_FAIL;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
#define EXECUTIONS 10000
|
||||
#define THRESHOLD 100
|
||||
|
||||
int test__bp_signal_overflow(void)
|
||||
{
|
||||
struct perf_event_attr pe;
|
||||
struct sigaction sa;
|
||||
long long count;
|
||||
int fd, i, fails = 0;
|
||||
|
||||
/* setup SIGIO signal handler */
|
||||
memset(&sa, 0, sizeof(struct sigaction));
|
||||
sa.sa_sigaction = (void *) sig_handler;
|
||||
sa.sa_flags = SA_SIGINFO;
|
||||
|
||||
if (sigaction(SIGIO, &sa, NULL) < 0) {
|
||||
pr_debug("failed setting up signal handler\n");
|
||||
return TEST_FAIL;
|
||||
}
|
||||
|
||||
memset(&pe, 0, sizeof(struct perf_event_attr));
|
||||
pe.type = PERF_TYPE_BREAKPOINT;
|
||||
pe.size = sizeof(struct perf_event_attr);
|
||||
|
||||
pe.config = 0;
|
||||
pe.bp_type = HW_BREAKPOINT_X;
|
||||
pe.bp_addr = (unsigned long) test_function;
|
||||
pe.bp_len = sizeof(long);
|
||||
|
||||
pe.sample_period = THRESHOLD;
|
||||
pe.sample_type = PERF_SAMPLE_IP;
|
||||
pe.wakeup_events = 1;
|
||||
|
||||
pe.disabled = 1;
|
||||
pe.exclude_kernel = 1;
|
||||
pe.exclude_hv = 1;
|
||||
|
||||
fd = sys_perf_event_open(&pe, 0, -1, -1, 0);
|
||||
if (fd < 0) {
|
||||
pr_debug("failed opening event %llx\n", pe.config);
|
||||
return TEST_FAIL;
|
||||
}
|
||||
|
||||
fcntl(fd, F_SETFL, O_RDWR|O_NONBLOCK|O_ASYNC);
|
||||
fcntl(fd, F_SETSIG, SIGIO);
|
||||
fcntl(fd, F_SETOWN, getpid());
|
||||
|
||||
ioctl(fd, PERF_EVENT_IOC_RESET, 0);
|
||||
ioctl(fd, PERF_EVENT_IOC_ENABLE, 0);
|
||||
|
||||
for (i = 0; i < EXECUTIONS; i++)
|
||||
test_function();
|
||||
|
||||
ioctl(fd, PERF_EVENT_IOC_DISABLE, 0);
|
||||
|
||||
count = bp_count(fd);
|
||||
|
||||
close(fd);
|
||||
|
||||
pr_debug("count %lld, overflow %d\n",
|
||||
count, overflows);
|
||||
|
||||
if (count != EXECUTIONS) {
|
||||
pr_debug("\tWrong number of executions %lld != %d\n",
|
||||
count, EXECUTIONS);
|
||||
fails++;
|
||||
}
|
||||
|
||||
if (overflows != EXECUTIONS / THRESHOLD) {
|
||||
pr_debug("\tWrong number of overflows %d != %d\n",
|
||||
overflows, EXECUTIONS / THRESHOLD);
|
||||
fails++;
|
||||
}
|
||||
|
||||
return fails ? TEST_FAIL : TEST_OK;
|
||||
}
|
|
@ -77,6 +77,22 @@ static struct test {
|
|||
.desc = "Try 'use perf' in python, checking link problems",
|
||||
.func = test__python_use,
|
||||
},
|
||||
{
|
||||
.desc = "Test breakpoint overflow signal handler",
|
||||
.func = test__bp_signal,
|
||||
},
|
||||
{
|
||||
.desc = "Test breakpoint overflow sampling",
|
||||
.func = test__bp_signal_overflow,
|
||||
},
|
||||
{
|
||||
.desc = "Test number of exit event of a simple workload",
|
||||
.func = test__task_exit,
|
||||
},
|
||||
{
|
||||
.desc = "Test software clock events have valid period values",
|
||||
.func = test__sw_clock_freq,
|
||||
},
|
||||
{
|
||||
.func = NULL,
|
||||
},
|
||||
|
|
|
@ -8,7 +8,7 @@ static int perf_evsel__roundtrip_cache_name_test(void)
|
|||
char name[128];
|
||||
int type, op, err = 0, ret = 0, i, idx;
|
||||
struct perf_evsel *evsel;
|
||||
struct perf_evlist *evlist = perf_evlist__new(NULL, NULL);
|
||||
struct perf_evlist *evlist = perf_evlist__new();
|
||||
|
||||
if (evlist == NULL)
|
||||
return -ENOMEM;
|
||||
|
@ -64,7 +64,7 @@ static int __perf_evsel__name_array_test(const char *names[], int nr_names)
|
|||
{
|
||||
int i, err;
|
||||
struct perf_evsel *evsel;
|
||||
struct perf_evlist *evlist = perf_evlist__new(NULL, NULL);
|
||||
struct perf_evlist *evlist = perf_evlist__new();
|
||||
|
||||
if (evlist == NULL)
|
||||
return -ENOMEM;
|
||||
|
|
|
@ -223,7 +223,7 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine)
|
|||
&sample, 0) < 0)
|
||||
goto out;
|
||||
|
||||
he = __hists__add_entry(&evsel->hists, &al, NULL, 1);
|
||||
he = __hists__add_entry(&evsel->hists, &al, NULL, 1, 1);
|
||||
if (he == NULL)
|
||||
goto out;
|
||||
|
||||
|
@ -247,7 +247,7 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine)
|
|||
&sample, 0) < 0)
|
||||
goto out;
|
||||
|
||||
he = __hists__add_entry(&evsel->hists, &al, NULL, 1);
|
||||
he = __hists__add_entry(&evsel->hists, &al, NULL, 1, 1);
|
||||
if (he == NULL)
|
||||
goto out;
|
||||
|
||||
|
@ -436,7 +436,7 @@ int test__hists_link(void)
|
|||
struct machines machines;
|
||||
struct machine *machine = NULL;
|
||||
struct perf_evsel *evsel, *first;
|
||||
struct perf_evlist *evlist = perf_evlist__new(NULL, NULL);
|
||||
struct perf_evlist *evlist = perf_evlist__new();
|
||||
|
||||
if (evlist == NULL)
|
||||
return -ENOMEM;
|
||||
|
|
|
@ -53,12 +53,14 @@ int test__basic_mmap(void)
|
|||
goto out_free_cpus;
|
||||
}
|
||||
|
||||
evlist = perf_evlist__new(cpus, threads);
|
||||
evlist = perf_evlist__new();
|
||||
if (evlist == NULL) {
|
||||
pr_debug("perf_evlist__new\n");
|
||||
goto out_free_cpus;
|
||||
}
|
||||
|
||||
perf_evlist__set_maps(evlist, cpus, threads);
|
||||
|
||||
for (i = 0; i < nsyscalls; ++i) {
|
||||
char name[64];
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ int test__syscall_open_tp_fields(void)
|
|||
};
|
||||
const char *filename = "/etc/passwd";
|
||||
int flags = O_RDONLY | O_DIRECTORY;
|
||||
struct perf_evlist *evlist = perf_evlist__new(NULL, NULL);
|
||||
struct perf_evlist *evlist = perf_evlist__new();
|
||||
struct perf_evsel *evsel;
|
||||
int err = -1, i, nr_events = 0, nr_polls = 0;
|
||||
|
||||
|
@ -48,13 +48,13 @@ int test__syscall_open_tp_fields(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;
|
||||
}
|
||||
|
||||
err = perf_evlist__mmap(evlist, UINT_MAX, false);
|
||||
if (err < 0) {
|
||||
pr_debug("perf_evlist__mmap: %s\n", strerror(errno));
|
||||
goto out_delete_evlist;
|
||||
goto out_close_evlist;
|
||||
}
|
||||
|
||||
perf_evlist__enable(evlist);
|
||||
|
@ -110,6 +110,10 @@ out_ok:
|
|||
err = 0;
|
||||
out_munmap:
|
||||
perf_evlist__munmap(evlist);
|
||||
out_close_evlist:
|
||||
perf_evlist__close(evlist);
|
||||
out_delete_maps:
|
||||
perf_evlist__delete_maps(evlist);
|
||||
out_delete_evlist:
|
||||
perf_evlist__delete(evlist);
|
||||
out:
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#include "evsel.h"
|
||||
#include "evlist.h"
|
||||
#include "sysfs.h"
|
||||
#include "debugfs.h"
|
||||
#include <lk/debugfs.h>
|
||||
#include "tests.h"
|
||||
#include <linux/hw_breakpoint.h>
|
||||
|
||||
|
@ -1218,7 +1218,7 @@ static int test_event(struct evlist_test *e)
|
|||
struct perf_evlist *evlist;
|
||||
int ret;
|
||||
|
||||
evlist = perf_evlist__new(NULL, NULL);
|
||||
evlist = perf_evlist__new();
|
||||
if (evlist == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ int test__PERF_RECORD(void)
|
|||
};
|
||||
cpu_set_t cpu_mask;
|
||||
size_t cpu_mask_size = sizeof(cpu_mask);
|
||||
struct perf_evlist *evlist = perf_evlist__new(NULL, NULL);
|
||||
struct perf_evlist *evlist = perf_evlist__new();
|
||||
struct perf_evsel *evsel;
|
||||
struct perf_sample sample;
|
||||
const char *cmd = "sleep";
|
||||
|
@ -93,7 +93,8 @@ int test__PERF_RECORD(void)
|
|||
* so that we have time to open the evlist (calling sys_perf_event_open
|
||||
* on all the fds) and then mmap them.
|
||||
*/
|
||||
err = perf_evlist__prepare_workload(evlist, &opts, argv);
|
||||
err = perf_evlist__prepare_workload(evlist, &opts.target, argv,
|
||||
false, false);
|
||||
if (err < 0) {
|
||||
pr_debug("Couldn't run the workload!\n");
|
||||
goto out_delete_maps;
|
||||
|
@ -142,7 +143,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_maps;
|
||||
goto out_close_evlist;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -305,6 +306,8 @@ found_exit:
|
|||
}
|
||||
out_err:
|
||||
perf_evlist__munmap(evlist);
|
||||
out_close_evlist:
|
||||
perf_evlist__close(evlist);
|
||||
out_delete_maps:
|
||||
perf_evlist__delete_maps(evlist);
|
||||
out_delete_evlist:
|
||||
|
|
119
tools/perf/tests/sw-clock.c
Normal file
119
tools/perf/tests/sw-clock.c
Normal file
|
@ -0,0 +1,119 @@
|
|||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "tests.h"
|
||||
#include "util/evsel.h"
|
||||
#include "util/evlist.h"
|
||||
#include "util/cpumap.h"
|
||||
#include "util/thread_map.h"
|
||||
|
||||
#define NR_LOOPS 1000000
|
||||
|
||||
/*
|
||||
* This test will open software clock events (cpu-clock, task-clock)
|
||||
* then check their frequency -> period conversion has no artifact of
|
||||
* setting period to 1 forcefully.
|
||||
*/
|
||||
static int __test__sw_clock_freq(enum perf_sw_ids clock_id)
|
||||
{
|
||||
int i, err = -1;
|
||||
volatile int tmp = 0;
|
||||
u64 total_periods = 0;
|
||||
int nr_samples = 0;
|
||||
union perf_event *event;
|
||||
struct perf_evsel *evsel;
|
||||
struct perf_evlist *evlist;
|
||||
struct perf_event_attr attr = {
|
||||
.type = PERF_TYPE_SOFTWARE,
|
||||
.config = clock_id,
|
||||
.sample_type = PERF_SAMPLE_PERIOD,
|
||||
.exclude_kernel = 1,
|
||||
.disabled = 1,
|
||||
.freq = 1,
|
||||
};
|
||||
|
||||
attr.sample_freq = 10000;
|
||||
|
||||
evlist = perf_evlist__new();
|
||||
if (evlist == NULL) {
|
||||
pr_debug("perf_evlist__new\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
evsel = perf_evsel__new(&attr, 0);
|
||||
if (evsel == NULL) {
|
||||
pr_debug("perf_evsel__new\n");
|
||||
goto out_free_evlist;
|
||||
}
|
||||
perf_evlist__add(evlist, evsel);
|
||||
|
||||
evlist->cpus = cpu_map__dummy_new();
|
||||
evlist->threads = thread_map__new_by_tid(getpid());
|
||||
if (!evlist->cpus || !evlist->threads) {
|
||||
err = -ENOMEM;
|
||||
pr_debug("Not enough memory to create thread/cpu maps\n");
|
||||
goto out_delete_maps;
|
||||
}
|
||||
|
||||
perf_evlist__open(evlist);
|
||||
|
||||
err = perf_evlist__mmap(evlist, 128, true);
|
||||
if (err < 0) {
|
||||
pr_debug("failed to mmap event: %d (%s)\n", errno,
|
||||
strerror(errno));
|
||||
goto out_close_evlist;
|
||||
}
|
||||
|
||||
perf_evlist__enable(evlist);
|
||||
|
||||
/* collect samples */
|
||||
for (i = 0; i < NR_LOOPS; i++)
|
||||
tmp++;
|
||||
|
||||
perf_evlist__disable(evlist);
|
||||
|
||||
while ((event = perf_evlist__mmap_read(evlist, 0)) != NULL) {
|
||||
struct perf_sample sample;
|
||||
|
||||
if (event->header.type != PERF_RECORD_SAMPLE)
|
||||
continue;
|
||||
|
||||
err = perf_evlist__parse_sample(evlist, event, &sample);
|
||||
if (err < 0) {
|
||||
pr_debug("Error during parse sample\n");
|
||||
goto out_unmap_evlist;
|
||||
}
|
||||
|
||||
total_periods += sample.period;
|
||||
nr_samples++;
|
||||
}
|
||||
|
||||
if ((u64) nr_samples == total_periods) {
|
||||
pr_debug("All (%d) samples have period value of 1!\n",
|
||||
nr_samples);
|
||||
err = -1;
|
||||
}
|
||||
|
||||
out_unmap_evlist:
|
||||
perf_evlist__munmap(evlist);
|
||||
out_close_evlist:
|
||||
perf_evlist__close(evlist);
|
||||
out_delete_maps:
|
||||
perf_evlist__delete_maps(evlist);
|
||||
out_free_evlist:
|
||||
perf_evlist__delete(evlist);
|
||||
return err;
|
||||
}
|
||||
|
||||
int test__sw_clock_freq(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = __test__sw_clock_freq(PERF_COUNT_SW_CPU_CLOCK);
|
||||
if (!ret)
|
||||
ret = __test__sw_clock_freq(PERF_COUNT_SW_TASK_CLOCK);
|
||||
|
||||
return ret;
|
||||
}
|
123
tools/perf/tests/task-exit.c
Normal file
123
tools/perf/tests/task-exit.c
Normal file
|
@ -0,0 +1,123 @@
|
|||
#include "evlist.h"
|
||||
#include "evsel.h"
|
||||
#include "thread_map.h"
|
||||
#include "cpumap.h"
|
||||
#include "tests.h"
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
static int exited;
|
||||
static int nr_exit;
|
||||
|
||||
static void sig_handler(int sig)
|
||||
{
|
||||
exited = 1;
|
||||
|
||||
if (sig == SIGUSR1)
|
||||
nr_exit = -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* This test will start a workload that does nothing then it checks
|
||||
* if the number of exit event reported by the kernel is 1 or not
|
||||
* in order to check the kernel returns correct number of event.
|
||||
*/
|
||||
int test__task_exit(void)
|
||||
{
|
||||
int err = -1;
|
||||
union perf_event *event;
|
||||
struct perf_evsel *evsel;
|
||||
struct perf_evlist *evlist;
|
||||
struct perf_target target = {
|
||||
.uid = UINT_MAX,
|
||||
.uses_mmap = true,
|
||||
};
|
||||
const char *argv[] = { "true", NULL };
|
||||
|
||||
signal(SIGCHLD, sig_handler);
|
||||
signal(SIGUSR1, sig_handler);
|
||||
|
||||
evlist = perf_evlist__new();
|
||||
if (evlist == NULL) {
|
||||
pr_debug("perf_evlist__new\n");
|
||||
return -1;
|
||||
}
|
||||
/*
|
||||
* We need at least one evsel in the evlist, use the default
|
||||
* one: "cycles".
|
||||
*/
|
||||
err = perf_evlist__add_default(evlist);
|
||||
if (err < 0) {
|
||||
pr_debug("Not enough memory to create evsel\n");
|
||||
goto out_free_evlist;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create maps of threads and cpus to monitor. In this case
|
||||
* we start with all threads and cpus (-1, -1) but then in
|
||||
* perf_evlist__prepare_workload we'll fill in the only thread
|
||||
* we're monitoring, the one forked there.
|
||||
*/
|
||||
evlist->cpus = cpu_map__dummy_new();
|
||||
evlist->threads = thread_map__new_by_tid(-1);
|
||||
if (!evlist->cpus || !evlist->threads) {
|
||||
err = -ENOMEM;
|
||||
pr_debug("Not enough memory to create thread/cpu maps\n");
|
||||
goto out_delete_maps;
|
||||
}
|
||||
|
||||
err = perf_evlist__prepare_workload(evlist, &target, argv, false, true);
|
||||
if (err < 0) {
|
||||
pr_debug("Couldn't run the workload!\n");
|
||||
goto out_delete_maps;
|
||||
}
|
||||
|
||||
evsel = perf_evlist__first(evlist);
|
||||
evsel->attr.task = 1;
|
||||
evsel->attr.sample_freq = 0;
|
||||
evsel->attr.inherit = 0;
|
||||
evsel->attr.watermark = 0;
|
||||
evsel->attr.wakeup_events = 1;
|
||||
evsel->attr.exclude_kernel = 1;
|
||||
|
||||
err = perf_evlist__open(evlist);
|
||||
if (err < 0) {
|
||||
pr_debug("Couldn't open the evlist: %s\n", strerror(-err));
|
||||
goto out_delete_maps;
|
||||
}
|
||||
|
||||
if (perf_evlist__mmap(evlist, 128, true) < 0) {
|
||||
pr_debug("failed to mmap events: %d (%s)\n", errno,
|
||||
strerror(errno));
|
||||
goto out_close_evlist;
|
||||
}
|
||||
|
||||
perf_evlist__start_workload(evlist);
|
||||
|
||||
retry:
|
||||
while ((event = perf_evlist__mmap_read(evlist, 0)) != NULL) {
|
||||
if (event->header.type != PERF_RECORD_EXIT)
|
||||
continue;
|
||||
|
||||
nr_exit++;
|
||||
}
|
||||
|
||||
if (!exited || !nr_exit) {
|
||||
poll(evlist->pollfd, evlist->nr_fds, -1);
|
||||
goto retry;
|
||||
}
|
||||
|
||||
if (nr_exit != 1) {
|
||||
pr_debug("received %d EXIT records\n", nr_exit);
|
||||
err = -1;
|
||||
}
|
||||
|
||||
perf_evlist__munmap(evlist);
|
||||
out_close_evlist:
|
||||
perf_evlist__close(evlist);
|
||||
out_delete_maps:
|
||||
perf_evlist__delete_maps(evlist);
|
||||
out_free_evlist:
|
||||
perf_evlist__delete(evlist);
|
||||
return err;
|
||||
}
|
|
@ -23,5 +23,9 @@ int test__dso_data(void);
|
|||
int test__parse_events(void);
|
||||
int test__hists_link(void);
|
||||
int test__python_use(void);
|
||||
int test__bp_signal(void);
|
||||
int test__bp_signal_overflow(void);
|
||||
int test__task_exit(void);
|
||||
int test__sw_clock_freq(void);
|
||||
|
||||
#endif /* TESTS_H */
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
#include "../cache.h"
|
||||
#include "../../perf.h"
|
||||
#include "libslang.h"
|
||||
#include <newt.h>
|
||||
#include "ui.h"
|
||||
#include "util.h"
|
||||
#include <linux/compiler.h>
|
||||
|
@ -234,7 +233,7 @@ void ui_browser__reset_index(struct ui_browser *browser)
|
|||
void __ui_browser__show_title(struct ui_browser *browser, const char *title)
|
||||
{
|
||||
SLsmg_gotorc(0, 0);
|
||||
ui_browser__set_color(browser, NEWT_COLORSET_ROOT);
|
||||
ui_browser__set_color(browser, HE_COLORSET_ROOT);
|
||||
slsmg_write_nstring(title, browser->width + 1);
|
||||
}
|
||||
|
||||
|
@ -513,6 +512,12 @@ static struct ui_browser_colorset {
|
|||
.fg = "magenta",
|
||||
.bg = "default",
|
||||
},
|
||||
{
|
||||
.colorset = HE_COLORSET_ROOT,
|
||||
.name = "root",
|
||||
.fg = "white",
|
||||
.bg = "blue",
|
||||
},
|
||||
{
|
||||
.name = NULL,
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#define HE_COLORSET_SELECTED 53
|
||||
#define HE_COLORSET_CODE 54
|
||||
#define HE_COLORSET_ADDR 55
|
||||
#define HE_COLORSET_ROOT 56
|
||||
|
||||
struct ui_browser {
|
||||
u64 index, top_idx;
|
||||
|
|
|
@ -8,15 +8,19 @@
|
|||
#include "../../util/hist.h"
|
||||
#include "../../util/sort.h"
|
||||
#include "../../util/symbol.h"
|
||||
#include "../../util/evsel.h"
|
||||
#include <pthread.h>
|
||||
#include <newt.h>
|
||||
|
||||
struct browser_disasm_line {
|
||||
struct rb_node rb_node;
|
||||
double percent;
|
||||
u32 idx;
|
||||
int idx_asm;
|
||||
int jump_sources;
|
||||
/*
|
||||
* actual length of this array is saved on the nr_events field
|
||||
* of the struct annotate_browser
|
||||
*/
|
||||
double percent[1];
|
||||
};
|
||||
|
||||
static struct annotate_browser_opt {
|
||||
|
@ -33,8 +37,9 @@ struct annotate_browser {
|
|||
struct ui_browser b;
|
||||
struct rb_root entries;
|
||||
struct rb_node *curr_hot;
|
||||
struct disasm_line *selection;
|
||||
struct disasm_line *selection;
|
||||
struct disasm_line **offsets;
|
||||
int nr_events;
|
||||
u64 start;
|
||||
int nr_asm_entries;
|
||||
int nr_entries;
|
||||
|
@ -94,14 +99,24 @@ static void annotate_browser__write(struct ui_browser *browser, void *entry, int
|
|||
(!current_entry || (browser->use_navkeypressed &&
|
||||
!browser->navkeypressed)));
|
||||
int width = browser->width, printed;
|
||||
int i, pcnt_width = 7 * ab->nr_events;
|
||||
double percent_max = 0.0;
|
||||
char bf[256];
|
||||
|
||||
if (dl->offset != -1 && bdl->percent != 0.0) {
|
||||
ui_browser__set_percent_color(browser, bdl->percent, current_entry);
|
||||
slsmg_printf("%6.2f ", bdl->percent);
|
||||
for (i = 0; i < ab->nr_events; i++) {
|
||||
if (bdl->percent[i] > percent_max)
|
||||
percent_max = bdl->percent[i];
|
||||
}
|
||||
|
||||
if (dl->offset != -1 && percent_max != 0.0) {
|
||||
for (i = 0; i < ab->nr_events; i++) {
|
||||
ui_browser__set_percent_color(browser, bdl->percent[i],
|
||||
current_entry);
|
||||
slsmg_printf("%6.2f ", bdl->percent[i]);
|
||||
}
|
||||
} else {
|
||||
ui_browser__set_percent_color(browser, 0, current_entry);
|
||||
slsmg_write_nstring(" ", 7);
|
||||
slsmg_write_nstring(" ", pcnt_width);
|
||||
}
|
||||
|
||||
SLsmg_write_char(' ');
|
||||
|
@ -111,12 +126,12 @@ static void annotate_browser__write(struct ui_browser *browser, void *entry, int
|
|||
width += 1;
|
||||
|
||||
if (!*dl->line)
|
||||
slsmg_write_nstring(" ", width - 7);
|
||||
slsmg_write_nstring(" ", width - pcnt_width);
|
||||
else if (dl->offset == -1) {
|
||||
printed = scnprintf(bf, sizeof(bf), "%*s ",
|
||||
ab->addr_width, " ");
|
||||
slsmg_write_nstring(bf, printed);
|
||||
slsmg_write_nstring(dl->line, width - printed - 6);
|
||||
slsmg_write_nstring(dl->line, width - printed - pcnt_width + 1);
|
||||
} else {
|
||||
u64 addr = dl->offset;
|
||||
int color = -1;
|
||||
|
@ -175,7 +190,7 @@ static void annotate_browser__write(struct ui_browser *browser, void *entry, int
|
|||
}
|
||||
|
||||
disasm_line__scnprintf(dl, bf, sizeof(bf), !annotate_browser__opts.use_offset);
|
||||
slsmg_write_nstring(bf, width - 10 - printed);
|
||||
slsmg_write_nstring(bf, width - pcnt_width - 3 - printed);
|
||||
}
|
||||
|
||||
if (current_entry)
|
||||
|
@ -200,6 +215,7 @@ static void annotate_browser__draw_current_jump(struct ui_browser *browser)
|
|||
unsigned int from, to;
|
||||
struct map_symbol *ms = ab->b.priv;
|
||||
struct symbol *sym = ms->sym;
|
||||
u8 pcnt_width = 7;
|
||||
|
||||
/* PLT symbols contain external offsets */
|
||||
if (strstr(sym->name, "@plt"))
|
||||
|
@ -223,57 +239,44 @@ static void annotate_browser__draw_current_jump(struct ui_browser *browser)
|
|||
to = (u64)btarget->idx;
|
||||
}
|
||||
|
||||
pcnt_width *= ab->nr_events;
|
||||
|
||||
ui_browser__set_color(browser, HE_COLORSET_CODE);
|
||||
__ui_browser__line_arrow(browser, 9 + ab->addr_width, from, to);
|
||||
__ui_browser__line_arrow(browser, pcnt_width + 2 + ab->addr_width,
|
||||
from, to);
|
||||
}
|
||||
|
||||
static unsigned int annotate_browser__refresh(struct ui_browser *browser)
|
||||
{
|
||||
struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
|
||||
int ret = ui_browser__list_head_refresh(browser);
|
||||
int pcnt_width;
|
||||
|
||||
pcnt_width = 7 * ab->nr_events;
|
||||
|
||||
if (annotate_browser__opts.jump_arrows)
|
||||
annotate_browser__draw_current_jump(browser);
|
||||
|
||||
ui_browser__set_color(browser, HE_COLORSET_NORMAL);
|
||||
__ui_browser__vline(browser, 7, 0, browser->height - 1);
|
||||
__ui_browser__vline(browser, pcnt_width, 0, browser->height - 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static double disasm_line__calc_percent(struct disasm_line *dl, struct symbol *sym, int evidx)
|
||||
static int disasm__cmp(struct browser_disasm_line *a,
|
||||
struct browser_disasm_line *b, int nr_pcnt)
|
||||
{
|
||||
double percent = 0.0;
|
||||
int i;
|
||||
|
||||
if (dl->offset != -1) {
|
||||
int len = sym->end - sym->start;
|
||||
unsigned int hits = 0;
|
||||
struct annotation *notes = symbol__annotation(sym);
|
||||
struct source_line *src_line = notes->src->lines;
|
||||
struct sym_hist *h = annotation__histogram(notes, evidx);
|
||||
s64 offset = dl->offset;
|
||||
struct disasm_line *next;
|
||||
|
||||
next = disasm__get_next_ip_line(¬es->src->source, dl);
|
||||
while (offset < (s64)len &&
|
||||
(next == NULL || offset < next->offset)) {
|
||||
if (src_line) {
|
||||
percent += src_line[offset].percent;
|
||||
} else
|
||||
hits += h->addr[offset];
|
||||
|
||||
++offset;
|
||||
}
|
||||
/*
|
||||
* If the percentage wasn't already calculated in
|
||||
* symbol__get_source_line, do it now:
|
||||
*/
|
||||
if (src_line == NULL && h->sum)
|
||||
percent = 100.0 * hits / h->sum;
|
||||
for (i = 0; i < nr_pcnt; i++) {
|
||||
if (a->percent[i] == b->percent[i])
|
||||
continue;
|
||||
return a->percent[i] < b->percent[i];
|
||||
}
|
||||
|
||||
return percent;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_line *bdl)
|
||||
static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_line *bdl,
|
||||
int nr_events)
|
||||
{
|
||||
struct rb_node **p = &root->rb_node;
|
||||
struct rb_node *parent = NULL;
|
||||
|
@ -282,7 +285,8 @@ static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_l
|
|||
while (*p != NULL) {
|
||||
parent = *p;
|
||||
l = rb_entry(parent, struct browser_disasm_line, rb_node);
|
||||
if (bdl->percent < l->percent)
|
||||
|
||||
if (disasm__cmp(bdl, l, nr_events))
|
||||
p = &(*p)->rb_left;
|
||||
else
|
||||
p = &(*p)->rb_right;
|
||||
|
@ -331,12 +335,13 @@ static void annotate_browser__set_rb_top(struct annotate_browser *browser,
|
|||
}
|
||||
|
||||
static void annotate_browser__calc_percent(struct annotate_browser *browser,
|
||||
int evidx)
|
||||
struct perf_evsel *evsel)
|
||||
{
|
||||
struct map_symbol *ms = browser->b.priv;
|
||||
struct symbol *sym = ms->sym;
|
||||
struct annotation *notes = symbol__annotation(sym);
|
||||
struct disasm_line *pos;
|
||||
struct disasm_line *pos, *next;
|
||||
s64 len = symbol__size(sym);
|
||||
|
||||
browser->entries = RB_ROOT;
|
||||
|
||||
|
@ -344,12 +349,34 @@ static void annotate_browser__calc_percent(struct annotate_browser *browser,
|
|||
|
||||
list_for_each_entry(pos, ¬es->src->source, node) {
|
||||
struct browser_disasm_line *bpos = disasm_line__browser(pos);
|
||||
bpos->percent = disasm_line__calc_percent(pos, sym, evidx);
|
||||
if (bpos->percent < 0.01) {
|
||||
const char *path = NULL;
|
||||
double max_percent = 0.0;
|
||||
int i;
|
||||
|
||||
if (pos->offset == -1) {
|
||||
RB_CLEAR_NODE(&bpos->rb_node);
|
||||
continue;
|
||||
}
|
||||
disasm_rb_tree__insert(&browser->entries, bpos);
|
||||
|
||||
next = disasm__get_next_ip_line(¬es->src->source, pos);
|
||||
|
||||
for (i = 0; i < browser->nr_events; i++) {
|
||||
bpos->percent[i] = disasm__calc_percent(notes,
|
||||
evsel->idx + i,
|
||||
pos->offset,
|
||||
next ? next->offset : len,
|
||||
&path);
|
||||
|
||||
if (max_percent < bpos->percent[i])
|
||||
max_percent = bpos->percent[i];
|
||||
}
|
||||
|
||||
if (max_percent < 0.01) {
|
||||
RB_CLEAR_NODE(&bpos->rb_node);
|
||||
continue;
|
||||
}
|
||||
disasm_rb_tree__insert(&browser->entries, bpos,
|
||||
browser->nr_events);
|
||||
}
|
||||
pthread_mutex_unlock(¬es->lock);
|
||||
|
||||
|
@ -401,7 +428,8 @@ static void annotate_browser__init_asm_mode(struct annotate_browser *browser)
|
|||
browser->b.nr_entries = browser->nr_asm_entries;
|
||||
}
|
||||
|
||||
static bool annotate_browser__callq(struct annotate_browser *browser, int evidx,
|
||||
static bool annotate_browser__callq(struct annotate_browser *browser,
|
||||
struct perf_evsel *evsel,
|
||||
struct hist_browser_timer *hbt)
|
||||
{
|
||||
struct map_symbol *ms = browser->b.priv;
|
||||
|
@ -432,7 +460,7 @@ static bool annotate_browser__callq(struct annotate_browser *browser, int evidx,
|
|||
}
|
||||
|
||||
pthread_mutex_unlock(¬es->lock);
|
||||
symbol__tui_annotate(target, ms->map, evidx, hbt);
|
||||
symbol__tui_annotate(target, ms->map, evsel, hbt);
|
||||
ui_browser__show_title(&browser->b, sym->name);
|
||||
return true;
|
||||
}
|
||||
|
@ -615,7 +643,8 @@ static void annotate_browser__update_addr_width(struct annotate_browser *browser
|
|||
browser->addr_width += browser->jumps_width + 1;
|
||||
}
|
||||
|
||||
static int annotate_browser__run(struct annotate_browser *browser, int evidx,
|
||||
static int annotate_browser__run(struct annotate_browser *browser,
|
||||
struct perf_evsel *evsel,
|
||||
struct hist_browser_timer *hbt)
|
||||
{
|
||||
struct rb_node *nd = NULL;
|
||||
|
@ -628,7 +657,7 @@ static int annotate_browser__run(struct annotate_browser *browser, int evidx,
|
|||
if (ui_browser__show(&browser->b, sym->name, help) < 0)
|
||||
return -1;
|
||||
|
||||
annotate_browser__calc_percent(browser, evidx);
|
||||
annotate_browser__calc_percent(browser, evsel);
|
||||
|
||||
if (browser->curr_hot) {
|
||||
annotate_browser__set_rb_top(browser, browser->curr_hot);
|
||||
|
@ -641,7 +670,7 @@ static int annotate_browser__run(struct annotate_browser *browser, int evidx,
|
|||
key = ui_browser__run(&browser->b, delay_secs);
|
||||
|
||||
if (delay_secs != 0) {
|
||||
annotate_browser__calc_percent(browser, evidx);
|
||||
annotate_browser__calc_percent(browser, evsel);
|
||||
/*
|
||||
* Current line focus got out of the list of most active
|
||||
* lines, NULL it so that if TAB|UNTAB is pressed, we
|
||||
|
@ -657,7 +686,7 @@ static int annotate_browser__run(struct annotate_browser *browser, int evidx,
|
|||
hbt->timer(hbt->arg);
|
||||
|
||||
if (delay_secs != 0)
|
||||
symbol__annotate_decay_histogram(sym, evidx);
|
||||
symbol__annotate_decay_histogram(sym, evsel->idx);
|
||||
continue;
|
||||
case K_TAB:
|
||||
if (nd != NULL) {
|
||||
|
@ -754,7 +783,7 @@ show_help:
|
|||
goto show_sup_ins;
|
||||
goto out;
|
||||
} else if (!(annotate_browser__jump(browser) ||
|
||||
annotate_browser__callq(browser, evidx, hbt))) {
|
||||
annotate_browser__callq(browser, evsel, hbt))) {
|
||||
show_sup_ins:
|
||||
ui_helpline__puts("Actions are only available for 'callq', 'retq' & jump instructions.");
|
||||
}
|
||||
|
@ -776,10 +805,10 @@ out:
|
|||
return key;
|
||||
}
|
||||
|
||||
int hist_entry__tui_annotate(struct hist_entry *he, int evidx,
|
||||
int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
|
||||
struct hist_browser_timer *hbt)
|
||||
{
|
||||
return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx, hbt);
|
||||
return symbol__tui_annotate(he->ms.sym, he->ms.map, evsel, hbt);
|
||||
}
|
||||
|
||||
static void annotate_browser__mark_jump_targets(struct annotate_browser *browser,
|
||||
|
@ -826,7 +855,8 @@ static inline int width_jumps(int n)
|
|||
return 1;
|
||||
}
|
||||
|
||||
int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
|
||||
int symbol__tui_annotate(struct symbol *sym, struct map *map,
|
||||
struct perf_evsel *evsel,
|
||||
struct hist_browser_timer *hbt)
|
||||
{
|
||||
struct disasm_line *pos, *n;
|
||||
|
@ -847,6 +877,8 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
|
|||
},
|
||||
};
|
||||
int ret = -1;
|
||||
int nr_pcnt = 1;
|
||||
size_t sizeof_bdl = sizeof(struct browser_disasm_line);
|
||||
|
||||
if (sym == NULL)
|
||||
return -1;
|
||||
|
@ -862,7 +894,12 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (symbol__annotate(sym, map, sizeof(struct browser_disasm_line)) < 0) {
|
||||
if (perf_evsel__is_group_event(evsel)) {
|
||||
nr_pcnt = evsel->nr_members;
|
||||
sizeof_bdl += sizeof(double) * (nr_pcnt - 1);
|
||||
}
|
||||
|
||||
if (symbol__annotate(sym, map, sizeof_bdl) < 0) {
|
||||
ui__error("%s", ui_helpline__last_msg);
|
||||
goto out_free_offsets;
|
||||
}
|
||||
|
@ -900,6 +937,7 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
|
|||
browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size);
|
||||
browser.max_addr_width = hex_width(sym->end);
|
||||
browser.jumps_width = width_jumps(browser.max_jump_sources);
|
||||
browser.nr_events = nr_pcnt;
|
||||
browser.b.nr_entries = browser.nr_entries;
|
||||
browser.b.entries = ¬es->src->source,
|
||||
browser.b.width += 18; /* Percentage */
|
||||
|
@ -909,7 +947,7 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
|
|||
|
||||
annotate_browser__update_addr_width(&browser);
|
||||
|
||||
ret = annotate_browser__run(&browser, evidx, hbt);
|
||||
ret = annotate_browser__run(&browser, evsel, hbt);
|
||||
list_for_each_entry_safe(pos, n, ¬es->src->source, node) {
|
||||
list_del(&pos->node);
|
||||
disasm_line__free(pos);
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
#include "../libslang.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <newt.h>
|
||||
#include <linux/rbtree.h>
|
||||
|
||||
#include "../../util/evsel.h"
|
||||
|
@ -1193,7 +1192,7 @@ static int hists__browser_title(struct hists *hists, char *bf, size_t size,
|
|||
char buf[512];
|
||||
size_t buflen = sizeof(buf);
|
||||
|
||||
if (symbol_conf.event_group && evsel->nr_members > 1) {
|
||||
if (perf_evsel__is_group_event(evsel)) {
|
||||
struct perf_evsel *pos;
|
||||
|
||||
perf_evsel__group_desc(evsel, buf, buflen);
|
||||
|
@ -1599,7 +1598,7 @@ do_annotate:
|
|||
* Don't let this be freed, say, by hists__decay_entry.
|
||||
*/
|
||||
he->used = true;
|
||||
err = hist_entry__tui_annotate(he, evsel->idx, hbt);
|
||||
err = hist_entry__tui_annotate(he, evsel, hbt);
|
||||
he->used = false;
|
||||
/*
|
||||
* offer option to annotate the other branch source or target
|
||||
|
@ -1709,7 +1708,7 @@ 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) {
|
||||
if (perf_evsel__is_group_event(evsel)) {
|
||||
struct perf_evsel *pos;
|
||||
|
||||
ev_name = perf_evsel__group_name(evsel);
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#include "../libslang.h"
|
||||
#include <elf.h>
|
||||
#include <newt.h>
|
||||
#include <inttypes.h>
|
||||
#include <sys/ttydefaults.h>
|
||||
#include <string.h>
|
||||
|
@ -10,41 +9,9 @@
|
|||
#include "../../util/symbol.h"
|
||||
#include "../browser.h"
|
||||
#include "../helpline.h"
|
||||
#include "../keysyms.h"
|
||||
#include "map.h"
|
||||
|
||||
static int ui_entry__read(const char *title, char *bf, size_t size, int width)
|
||||
{
|
||||
struct newtExitStruct es;
|
||||
newtComponent form, entry;
|
||||
const char *result;
|
||||
int err = -1;
|
||||
|
||||
newtCenteredWindow(width, 1, title);
|
||||
form = newtForm(NULL, NULL, 0);
|
||||
if (form == NULL)
|
||||
return -1;
|
||||
|
||||
entry = newtEntry(0, 0, "0x", width, &result, NEWT_FLAG_SCROLL);
|
||||
if (entry == NULL)
|
||||
goto out_free_form;
|
||||
|
||||
newtFormAddComponent(form, entry);
|
||||
newtFormAddHotKey(form, NEWT_KEY_ENTER);
|
||||
newtFormAddHotKey(form, NEWT_KEY_ESCAPE);
|
||||
newtFormAddHotKey(form, NEWT_KEY_LEFT);
|
||||
newtFormAddHotKey(form, CTRL('c'));
|
||||
newtFormRun(form, &es);
|
||||
|
||||
if (result != NULL) {
|
||||
strncpy(bf, result, size);
|
||||
err = 0;
|
||||
}
|
||||
out_free_form:
|
||||
newtPopWindow();
|
||||
newtFormDestroy(form);
|
||||
return err;
|
||||
}
|
||||
|
||||
struct map_browser {
|
||||
struct ui_browser b;
|
||||
struct map *map;
|
||||
|
@ -78,10 +45,11 @@ static int map_browser__search(struct map_browser *self)
|
|||
{
|
||||
char target[512];
|
||||
struct symbol *sym;
|
||||
int err = ui_entry__read("Search by name/addr", target, sizeof(target), 40);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
int err = ui_browser__input_window("Search by name/addr",
|
||||
"Prefix with 0x to search by address",
|
||||
target, "ENTER: OK, ESC: Cancel", 0);
|
||||
if (err != K_ENTER)
|
||||
return -1;
|
||||
|
||||
if (target[0] == '0' && tolower(target[1]) == 'x') {
|
||||
u64 addr = strtoull(target, NULL, 16);
|
||||
|
@ -112,12 +80,20 @@ static int map_browser__run(struct map_browser *self)
|
|||
while (1) {
|
||||
key = ui_browser__run(&self->b, 0);
|
||||
|
||||
if (verbose && key == '/')
|
||||
map_browser__search(self);
|
||||
else
|
||||
switch (key) {
|
||||
case '/':
|
||||
if (verbose)
|
||||
map_browser__search(self);
|
||||
default:
|
||||
break;
|
||||
case K_LEFT:
|
||||
case K_ESC:
|
||||
case 'q':
|
||||
case CTRL('c'):
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
ui_browser__hide(&self->b);
|
||||
return key;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#include <elf.h>
|
||||
#include <newt.h>
|
||||
#include <inttypes.h>
|
||||
#include <sys/ttydefaults.h>
|
||||
#include <string.h>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "gtk.h"
|
||||
#include "util/debug.h"
|
||||
#include "util/annotate.h"
|
||||
#include "util/evsel.h"
|
||||
#include "ui/helpline.h"
|
||||
|
||||
|
||||
|
@ -32,7 +33,7 @@ static int perf_gtk__get_percent(char *buf, size_t size, struct symbol *sym,
|
|||
return 0;
|
||||
|
||||
symhist = annotation__histogram(symbol__annotation(sym), evidx);
|
||||
if (!symhist->addr[dl->offset])
|
||||
if (!symbol_conf.event_group && !symhist->addr[dl->offset])
|
||||
return 0;
|
||||
|
||||
percent = 100.0 * symhist->addr[dl->offset] / symhist->sum;
|
||||
|
@ -85,7 +86,7 @@ static int perf_gtk__get_line(char *buf, size_t size, struct disasm_line *dl)
|
|||
}
|
||||
|
||||
static int perf_gtk__annotate_symbol(GtkWidget *window, struct symbol *sym,
|
||||
struct map *map, int evidx,
|
||||
struct map *map, struct perf_evsel *evsel,
|
||||
struct hist_browser_timer *hbt __maybe_unused)
|
||||
{
|
||||
struct disasm_line *pos, *n;
|
||||
|
@ -118,10 +119,24 @@ static int perf_gtk__annotate_symbol(GtkWidget *window, struct symbol *sym,
|
|||
|
||||
list_for_each_entry(pos, ¬es->src->source, node) {
|
||||
GtkTreeIter iter;
|
||||
int ret = 0;
|
||||
|
||||
gtk_list_store_append(store, &iter);
|
||||
|
||||
if (perf_gtk__get_percent(s, sizeof(s), sym, pos, evidx))
|
||||
if (perf_evsel__is_group_event(evsel)) {
|
||||
for (i = 0; i < evsel->nr_members; i++) {
|
||||
ret += perf_gtk__get_percent(s + ret,
|
||||
sizeof(s) - ret,
|
||||
sym, pos,
|
||||
evsel->idx + i);
|
||||
ret += scnprintf(s + ret, sizeof(s) - ret, " ");
|
||||
}
|
||||
} else {
|
||||
ret = perf_gtk__get_percent(s, sizeof(s), sym, pos,
|
||||
evsel->idx);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
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);
|
||||
|
@ -139,7 +154,8 @@ static int perf_gtk__annotate_symbol(GtkWidget *window, struct symbol *sym,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int symbol__gtk_annotate(struct symbol *sym, struct map *map, int evidx,
|
||||
int symbol__gtk_annotate(struct symbol *sym, struct map *map,
|
||||
struct perf_evsel *evsel,
|
||||
struct hist_browser_timer *hbt)
|
||||
{
|
||||
GtkWidget *window;
|
||||
|
@ -206,7 +222,7 @@ int symbol__gtk_annotate(struct symbol *sym, struct map *map, int evidx,
|
|||
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled_window,
|
||||
tab_label);
|
||||
|
||||
perf_gtk__annotate_symbol(scrolled_window, sym, map, evidx, hbt);
|
||||
perf_gtk__annotate_symbol(scrolled_window, sym, map, evsel, hbt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -32,21 +32,18 @@ static int __hpp__color_fmt(struct perf_hpp *hpp, struct hist_entry *he,
|
|||
int ret;
|
||||
double percent = 0.0;
|
||||
struct hists *hists = he->hists;
|
||||
struct perf_evsel *evsel = hists_to_evsel(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) {
|
||||
if (perf_evsel__is_group_event(evsel)) {
|
||||
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) {
|
||||
|
|
|
@ -16,6 +16,7 @@ static int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
|
|||
{
|
||||
int ret;
|
||||
struct hists *hists = he->hists;
|
||||
struct perf_evsel *evsel = hists_to_evsel(hists);
|
||||
|
||||
if (fmt_percent) {
|
||||
double percent = 0.0;
|
||||
|
@ -28,15 +29,11 @@ static int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
|
|||
} else
|
||||
ret = print_fn(hpp->buf, hpp->size, fmt, get_field(he));
|
||||
|
||||
if (symbol_conf.event_group) {
|
||||
if (perf_evsel__is_group_event(evsel)) {
|
||||
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) {
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
#include <newt.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
|
@ -88,13 +87,6 @@ int ui__getch(int delay_secs)
|
|||
return SLkp_getkey();
|
||||
}
|
||||
|
||||
static void newt_suspend(void *d __maybe_unused)
|
||||
{
|
||||
newtSuspend();
|
||||
raise(SIGTSTP);
|
||||
newtResume();
|
||||
}
|
||||
|
||||
static void ui__signal(int sig)
|
||||
{
|
||||
ui__exit(false);
|
||||
|
@ -106,7 +98,17 @@ int ui__init(void)
|
|||
{
|
||||
int err;
|
||||
|
||||
newtInit();
|
||||
SLutf8_enable(-1);
|
||||
SLtt_get_terminfo();
|
||||
SLtt_get_screen_size();
|
||||
|
||||
err = SLsmg_init_smg();
|
||||
if (err < 0)
|
||||
goto out;
|
||||
err = SLang_init_tty(0, 0, 0);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
err = SLkp_init();
|
||||
if (err < 0) {
|
||||
pr_err("TUI initialization failed.\n");
|
||||
|
@ -115,7 +117,6 @@ int ui__init(void)
|
|||
|
||||
SLkp_define_keysym((char *)"^(kB)", SL_KEY_UNTAB);
|
||||
|
||||
newtSetSuspendCallback(newt_suspend, NULL);
|
||||
ui_helpline__init();
|
||||
ui_browser__init();
|
||||
ui_progress__init();
|
||||
|
|
|
@ -12,7 +12,7 @@ extern int use_browser;
|
|||
void setup_browser(bool fallback_to_pager);
|
||||
void exit_browser(bool wait_for_ok);
|
||||
|
||||
#ifdef NEWT_SUPPORT
|
||||
#ifdef SLANG_SUPPORT
|
||||
int ui__init(void);
|
||||
void ui__exit(bool wait_for_ok);
|
||||
#else
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "symbol.h"
|
||||
#include "debug.h"
|
||||
#include "annotate.h"
|
||||
#include "evsel.h"
|
||||
#include <pthread.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
|
@ -602,8 +603,42 @@ struct disasm_line *disasm__get_next_ip_line(struct list_head *head, struct disa
|
|||
return NULL;
|
||||
}
|
||||
|
||||
double disasm__calc_percent(struct annotation *notes, int evidx, s64 offset,
|
||||
s64 end, const char **path)
|
||||
{
|
||||
struct source_line *src_line = notes->src->lines;
|
||||
double percent = 0.0;
|
||||
|
||||
if (src_line) {
|
||||
size_t sizeof_src_line = sizeof(*src_line) +
|
||||
sizeof(src_line->p) * (src_line->nr_pcnt - 1);
|
||||
|
||||
while (offset < end) {
|
||||
src_line = (void *)notes->src->lines +
|
||||
(sizeof_src_line * offset);
|
||||
|
||||
if (*path == NULL)
|
||||
*path = src_line->path;
|
||||
|
||||
percent += src_line->p[evidx].percent;
|
||||
offset++;
|
||||
}
|
||||
} else {
|
||||
struct sym_hist *h = annotation__histogram(notes, evidx);
|
||||
unsigned int hits = 0;
|
||||
|
||||
while (offset < end)
|
||||
hits += h->addr[offset++];
|
||||
|
||||
if (h->sum)
|
||||
percent = 100.0 * hits / h->sum;
|
||||
}
|
||||
|
||||
return percent;
|
||||
}
|
||||
|
||||
static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 start,
|
||||
int evidx, u64 len, int min_pcnt, int printed,
|
||||
struct perf_evsel *evsel, u64 len, int min_pcnt, int printed,
|
||||
int max_lines, struct disasm_line *queue)
|
||||
{
|
||||
static const char *prev_line;
|
||||
|
@ -611,34 +646,37 @@ static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 st
|
|||
|
||||
if (dl->offset != -1) {
|
||||
const char *path = NULL;
|
||||
unsigned int hits = 0;
|
||||
double percent = 0.0;
|
||||
double percent, max_percent = 0.0;
|
||||
double *ppercents = &percent;
|
||||
int i, nr_percent = 1;
|
||||
const char *color;
|
||||
struct annotation *notes = symbol__annotation(sym);
|
||||
struct source_line *src_line = notes->src->lines;
|
||||
struct sym_hist *h = annotation__histogram(notes, evidx);
|
||||
s64 offset = dl->offset;
|
||||
const u64 addr = start + offset;
|
||||
struct disasm_line *next;
|
||||
|
||||
next = disasm__get_next_ip_line(¬es->src->source, dl);
|
||||
|
||||
while (offset < (s64)len &&
|
||||
(next == NULL || offset < next->offset)) {
|
||||
if (src_line) {
|
||||
if (path == NULL)
|
||||
path = src_line[offset].path;
|
||||
percent += src_line[offset].percent;
|
||||
} else
|
||||
hits += h->addr[offset];
|
||||
|
||||
++offset;
|
||||
if (perf_evsel__is_group_event(evsel)) {
|
||||
nr_percent = evsel->nr_members;
|
||||
ppercents = calloc(nr_percent, sizeof(double));
|
||||
if (ppercents == NULL)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (src_line == NULL && h->sum)
|
||||
percent = 100.0 * hits / h->sum;
|
||||
for (i = 0; i < nr_percent; i++) {
|
||||
percent = disasm__calc_percent(notes,
|
||||
notes->src->lines ? i : evsel->idx + i,
|
||||
offset,
|
||||
next ? next->offset : (s64) len,
|
||||
&path);
|
||||
|
||||
if (percent < min_pcnt)
|
||||
ppercents[i] = percent;
|
||||
if (percent > max_percent)
|
||||
max_percent = percent;
|
||||
}
|
||||
|
||||
if (max_percent < min_pcnt)
|
||||
return -1;
|
||||
|
||||
if (max_lines && printed >= max_lines)
|
||||
|
@ -648,12 +686,12 @@ static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 st
|
|||
list_for_each_entry_from(queue, ¬es->src->source, node) {
|
||||
if (queue == dl)
|
||||
break;
|
||||
disasm_line__print(queue, sym, start, evidx, len,
|
||||
disasm_line__print(queue, sym, start, evsel, len,
|
||||
0, 0, 1, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
color = get_percent_color(percent);
|
||||
color = get_percent_color(max_percent);
|
||||
|
||||
/*
|
||||
* Also color the filename and line if needed, with
|
||||
|
@ -669,25 +707,59 @@ static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 st
|
|||
}
|
||||
}
|
||||
|
||||
color_fprintf(stdout, color, " %7.2f", percent);
|
||||
for (i = 0; i < nr_percent; i++) {
|
||||
percent = ppercents[i];
|
||||
color = get_percent_color(percent);
|
||||
color_fprintf(stdout, color, " %7.2f", percent);
|
||||
}
|
||||
|
||||
printf(" : ");
|
||||
color_fprintf(stdout, PERF_COLOR_MAGENTA, " %" PRIx64 ":", addr);
|
||||
color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", dl->line);
|
||||
|
||||
if (ppercents != &percent)
|
||||
free(ppercents);
|
||||
|
||||
} else if (max_lines && printed >= max_lines)
|
||||
return 1;
|
||||
else {
|
||||
int width = 8;
|
||||
|
||||
if (queue)
|
||||
return -1;
|
||||
|
||||
if (perf_evsel__is_group_event(evsel))
|
||||
width *= evsel->nr_members;
|
||||
|
||||
if (!*dl->line)
|
||||
printf(" :\n");
|
||||
printf(" %*s:\n", width, " ");
|
||||
else
|
||||
printf(" : %s\n", dl->line);
|
||||
printf(" %*s: %s\n", width, " ", dl->line);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* symbol__parse_objdump_line() parses objdump output (with -d --no-show-raw)
|
||||
* which looks like following
|
||||
*
|
||||
* 0000000000415500 <_init>:
|
||||
* 415500: sub $0x8,%rsp
|
||||
* 415504: mov 0x2f5ad5(%rip),%rax # 70afe0 <_DYNAMIC+0x2f8>
|
||||
* 41550b: test %rax,%rax
|
||||
* 41550e: je 415515 <_init+0x15>
|
||||
* 415510: callq 416e70 <__gmon_start__@plt>
|
||||
* 415515: add $0x8,%rsp
|
||||
* 415519: retq
|
||||
*
|
||||
* it will be parsed and saved into struct disasm_line as
|
||||
* <offset> <name> <ops.raw>
|
||||
*
|
||||
* The offset will be a relative offset from the start of the symbol and -1
|
||||
* means that it's not a disassembly line so should be treated differently.
|
||||
* The ops.raw part will be parsed further according to type of the instruction.
|
||||
*/
|
||||
static int symbol__parse_objdump_line(struct symbol *sym, struct map *map,
|
||||
FILE *file, size_t privsize)
|
||||
{
|
||||
|
@ -858,7 +930,7 @@ static void insert_source_line(struct rb_root *root, struct source_line *src_lin
|
|||
struct source_line *iter;
|
||||
struct rb_node **p = &root->rb_node;
|
||||
struct rb_node *parent = NULL;
|
||||
int ret;
|
||||
int i, ret;
|
||||
|
||||
while (*p != NULL) {
|
||||
parent = *p;
|
||||
|
@ -866,7 +938,8 @@ static void insert_source_line(struct rb_root *root, struct source_line *src_lin
|
|||
|
||||
ret = strcmp(iter->path, src_line->path);
|
||||
if (ret == 0) {
|
||||
iter->percent_sum += src_line->percent;
|
||||
for (i = 0; i < src_line->nr_pcnt; i++)
|
||||
iter->p[i].percent_sum += src_line->p[i].percent;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -876,12 +949,26 @@ static void insert_source_line(struct rb_root *root, struct source_line *src_lin
|
|||
p = &(*p)->rb_right;
|
||||
}
|
||||
|
||||
src_line->percent_sum = src_line->percent;
|
||||
for (i = 0; i < src_line->nr_pcnt; i++)
|
||||
src_line->p[i].percent_sum = src_line->p[i].percent;
|
||||
|
||||
rb_link_node(&src_line->node, parent, p);
|
||||
rb_insert_color(&src_line->node, root);
|
||||
}
|
||||
|
||||
static int cmp_source_line(struct source_line *a, struct source_line *b)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < a->nr_pcnt; i++) {
|
||||
if (a->p[i].percent_sum == b->p[i].percent_sum)
|
||||
continue;
|
||||
return a->p[i].percent_sum > b->p[i].percent_sum;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __resort_source_line(struct rb_root *root, struct source_line *src_line)
|
||||
{
|
||||
struct source_line *iter;
|
||||
|
@ -892,7 +979,7 @@ static void __resort_source_line(struct rb_root *root, struct source_line *src_l
|
|||
parent = *p;
|
||||
iter = rb_entry(parent, struct source_line, node);
|
||||
|
||||
if (src_line->percent_sum > iter->percent_sum)
|
||||
if (cmp_source_line(src_line, iter))
|
||||
p = &(*p)->rb_left;
|
||||
else
|
||||
p = &(*p)->rb_right;
|
||||
|
@ -924,32 +1011,52 @@ static void symbol__free_source_line(struct symbol *sym, int len)
|
|||
{
|
||||
struct annotation *notes = symbol__annotation(sym);
|
||||
struct source_line *src_line = notes->src->lines;
|
||||
size_t sizeof_src_line;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
free(src_line[i].path);
|
||||
sizeof_src_line = sizeof(*src_line) +
|
||||
(sizeof(src_line->p) * (src_line->nr_pcnt - 1));
|
||||
|
||||
free(src_line);
|
||||
for (i = 0; i < len; i++) {
|
||||
free(src_line->path);
|
||||
src_line = (void *)src_line + sizeof_src_line;
|
||||
}
|
||||
|
||||
free(notes->src->lines);
|
||||
notes->src->lines = NULL;
|
||||
}
|
||||
|
||||
/* Get the filename:line for the colored entries */
|
||||
static int symbol__get_source_line(struct symbol *sym, struct map *map,
|
||||
int evidx, struct rb_root *root, int len,
|
||||
struct perf_evsel *evsel,
|
||||
struct rb_root *root, int len,
|
||||
const char *filename)
|
||||
{
|
||||
u64 start;
|
||||
int i;
|
||||
int i, k;
|
||||
int evidx = evsel->idx;
|
||||
char cmd[PATH_MAX * 2];
|
||||
struct source_line *src_line;
|
||||
struct annotation *notes = symbol__annotation(sym);
|
||||
struct sym_hist *h = annotation__histogram(notes, evidx);
|
||||
struct rb_root tmp_root = RB_ROOT;
|
||||
int nr_pcnt = 1;
|
||||
u64 h_sum = h->sum;
|
||||
size_t sizeof_src_line = sizeof(struct source_line);
|
||||
|
||||
if (!h->sum)
|
||||
if (perf_evsel__is_group_event(evsel)) {
|
||||
for (i = 1; i < evsel->nr_members; i++) {
|
||||
h = annotation__histogram(notes, evidx + i);
|
||||
h_sum += h->sum;
|
||||
}
|
||||
nr_pcnt = evsel->nr_members;
|
||||
sizeof_src_line += (nr_pcnt - 1) * sizeof(src_line->p);
|
||||
}
|
||||
|
||||
if (!h_sum)
|
||||
return 0;
|
||||
|
||||
src_line = notes->src->lines = calloc(len, sizeof(struct source_line));
|
||||
src_line = notes->src->lines = calloc(len, sizeof_src_line);
|
||||
if (!notes->src->lines)
|
||||
return -1;
|
||||
|
||||
|
@ -960,29 +1067,41 @@ static int symbol__get_source_line(struct symbol *sym, struct map *map,
|
|||
size_t line_len;
|
||||
u64 offset;
|
||||
FILE *fp;
|
||||
double percent_max = 0.0;
|
||||
|
||||
src_line[i].percent = 100.0 * h->addr[i] / h->sum;
|
||||
if (src_line[i].percent <= 0.5)
|
||||
continue;
|
||||
src_line->nr_pcnt = nr_pcnt;
|
||||
|
||||
for (k = 0; k < nr_pcnt; k++) {
|
||||
h = annotation__histogram(notes, evidx + k);
|
||||
src_line->p[k].percent = 100.0 * h->addr[i] / h->sum;
|
||||
|
||||
if (src_line->p[k].percent > percent_max)
|
||||
percent_max = src_line->p[k].percent;
|
||||
}
|
||||
|
||||
if (percent_max <= 0.5)
|
||||
goto next;
|
||||
|
||||
offset = start + i;
|
||||
sprintf(cmd, "addr2line -e %s %016" PRIx64, filename, offset);
|
||||
fp = popen(cmd, "r");
|
||||
if (!fp)
|
||||
continue;
|
||||
goto next;
|
||||
|
||||
if (getline(&path, &line_len, fp) < 0 || !line_len)
|
||||
goto next;
|
||||
goto next_close;
|
||||
|
||||
src_line[i].path = malloc(sizeof(char) * line_len + 1);
|
||||
if (!src_line[i].path)
|
||||
goto next;
|
||||
src_line->path = malloc(sizeof(char) * line_len + 1);
|
||||
if (!src_line->path)
|
||||
goto next_close;
|
||||
|
||||
strcpy(src_line[i].path, path);
|
||||
insert_source_line(&tmp_root, &src_line[i]);
|
||||
strcpy(src_line->path, path);
|
||||
insert_source_line(&tmp_root, src_line);
|
||||
|
||||
next:
|
||||
next_close:
|
||||
pclose(fp);
|
||||
next:
|
||||
src_line = (void *)src_line + sizeof_src_line;
|
||||
}
|
||||
|
||||
resort_source_line(root, &tmp_root);
|
||||
|
@ -1004,24 +1123,33 @@ static void print_summary(struct rb_root *root, const char *filename)
|
|||
|
||||
node = rb_first(root);
|
||||
while (node) {
|
||||
double percent;
|
||||
double percent, percent_max = 0.0;
|
||||
const char *color;
|
||||
char *path;
|
||||
int i;
|
||||
|
||||
src_line = rb_entry(node, struct source_line, node);
|
||||
percent = src_line->percent_sum;
|
||||
color = get_percent_color(percent);
|
||||
path = src_line->path;
|
||||
for (i = 0; i < src_line->nr_pcnt; i++) {
|
||||
percent = src_line->p[i].percent_sum;
|
||||
color = get_percent_color(percent);
|
||||
color_fprintf(stdout, color, " %7.2f", percent);
|
||||
|
||||
if (percent > percent_max)
|
||||
percent_max = percent;
|
||||
}
|
||||
|
||||
path = src_line->path;
|
||||
color = get_percent_color(percent_max);
|
||||
color_fprintf(stdout, color, " %s", path);
|
||||
|
||||
color_fprintf(stdout, color, " %7.2f %s", percent, path);
|
||||
node = rb_next(node);
|
||||
}
|
||||
}
|
||||
|
||||
static void symbol__annotate_hits(struct symbol *sym, int evidx)
|
||||
static void symbol__annotate_hits(struct symbol *sym, struct perf_evsel *evsel)
|
||||
{
|
||||
struct annotation *notes = symbol__annotation(sym);
|
||||
struct sym_hist *h = annotation__histogram(notes, evidx);
|
||||
struct sym_hist *h = annotation__histogram(notes, evsel->idx);
|
||||
u64 len = symbol__size(sym), offset;
|
||||
|
||||
for (offset = 0; offset < len; ++offset)
|
||||
|
@ -1031,9 +1159,9 @@ static void symbol__annotate_hits(struct symbol *sym, int evidx)
|
|||
printf("%*s: %" PRIu64 "\n", BITS_PER_LONG / 2, "h->sum", h->sum);
|
||||
}
|
||||
|
||||
int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx,
|
||||
bool full_paths, int min_pcnt, int max_lines,
|
||||
int context)
|
||||
int symbol__annotate_printf(struct symbol *sym, struct map *map,
|
||||
struct perf_evsel *evsel, bool full_paths,
|
||||
int min_pcnt, int max_lines, int context)
|
||||
{
|
||||
struct dso *dso = map->dso;
|
||||
char *filename;
|
||||
|
@ -1044,6 +1172,8 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx,
|
|||
int printed = 2, queue_len = 0;
|
||||
int more = 0;
|
||||
u64 len;
|
||||
int width = 8;
|
||||
int namelen;
|
||||
|
||||
filename = strdup(dso->long_name);
|
||||
if (!filename)
|
||||
|
@ -1055,12 +1185,18 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx,
|
|||
d_filename = basename(filename);
|
||||
|
||||
len = symbol__size(sym);
|
||||
namelen = strlen(d_filename);
|
||||
|
||||
printf(" Percent | Source code & Disassembly of %s\n", d_filename);
|
||||
printf("------------------------------------------------\n");
|
||||
if (perf_evsel__is_group_event(evsel))
|
||||
width *= evsel->nr_members;
|
||||
|
||||
printf(" %-*.*s| Source code & Disassembly of %s\n",
|
||||
width, width, "Percent", d_filename);
|
||||
printf("-%-*.*s-------------------------------------\n",
|
||||
width+namelen, width+namelen, graph_dotted_line);
|
||||
|
||||
if (verbose)
|
||||
symbol__annotate_hits(sym, evidx);
|
||||
symbol__annotate_hits(sym, evsel);
|
||||
|
||||
list_for_each_entry(pos, ¬es->src->source, node) {
|
||||
if (context && queue == NULL) {
|
||||
|
@ -1068,7 +1204,7 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx,
|
|||
queue_len = 0;
|
||||
}
|
||||
|
||||
switch (disasm_line__print(pos, sym, start, evidx, len,
|
||||
switch (disasm_line__print(pos, sym, start, evsel, len,
|
||||
min_pcnt, printed, max_lines,
|
||||
queue)) {
|
||||
case 0:
|
||||
|
@ -1163,9 +1299,9 @@ size_t disasm__fprintf(struct list_head *head, FILE *fp)
|
|||
return printed;
|
||||
}
|
||||
|
||||
int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx,
|
||||
bool print_lines, bool full_paths, int min_pcnt,
|
||||
int max_lines)
|
||||
int symbol__tty_annotate(struct symbol *sym, struct map *map,
|
||||
struct perf_evsel *evsel, bool print_lines,
|
||||
bool full_paths, int min_pcnt, int max_lines)
|
||||
{
|
||||
struct dso *dso = map->dso;
|
||||
const char *filename = dso->long_name;
|
||||
|
@ -1178,12 +1314,12 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx,
|
|||
len = symbol__size(sym);
|
||||
|
||||
if (print_lines) {
|
||||
symbol__get_source_line(sym, map, evidx, &source_line,
|
||||
symbol__get_source_line(sym, map, evsel, &source_line,
|
||||
len, filename);
|
||||
print_summary(&source_line, filename);
|
||||
}
|
||||
|
||||
symbol__annotate_printf(sym, map, evidx, full_paths,
|
||||
symbol__annotate_printf(sym, map, evsel, full_paths,
|
||||
min_pcnt, max_lines, 0);
|
||||
if (print_lines)
|
||||
symbol__free_source_line(sym, len);
|
||||
|
|
|
@ -50,6 +50,8 @@ bool ins__is_jump(const struct ins *ins);
|
|||
bool ins__is_call(const struct ins *ins);
|
||||
int ins__scnprintf(struct ins *ins, char *bf, size_t size, struct ins_operands *ops);
|
||||
|
||||
struct annotation;
|
||||
|
||||
struct disasm_line {
|
||||
struct list_head node;
|
||||
s64 offset;
|
||||
|
@ -68,17 +70,24 @@ void disasm_line__free(struct disasm_line *dl);
|
|||
struct disasm_line *disasm__get_next_ip_line(struct list_head *head, struct disasm_line *pos);
|
||||
int disasm_line__scnprintf(struct disasm_line *dl, char *bf, size_t size, bool raw);
|
||||
size_t disasm__fprintf(struct list_head *head, FILE *fp);
|
||||
double disasm__calc_percent(struct annotation *notes, int evidx, s64 offset,
|
||||
s64 end, const char **path);
|
||||
|
||||
struct sym_hist {
|
||||
u64 sum;
|
||||
u64 addr[0];
|
||||
};
|
||||
|
||||
struct source_line {
|
||||
struct rb_node node;
|
||||
struct source_line_percent {
|
||||
double percent;
|
||||
double percent_sum;
|
||||
};
|
||||
|
||||
struct source_line {
|
||||
struct rb_node node;
|
||||
char *path;
|
||||
int nr_pcnt;
|
||||
struct source_line_percent p[1];
|
||||
};
|
||||
|
||||
/** struct annotated_source - symbols with hits have this attached as in sannotation
|
||||
|
@ -130,47 +139,49 @@ void symbol__annotate_zero_histograms(struct symbol *sym);
|
|||
|
||||
int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize);
|
||||
int symbol__annotate_init(struct map *map __maybe_unused, struct symbol *sym);
|
||||
int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx,
|
||||
bool full_paths, int min_pcnt, int max_lines,
|
||||
int context);
|
||||
int symbol__annotate_printf(struct symbol *sym, struct map *map,
|
||||
struct perf_evsel *evsel, bool full_paths,
|
||||
int min_pcnt, int max_lines, int context);
|
||||
void symbol__annotate_zero_histogram(struct symbol *sym, int evidx);
|
||||
void symbol__annotate_decay_histogram(struct symbol *sym, int evidx);
|
||||
void disasm__purge(struct list_head *head);
|
||||
|
||||
int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx,
|
||||
bool print_lines, bool full_paths, int min_pcnt,
|
||||
int max_lines);
|
||||
int symbol__tty_annotate(struct symbol *sym, struct map *map,
|
||||
struct perf_evsel *evsel, bool print_lines,
|
||||
bool full_paths, int min_pcnt, int max_lines);
|
||||
|
||||
#ifdef NEWT_SUPPORT
|
||||
int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
|
||||
#ifdef SLANG_SUPPORT
|
||||
int symbol__tui_annotate(struct symbol *sym, struct map *map,
|
||||
struct perf_evsel *evsel,
|
||||
struct hist_browser_timer *hbt);
|
||||
#else
|
||||
static inline int symbol__tui_annotate(struct symbol *sym __maybe_unused,
|
||||
struct map *map __maybe_unused,
|
||||
int evidx __maybe_unused,
|
||||
struct hist_browser_timer *hbt
|
||||
__maybe_unused)
|
||||
struct map *map __maybe_unused,
|
||||
struct perf_evsel *evsel __maybe_unused,
|
||||
struct hist_browser_timer *hbt
|
||||
__maybe_unused)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef GTK2_SUPPORT
|
||||
int symbol__gtk_annotate(struct symbol *sym, struct map *map, int evidx,
|
||||
int symbol__gtk_annotate(struct symbol *sym, struct map *map,
|
||||
struct perf_evsel *evsel,
|
||||
struct hist_browser_timer *hbt);
|
||||
|
||||
static inline int hist_entry__gtk_annotate(struct hist_entry *he, int evidx,
|
||||
static inline int hist_entry__gtk_annotate(struct hist_entry *he,
|
||||
struct perf_evsel *evsel,
|
||||
struct hist_browser_timer *hbt)
|
||||
{
|
||||
return symbol__gtk_annotate(he->ms.sym, he->ms.map, evidx, hbt);
|
||||
return symbol__gtk_annotate(he->ms.sym, he->ms.map, evsel, 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)
|
||||
struct perf_evsel *evsel __maybe_unused,
|
||||
struct hist_browser_timer *hbt __maybe_unused)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "cpumap.h"
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static struct cpu_map *cpu_map__default_new(void)
|
||||
{
|
||||
|
@ -219,7 +220,7 @@ int cpu_map__get_socket(struct cpu_map *map, int idx)
|
|||
if (!mnt)
|
||||
return -1;
|
||||
|
||||
sprintf(path,
|
||||
snprintf(path, PATH_MAX,
|
||||
"%s/devices/system/cpu/cpu%d/topology/physical_package_id",
|
||||
mnt, cpu);
|
||||
|
||||
|
@ -231,27 +232,88 @@ int cpu_map__get_socket(struct cpu_map *map, int idx)
|
|||
return ret == 1 ? cpu : -1;
|
||||
}
|
||||
|
||||
int cpu_map__build_socket_map(struct cpu_map *cpus, struct cpu_map **sockp)
|
||||
static int cmp_ids(const void *a, const void *b)
|
||||
{
|
||||
struct cpu_map *sock;
|
||||
return *(int *)a - *(int *)b;
|
||||
}
|
||||
|
||||
static int cpu_map__build_map(struct cpu_map *cpus, struct cpu_map **res,
|
||||
int (*f)(struct cpu_map *map, int cpu))
|
||||
{
|
||||
struct cpu_map *c;
|
||||
int nr = cpus->nr;
|
||||
int cpu, s1, s2;
|
||||
|
||||
sock = calloc(1, sizeof(*sock) + nr * sizeof(int));
|
||||
if (!sock)
|
||||
/* allocate as much as possible */
|
||||
c = calloc(1, sizeof(*c) + nr * sizeof(int));
|
||||
if (!c)
|
||||
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])
|
||||
s1 = f(cpus, cpu);
|
||||
for (s2 = 0; s2 < c->nr; s2++) {
|
||||
if (s1 == c->map[s2])
|
||||
break;
|
||||
}
|
||||
if (s2 == sock->nr) {
|
||||
sock->map[sock->nr] = s1;
|
||||
sock->nr++;
|
||||
if (s2 == c->nr) {
|
||||
c->map[c->nr] = s1;
|
||||
c->nr++;
|
||||
}
|
||||
}
|
||||
*sockp = sock;
|
||||
/* ensure we process id in increasing order */
|
||||
qsort(c->map, c->nr, sizeof(int), cmp_ids);
|
||||
|
||||
*res = c;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cpu_map__get_core(struct cpu_map *map, int idx)
|
||||
{
|
||||
FILE *fp;
|
||||
const char *mnt;
|
||||
char path[PATH_MAX];
|
||||
int cpu, ret, s;
|
||||
|
||||
if (idx > map->nr)
|
||||
return -1;
|
||||
|
||||
cpu = map->map[idx];
|
||||
|
||||
mnt = sysfs_find_mountpoint();
|
||||
if (!mnt)
|
||||
return -1;
|
||||
|
||||
snprintf(path, PATH_MAX,
|
||||
"%s/devices/system/cpu/cpu%d/topology/core_id",
|
||||
mnt, cpu);
|
||||
|
||||
fp = fopen(path, "r");
|
||||
if (!fp)
|
||||
return -1;
|
||||
ret = fscanf(fp, "%d", &cpu);
|
||||
fclose(fp);
|
||||
if (ret != 1)
|
||||
return -1;
|
||||
|
||||
s = cpu_map__get_socket(map, idx);
|
||||
if (s == -1)
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* encode socket in upper 16 bits
|
||||
* core_id is relative to socket, and
|
||||
* we need a global id. So we combine
|
||||
* socket+ core id
|
||||
*/
|
||||
return (s << 16) | (cpu & 0xffff);
|
||||
}
|
||||
|
||||
int cpu_map__build_socket_map(struct cpu_map *cpus, struct cpu_map **sockp)
|
||||
{
|
||||
return cpu_map__build_map(cpus, sockp, cpu_map__get_socket);
|
||||
}
|
||||
|
||||
int cpu_map__build_core_map(struct cpu_map *cpus, struct cpu_map **corep)
|
||||
{
|
||||
return cpu_map__build_map(cpus, corep, cpu_map__get_core);
|
||||
}
|
||||
|
|
|
@ -15,7 +15,9 @@ 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__get_core(struct cpu_map *map, int idx);
|
||||
int cpu_map__build_socket_map(struct cpu_map *cpus, struct cpu_map **sockp);
|
||||
int cpu_map__build_core_map(struct cpu_map *cpus, struct cpu_map **corep);
|
||||
|
||||
static inline int cpu_map__socket(struct cpu_map *sock, int s)
|
||||
{
|
||||
|
@ -24,6 +26,16 @@ static inline int cpu_map__socket(struct cpu_map *sock, int s)
|
|||
return sock->map[s];
|
||||
}
|
||||
|
||||
static inline int cpu_map__id_to_socket(int id)
|
||||
{
|
||||
return id >> 16;
|
||||
}
|
||||
|
||||
static inline int cpu_map__id_to_cpu(int id)
|
||||
{
|
||||
return id & 0xffff;
|
||||
}
|
||||
|
||||
static inline int cpu_map__nr(const struct cpu_map *map)
|
||||
{
|
||||
return map ? map->nr : 1;
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
#ifndef __DEBUGFS_H__
|
||||
#define __DEBUGFS_H__
|
||||
|
||||
const char *debugfs_find_mountpoint(void);
|
||||
int debugfs_valid_mountpoint(const char *debugfs);
|
||||
char *debugfs_mount(const char *mountpoint);
|
||||
void debugfs_set_path(const char *mountpoint);
|
||||
|
||||
extern char debugfs_mountpoint[];
|
||||
extern char tracing_events_path[];
|
||||
|
||||
#endif /* __DEBUGFS_H__ */
|
|
@ -88,8 +88,10 @@ struct perf_sample {
|
|||
u64 id;
|
||||
u64 stream_id;
|
||||
u64 period;
|
||||
u64 weight;
|
||||
u32 cpu;
|
||||
u32 raw_size;
|
||||
u64 data_src;
|
||||
void *raw_data;
|
||||
struct ip_callchain *callchain;
|
||||
struct branch_stack *branch_stack;
|
||||
|
@ -97,6 +99,13 @@ struct perf_sample {
|
|||
struct stack_dump user_stack;
|
||||
};
|
||||
|
||||
#define PERF_MEM_DATA_SRC_NONE \
|
||||
(PERF_MEM_S(OP, NA) |\
|
||||
PERF_MEM_S(LVL, NA) |\
|
||||
PERF_MEM_S(SNOOP, NA) |\
|
||||
PERF_MEM_S(LOCK, NA) |\
|
||||
PERF_MEM_S(TLB, NA))
|
||||
|
||||
struct build_id_event {
|
||||
struct perf_event_header header;
|
||||
pid_t pid;
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* Released under the GPL v2. (and only v2, not any later version)
|
||||
*/
|
||||
#include "util.h"
|
||||
#include "debugfs.h"
|
||||
#include <lk/debugfs.h>
|
||||
#include <poll.h>
|
||||
#include "cpumap.h"
|
||||
#include "thread_map.h"
|
||||
|
@ -38,13 +38,12 @@ void perf_evlist__init(struct perf_evlist *evlist, struct cpu_map *cpus,
|
|||
evlist->workload.pid = -1;
|
||||
}
|
||||
|
||||
struct perf_evlist *perf_evlist__new(struct cpu_map *cpus,
|
||||
struct thread_map *threads)
|
||||
struct perf_evlist *perf_evlist__new(void)
|
||||
{
|
||||
struct perf_evlist *evlist = zalloc(sizeof(*evlist));
|
||||
|
||||
if (evlist != NULL)
|
||||
perf_evlist__init(evlist, cpus, threads);
|
||||
perf_evlist__init(evlist, NULL, NULL);
|
||||
|
||||
return evlist;
|
||||
}
|
||||
|
@ -228,12 +227,14 @@ void perf_evlist__disable(struct perf_evlist *evlist)
|
|||
{
|
||||
int cpu, thread;
|
||||
struct perf_evsel *pos;
|
||||
int nr_cpus = cpu_map__nr(evlist->cpus);
|
||||
int nr_threads = thread_map__nr(evlist->threads);
|
||||
|
||||
for (cpu = 0; cpu < evlist->cpus->nr; cpu++) {
|
||||
for (cpu = 0; cpu < nr_cpus; cpu++) {
|
||||
list_for_each_entry(pos, &evlist->entries, node) {
|
||||
if (!perf_evsel__is_group_leader(pos))
|
||||
continue;
|
||||
for (thread = 0; thread < evlist->threads->nr; thread++)
|
||||
for (thread = 0; thread < nr_threads; thread++)
|
||||
ioctl(FD(pos, cpu, thread),
|
||||
PERF_EVENT_IOC_DISABLE, 0);
|
||||
}
|
||||
|
@ -244,12 +245,14 @@ void perf_evlist__enable(struct perf_evlist *evlist)
|
|||
{
|
||||
int cpu, thread;
|
||||
struct perf_evsel *pos;
|
||||
int nr_cpus = cpu_map__nr(evlist->cpus);
|
||||
int nr_threads = thread_map__nr(evlist->threads);
|
||||
|
||||
for (cpu = 0; cpu < cpu_map__nr(evlist->cpus); cpu++) {
|
||||
for (cpu = 0; cpu < nr_cpus; cpu++) {
|
||||
list_for_each_entry(pos, &evlist->entries, node) {
|
||||
if (!perf_evsel__is_group_leader(pos))
|
||||
continue;
|
||||
for (thread = 0; thread < evlist->threads->nr; thread++)
|
||||
for (thread = 0; thread < nr_threads; thread++)
|
||||
ioctl(FD(pos, cpu, thread),
|
||||
PERF_EVENT_IOC_ENABLE, 0);
|
||||
}
|
||||
|
@ -258,7 +261,9 @@ void perf_evlist__enable(struct perf_evlist *evlist)
|
|||
|
||||
static int perf_evlist__alloc_pollfd(struct perf_evlist *evlist)
|
||||
{
|
||||
int nfds = cpu_map__nr(evlist->cpus) * evlist->threads->nr * evlist->nr_entries;
|
||||
int nr_cpus = cpu_map__nr(evlist->cpus);
|
||||
int nr_threads = thread_map__nr(evlist->threads);
|
||||
int nfds = nr_cpus * nr_threads * evlist->nr_entries;
|
||||
evlist->pollfd = malloc(sizeof(struct pollfd) * nfds);
|
||||
return evlist->pollfd != NULL ? 0 : -ENOMEM;
|
||||
}
|
||||
|
@ -417,7 +422,7 @@ static int perf_evlist__alloc_mmap(struct perf_evlist *evlist)
|
|||
{
|
||||
evlist->nr_mmaps = cpu_map__nr(evlist->cpus);
|
||||
if (cpu_map__all(evlist->cpus))
|
||||
evlist->nr_mmaps = evlist->threads->nr;
|
||||
evlist->nr_mmaps = thread_map__nr(evlist->threads);
|
||||
evlist->mmap = zalloc(evlist->nr_mmaps * sizeof(struct perf_mmap));
|
||||
return evlist->mmap != NULL ? 0 : -ENOMEM;
|
||||
}
|
||||
|
@ -442,11 +447,13 @@ static int perf_evlist__mmap_per_cpu(struct perf_evlist *evlist, int prot, int m
|
|||
{
|
||||
struct perf_evsel *evsel;
|
||||
int cpu, thread;
|
||||
int nr_cpus = cpu_map__nr(evlist->cpus);
|
||||
int nr_threads = thread_map__nr(evlist->threads);
|
||||
|
||||
for (cpu = 0; cpu < evlist->cpus->nr; cpu++) {
|
||||
for (cpu = 0; cpu < nr_cpus; cpu++) {
|
||||
int output = -1;
|
||||
|
||||
for (thread = 0; thread < evlist->threads->nr; thread++) {
|
||||
for (thread = 0; thread < nr_threads; thread++) {
|
||||
list_for_each_entry(evsel, &evlist->entries, node) {
|
||||
int fd = FD(evsel, cpu, thread);
|
||||
|
||||
|
@ -470,7 +477,7 @@ static int perf_evlist__mmap_per_cpu(struct perf_evlist *evlist, int prot, int m
|
|||
return 0;
|
||||
|
||||
out_unmap:
|
||||
for (cpu = 0; cpu < evlist->cpus->nr; cpu++) {
|
||||
for (cpu = 0; cpu < nr_cpus; cpu++) {
|
||||
if (evlist->mmap[cpu].base != NULL) {
|
||||
munmap(evlist->mmap[cpu].base, evlist->mmap_len);
|
||||
evlist->mmap[cpu].base = NULL;
|
||||
|
@ -483,8 +490,9 @@ static int perf_evlist__mmap_per_thread(struct perf_evlist *evlist, int prot, in
|
|||
{
|
||||
struct perf_evsel *evsel;
|
||||
int thread;
|
||||
int nr_threads = thread_map__nr(evlist->threads);
|
||||
|
||||
for (thread = 0; thread < evlist->threads->nr; thread++) {
|
||||
for (thread = 0; thread < nr_threads; thread++) {
|
||||
int output = -1;
|
||||
|
||||
list_for_each_entry(evsel, &evlist->entries, node) {
|
||||
|
@ -509,7 +517,7 @@ static int perf_evlist__mmap_per_thread(struct perf_evlist *evlist, int prot, in
|
|||
return 0;
|
||||
|
||||
out_unmap:
|
||||
for (thread = 0; thread < evlist->threads->nr; thread++) {
|
||||
for (thread = 0; thread < nr_threads; thread++) {
|
||||
if (evlist->mmap[thread].base != NULL) {
|
||||
munmap(evlist->mmap[thread].base, evlist->mmap_len);
|
||||
evlist->mmap[thread].base = NULL;
|
||||
|
@ -610,7 +618,7 @@ int perf_evlist__apply_filters(struct perf_evlist *evlist)
|
|||
struct perf_evsel *evsel;
|
||||
int err = 0;
|
||||
const int ncpus = cpu_map__nr(evlist->cpus),
|
||||
nthreads = evlist->threads->nr;
|
||||
nthreads = thread_map__nr(evlist->threads);
|
||||
|
||||
list_for_each_entry(evsel, &evlist->entries, node) {
|
||||
if (evsel->filter == NULL)
|
||||
|
@ -629,7 +637,7 @@ int perf_evlist__set_filter(struct perf_evlist *evlist, const char *filter)
|
|||
struct perf_evsel *evsel;
|
||||
int err = 0;
|
||||
const int ncpus = cpu_map__nr(evlist->cpus),
|
||||
nthreads = evlist->threads->nr;
|
||||
nthreads = thread_map__nr(evlist->threads);
|
||||
|
||||
list_for_each_entry(evsel, &evlist->entries, node) {
|
||||
err = perf_evsel__set_filter(evsel, ncpus, nthreads, filter);
|
||||
|
@ -712,10 +720,20 @@ void perf_evlist__set_selected(struct perf_evlist *evlist,
|
|||
evlist->selected = evsel;
|
||||
}
|
||||
|
||||
void perf_evlist__close(struct perf_evlist *evlist)
|
||||
{
|
||||
struct perf_evsel *evsel;
|
||||
int ncpus = cpu_map__nr(evlist->cpus);
|
||||
int nthreads = thread_map__nr(evlist->threads);
|
||||
|
||||
list_for_each_entry_reverse(evsel, &evlist->entries, node)
|
||||
perf_evsel__close(evsel, ncpus, nthreads);
|
||||
}
|
||||
|
||||
int perf_evlist__open(struct perf_evlist *evlist)
|
||||
{
|
||||
struct perf_evsel *evsel;
|
||||
int err, ncpus, nthreads;
|
||||
int err;
|
||||
|
||||
list_for_each_entry(evsel, &evlist->entries, node) {
|
||||
err = perf_evsel__open(evsel, evlist->cpus, evlist->threads);
|
||||
|
@ -725,19 +743,15 @@ int perf_evlist__open(struct perf_evlist *evlist)
|
|||
|
||||
return 0;
|
||||
out_err:
|
||||
ncpus = evlist->cpus ? evlist->cpus->nr : 1;
|
||||
nthreads = evlist->threads ? evlist->threads->nr : 1;
|
||||
|
||||
list_for_each_entry_reverse(evsel, &evlist->entries, node)
|
||||
perf_evsel__close(evsel, ncpus, nthreads);
|
||||
|
||||
perf_evlist__close(evlist);
|
||||
errno = -err;
|
||||
return err;
|
||||
}
|
||||
|
||||
int perf_evlist__prepare_workload(struct perf_evlist *evlist,
|
||||
struct perf_record_opts *opts,
|
||||
const char *argv[])
|
||||
struct perf_target *target,
|
||||
const char *argv[], bool pipe_output,
|
||||
bool want_signal)
|
||||
{
|
||||
int child_ready_pipe[2], go_pipe[2];
|
||||
char bf;
|
||||
|
@ -759,7 +773,7 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist,
|
|||
}
|
||||
|
||||
if (!evlist->workload.pid) {
|
||||
if (opts->pipe_output)
|
||||
if (pipe_output)
|
||||
dup2(2, 1);
|
||||
|
||||
close(child_ready_pipe[0]);
|
||||
|
@ -787,11 +801,12 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist,
|
|||
execvp(argv[0], (char **)argv);
|
||||
|
||||
perror(argv[0]);
|
||||
kill(getppid(), SIGUSR1);
|
||||
if (want_signal)
|
||||
kill(getppid(), SIGUSR1);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (perf_target__none(&opts->target))
|
||||
if (perf_target__none(target))
|
||||
evlist->threads->map[0] = evlist->workload.pid;
|
||||
|
||||
close(child_ready_pipe[1]);
|
||||
|
|
|
@ -49,8 +49,7 @@ struct perf_evsel_str_handler {
|
|||
void *handler;
|
||||
};
|
||||
|
||||
struct perf_evlist *perf_evlist__new(struct cpu_map *cpus,
|
||||
struct thread_map *threads);
|
||||
struct perf_evlist *perf_evlist__new(void);
|
||||
void perf_evlist__init(struct perf_evlist *evlist, struct cpu_map *cpus,
|
||||
struct thread_map *threads);
|
||||
void perf_evlist__exit(struct perf_evlist *evlist);
|
||||
|
@ -82,13 +81,15 @@ struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id);
|
|||
union perf_event *perf_evlist__mmap_read(struct perf_evlist *self, int idx);
|
||||
|
||||
int perf_evlist__open(struct perf_evlist *evlist);
|
||||
void perf_evlist__close(struct perf_evlist *evlist);
|
||||
|
||||
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,
|
||||
const char *argv[]);
|
||||
struct perf_target *target,
|
||||
const char *argv[], bool pipe_output,
|
||||
bool want_signal);
|
||||
int perf_evlist__start_workload(struct perf_evlist *evlist);
|
||||
|
||||
int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages,
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
#include <byteswap.h>
|
||||
#include <linux/bitops.h>
|
||||
#include "asm/bug.h"
|
||||
#include "debugfs.h"
|
||||
#include <lk/debugfs.h>
|
||||
#include "event-parse.h"
|
||||
#include "evsel.h"
|
||||
#include "evlist.h"
|
||||
|
@ -554,6 +554,9 @@ void perf_evsel__config(struct perf_evsel *evsel,
|
|||
perf_evsel__set_sample_bit(evsel, CPU);
|
||||
}
|
||||
|
||||
if (opts->sample_address)
|
||||
attr->sample_type |= PERF_SAMPLE_DATA_SRC;
|
||||
|
||||
if (opts->no_delay) {
|
||||
attr->watermark = 0;
|
||||
attr->wakeup_events = 1;
|
||||
|
@ -563,6 +566,9 @@ void perf_evsel__config(struct perf_evsel *evsel,
|
|||
attr->branch_sample_type = opts->branch_stack;
|
||||
}
|
||||
|
||||
if (opts->sample_weight)
|
||||
attr->sample_type |= PERF_SAMPLE_WEIGHT;
|
||||
|
||||
attr->mmap = track;
|
||||
attr->comm = track;
|
||||
|
||||
|
@ -633,6 +639,12 @@ int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads)
|
|||
return 0;
|
||||
}
|
||||
|
||||
void perf_evsel__reset_counts(struct perf_evsel *evsel, int ncpus)
|
||||
{
|
||||
memset(evsel->counts, 0, (sizeof(*evsel->counts) +
|
||||
(ncpus * sizeof(struct perf_counts_values))));
|
||||
}
|
||||
|
||||
int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus)
|
||||
{
|
||||
evsel->counts = zalloc((sizeof(*evsel->counts) +
|
||||
|
@ -673,9 +685,8 @@ void perf_evsel__free_counts(struct perf_evsel *evsel)
|
|||
void perf_evsel__exit(struct perf_evsel *evsel)
|
||||
{
|
||||
assert(list_empty(&evsel->node));
|
||||
xyarray__delete(evsel->fd);
|
||||
xyarray__delete(evsel->sample_id);
|
||||
free(evsel->id);
|
||||
perf_evsel__free_fd(evsel);
|
||||
perf_evsel__free_id(evsel);
|
||||
}
|
||||
|
||||
void perf_evsel__delete(struct perf_evsel *evsel)
|
||||
|
@ -1012,6 +1023,7 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,
|
|||
data->cpu = data->pid = data->tid = -1;
|
||||
data->stream_id = data->id = data->time = -1ULL;
|
||||
data->period = 1;
|
||||
data->weight = 0;
|
||||
|
||||
if (event->header.type != PERF_RECORD_SAMPLE) {
|
||||
if (!evsel->attr.sample_id_all)
|
||||
|
@ -1162,6 +1174,18 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,
|
|||
}
|
||||
}
|
||||
|
||||
data->weight = 0;
|
||||
if (type & PERF_SAMPLE_WEIGHT) {
|
||||
data->weight = *array;
|
||||
array++;
|
||||
}
|
||||
|
||||
data->data_src = PERF_MEM_DATA_SRC_NONE;
|
||||
if (type & PERF_SAMPLE_DATA_SRC) {
|
||||
data->data_src = *array;
|
||||
array++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "xyarray.h"
|
||||
#include "cgroup.h"
|
||||
#include "hist.h"
|
||||
#include "symbol.h"
|
||||
|
||||
struct perf_counts_values {
|
||||
union {
|
||||
|
@ -120,6 +121,7 @@ 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__reset_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);
|
||||
|
@ -246,11 +248,34 @@ static inline struct perf_evsel *perf_evsel__next(struct perf_evsel *evsel)
|
|||
return list_entry(evsel->node.next, struct perf_evsel, node);
|
||||
}
|
||||
|
||||
/**
|
||||
* perf_evsel__is_group_leader - Return whether given evsel is a leader event
|
||||
*
|
||||
* @evsel - evsel selector to be tested
|
||||
*
|
||||
* Return %true if @evsel is a group leader or a stand-alone event
|
||||
*/
|
||||
static inline bool perf_evsel__is_group_leader(const struct perf_evsel *evsel)
|
||||
{
|
||||
return evsel->leader == evsel;
|
||||
}
|
||||
|
||||
/**
|
||||
* perf_evsel__is_group_event - Return whether given evsel is a group event
|
||||
*
|
||||
* @evsel - evsel selector to be tested
|
||||
*
|
||||
* Return %true iff event group view is enabled and @evsel is a actual group
|
||||
* leader which has other members in the group
|
||||
*/
|
||||
static inline bool perf_evsel__is_group_event(struct perf_evsel *evsel)
|
||||
{
|
||||
if (!symbol_conf.event_group)
|
||||
return false;
|
||||
|
||||
return perf_evsel__is_group_leader(evsel) && evsel->nr_members > 1;
|
||||
}
|
||||
|
||||
struct perf_attr_details {
|
||||
bool freq;
|
||||
bool verbose;
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#define _FILE_OFFSET_BITS 64
|
||||
|
||||
#include "util.h"
|
||||
#include <sys/types.h>
|
||||
#include <byteswap.h>
|
||||
|
@ -1672,8 +1670,8 @@ static int process_tracing_data(struct perf_file_section *section __maybe_unused
|
|||
struct perf_header *ph __maybe_unused,
|
||||
int fd, void *data)
|
||||
{
|
||||
trace_report(fd, data, false);
|
||||
return 0;
|
||||
ssize_t ret = trace_report(fd, data, false);
|
||||
return ret < 0 ? -1 : 0;
|
||||
}
|
||||
|
||||
static int process_build_id(struct perf_file_section *section,
|
||||
|
@ -2752,6 +2750,11 @@ static int perf_evsel__prepare_tracepoint_event(struct perf_evsel *evsel,
|
|||
if (evsel->tp_format)
|
||||
return 0;
|
||||
|
||||
if (pevent == NULL) {
|
||||
pr_debug("broken or missing trace data\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
event = pevent_find_event(pevent, evsel->attr.config);
|
||||
if (event == NULL)
|
||||
return -1;
|
||||
|
@ -2789,7 +2792,7 @@ int perf_session__read_header(struct perf_session *session, int fd)
|
|||
u64 f_id;
|
||||
int nr_attrs, nr_ids, i, j;
|
||||
|
||||
session->evlist = perf_evlist__new(NULL, NULL);
|
||||
session->evlist = perf_evlist__new();
|
||||
if (session->evlist == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -2940,7 +2943,7 @@ int perf_event__process_attr(union perf_event *event,
|
|||
struct perf_evlist *evlist = *pevlist;
|
||||
|
||||
if (evlist == NULL) {
|
||||
*pevlist = evlist = perf_evlist__new(NULL, NULL);
|
||||
*pevlist = evlist = perf_evlist__new();
|
||||
if (evlist == NULL)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
|
|
@ -67,12 +67,16 @@ static void hists__set_unres_dso_col_len(struct hists *hists, int dso)
|
|||
void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
|
||||
{
|
||||
const unsigned int unresolved_col_width = BITS_PER_LONG / 4;
|
||||
int symlen;
|
||||
u16 len;
|
||||
|
||||
if (h->ms.sym)
|
||||
hists__new_col_len(hists, HISTC_SYMBOL, h->ms.sym->namelen + 4);
|
||||
else
|
||||
else {
|
||||
symlen = unresolved_col_width + 4 + 2;
|
||||
hists__new_col_len(hists, HISTC_SYMBOL, symlen);
|
||||
hists__set_unres_dso_col_len(hists, HISTC_DSO);
|
||||
}
|
||||
|
||||
len = thread__comm_len(h->thread);
|
||||
if (hists__new_col_len(hists, HISTC_COMM, len))
|
||||
|
@ -87,7 +91,6 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
|
|||
hists__new_col_len(hists, HISTC_PARENT, h->parent->namelen);
|
||||
|
||||
if (h->branch_info) {
|
||||
int symlen;
|
||||
/*
|
||||
* +4 accounts for '[x] ' priv level info
|
||||
* +2 account of 0x prefix on raw addresses
|
||||
|
@ -116,6 +119,42 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
|
|||
hists__set_unres_dso_col_len(hists, HISTC_DSO_TO);
|
||||
}
|
||||
}
|
||||
|
||||
if (h->mem_info) {
|
||||
/*
|
||||
* +4 accounts for '[x] ' priv level info
|
||||
* +2 account of 0x prefix on raw addresses
|
||||
*/
|
||||
if (h->mem_info->daddr.sym) {
|
||||
symlen = (int)h->mem_info->daddr.sym->namelen + 4
|
||||
+ unresolved_col_width + 2;
|
||||
hists__new_col_len(hists, HISTC_MEM_DADDR_SYMBOL,
|
||||
symlen);
|
||||
} else {
|
||||
symlen = unresolved_col_width + 4 + 2;
|
||||
hists__new_col_len(hists, HISTC_MEM_DADDR_SYMBOL,
|
||||
symlen);
|
||||
}
|
||||
if (h->mem_info->daddr.map) {
|
||||
symlen = dso__name_len(h->mem_info->daddr.map->dso);
|
||||
hists__new_col_len(hists, HISTC_MEM_DADDR_DSO,
|
||||
symlen);
|
||||
} else {
|
||||
symlen = unresolved_col_width + 4 + 2;
|
||||
hists__set_unres_dso_col_len(hists, HISTC_MEM_DADDR_DSO);
|
||||
}
|
||||
} else {
|
||||
symlen = unresolved_col_width + 4 + 2;
|
||||
hists__new_col_len(hists, HISTC_MEM_DADDR_SYMBOL, symlen);
|
||||
hists__set_unres_dso_col_len(hists, HISTC_MEM_DADDR_DSO);
|
||||
}
|
||||
|
||||
hists__new_col_len(hists, HISTC_MEM_LOCKED, 6);
|
||||
hists__new_col_len(hists, HISTC_MEM_TLB, 22);
|
||||
hists__new_col_len(hists, HISTC_MEM_SNOOP, 12);
|
||||
hists__new_col_len(hists, HISTC_MEM_LVL, 21 + 3);
|
||||
hists__new_col_len(hists, HISTC_LOCAL_WEIGHT, 12);
|
||||
hists__new_col_len(hists, HISTC_GLOBAL_WEIGHT, 12);
|
||||
}
|
||||
|
||||
void hists__output_recalc_col_len(struct hists *hists, int max_rows)
|
||||
|
@ -155,9 +194,12 @@ static void hist_entry__add_cpumode_period(struct hist_entry *he,
|
|||
}
|
||||
}
|
||||
|
||||
static void he_stat__add_period(struct he_stat *he_stat, u64 period)
|
||||
static void he_stat__add_period(struct he_stat *he_stat, u64 period,
|
||||
u64 weight)
|
||||
{
|
||||
|
||||
he_stat->period += period;
|
||||
he_stat->weight += weight;
|
||||
he_stat->nr_events += 1;
|
||||
}
|
||||
|
||||
|
@ -169,12 +211,14 @@ static void he_stat__add_stat(struct he_stat *dest, struct he_stat *src)
|
|||
dest->period_guest_sys += src->period_guest_sys;
|
||||
dest->period_guest_us += src->period_guest_us;
|
||||
dest->nr_events += src->nr_events;
|
||||
dest->weight += src->weight;
|
||||
}
|
||||
|
||||
static void hist_entry__decay(struct hist_entry *he)
|
||||
{
|
||||
he->stat.period = (he->stat.period * 7) / 8;
|
||||
he->stat.nr_events = (he->stat.nr_events * 7) / 8;
|
||||
/* XXX need decay for weight too? */
|
||||
}
|
||||
|
||||
static bool hists__decay_entry(struct hists *hists, struct hist_entry *he)
|
||||
|
@ -239,7 +283,7 @@ void hists__decay_entries_threaded(struct hists *hists,
|
|||
static struct hist_entry *hist_entry__new(struct hist_entry *template)
|
||||
{
|
||||
size_t callchain_size = symbol_conf.use_callchain ? sizeof(struct callchain_root) : 0;
|
||||
struct hist_entry *he = malloc(sizeof(*he) + callchain_size);
|
||||
struct hist_entry *he = zalloc(sizeof(*he) + callchain_size);
|
||||
|
||||
if (he != NULL) {
|
||||
*he = *template;
|
||||
|
@ -254,6 +298,13 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template)
|
|||
he->branch_info->to.map->referenced = true;
|
||||
}
|
||||
|
||||
if (he->mem_info) {
|
||||
if (he->mem_info->iaddr.map)
|
||||
he->mem_info->iaddr.map->referenced = true;
|
||||
if (he->mem_info->daddr.map)
|
||||
he->mem_info->daddr.map->referenced = true;
|
||||
}
|
||||
|
||||
if (symbol_conf.use_callchain)
|
||||
callchain_init(he->callchain);
|
||||
|
||||
|
@ -282,7 +333,8 @@ static u8 symbol__parent_filter(const struct symbol *parent)
|
|||
static struct hist_entry *add_hist_entry(struct hists *hists,
|
||||
struct hist_entry *entry,
|
||||
struct addr_location *al,
|
||||
u64 period)
|
||||
u64 period,
|
||||
u64 weight)
|
||||
{
|
||||
struct rb_node **p;
|
||||
struct rb_node *parent = NULL;
|
||||
|
@ -306,7 +358,7 @@ static struct hist_entry *add_hist_entry(struct hists *hists,
|
|||
cmp = hist_entry__cmp(he, entry);
|
||||
|
||||
if (!cmp) {
|
||||
he_stat__add_period(&he->stat, period);
|
||||
he_stat__add_period(&he->stat, period, weight);
|
||||
|
||||
/* If the map of an existing hist_entry has
|
||||
* become out-of-date due to an exec() or
|
||||
|
@ -341,11 +393,42 @@ out_unlock:
|
|||
return he;
|
||||
}
|
||||
|
||||
struct hist_entry *__hists__add_mem_entry(struct hists *self,
|
||||
struct addr_location *al,
|
||||
struct symbol *sym_parent,
|
||||
struct mem_info *mi,
|
||||
u64 period,
|
||||
u64 weight)
|
||||
{
|
||||
struct hist_entry entry = {
|
||||
.thread = al->thread,
|
||||
.ms = {
|
||||
.map = al->map,
|
||||
.sym = al->sym,
|
||||
},
|
||||
.stat = {
|
||||
.period = period,
|
||||
.weight = weight,
|
||||
.nr_events = 1,
|
||||
},
|
||||
.cpu = al->cpu,
|
||||
.ip = al->addr,
|
||||
.level = al->level,
|
||||
.parent = sym_parent,
|
||||
.filtered = symbol__parent_filter(sym_parent),
|
||||
.hists = self,
|
||||
.mem_info = mi,
|
||||
.branch_info = NULL,
|
||||
};
|
||||
return add_hist_entry(self, &entry, al, period, weight);
|
||||
}
|
||||
|
||||
struct hist_entry *__hists__add_branch_entry(struct hists *self,
|
||||
struct addr_location *al,
|
||||
struct symbol *sym_parent,
|
||||
struct branch_info *bi,
|
||||
u64 period)
|
||||
u64 period,
|
||||
u64 weight)
|
||||
{
|
||||
struct hist_entry entry = {
|
||||
.thread = al->thread,
|
||||
|
@ -359,19 +442,22 @@ struct hist_entry *__hists__add_branch_entry(struct hists *self,
|
|||
.stat = {
|
||||
.period = period,
|
||||
.nr_events = 1,
|
||||
.weight = weight,
|
||||
},
|
||||
.parent = sym_parent,
|
||||
.filtered = symbol__parent_filter(sym_parent),
|
||||
.branch_info = bi,
|
||||
.hists = self,
|
||||
.mem_info = NULL,
|
||||
};
|
||||
|
||||
return add_hist_entry(self, &entry, al, period);
|
||||
return add_hist_entry(self, &entry, al, period, weight);
|
||||
}
|
||||
|
||||
struct hist_entry *__hists__add_entry(struct hists *self,
|
||||
struct addr_location *al,
|
||||
struct symbol *sym_parent, u64 period)
|
||||
struct symbol *sym_parent, u64 period,
|
||||
u64 weight)
|
||||
{
|
||||
struct hist_entry entry = {
|
||||
.thread = al->thread,
|
||||
|
@ -385,13 +471,16 @@ struct hist_entry *__hists__add_entry(struct hists *self,
|
|||
.stat = {
|
||||
.period = period,
|
||||
.nr_events = 1,
|
||||
.weight = weight,
|
||||
},
|
||||
.parent = sym_parent,
|
||||
.filtered = symbol__parent_filter(sym_parent),
|
||||
.hists = self,
|
||||
.branch_info = NULL,
|
||||
.mem_info = NULL,
|
||||
};
|
||||
|
||||
return add_hist_entry(self, &entry, al, period);
|
||||
return add_hist_entry(self, &entry, al, period, weight);
|
||||
}
|
||||
|
||||
int64_t
|
||||
|
@ -431,6 +520,7 @@ hist_entry__collapse(struct hist_entry *left, struct hist_entry *right)
|
|||
void hist_entry__free(struct hist_entry *he)
|
||||
{
|
||||
free(he->branch_info);
|
||||
free(he->mem_info);
|
||||
free(he);
|
||||
}
|
||||
|
||||
|
|
|
@ -49,6 +49,14 @@ enum hist_column {
|
|||
HISTC_DSO_FROM,
|
||||
HISTC_DSO_TO,
|
||||
HISTC_SRCLINE,
|
||||
HISTC_LOCAL_WEIGHT,
|
||||
HISTC_GLOBAL_WEIGHT,
|
||||
HISTC_MEM_DADDR_SYMBOL,
|
||||
HISTC_MEM_DADDR_DSO,
|
||||
HISTC_MEM_LOCKED,
|
||||
HISTC_MEM_TLB,
|
||||
HISTC_MEM_LVL,
|
||||
HISTC_MEM_SNOOP,
|
||||
HISTC_NR_COLS, /* Last entry */
|
||||
};
|
||||
|
||||
|
@ -73,7 +81,8 @@ struct hists {
|
|||
|
||||
struct hist_entry *__hists__add_entry(struct hists *self,
|
||||
struct addr_location *al,
|
||||
struct symbol *parent, u64 period);
|
||||
struct symbol *parent, u64 period,
|
||||
u64 weight);
|
||||
int64_t hist_entry__cmp(struct hist_entry *left, struct hist_entry *right);
|
||||
int64_t hist_entry__collapse(struct hist_entry *left, struct hist_entry *right);
|
||||
int hist_entry__sort_snprintf(struct hist_entry *self, char *bf, size_t size,
|
||||
|
@ -84,7 +93,15 @@ struct hist_entry *__hists__add_branch_entry(struct hists *self,
|
|||
struct addr_location *al,
|
||||
struct symbol *sym_parent,
|
||||
struct branch_info *bi,
|
||||
u64 period);
|
||||
u64 period,
|
||||
u64 weight);
|
||||
|
||||
struct hist_entry *__hists__add_mem_entry(struct hists *self,
|
||||
struct addr_location *al,
|
||||
struct symbol *sym_parent,
|
||||
struct mem_info *mi,
|
||||
u64 period,
|
||||
u64 weight);
|
||||
|
||||
void hists__output_resort(struct hists *self);
|
||||
void hists__output_resort_threaded(struct hists *hists);
|
||||
|
@ -175,9 +192,9 @@ struct hist_browser_timer {
|
|||
int refresh;
|
||||
};
|
||||
|
||||
#ifdef NEWT_SUPPORT
|
||||
#ifdef SLANG_SUPPORT
|
||||
#include "../ui/keysyms.h"
|
||||
int hist_entry__tui_annotate(struct hist_entry *he, int evidx,
|
||||
int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
|
||||
struct hist_browser_timer *hbt);
|
||||
|
||||
int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
|
||||
|
@ -196,7 +213,8 @@ int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __maybe_unused,
|
|||
|
||||
static inline int hist_entry__tui_annotate(struct hist_entry *self
|
||||
__maybe_unused,
|
||||
int evidx __maybe_unused,
|
||||
struct perf_evsel *evsel
|
||||
__maybe_unused,
|
||||
struct hist_browser_timer *hbt
|
||||
__maybe_unused)
|
||||
{
|
||||
|
|
|
@ -955,6 +955,7 @@ int machine__process_mmap_event(struct machine *machine, union perf_event *event
|
|||
u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
|
||||
struct thread *thread;
|
||||
struct map *map;
|
||||
enum map_type type;
|
||||
int ret = 0;
|
||||
|
||||
if (dump_trace)
|
||||
|
@ -971,10 +972,17 @@ int machine__process_mmap_event(struct machine *machine, union perf_event *event
|
|||
thread = machine__findnew_thread(machine, event->mmap.pid);
|
||||
if (thread == NULL)
|
||||
goto out_problem;
|
||||
|
||||
if (event->header.misc & PERF_RECORD_MISC_MMAP_DATA)
|
||||
type = MAP__VARIABLE;
|
||||
else
|
||||
type = MAP__FUNCTION;
|
||||
|
||||
map = map__new(&machine->user_dsos, event->mmap.start,
|
||||
event->mmap.len, event->mmap.pgoff,
|
||||
event->mmap.pid, event->mmap.filename,
|
||||
MAP__FUNCTION);
|
||||
type);
|
||||
|
||||
if (map == NULL)
|
||||
goto out_problem;
|
||||
|
||||
|
@ -1003,6 +1011,17 @@ int machine__process_fork_event(struct machine *machine, union perf_event *event
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void machine__remove_thread(struct machine *machine, struct thread *th)
|
||||
{
|
||||
machine->last_match = NULL;
|
||||
rb_erase(&th->rb_node, &machine->threads);
|
||||
/*
|
||||
* We may have references to this thread, for instance in some hist_entry
|
||||
* instances, so just move them to a separate list.
|
||||
*/
|
||||
list_add_tail(&th->node, &machine->dead_threads);
|
||||
}
|
||||
|
||||
int machine__process_exit_event(struct machine *machine, union perf_event *event)
|
||||
{
|
||||
struct thread *thread = machine__find_thread(machine, event->fork.tid);
|
||||
|
@ -1039,17 +1058,6 @@ int machine__process_event(struct machine *machine, union perf_event *event)
|
|||
return ret;
|
||||
}
|
||||
|
||||
void machine__remove_thread(struct machine *machine, struct thread *th)
|
||||
{
|
||||
machine->last_match = NULL;
|
||||
rb_erase(&th->rb_node, &machine->threads);
|
||||
/*
|
||||
* We may have references to this thread, for instance in some hist_entry
|
||||
* instances, so just move them to a separate list.
|
||||
*/
|
||||
list_add_tail(&th->node, &machine->dead_threads);
|
||||
}
|
||||
|
||||
static bool symbol__match_parent_regex(struct symbol *sym)
|
||||
{
|
||||
if (sym->name && !regexec(&parent_regex, sym->name, 0, NULL, 0))
|
||||
|
@ -1097,6 +1105,38 @@ found:
|
|||
ams->map = al.map;
|
||||
}
|
||||
|
||||
static void ip__resolve_data(struct machine *machine, struct thread *thread,
|
||||
u8 m, struct addr_map_symbol *ams, u64 addr)
|
||||
{
|
||||
struct addr_location al;
|
||||
|
||||
memset(&al, 0, sizeof(al));
|
||||
|
||||
thread__find_addr_location(thread, machine, m, MAP__VARIABLE, addr, &al,
|
||||
NULL);
|
||||
ams->addr = addr;
|
||||
ams->al_addr = al.addr;
|
||||
ams->sym = al.sym;
|
||||
ams->map = al.map;
|
||||
}
|
||||
|
||||
struct mem_info *machine__resolve_mem(struct machine *machine,
|
||||
struct thread *thr,
|
||||
struct perf_sample *sample,
|
||||
u8 cpumode)
|
||||
{
|
||||
struct mem_info *mi = zalloc(sizeof(*mi));
|
||||
|
||||
if (!mi)
|
||||
return NULL;
|
||||
|
||||
ip__resolve_ams(machine, thr, &mi->iaddr, sample->ip);
|
||||
ip__resolve_data(machine, thr, cpumode, &mi->daddr, sample->addr);
|
||||
mi->data_src.val = sample->data_src;
|
||||
|
||||
return mi;
|
||||
}
|
||||
|
||||
struct branch_info *machine__resolve_bstack(struct machine *machine,
|
||||
struct thread *thr,
|
||||
struct branch_stack *bs)
|
||||
|
|
|
@ -76,6 +76,9 @@ void machine__delete(struct machine *machine);
|
|||
struct branch_info *machine__resolve_bstack(struct machine *machine,
|
||||
struct thread *thread,
|
||||
struct branch_stack *bs);
|
||||
struct mem_info *machine__resolve_mem(struct machine *machine,
|
||||
struct thread *thread,
|
||||
struct perf_sample *sample, u8 cpumode);
|
||||
int machine__resolve_callchain(struct machine *machine,
|
||||
struct perf_evsel *evsel,
|
||||
struct thread *thread,
|
||||
|
@ -97,7 +100,6 @@ static inline bool machine__is_host(struct machine *machine)
|
|||
}
|
||||
|
||||
struct thread *machine__findnew_thread(struct machine *machine, pid_t pid);
|
||||
void machine__remove_thread(struct machine *machine, struct thread *th);
|
||||
|
||||
size_t machine__fprintf(struct machine *machine, FILE *fp);
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
#include "symbol.h"
|
||||
#include "cache.h"
|
||||
#include "header.h"
|
||||
#include "debugfs.h"
|
||||
#include <lk/debugfs.h>
|
||||
#include "parse-events-bison.h"
|
||||
#define YY_EXTRA_TYPE int
|
||||
#include "parse-events-flex.h"
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
#include "color.h"
|
||||
#include "symbol.h"
|
||||
#include "thread.h"
|
||||
#include "debugfs.h"
|
||||
#include <lk/debugfs.h>
|
||||
#include "trace-event.h" /* For __maybe_unused */
|
||||
#include "probe-event.h"
|
||||
#include "probe-finder.h"
|
||||
|
|
|
@ -15,7 +15,6 @@ util/thread_map.c
|
|||
util/util.c
|
||||
util/xyarray.c
|
||||
util/cgroup.c
|
||||
util/debugfs.c
|
||||
util/rblist.c
|
||||
util/strlist.c
|
||||
util/sysfs.c
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#define _FILE_OFFSET_BITS 64
|
||||
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#include <byteswap.h>
|
||||
|
@ -800,6 +798,12 @@ static void dump_sample(struct perf_evsel *evsel, union perf_event *event,
|
|||
|
||||
if (sample_type & PERF_SAMPLE_STACK_USER)
|
||||
stack_user__printf(&sample->user_stack);
|
||||
|
||||
if (sample_type & PERF_SAMPLE_WEIGHT)
|
||||
printf("... weight: %" PRIu64 "\n", sample->weight);
|
||||
|
||||
if (sample_type & PERF_SAMPLE_DATA_SRC)
|
||||
printf(" . data_src: 0x%"PRIx64"\n", sample->data_src);
|
||||
}
|
||||
|
||||
static struct machine *
|
||||
|
@ -1365,18 +1369,6 @@ size_t perf_session__fprintf(struct perf_session *session, FILE *fp)
|
|||
return machine__fprintf(&session->machines.host, fp);
|
||||
}
|
||||
|
||||
void perf_session__remove_thread(struct perf_session *session,
|
||||
struct thread *th)
|
||||
{
|
||||
/*
|
||||
* FIXME: This one makes no sense, we need to remove the thread from
|
||||
* the machine it belongs to, perf_session can have many machines, so
|
||||
* doing it always on ->machines.host is wrong. Fix when auditing all
|
||||
* the 'perf kvm' code.
|
||||
*/
|
||||
machine__remove_thread(&session->machines.host, th);
|
||||
}
|
||||
|
||||
struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session,
|
||||
unsigned int type)
|
||||
{
|
||||
|
|
|
@ -72,7 +72,6 @@ void perf_event__attr_swap(struct perf_event_attr *attr);
|
|||
int perf_session__create_kernel_maps(struct perf_session *self);
|
||||
|
||||
void perf_session__set_id_hdr_size(struct perf_session *session);
|
||||
void perf_session__remove_thread(struct perf_session *self, struct thread *th);
|
||||
|
||||
static inline
|
||||
struct machine *perf_session__find_machine(struct perf_session *self, pid_t pid)
|
||||
|
|
|
@ -24,6 +24,7 @@ cflags += getenv('CFLAGS', '').split()
|
|||
build_lib = getenv('PYTHON_EXTBUILD_LIB')
|
||||
build_tmp = getenv('PYTHON_EXTBUILD_TMP')
|
||||
libtraceevent = getenv('LIBTRACEEVENT')
|
||||
liblk = getenv('LIBLK')
|
||||
|
||||
ext_sources = [f.strip() for f in file('util/python-ext-sources')
|
||||
if len(f.strip()) > 0 and f[0] != '#']
|
||||
|
@ -32,7 +33,7 @@ perf = Extension('perf',
|
|||
sources = ext_sources,
|
||||
include_dirs = ['util/include'],
|
||||
extra_compile_args = cflags,
|
||||
extra_objects = [libtraceevent],
|
||||
extra_objects = [libtraceevent, liblk],
|
||||
)
|
||||
|
||||
setup(name='perf',
|
||||
|
|
|
@ -198,11 +198,19 @@ static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym,
|
|||
}
|
||||
|
||||
ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level);
|
||||
if (sym)
|
||||
ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
|
||||
width - ret,
|
||||
sym->name);
|
||||
else {
|
||||
if (sym && map) {
|
||||
if (map->type == MAP__VARIABLE) {
|
||||
ret += repsep_snprintf(bf + ret, size - ret, "%s", sym->name);
|
||||
ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx",
|
||||
ip - map->unmap_ip(map, sym->start));
|
||||
ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
|
||||
width - ret, "");
|
||||
} else {
|
||||
ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
|
||||
width - ret,
|
||||
sym->name);
|
||||
}
|
||||
} else {
|
||||
size_t len = BITS_PER_LONG / 4;
|
||||
ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx",
|
||||
len, ip);
|
||||
|
@ -457,6 +465,304 @@ static int hist_entry__mispredict_snprintf(struct hist_entry *self, char *bf,
|
|||
return repsep_snprintf(bf, size, "%-*s", width, out);
|
||||
}
|
||||
|
||||
/* --sort daddr_sym */
|
||||
static int64_t
|
||||
sort__daddr_cmp(struct hist_entry *left, struct hist_entry *right)
|
||||
{
|
||||
uint64_t l = 0, r = 0;
|
||||
|
||||
if (left->mem_info)
|
||||
l = left->mem_info->daddr.addr;
|
||||
if (right->mem_info)
|
||||
r = right->mem_info->daddr.addr;
|
||||
|
||||
return (int64_t)(r - l);
|
||||
}
|
||||
|
||||
static int hist_entry__daddr_snprintf(struct hist_entry *self, char *bf,
|
||||
size_t size, unsigned int width)
|
||||
{
|
||||
uint64_t addr = 0;
|
||||
struct map *map = NULL;
|
||||
struct symbol *sym = NULL;
|
||||
|
||||
if (self->mem_info) {
|
||||
addr = self->mem_info->daddr.addr;
|
||||
map = self->mem_info->daddr.map;
|
||||
sym = self->mem_info->daddr.sym;
|
||||
}
|
||||
return _hist_entry__sym_snprintf(map, sym, addr, self->level, bf, size,
|
||||
width);
|
||||
}
|
||||
|
||||
static int64_t
|
||||
sort__dso_daddr_cmp(struct hist_entry *left, struct hist_entry *right)
|
||||
{
|
||||
struct map *map_l = NULL;
|
||||
struct map *map_r = NULL;
|
||||
|
||||
if (left->mem_info)
|
||||
map_l = left->mem_info->daddr.map;
|
||||
if (right->mem_info)
|
||||
map_r = right->mem_info->daddr.map;
|
||||
|
||||
return _sort__dso_cmp(map_l, map_r);
|
||||
}
|
||||
|
||||
static int hist_entry__dso_daddr_snprintf(struct hist_entry *self, char *bf,
|
||||
size_t size, unsigned int width)
|
||||
{
|
||||
struct map *map = NULL;
|
||||
|
||||
if (self->mem_info)
|
||||
map = self->mem_info->daddr.map;
|
||||
|
||||
return _hist_entry__dso_snprintf(map, bf, size, width);
|
||||
}
|
||||
|
||||
static int64_t
|
||||
sort__locked_cmp(struct hist_entry *left, struct hist_entry *right)
|
||||
{
|
||||
union perf_mem_data_src data_src_l;
|
||||
union perf_mem_data_src data_src_r;
|
||||
|
||||
if (left->mem_info)
|
||||
data_src_l = left->mem_info->data_src;
|
||||
else
|
||||
data_src_l.mem_lock = PERF_MEM_LOCK_NA;
|
||||
|
||||
if (right->mem_info)
|
||||
data_src_r = right->mem_info->data_src;
|
||||
else
|
||||
data_src_r.mem_lock = PERF_MEM_LOCK_NA;
|
||||
|
||||
return (int64_t)(data_src_r.mem_lock - data_src_l.mem_lock);
|
||||
}
|
||||
|
||||
static int hist_entry__locked_snprintf(struct hist_entry *self, char *bf,
|
||||
size_t size, unsigned int width)
|
||||
{
|
||||
const char *out;
|
||||
u64 mask = PERF_MEM_LOCK_NA;
|
||||
|
||||
if (self->mem_info)
|
||||
mask = self->mem_info->data_src.mem_lock;
|
||||
|
||||
if (mask & PERF_MEM_LOCK_NA)
|
||||
out = "N/A";
|
||||
else if (mask & PERF_MEM_LOCK_LOCKED)
|
||||
out = "Yes";
|
||||
else
|
||||
out = "No";
|
||||
|
||||
return repsep_snprintf(bf, size, "%-*s", width, out);
|
||||
}
|
||||
|
||||
static int64_t
|
||||
sort__tlb_cmp(struct hist_entry *left, struct hist_entry *right)
|
||||
{
|
||||
union perf_mem_data_src data_src_l;
|
||||
union perf_mem_data_src data_src_r;
|
||||
|
||||
if (left->mem_info)
|
||||
data_src_l = left->mem_info->data_src;
|
||||
else
|
||||
data_src_l.mem_dtlb = PERF_MEM_TLB_NA;
|
||||
|
||||
if (right->mem_info)
|
||||
data_src_r = right->mem_info->data_src;
|
||||
else
|
||||
data_src_r.mem_dtlb = PERF_MEM_TLB_NA;
|
||||
|
||||
return (int64_t)(data_src_r.mem_dtlb - data_src_l.mem_dtlb);
|
||||
}
|
||||
|
||||
static const char * const tlb_access[] = {
|
||||
"N/A",
|
||||
"HIT",
|
||||
"MISS",
|
||||
"L1",
|
||||
"L2",
|
||||
"Walker",
|
||||
"Fault",
|
||||
};
|
||||
#define NUM_TLB_ACCESS (sizeof(tlb_access)/sizeof(const char *))
|
||||
|
||||
static int hist_entry__tlb_snprintf(struct hist_entry *self, char *bf,
|
||||
size_t size, unsigned int width)
|
||||
{
|
||||
char out[64];
|
||||
size_t sz = sizeof(out) - 1; /* -1 for null termination */
|
||||
size_t l = 0, i;
|
||||
u64 m = PERF_MEM_TLB_NA;
|
||||
u64 hit, miss;
|
||||
|
||||
out[0] = '\0';
|
||||
|
||||
if (self->mem_info)
|
||||
m = self->mem_info->data_src.mem_dtlb;
|
||||
|
||||
hit = m & PERF_MEM_TLB_HIT;
|
||||
miss = m & PERF_MEM_TLB_MISS;
|
||||
|
||||
/* already taken care of */
|
||||
m &= ~(PERF_MEM_TLB_HIT|PERF_MEM_TLB_MISS);
|
||||
|
||||
for (i = 0; m && i < NUM_TLB_ACCESS; i++, m >>= 1) {
|
||||
if (!(m & 0x1))
|
||||
continue;
|
||||
if (l) {
|
||||
strcat(out, " or ");
|
||||
l += 4;
|
||||
}
|
||||
strncat(out, tlb_access[i], sz - l);
|
||||
l += strlen(tlb_access[i]);
|
||||
}
|
||||
if (*out == '\0')
|
||||
strcpy(out, "N/A");
|
||||
if (hit)
|
||||
strncat(out, " hit", sz - l);
|
||||
if (miss)
|
||||
strncat(out, " miss", sz - l);
|
||||
|
||||
return repsep_snprintf(bf, size, "%-*s", width, out);
|
||||
}
|
||||
|
||||
static int64_t
|
||||
sort__lvl_cmp(struct hist_entry *left, struct hist_entry *right)
|
||||
{
|
||||
union perf_mem_data_src data_src_l;
|
||||
union perf_mem_data_src data_src_r;
|
||||
|
||||
if (left->mem_info)
|
||||
data_src_l = left->mem_info->data_src;
|
||||
else
|
||||
data_src_l.mem_lvl = PERF_MEM_LVL_NA;
|
||||
|
||||
if (right->mem_info)
|
||||
data_src_r = right->mem_info->data_src;
|
||||
else
|
||||
data_src_r.mem_lvl = PERF_MEM_LVL_NA;
|
||||
|
||||
return (int64_t)(data_src_r.mem_lvl - data_src_l.mem_lvl);
|
||||
}
|
||||
|
||||
static const char * const mem_lvl[] = {
|
||||
"N/A",
|
||||
"HIT",
|
||||
"MISS",
|
||||
"L1",
|
||||
"LFB",
|
||||
"L2",
|
||||
"L3",
|
||||
"Local RAM",
|
||||
"Remote RAM (1 hop)",
|
||||
"Remote RAM (2 hops)",
|
||||
"Remote Cache (1 hop)",
|
||||
"Remote Cache (2 hops)",
|
||||
"I/O",
|
||||
"Uncached",
|
||||
};
|
||||
#define NUM_MEM_LVL (sizeof(mem_lvl)/sizeof(const char *))
|
||||
|
||||
static int hist_entry__lvl_snprintf(struct hist_entry *self, char *bf,
|
||||
size_t size, unsigned int width)
|
||||
{
|
||||
char out[64];
|
||||
size_t sz = sizeof(out) - 1; /* -1 for null termination */
|
||||
size_t i, l = 0;
|
||||
u64 m = PERF_MEM_LVL_NA;
|
||||
u64 hit, miss;
|
||||
|
||||
if (self->mem_info)
|
||||
m = self->mem_info->data_src.mem_lvl;
|
||||
|
||||
out[0] = '\0';
|
||||
|
||||
hit = m & PERF_MEM_LVL_HIT;
|
||||
miss = m & PERF_MEM_LVL_MISS;
|
||||
|
||||
/* already taken care of */
|
||||
m &= ~(PERF_MEM_LVL_HIT|PERF_MEM_LVL_MISS);
|
||||
|
||||
for (i = 0; m && i < NUM_MEM_LVL; i++, m >>= 1) {
|
||||
if (!(m & 0x1))
|
||||
continue;
|
||||
if (l) {
|
||||
strcat(out, " or ");
|
||||
l += 4;
|
||||
}
|
||||
strncat(out, mem_lvl[i], sz - l);
|
||||
l += strlen(mem_lvl[i]);
|
||||
}
|
||||
if (*out == '\0')
|
||||
strcpy(out, "N/A");
|
||||
if (hit)
|
||||
strncat(out, " hit", sz - l);
|
||||
if (miss)
|
||||
strncat(out, " miss", sz - l);
|
||||
|
||||
return repsep_snprintf(bf, size, "%-*s", width, out);
|
||||
}
|
||||
|
||||
static int64_t
|
||||
sort__snoop_cmp(struct hist_entry *left, struct hist_entry *right)
|
||||
{
|
||||
union perf_mem_data_src data_src_l;
|
||||
union perf_mem_data_src data_src_r;
|
||||
|
||||
if (left->mem_info)
|
||||
data_src_l = left->mem_info->data_src;
|
||||
else
|
||||
data_src_l.mem_snoop = PERF_MEM_SNOOP_NA;
|
||||
|
||||
if (right->mem_info)
|
||||
data_src_r = right->mem_info->data_src;
|
||||
else
|
||||
data_src_r.mem_snoop = PERF_MEM_SNOOP_NA;
|
||||
|
||||
return (int64_t)(data_src_r.mem_snoop - data_src_l.mem_snoop);
|
||||
}
|
||||
|
||||
static const char * const snoop_access[] = {
|
||||
"N/A",
|
||||
"None",
|
||||
"Miss",
|
||||
"Hit",
|
||||
"HitM",
|
||||
};
|
||||
#define NUM_SNOOP_ACCESS (sizeof(snoop_access)/sizeof(const char *))
|
||||
|
||||
static int hist_entry__snoop_snprintf(struct hist_entry *self, char *bf,
|
||||
size_t size, unsigned int width)
|
||||
{
|
||||
char out[64];
|
||||
size_t sz = sizeof(out) - 1; /* -1 for null termination */
|
||||
size_t i, l = 0;
|
||||
u64 m = PERF_MEM_SNOOP_NA;
|
||||
|
||||
out[0] = '\0';
|
||||
|
||||
if (self->mem_info)
|
||||
m = self->mem_info->data_src.mem_snoop;
|
||||
|
||||
for (i = 0; m && i < NUM_SNOOP_ACCESS; i++, m >>= 1) {
|
||||
if (!(m & 0x1))
|
||||
continue;
|
||||
if (l) {
|
||||
strcat(out, " or ");
|
||||
l += 4;
|
||||
}
|
||||
strncat(out, snoop_access[i], sz - l);
|
||||
l += strlen(snoop_access[i]);
|
||||
}
|
||||
|
||||
if (*out == '\0')
|
||||
strcpy(out, "N/A");
|
||||
|
||||
return repsep_snprintf(bf, size, "%-*s", width, out);
|
||||
}
|
||||
|
||||
struct sort_entry sort_mispredict = {
|
||||
.se_header = "Branch Mispredicted",
|
||||
.se_cmp = sort__mispredict_cmp,
|
||||
|
@ -464,6 +770,91 @@ struct sort_entry sort_mispredict = {
|
|||
.se_width_idx = HISTC_MISPREDICT,
|
||||
};
|
||||
|
||||
static u64 he_weight(struct hist_entry *he)
|
||||
{
|
||||
return he->stat.nr_events ? he->stat.weight / he->stat.nr_events : 0;
|
||||
}
|
||||
|
||||
static int64_t
|
||||
sort__local_weight_cmp(struct hist_entry *left, struct hist_entry *right)
|
||||
{
|
||||
return he_weight(left) - he_weight(right);
|
||||
}
|
||||
|
||||
static int hist_entry__local_weight_snprintf(struct hist_entry *self, char *bf,
|
||||
size_t size, unsigned int width)
|
||||
{
|
||||
return repsep_snprintf(bf, size, "%-*llu", width, he_weight(self));
|
||||
}
|
||||
|
||||
struct sort_entry sort_local_weight = {
|
||||
.se_header = "Local Weight",
|
||||
.se_cmp = sort__local_weight_cmp,
|
||||
.se_snprintf = hist_entry__local_weight_snprintf,
|
||||
.se_width_idx = HISTC_LOCAL_WEIGHT,
|
||||
};
|
||||
|
||||
static int64_t
|
||||
sort__global_weight_cmp(struct hist_entry *left, struct hist_entry *right)
|
||||
{
|
||||
return left->stat.weight - right->stat.weight;
|
||||
}
|
||||
|
||||
static int hist_entry__global_weight_snprintf(struct hist_entry *self, char *bf,
|
||||
size_t size, unsigned int width)
|
||||
{
|
||||
return repsep_snprintf(bf, size, "%-*llu", width, self->stat.weight);
|
||||
}
|
||||
|
||||
struct sort_entry sort_global_weight = {
|
||||
.se_header = "Weight",
|
||||
.se_cmp = sort__global_weight_cmp,
|
||||
.se_snprintf = hist_entry__global_weight_snprintf,
|
||||
.se_width_idx = HISTC_GLOBAL_WEIGHT,
|
||||
};
|
||||
|
||||
struct sort_entry sort_mem_daddr_sym = {
|
||||
.se_header = "Data Symbol",
|
||||
.se_cmp = sort__daddr_cmp,
|
||||
.se_snprintf = hist_entry__daddr_snprintf,
|
||||
.se_width_idx = HISTC_MEM_DADDR_SYMBOL,
|
||||
};
|
||||
|
||||
struct sort_entry sort_mem_daddr_dso = {
|
||||
.se_header = "Data Object",
|
||||
.se_cmp = sort__dso_daddr_cmp,
|
||||
.se_snprintf = hist_entry__dso_daddr_snprintf,
|
||||
.se_width_idx = HISTC_MEM_DADDR_SYMBOL,
|
||||
};
|
||||
|
||||
struct sort_entry sort_mem_locked = {
|
||||
.se_header = "Locked",
|
||||
.se_cmp = sort__locked_cmp,
|
||||
.se_snprintf = hist_entry__locked_snprintf,
|
||||
.se_width_idx = HISTC_MEM_LOCKED,
|
||||
};
|
||||
|
||||
struct sort_entry sort_mem_tlb = {
|
||||
.se_header = "TLB access",
|
||||
.se_cmp = sort__tlb_cmp,
|
||||
.se_snprintf = hist_entry__tlb_snprintf,
|
||||
.se_width_idx = HISTC_MEM_TLB,
|
||||
};
|
||||
|
||||
struct sort_entry sort_mem_lvl = {
|
||||
.se_header = "Memory access",
|
||||
.se_cmp = sort__lvl_cmp,
|
||||
.se_snprintf = hist_entry__lvl_snprintf,
|
||||
.se_width_idx = HISTC_MEM_LVL,
|
||||
};
|
||||
|
||||
struct sort_entry sort_mem_snoop = {
|
||||
.se_header = "Snoop",
|
||||
.se_cmp = sort__snoop_cmp,
|
||||
.se_snprintf = hist_entry__snoop_snprintf,
|
||||
.se_width_idx = HISTC_MEM_SNOOP,
|
||||
};
|
||||
|
||||
struct sort_dimension {
|
||||
const char *name;
|
||||
struct sort_entry *entry;
|
||||
|
@ -480,6 +871,14 @@ static struct sort_dimension common_sort_dimensions[] = {
|
|||
DIM(SORT_PARENT, "parent", sort_parent),
|
||||
DIM(SORT_CPU, "cpu", sort_cpu),
|
||||
DIM(SORT_SRCLINE, "srcline", sort_srcline),
|
||||
DIM(SORT_LOCAL_WEIGHT, "local_weight", sort_local_weight),
|
||||
DIM(SORT_GLOBAL_WEIGHT, "weight", sort_global_weight),
|
||||
DIM(SORT_MEM_DADDR_SYMBOL, "symbol_daddr", sort_mem_daddr_sym),
|
||||
DIM(SORT_MEM_DADDR_DSO, "dso_daddr", sort_mem_daddr_dso),
|
||||
DIM(SORT_MEM_LOCKED, "locked", sort_mem_locked),
|
||||
DIM(SORT_MEM_TLB, "tlb", sort_mem_tlb),
|
||||
DIM(SORT_MEM_LVL, "mem", sort_mem_lvl),
|
||||
DIM(SORT_MEM_SNOOP, "snoop", sort_mem_snoop),
|
||||
};
|
||||
|
||||
#undef DIM
|
||||
|
@ -516,7 +915,10 @@ int sort_dimension__add(const char *tok)
|
|||
return -EINVAL;
|
||||
}
|
||||
sort__has_parent = 1;
|
||||
} else if (sd->entry == &sort_sym) {
|
||||
} else if (sd->entry == &sort_sym ||
|
||||
sd->entry == &sort_sym_from ||
|
||||
sd->entry == &sort_sym_to ||
|
||||
sd->entry == &sort_mem_daddr_sym) {
|
||||
sort__has_sym = 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -49,6 +49,7 @@ struct he_stat {
|
|||
u64 period_us;
|
||||
u64 period_guest_sys;
|
||||
u64 period_guest_us;
|
||||
u64 weight;
|
||||
u32 nr_events;
|
||||
};
|
||||
|
||||
|
@ -100,7 +101,8 @@ struct hist_entry {
|
|||
struct rb_root sorted_chain;
|
||||
struct branch_info *branch_info;
|
||||
struct hists *hists;
|
||||
struct callchain_root callchain[0];
|
||||
struct mem_info *mem_info;
|
||||
struct callchain_root callchain[0]; /* must be last member */
|
||||
};
|
||||
|
||||
static inline bool hist_entry__has_pairs(struct hist_entry *he)
|
||||
|
@ -130,6 +132,14 @@ enum sort_type {
|
|||
SORT_PARENT,
|
||||
SORT_CPU,
|
||||
SORT_SRCLINE,
|
||||
SORT_LOCAL_WEIGHT,
|
||||
SORT_GLOBAL_WEIGHT,
|
||||
SORT_MEM_DADDR_SYMBOL,
|
||||
SORT_MEM_DADDR_DSO,
|
||||
SORT_MEM_LOCKED,
|
||||
SORT_MEM_TLB,
|
||||
SORT_MEM_LVL,
|
||||
SORT_MEM_SNOOP,
|
||||
|
||||
/* branch stack specific sort keys */
|
||||
__SORT_BRANCH_STACK,
|
||||
|
|
|
@ -806,9 +806,12 @@ int dso__load_sym(struct dso *dso, struct map *map,
|
|||
* DWARF DW_compile_unit has this, but we don't always have access
|
||||
* to it...
|
||||
*/
|
||||
demangled = bfd_demangle(NULL, elf_name, DMGL_PARAMS | DMGL_ANSI);
|
||||
if (demangled != NULL)
|
||||
elf_name = demangled;
|
||||
if (symbol_conf.demangle) {
|
||||
demangled = bfd_demangle(NULL, elf_name,
|
||||
DMGL_PARAMS | DMGL_ANSI);
|
||||
if (demangled != NULL)
|
||||
elf_name = demangled;
|
||||
}
|
||||
new_symbol:
|
||||
f = symbol__new(sym.st_value, sym.st_size,
|
||||
GELF_ST_BIND(sym.st_info), elf_name);
|
||||
|
|
|
@ -36,6 +36,7 @@ struct symbol_conf symbol_conf = {
|
|||
.use_modules = true,
|
||||
.try_vmlinux_path = true,
|
||||
.annotate_src = true,
|
||||
.demangle = true,
|
||||
.symfs = "",
|
||||
};
|
||||
|
||||
|
|
|
@ -97,7 +97,8 @@ struct symbol_conf {
|
|||
kptr_restrict,
|
||||
annotate_asm_raw,
|
||||
annotate_src,
|
||||
event_group;
|
||||
event_group,
|
||||
demangle;
|
||||
const char *vmlinux_name,
|
||||
*kallsyms_name,
|
||||
*source_prefix,
|
||||
|
@ -155,6 +156,12 @@ struct branch_info {
|
|||
struct branch_flags flags;
|
||||
};
|
||||
|
||||
struct mem_info {
|
||||
struct addr_map_symbol iaddr;
|
||||
struct addr_map_symbol daddr;
|
||||
union perf_mem_data_src data_src;
|
||||
};
|
||||
|
||||
struct addr_location {
|
||||
struct thread *thread;
|
||||
struct map *map;
|
||||
|
|
|
@ -21,4 +21,9 @@ void thread_map__delete(struct thread_map *threads);
|
|||
|
||||
size_t thread_map__fprintf(struct thread_map *threads, FILE *fp);
|
||||
|
||||
static inline int thread_map__nr(struct thread_map *threads)
|
||||
{
|
||||
return threads ? threads->nr : 1;
|
||||
}
|
||||
|
||||
#endif /* __PERF_THREAD_MAP_H */
|
||||
|
|
|
@ -38,52 +38,20 @@
|
|||
|
||||
#include "../perf.h"
|
||||
#include "trace-event.h"
|
||||
#include "debugfs.h"
|
||||
#include <lk/debugfs.h>
|
||||
#include "evsel.h"
|
||||
|
||||
#define VERSION "0.5"
|
||||
|
||||
#define TRACE_CTRL "tracing_on"
|
||||
#define TRACE "trace"
|
||||
#define AVAILABLE "available_tracers"
|
||||
#define CURRENT "current_tracer"
|
||||
#define ITER_CTRL "trace_options"
|
||||
#define MAX_LATENCY "tracing_max_latency"
|
||||
|
||||
unsigned int page_size;
|
||||
|
||||
static const char *output_file = "trace.info";
|
||||
static int output_fd;
|
||||
|
||||
struct event_list {
|
||||
struct event_list *next;
|
||||
const char *event;
|
||||
};
|
||||
|
||||
struct events {
|
||||
struct events *sibling;
|
||||
struct events *children;
|
||||
struct events *next;
|
||||
char *name;
|
||||
};
|
||||
|
||||
|
||||
static void *malloc_or_die(unsigned int size)
|
||||
{
|
||||
void *data;
|
||||
|
||||
data = malloc(size);
|
||||
if (!data)
|
||||
die("malloc");
|
||||
return data;
|
||||
}
|
||||
|
||||
static const char *find_debugfs(void)
|
||||
{
|
||||
const char *path = debugfs_mount(NULL);
|
||||
const char *path = perf_debugfs_mount(NULL);
|
||||
|
||||
if (!path)
|
||||
die("Your kernel not support debugfs filesystem");
|
||||
pr_debug("Your kernel does not support the debugfs filesystem");
|
||||
|
||||
return path;
|
||||
}
|
||||
|
@ -102,8 +70,12 @@ static const char *find_tracing_dir(void)
|
|||
return tracing;
|
||||
|
||||
debugfs = find_debugfs();
|
||||
if (!debugfs)
|
||||
return NULL;
|
||||
|
||||
tracing = malloc_or_die(strlen(debugfs) + 9);
|
||||
tracing = malloc(strlen(debugfs) + 9);
|
||||
if (!tracing)
|
||||
return NULL;
|
||||
|
||||
sprintf(tracing, "%s/tracing", debugfs);
|
||||
|
||||
|
@ -120,7 +92,9 @@ static char *get_tracing_file(const char *name)
|
|||
if (!tracing)
|
||||
return NULL;
|
||||
|
||||
file = malloc_or_die(strlen(tracing) + strlen(name) + 2);
|
||||
file = malloc(strlen(tracing) + strlen(name) + 2);
|
||||
if (!file)
|
||||
return NULL;
|
||||
|
||||
sprintf(file, "%s/%s", tracing, name);
|
||||
return file;
|
||||
|
@ -131,24 +105,6 @@ static void put_tracing_file(char *file)
|
|||
free(file);
|
||||
}
|
||||
|
||||
static ssize_t calc_data_size;
|
||||
|
||||
static ssize_t write_or_die(const void *buf, size_t len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (calc_data_size) {
|
||||
calc_data_size += len;
|
||||
return len;
|
||||
}
|
||||
|
||||
ret = write(output_fd, buf, len);
|
||||
if (ret < 0)
|
||||
die("writing to '%s'", output_file);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int bigendian(void)
|
||||
{
|
||||
unsigned char str[] = { 0x1, 0x2, 0x3, 0x4, 0x0, 0x0, 0x0, 0x0};
|
||||
|
@ -159,59 +115,106 @@ int bigendian(void)
|
|||
}
|
||||
|
||||
/* unfortunately, you can not stat debugfs or proc files for size */
|
||||
static void record_file(const char *file, size_t hdr_sz)
|
||||
static int record_file(const char *file, ssize_t hdr_sz)
|
||||
{
|
||||
unsigned long long size = 0;
|
||||
char buf[BUFSIZ], *sizep;
|
||||
off_t hdr_pos = lseek(output_fd, 0, SEEK_CUR);
|
||||
int r, fd;
|
||||
int err = -EIO;
|
||||
|
||||
fd = open(file, O_RDONLY);
|
||||
if (fd < 0)
|
||||
die("Can't read '%s'", file);
|
||||
if (fd < 0) {
|
||||
pr_debug("Can't read '%s'", file);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
/* put in zeros for file size, then fill true size later */
|
||||
if (hdr_sz)
|
||||
write_or_die(&size, hdr_sz);
|
||||
if (hdr_sz) {
|
||||
if (write(output_fd, &size, hdr_sz) != hdr_sz)
|
||||
goto out;
|
||||
}
|
||||
|
||||
do {
|
||||
r = read(fd, buf, BUFSIZ);
|
||||
if (r > 0) {
|
||||
size += r;
|
||||
write_or_die(buf, r);
|
||||
if (write(output_fd, buf, r) != r)
|
||||
goto out;
|
||||
}
|
||||
} while (r > 0);
|
||||
close(fd);
|
||||
|
||||
/* ugh, handle big-endian hdr_size == 4 */
|
||||
sizep = (char*)&size;
|
||||
if (bigendian())
|
||||
sizep += sizeof(u64) - hdr_sz;
|
||||
|
||||
if (hdr_sz && pwrite(output_fd, sizep, hdr_sz, hdr_pos) < 0)
|
||||
die("writing to %s", output_file);
|
||||
if (hdr_sz && pwrite(output_fd, sizep, hdr_sz, hdr_pos) < 0) {
|
||||
pr_debug("writing file size failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = 0;
|
||||
out:
|
||||
close(fd);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void read_header_files(void)
|
||||
static int read_header_files(void)
|
||||
{
|
||||
char *path;
|
||||
struct stat st;
|
||||
int err = -EIO;
|
||||
|
||||
path = get_tracing_file("events/header_page");
|
||||
if (stat(path, &st) < 0)
|
||||
die("can't read '%s'", path);
|
||||
if (!path) {
|
||||
pr_debug("can't get tracing/events/header_page");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (stat(path, &st) < 0) {
|
||||
pr_debug("can't read '%s'", path);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (write(output_fd, "header_page", 12) != 12) {
|
||||
pr_debug("can't write header_page\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (record_file(path, 8) < 0) {
|
||||
pr_debug("can't record header_page file\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
write_or_die("header_page", 12);
|
||||
record_file(path, 8);
|
||||
put_tracing_file(path);
|
||||
|
||||
path = get_tracing_file("events/header_event");
|
||||
if (stat(path, &st) < 0)
|
||||
die("can't read '%s'", path);
|
||||
if (!path) {
|
||||
pr_debug("can't get tracing/events/header_event");
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
write_or_die("header_event", 13);
|
||||
record_file(path, 8);
|
||||
if (stat(path, &st) < 0) {
|
||||
pr_debug("can't read '%s'", path);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (write(output_fd, "header_event", 13) != 13) {
|
||||
pr_debug("can't write header_event\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (record_file(path, 8) < 0) {
|
||||
pr_debug("can't record header_event file\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = 0;
|
||||
out:
|
||||
put_tracing_file(path);
|
||||
return err;
|
||||
}
|
||||
|
||||
static bool name_in_tp_list(char *sys, struct tracepoint_path *tps)
|
||||
|
@ -225,7 +228,7 @@ static bool name_in_tp_list(char *sys, struct tracepoint_path *tps)
|
|||
return false;
|
||||
}
|
||||
|
||||
static void copy_event_system(const char *sys, struct tracepoint_path *tps)
|
||||
static int copy_event_system(const char *sys, struct tracepoint_path *tps)
|
||||
{
|
||||
struct dirent *dent;
|
||||
struct stat st;
|
||||
|
@ -233,10 +236,13 @@ static void copy_event_system(const char *sys, struct tracepoint_path *tps)
|
|||
DIR *dir;
|
||||
int count = 0;
|
||||
int ret;
|
||||
int err;
|
||||
|
||||
dir = opendir(sys);
|
||||
if (!dir)
|
||||
die("can't read directory '%s'", sys);
|
||||
if (!dir) {
|
||||
pr_debug("can't read directory '%s'", sys);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
while ((dent = readdir(dir))) {
|
||||
if (dent->d_type != DT_DIR ||
|
||||
|
@ -244,7 +250,11 @@ static void copy_event_system(const char *sys, struct tracepoint_path *tps)
|
|||
strcmp(dent->d_name, "..") == 0 ||
|
||||
!name_in_tp_list(dent->d_name, tps))
|
||||
continue;
|
||||
format = malloc_or_die(strlen(sys) + strlen(dent->d_name) + 10);
|
||||
format = malloc(strlen(sys) + strlen(dent->d_name) + 10);
|
||||
if (!format) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
sprintf(format, "%s/%s/format", sys, dent->d_name);
|
||||
ret = stat(format, &st);
|
||||
free(format);
|
||||
|
@ -253,7 +263,11 @@ static void copy_event_system(const char *sys, struct tracepoint_path *tps)
|
|||
count++;
|
||||
}
|
||||
|
||||
write_or_die(&count, 4);
|
||||
if (write(output_fd, &count, 4) != 4) {
|
||||
err = -EIO;
|
||||
pr_debug("can't write count\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
rewinddir(dir);
|
||||
while ((dent = readdir(dir))) {
|
||||
|
@ -262,27 +276,45 @@ static void copy_event_system(const char *sys, struct tracepoint_path *tps)
|
|||
strcmp(dent->d_name, "..") == 0 ||
|
||||
!name_in_tp_list(dent->d_name, tps))
|
||||
continue;
|
||||
format = malloc_or_die(strlen(sys) + strlen(dent->d_name) + 10);
|
||||
format = malloc(strlen(sys) + strlen(dent->d_name) + 10);
|
||||
if (!format) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
sprintf(format, "%s/%s/format", sys, dent->d_name);
|
||||
ret = stat(format, &st);
|
||||
|
||||
if (ret >= 0)
|
||||
record_file(format, 8);
|
||||
|
||||
if (ret >= 0) {
|
||||
err = record_file(format, 8);
|
||||
if (err) {
|
||||
free(format);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
free(format);
|
||||
}
|
||||
err = 0;
|
||||
out:
|
||||
closedir(dir);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void read_ftrace_files(struct tracepoint_path *tps)
|
||||
static int read_ftrace_files(struct tracepoint_path *tps)
|
||||
{
|
||||
char *path;
|
||||
int ret;
|
||||
|
||||
path = get_tracing_file("events/ftrace");
|
||||
if (!path) {
|
||||
pr_debug("can't get tracing/events/ftrace");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
copy_event_system(path, tps);
|
||||
ret = copy_event_system(path, tps);
|
||||
|
||||
put_tracing_file(path);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool system_in_tp_list(char *sys, struct tracepoint_path *tps)
|
||||
|
@ -296,7 +328,7 @@ static bool system_in_tp_list(char *sys, struct tracepoint_path *tps)
|
|||
return false;
|
||||
}
|
||||
|
||||
static void read_event_files(struct tracepoint_path *tps)
|
||||
static int read_event_files(struct tracepoint_path *tps)
|
||||
{
|
||||
struct dirent *dent;
|
||||
struct stat st;
|
||||
|
@ -305,12 +337,20 @@ static void read_event_files(struct tracepoint_path *tps)
|
|||
DIR *dir;
|
||||
int count = 0;
|
||||
int ret;
|
||||
int err;
|
||||
|
||||
path = get_tracing_file("events");
|
||||
if (!path) {
|
||||
pr_debug("can't get tracing/events");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dir = opendir(path);
|
||||
if (!dir)
|
||||
die("can't read directory '%s'", path);
|
||||
if (!dir) {
|
||||
err = -errno;
|
||||
pr_debug("can't read directory '%s'", path);
|
||||
goto out;
|
||||
}
|
||||
|
||||
while ((dent = readdir(dir))) {
|
||||
if (dent->d_type != DT_DIR ||
|
||||
|
@ -322,7 +362,11 @@ static void read_event_files(struct tracepoint_path *tps)
|
|||
count++;
|
||||
}
|
||||
|
||||
write_or_die(&count, 4);
|
||||
if (write(output_fd, &count, 4) != 4) {
|
||||
err = -EIO;
|
||||
pr_debug("can't write count\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
rewinddir(dir);
|
||||
while ((dent = readdir(dir))) {
|
||||
|
@ -332,76 +376,77 @@ static void read_event_files(struct tracepoint_path *tps)
|
|||
strcmp(dent->d_name, "ftrace") == 0 ||
|
||||
!system_in_tp_list(dent->d_name, tps))
|
||||
continue;
|
||||
sys = malloc_or_die(strlen(path) + strlen(dent->d_name) + 2);
|
||||
sys = malloc(strlen(path) + strlen(dent->d_name) + 2);
|
||||
if (!sys) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
sprintf(sys, "%s/%s", path, dent->d_name);
|
||||
ret = stat(sys, &st);
|
||||
if (ret >= 0) {
|
||||
write_or_die(dent->d_name, strlen(dent->d_name) + 1);
|
||||
copy_event_system(sys, tps);
|
||||
ssize_t size = strlen(dent->d_name) + 1;
|
||||
|
||||
if (write(output_fd, dent->d_name, size) != size ||
|
||||
copy_event_system(sys, tps) < 0) {
|
||||
err = -EIO;
|
||||
free(sys);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
free(sys);
|
||||
}
|
||||
|
||||
err = 0;
|
||||
out:
|
||||
closedir(dir);
|
||||
put_tracing_file(path);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void read_proc_kallsyms(void)
|
||||
static int read_proc_kallsyms(void)
|
||||
{
|
||||
unsigned int size;
|
||||
const char *path = "/proc/kallsyms";
|
||||
struct stat st;
|
||||
int ret;
|
||||
int ret, err = 0;
|
||||
|
||||
ret = stat(path, &st);
|
||||
if (ret < 0) {
|
||||
/* not found */
|
||||
size = 0;
|
||||
write_or_die(&size, 4);
|
||||
return;
|
||||
if (write(output_fd, &size, 4) != 4)
|
||||
err = -EIO;
|
||||
return err;
|
||||
}
|
||||
record_file(path, 4);
|
||||
return record_file(path, 4);
|
||||
}
|
||||
|
||||
static void read_ftrace_printk(void)
|
||||
static int read_ftrace_printk(void)
|
||||
{
|
||||
unsigned int size;
|
||||
char *path;
|
||||
struct stat st;
|
||||
int ret;
|
||||
int ret, err = 0;
|
||||
|
||||
path = get_tracing_file("printk_formats");
|
||||
if (!path) {
|
||||
pr_debug("can't get tracing/printk_formats");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = stat(path, &st);
|
||||
if (ret < 0) {
|
||||
/* not found */
|
||||
size = 0;
|
||||
write_or_die(&size, 4);
|
||||
if (write(output_fd, &size, 4) != 4)
|
||||
err = -EIO;
|
||||
goto out;
|
||||
}
|
||||
record_file(path, 4);
|
||||
err = record_file(path, 4);
|
||||
|
||||
out:
|
||||
put_tracing_file(path);
|
||||
}
|
||||
|
||||
static struct tracepoint_path *
|
||||
get_tracepoints_path(struct list_head *pattrs)
|
||||
{
|
||||
struct tracepoint_path path, *ppath = &path;
|
||||
struct perf_evsel *pos;
|
||||
int nr_tracepoints = 0;
|
||||
|
||||
list_for_each_entry(pos, pattrs, node) {
|
||||
if (pos->attr.type != PERF_TYPE_TRACEPOINT)
|
||||
continue;
|
||||
++nr_tracepoints;
|
||||
ppath->next = tracepoint_id_to_path(pos->attr.config);
|
||||
if (!ppath->next)
|
||||
die("%s\n", "No memory to alloc tracepoints list");
|
||||
ppath = ppath->next;
|
||||
}
|
||||
|
||||
return nr_tracepoints > 0 ? path.next : NULL;
|
||||
return err;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -417,6 +462,29 @@ put_tracepoints_path(struct tracepoint_path *tps)
|
|||
}
|
||||
}
|
||||
|
||||
static struct tracepoint_path *
|
||||
get_tracepoints_path(struct list_head *pattrs)
|
||||
{
|
||||
struct tracepoint_path path, *ppath = &path;
|
||||
struct perf_evsel *pos;
|
||||
int nr_tracepoints = 0;
|
||||
|
||||
list_for_each_entry(pos, pattrs, node) {
|
||||
if (pos->attr.type != PERF_TYPE_TRACEPOINT)
|
||||
continue;
|
||||
++nr_tracepoints;
|
||||
ppath->next = tracepoint_id_to_path(pos->attr.config);
|
||||
if (!ppath->next) {
|
||||
pr_debug("No memory to alloc tracepoints list\n");
|
||||
put_tracepoints_path(&path);
|
||||
return NULL;
|
||||
}
|
||||
ppath = ppath->next;
|
||||
}
|
||||
|
||||
return nr_tracepoints > 0 ? path.next : NULL;
|
||||
}
|
||||
|
||||
bool have_tracepoints(struct list_head *pattrs)
|
||||
{
|
||||
struct perf_evsel *pos;
|
||||
|
@ -428,9 +496,10 @@ bool have_tracepoints(struct list_head *pattrs)
|
|||
return false;
|
||||
}
|
||||
|
||||
static void tracing_data_header(void)
|
||||
static int tracing_data_header(void)
|
||||
{
|
||||
char buf[20];
|
||||
ssize_t size;
|
||||
|
||||
/* just guessing this is someone's birthday.. ;) */
|
||||
buf[0] = 23;
|
||||
|
@ -438,9 +507,12 @@ static void tracing_data_header(void)
|
|||
buf[2] = 68;
|
||||
memcpy(buf + 3, "tracing", 7);
|
||||
|
||||
write_or_die(buf, 10);
|
||||
if (write(output_fd, buf, 10) != 10)
|
||||
return -1;
|
||||
|
||||
write_or_die(VERSION, strlen(VERSION) + 1);
|
||||
size = strlen(VERSION) + 1;
|
||||
if (write(output_fd, VERSION, size) != size)
|
||||
return -1;
|
||||
|
||||
/* save endian */
|
||||
if (bigendian())
|
||||
|
@ -450,15 +522,19 @@ static void tracing_data_header(void)
|
|||
|
||||
read_trace_init(buf[0], buf[0]);
|
||||
|
||||
write_or_die(buf, 1);
|
||||
if (write(output_fd, buf, 1) != 1)
|
||||
return -1;
|
||||
|
||||
/* save size of long */
|
||||
buf[0] = sizeof(long);
|
||||
write_or_die(buf, 1);
|
||||
if (write(output_fd, buf, 1) != 1)
|
||||
return -1;
|
||||
|
||||
/* save page_size */
|
||||
page_size = sysconf(_SC_PAGESIZE);
|
||||
write_or_die(&page_size, 4);
|
||||
if (write(output_fd, &page_size, 4) != 4)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct tracing_data *tracing_data_get(struct list_head *pattrs,
|
||||
|
@ -466,6 +542,7 @@ struct tracing_data *tracing_data_get(struct list_head *pattrs,
|
|||
{
|
||||
struct tracepoint_path *tps;
|
||||
struct tracing_data *tdata;
|
||||
int err;
|
||||
|
||||
output_fd = fd;
|
||||
|
||||
|
@ -473,7 +550,10 @@ struct tracing_data *tracing_data_get(struct list_head *pattrs,
|
|||
if (!tps)
|
||||
return NULL;
|
||||
|
||||
tdata = malloc_or_die(sizeof(*tdata));
|
||||
tdata = malloc(sizeof(*tdata));
|
||||
if (!tdata)
|
||||
return NULL;
|
||||
|
||||
tdata->temp = temp;
|
||||
tdata->size = 0;
|
||||
|
||||
|
@ -482,12 +562,16 @@ struct tracing_data *tracing_data_get(struct list_head *pattrs,
|
|||
|
||||
snprintf(tdata->temp_file, sizeof(tdata->temp_file),
|
||||
"/tmp/perf-XXXXXX");
|
||||
if (!mkstemp(tdata->temp_file))
|
||||
die("Can't make temp file");
|
||||
if (!mkstemp(tdata->temp_file)) {
|
||||
pr_debug("Can't make temp file");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
temp_fd = open(tdata->temp_file, O_RDWR);
|
||||
if (temp_fd < 0)
|
||||
die("Can't read '%s'", tdata->temp_file);
|
||||
if (temp_fd < 0) {
|
||||
pr_debug("Can't read '%s'", tdata->temp_file);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the temp file the default output, so all the
|
||||
|
@ -496,13 +580,24 @@ struct tracing_data *tracing_data_get(struct list_head *pattrs,
|
|||
output_fd = temp_fd;
|
||||
}
|
||||
|
||||
tracing_data_header();
|
||||
read_header_files();
|
||||
read_ftrace_files(tps);
|
||||
read_event_files(tps);
|
||||
read_proc_kallsyms();
|
||||
read_ftrace_printk();
|
||||
err = tracing_data_header();
|
||||
if (err)
|
||||
goto out;
|
||||
err = read_header_files();
|
||||
if (err)
|
||||
goto out;
|
||||
err = read_ftrace_files(tps);
|
||||
if (err)
|
||||
goto out;
|
||||
err = read_event_files(tps);
|
||||
if (err)
|
||||
goto out;
|
||||
err = read_proc_kallsyms();
|
||||
if (err)
|
||||
goto out;
|
||||
err = read_ftrace_printk();
|
||||
|
||||
out:
|
||||
/*
|
||||
* All tracing data are stored by now, we can restore
|
||||
* the default output file in case we used temp file.
|
||||
|
@ -513,22 +608,31 @@ struct tracing_data *tracing_data_get(struct list_head *pattrs,
|
|||
output_fd = fd;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
free(tdata);
|
||||
tdata = NULL;
|
||||
}
|
||||
|
||||
put_tracepoints_path(tps);
|
||||
return tdata;
|
||||
}
|
||||
|
||||
void tracing_data_put(struct tracing_data *tdata)
|
||||
int tracing_data_put(struct tracing_data *tdata)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
if (tdata->temp) {
|
||||
record_file(tdata->temp_file, 0);
|
||||
err = record_file(tdata->temp_file, 0);
|
||||
unlink(tdata->temp_file);
|
||||
}
|
||||
|
||||
free(tdata);
|
||||
return err;
|
||||
}
|
||||
|
||||
int read_tracing_data(int fd, struct list_head *pattrs)
|
||||
{
|
||||
int err;
|
||||
struct tracing_data *tdata;
|
||||
|
||||
/*
|
||||
|
@ -539,6 +643,6 @@ int read_tracing_data(int fd, struct list_head *pattrs)
|
|||
if (!tdata)
|
||||
return -ENOMEM;
|
||||
|
||||
tracing_data_put(tdata);
|
||||
return 0;
|
||||
err = tracing_data_put(tdata);
|
||||
return err;
|
||||
}
|
||||
|
|
|
@ -183,43 +183,6 @@ void event_format__print(struct event_format *event,
|
|||
trace_seq_do_printf(&s);
|
||||
}
|
||||
|
||||
void print_trace_event(struct pevent *pevent, int cpu, void *data, int size)
|
||||
{
|
||||
int type = trace_parse_common_type(pevent, data);
|
||||
struct event_format *event = pevent_find_event(pevent, type);
|
||||
|
||||
if (!event) {
|
||||
warning("ug! no event found for type %d", type);
|
||||
return;
|
||||
}
|
||||
|
||||
event_format__print(event, cpu, data, size);
|
||||
}
|
||||
|
||||
void print_event(struct pevent *pevent, int cpu, void *data, int size,
|
||||
unsigned long long nsecs, char *comm)
|
||||
{
|
||||
struct pevent_record record;
|
||||
struct trace_seq s;
|
||||
int pid;
|
||||
|
||||
pevent->latency_format = latency_format;
|
||||
|
||||
record.ts = nsecs;
|
||||
record.cpu = cpu;
|
||||
record.size = size;
|
||||
record.data = data;
|
||||
pid = pevent_data_pid(pevent, &record);
|
||||
|
||||
if (!pevent_pid_is_registered(pevent, pid))
|
||||
pevent_register_comm(pevent, comm, pid);
|
||||
|
||||
trace_seq_init(&s);
|
||||
pevent_print_event(pevent, &s, &record);
|
||||
trace_seq_do_printf(&s);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void parse_proc_kallsyms(struct pevent *pevent,
|
||||
char *file, unsigned int size __maybe_unused)
|
||||
{
|
||||
|
|
|
@ -18,8 +18,6 @@
|
|||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*/
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
|
||||
#include <dirent.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -41,26 +39,14 @@
|
|||
|
||||
static int input_fd;
|
||||
|
||||
static int read_page;
|
||||
|
||||
int file_bigendian;
|
||||
int host_bigendian;
|
||||
static int long_size;
|
||||
|
||||
static ssize_t calc_data_size;
|
||||
static ssize_t trace_data_size;
|
||||
static bool repipe;
|
||||
|
||||
static void *malloc_or_die(int size)
|
||||
{
|
||||
void *ret;
|
||||
|
||||
ret = malloc(size);
|
||||
if (!ret)
|
||||
die("malloc");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int do_read(int fd, void *buf, int size)
|
||||
static int __do_read(int fd, void *buf, int size)
|
||||
{
|
||||
int rsize = size;
|
||||
|
||||
|
@ -73,8 +59,10 @@ static int do_read(int fd, void *buf, int size)
|
|||
if (repipe) {
|
||||
int retw = write(STDOUT_FILENO, buf, ret);
|
||||
|
||||
if (retw <= 0 || retw != ret)
|
||||
die("repiping input file");
|
||||
if (retw <= 0 || retw != ret) {
|
||||
pr_debug("repiping input file");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
size -= ret;
|
||||
|
@ -84,17 +72,18 @@ static int do_read(int fd, void *buf, int size)
|
|||
return rsize;
|
||||
}
|
||||
|
||||
static int read_or_die(void *data, int size)
|
||||
static int do_read(void *data, int size)
|
||||
{
|
||||
int r;
|
||||
|
||||
r = do_read(input_fd, data, size);
|
||||
if (r <= 0)
|
||||
die("reading input file (size expected=%d received=%d)",
|
||||
size, r);
|
||||
r = __do_read(input_fd, data, size);
|
||||
if (r <= 0) {
|
||||
pr_debug("reading input file (size expected=%d received=%d)",
|
||||
size, r);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (calc_data_size)
|
||||
calc_data_size += r;
|
||||
trace_data_size += r;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
@ -107,7 +96,7 @@ static void skip(int size)
|
|||
|
||||
while (size) {
|
||||
r = size > BUFSIZ ? BUFSIZ : size;
|
||||
read_or_die(buf, r);
|
||||
do_read(buf, r);
|
||||
size -= r;
|
||||
};
|
||||
}
|
||||
|
@ -116,7 +105,8 @@ static unsigned int read4(struct pevent *pevent)
|
|||
{
|
||||
unsigned int data;
|
||||
|
||||
read_or_die(&data, 4);
|
||||
if (do_read(&data, 4) < 0)
|
||||
return 0;
|
||||
return __data2host4(pevent, data);
|
||||
}
|
||||
|
||||
|
@ -124,7 +114,8 @@ static unsigned long long read8(struct pevent *pevent)
|
|||
{
|
||||
unsigned long long data;
|
||||
|
||||
read_or_die(&data, 8);
|
||||
if (do_read(&data, 8) < 0)
|
||||
return 0;
|
||||
return __data2host8(pevent, data);
|
||||
}
|
||||
|
||||
|
@ -138,17 +129,23 @@ static char *read_string(void)
|
|||
|
||||
for (;;) {
|
||||
r = read(input_fd, &c, 1);
|
||||
if (r < 0)
|
||||
die("reading input file");
|
||||
if (r < 0) {
|
||||
pr_debug("reading input file");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!r)
|
||||
die("no data");
|
||||
if (!r) {
|
||||
pr_debug("no data");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (repipe) {
|
||||
int retw = write(STDOUT_FILENO, &c, 1);
|
||||
|
||||
if (retw <= 0 || retw != r)
|
||||
die("repiping input file string");
|
||||
if (retw <= 0 || retw != r) {
|
||||
pr_debug("repiping input file string");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
buf[size++] = c;
|
||||
|
@ -157,60 +154,79 @@ static char *read_string(void)
|
|||
break;
|
||||
}
|
||||
|
||||
if (calc_data_size)
|
||||
calc_data_size += size;
|
||||
|
||||
str = malloc_or_die(size);
|
||||
memcpy(str, buf, size);
|
||||
trace_data_size += size;
|
||||
|
||||
str = malloc(size);
|
||||
if (str)
|
||||
memcpy(str, buf, size);
|
||||
out:
|
||||
return str;
|
||||
}
|
||||
|
||||
static void read_proc_kallsyms(struct pevent *pevent)
|
||||
static int read_proc_kallsyms(struct pevent *pevent)
|
||||
{
|
||||
unsigned int size;
|
||||
char *buf;
|
||||
|
||||
size = read4(pevent);
|
||||
if (!size)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
buf = malloc_or_die(size + 1);
|
||||
read_or_die(buf, size);
|
||||
buf = malloc(size + 1);
|
||||
if (buf == NULL)
|
||||
return -1;
|
||||
|
||||
if (do_read(buf, size) < 0) {
|
||||
free(buf);
|
||||
return -1;
|
||||
}
|
||||
buf[size] = '\0';
|
||||
|
||||
parse_proc_kallsyms(pevent, buf, size);
|
||||
|
||||
free(buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void read_ftrace_printk(struct pevent *pevent)
|
||||
static int read_ftrace_printk(struct pevent *pevent)
|
||||
{
|
||||
unsigned int size;
|
||||
char *buf;
|
||||
|
||||
/* it can have 0 size */
|
||||
size = read4(pevent);
|
||||
if (!size)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
buf = malloc_or_die(size);
|
||||
read_or_die(buf, size);
|
||||
buf = malloc(size);
|
||||
if (buf == NULL)
|
||||
return -1;
|
||||
|
||||
if (do_read(buf, size) < 0) {
|
||||
free(buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
parse_ftrace_printk(pevent, buf, size);
|
||||
|
||||
free(buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void read_header_files(struct pevent *pevent)
|
||||
static int read_header_files(struct pevent *pevent)
|
||||
{
|
||||
unsigned long long size;
|
||||
char *header_event;
|
||||
char buf[BUFSIZ];
|
||||
int ret = 0;
|
||||
|
||||
read_or_die(buf, 12);
|
||||
if (do_read(buf, 12) < 0)
|
||||
return -1;
|
||||
|
||||
if (memcmp(buf, "header_page", 12) != 0)
|
||||
die("did not read header page");
|
||||
if (memcmp(buf, "header_page", 12) != 0) {
|
||||
pr_debug("did not read header page");
|
||||
return -1;
|
||||
}
|
||||
|
||||
size = read8(pevent);
|
||||
skip(size);
|
||||
|
@ -221,269 +237,107 @@ static void read_header_files(struct pevent *pevent)
|
|||
*/
|
||||
long_size = header_page_size_size;
|
||||
|
||||
read_or_die(buf, 13);
|
||||
if (memcmp(buf, "header_event", 13) != 0)
|
||||
die("did not read header event");
|
||||
if (do_read(buf, 13) < 0)
|
||||
return -1;
|
||||
|
||||
if (memcmp(buf, "header_event", 13) != 0) {
|
||||
pr_debug("did not read header event");
|
||||
return -1;
|
||||
}
|
||||
|
||||
size = read8(pevent);
|
||||
header_event = malloc_or_die(size);
|
||||
read_or_die(header_event, size);
|
||||
header_event = malloc(size);
|
||||
if (header_event == NULL)
|
||||
return -1;
|
||||
|
||||
if (do_read(header_event, size) < 0)
|
||||
ret = -1;
|
||||
|
||||
free(header_event);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void read_ftrace_file(struct pevent *pevent, unsigned long long size)
|
||||
static int read_ftrace_file(struct pevent *pevent, unsigned long long size)
|
||||
{
|
||||
char *buf;
|
||||
|
||||
buf = malloc_or_die(size);
|
||||
read_or_die(buf, size);
|
||||
buf = malloc(size);
|
||||
if (buf == NULL)
|
||||
return -1;
|
||||
|
||||
if (do_read(buf, size) < 0) {
|
||||
free(buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
parse_ftrace_file(pevent, buf, size);
|
||||
free(buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void read_event_file(struct pevent *pevent, char *sys,
|
||||
static int read_event_file(struct pevent *pevent, char *sys,
|
||||
unsigned long long size)
|
||||
{
|
||||
char *buf;
|
||||
|
||||
buf = malloc_or_die(size);
|
||||
read_or_die(buf, size);
|
||||
buf = malloc(size);
|
||||
if (buf == NULL)
|
||||
return -1;
|
||||
|
||||
if (do_read(buf, size) < 0) {
|
||||
free(buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
parse_event_file(pevent, buf, size, sys);
|
||||
free(buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void read_ftrace_files(struct pevent *pevent)
|
||||
static int read_ftrace_files(struct pevent *pevent)
|
||||
{
|
||||
unsigned long long size;
|
||||
int count;
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
count = read4(pevent);
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
size = read8(pevent);
|
||||
read_ftrace_file(pevent, size);
|
||||
ret = read_ftrace_file(pevent, size);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void read_event_files(struct pevent *pevent)
|
||||
static int read_event_files(struct pevent *pevent)
|
||||
{
|
||||
unsigned long long size;
|
||||
char *sys;
|
||||
int systems;
|
||||
int count;
|
||||
int i,x;
|
||||
int ret;
|
||||
|
||||
systems = read4(pevent);
|
||||
|
||||
for (i = 0; i < systems; i++) {
|
||||
sys = read_string();
|
||||
if (sys == NULL)
|
||||
return -1;
|
||||
|
||||
count = read4(pevent);
|
||||
|
||||
for (x=0; x < count; x++) {
|
||||
size = read8(pevent);
|
||||
read_event_file(pevent, sys, size);
|
||||
ret = read_event_file(pevent, sys, size);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct cpu_data {
|
||||
unsigned long long offset;
|
||||
unsigned long long size;
|
||||
unsigned long long timestamp;
|
||||
struct pevent_record *next;
|
||||
char *page;
|
||||
int cpu;
|
||||
int index;
|
||||
int page_size;
|
||||
};
|
||||
|
||||
static struct cpu_data *cpu_data;
|
||||
|
||||
static void update_cpu_data_index(int cpu)
|
||||
{
|
||||
cpu_data[cpu].offset += page_size;
|
||||
cpu_data[cpu].size -= page_size;
|
||||
cpu_data[cpu].index = 0;
|
||||
}
|
||||
|
||||
static void get_next_page(int cpu)
|
||||
{
|
||||
off_t save_seek;
|
||||
off_t ret;
|
||||
|
||||
if (!cpu_data[cpu].page)
|
||||
return;
|
||||
|
||||
if (read_page) {
|
||||
if (cpu_data[cpu].size <= page_size) {
|
||||
free(cpu_data[cpu].page);
|
||||
cpu_data[cpu].page = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
update_cpu_data_index(cpu);
|
||||
|
||||
/* other parts of the code may expect the pointer to not move */
|
||||
save_seek = lseek(input_fd, 0, SEEK_CUR);
|
||||
|
||||
ret = lseek(input_fd, cpu_data[cpu].offset, SEEK_SET);
|
||||
if (ret == (off_t)-1)
|
||||
die("failed to lseek");
|
||||
ret = read(input_fd, cpu_data[cpu].page, page_size);
|
||||
if (ret < 0)
|
||||
die("failed to read page");
|
||||
|
||||
/* reset the file pointer back */
|
||||
lseek(input_fd, save_seek, SEEK_SET);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
munmap(cpu_data[cpu].page, page_size);
|
||||
cpu_data[cpu].page = NULL;
|
||||
|
||||
if (cpu_data[cpu].size <= page_size)
|
||||
return;
|
||||
|
||||
update_cpu_data_index(cpu);
|
||||
|
||||
cpu_data[cpu].page = mmap(NULL, page_size, PROT_READ, MAP_PRIVATE,
|
||||
input_fd, cpu_data[cpu].offset);
|
||||
if (cpu_data[cpu].page == MAP_FAILED)
|
||||
die("failed to mmap cpu %d at offset 0x%llx",
|
||||
cpu, cpu_data[cpu].offset);
|
||||
}
|
||||
|
||||
static unsigned int type_len4host(unsigned int type_len_ts)
|
||||
{
|
||||
if (file_bigendian)
|
||||
return (type_len_ts >> 27) & ((1 << 5) - 1);
|
||||
else
|
||||
return type_len_ts & ((1 << 5) - 1);
|
||||
}
|
||||
|
||||
static unsigned int ts4host(unsigned int type_len_ts)
|
||||
{
|
||||
if (file_bigendian)
|
||||
return type_len_ts & ((1 << 27) - 1);
|
||||
else
|
||||
return type_len_ts >> 5;
|
||||
}
|
||||
|
||||
static int calc_index(void *ptr, int cpu)
|
||||
{
|
||||
return (unsigned long)ptr - (unsigned long)cpu_data[cpu].page;
|
||||
}
|
||||
|
||||
struct pevent_record *trace_peek_data(struct pevent *pevent, int cpu)
|
||||
{
|
||||
struct pevent_record *data;
|
||||
void *page = cpu_data[cpu].page;
|
||||
int idx = cpu_data[cpu].index;
|
||||
void *ptr = page + idx;
|
||||
unsigned long long extend;
|
||||
unsigned int type_len_ts;
|
||||
unsigned int type_len;
|
||||
unsigned int delta;
|
||||
unsigned int length = 0;
|
||||
|
||||
if (cpu_data[cpu].next)
|
||||
return cpu_data[cpu].next;
|
||||
|
||||
if (!page)
|
||||
return NULL;
|
||||
|
||||
if (!idx) {
|
||||
/* FIXME: handle header page */
|
||||
if (header_page_ts_size != 8)
|
||||
die("expected a long long type for timestamp");
|
||||
cpu_data[cpu].timestamp = data2host8(pevent, ptr);
|
||||
ptr += 8;
|
||||
switch (header_page_size_size) {
|
||||
case 4:
|
||||
cpu_data[cpu].page_size = data2host4(pevent, ptr);
|
||||
ptr += 4;
|
||||
break;
|
||||
case 8:
|
||||
cpu_data[cpu].page_size = data2host8(pevent, ptr);
|
||||
ptr += 8;
|
||||
break;
|
||||
default:
|
||||
die("bad long size");
|
||||
}
|
||||
ptr = cpu_data[cpu].page + header_page_data_offset;
|
||||
}
|
||||
|
||||
read_again:
|
||||
idx = calc_index(ptr, cpu);
|
||||
|
||||
if (idx >= cpu_data[cpu].page_size) {
|
||||
get_next_page(cpu);
|
||||
return trace_peek_data(pevent, cpu);
|
||||
}
|
||||
|
||||
type_len_ts = data2host4(pevent, ptr);
|
||||
ptr += 4;
|
||||
|
||||
type_len = type_len4host(type_len_ts);
|
||||
delta = ts4host(type_len_ts);
|
||||
|
||||
switch (type_len) {
|
||||
case RINGBUF_TYPE_PADDING:
|
||||
if (!delta)
|
||||
die("error, hit unexpected end of page");
|
||||
length = data2host4(pevent, ptr);
|
||||
ptr += 4;
|
||||
length *= 4;
|
||||
ptr += length;
|
||||
goto read_again;
|
||||
|
||||
case RINGBUF_TYPE_TIME_EXTEND:
|
||||
extend = data2host4(pevent, ptr);
|
||||
ptr += 4;
|
||||
extend <<= TS_SHIFT;
|
||||
extend += delta;
|
||||
cpu_data[cpu].timestamp += extend;
|
||||
goto read_again;
|
||||
|
||||
case RINGBUF_TYPE_TIME_STAMP:
|
||||
ptr += 12;
|
||||
break;
|
||||
case 0:
|
||||
length = data2host4(pevent, ptr);
|
||||
ptr += 4;
|
||||
die("here! length=%d", length);
|
||||
break;
|
||||
default:
|
||||
length = type_len * 4;
|
||||
break;
|
||||
}
|
||||
|
||||
cpu_data[cpu].timestamp += delta;
|
||||
|
||||
data = malloc_or_die(sizeof(*data));
|
||||
memset(data, 0, sizeof(*data));
|
||||
|
||||
data->ts = cpu_data[cpu].timestamp;
|
||||
data->size = length;
|
||||
data->data = ptr;
|
||||
ptr += length;
|
||||
|
||||
cpu_data[cpu].index = calc_index(ptr, cpu);
|
||||
cpu_data[cpu].next = data;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
struct pevent_record *trace_read_data(struct pevent *pevent, int cpu)
|
||||
{
|
||||
struct pevent_record *data;
|
||||
|
||||
data = trace_peek_data(pevent, cpu);
|
||||
cpu_data[cpu].next = NULL;
|
||||
|
||||
return data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t trace_report(int fd, struct pevent **ppevent, bool __repipe)
|
||||
|
@ -494,58 +348,85 @@ ssize_t trace_report(int fd, struct pevent **ppevent, bool __repipe)
|
|||
int show_version = 0;
|
||||
int show_funcs = 0;
|
||||
int show_printk = 0;
|
||||
ssize_t size;
|
||||
ssize_t size = -1;
|
||||
struct pevent *pevent;
|
||||
int err;
|
||||
|
||||
*ppevent = NULL;
|
||||
|
||||
calc_data_size = 1;
|
||||
repipe = __repipe;
|
||||
|
||||
input_fd = fd;
|
||||
|
||||
read_or_die(buf, 3);
|
||||
if (memcmp(buf, test, 3) != 0)
|
||||
die("no trace data in the file");
|
||||
if (do_read(buf, 3) < 0)
|
||||
return -1;
|
||||
if (memcmp(buf, test, 3) != 0) {
|
||||
pr_debug("no trace data in the file");
|
||||
return -1;
|
||||
}
|
||||
|
||||
read_or_die(buf, 7);
|
||||
if (memcmp(buf, "tracing", 7) != 0)
|
||||
die("not a trace file (missing 'tracing' tag)");
|
||||
if (do_read(buf, 7) < 0)
|
||||
return -1;
|
||||
if (memcmp(buf, "tracing", 7) != 0) {
|
||||
pr_debug("not a trace file (missing 'tracing' tag)");
|
||||
return -1;
|
||||
}
|
||||
|
||||
version = read_string();
|
||||
if (version == NULL)
|
||||
return -1;
|
||||
if (show_version)
|
||||
printf("version = %s\n", version);
|
||||
free(version);
|
||||
|
||||
read_or_die(buf, 1);
|
||||
if (do_read(buf, 1) < 0)
|
||||
return -1;
|
||||
file_bigendian = buf[0];
|
||||
host_bigendian = bigendian();
|
||||
|
||||
*ppevent = read_trace_init(file_bigendian, host_bigendian);
|
||||
if (*ppevent == NULL)
|
||||
die("read_trace_init failed");
|
||||
pevent = read_trace_init(file_bigendian, host_bigendian);
|
||||
if (pevent == NULL) {
|
||||
pr_debug("read_trace_init failed");
|
||||
goto out;
|
||||
}
|
||||
|
||||
read_or_die(buf, 1);
|
||||
if (do_read(buf, 1) < 0)
|
||||
goto out;
|
||||
long_size = buf[0];
|
||||
|
||||
page_size = read4(*ppevent);
|
||||
page_size = read4(pevent);
|
||||
if (!page_size)
|
||||
goto out;
|
||||
|
||||
read_header_files(*ppevent);
|
||||
err = read_header_files(pevent);
|
||||
if (err)
|
||||
goto out;
|
||||
err = read_ftrace_files(pevent);
|
||||
if (err)
|
||||
goto out;
|
||||
err = read_event_files(pevent);
|
||||
if (err)
|
||||
goto out;
|
||||
err = read_proc_kallsyms(pevent);
|
||||
if (err)
|
||||
goto out;
|
||||
err = read_ftrace_printk(pevent);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
read_ftrace_files(*ppevent);
|
||||
read_event_files(*ppevent);
|
||||
read_proc_kallsyms(*ppevent);
|
||||
read_ftrace_printk(*ppevent);
|
||||
|
||||
size = calc_data_size - 1;
|
||||
calc_data_size = 0;
|
||||
size = trace_data_size;
|
||||
repipe = false;
|
||||
|
||||
if (show_funcs) {
|
||||
pevent_print_funcs(*ppevent);
|
||||
return size;
|
||||
}
|
||||
if (show_printk) {
|
||||
pevent_print_printk(*ppevent);
|
||||
return size;
|
||||
pevent_print_funcs(pevent);
|
||||
} else if (show_printk) {
|
||||
pevent_print_printk(pevent);
|
||||
}
|
||||
|
||||
*ppevent = pevent;
|
||||
pevent = NULL;
|
||||
|
||||
out:
|
||||
if (pevent)
|
||||
pevent_free(pevent);
|
||||
return size;
|
||||
}
|
||||
|
|
|
@ -30,13 +30,9 @@ enum {
|
|||
int bigendian(void);
|
||||
|
||||
struct pevent *read_trace_init(int file_bigendian, int host_bigendian);
|
||||
void print_trace_event(struct pevent *pevent, int cpu, void *data, int size);
|
||||
void event_format__print(struct event_format *event,
|
||||
int cpu, void *data, int size);
|
||||
|
||||
void print_event(struct pevent *pevent, int cpu, void *data, int size,
|
||||
unsigned long long nsecs, char *comm);
|
||||
|
||||
int parse_ftrace_file(struct pevent *pevent, char *buf, unsigned long size);
|
||||
int parse_event_file(struct pevent *pevent,
|
||||
char *buf, unsigned long size, char *sys);
|
||||
|
@ -72,7 +68,7 @@ struct tracing_data {
|
|||
|
||||
struct tracing_data *tracing_data_get(struct list_head *pattrs,
|
||||
int fd, bool temp);
|
||||
void tracing_data_put(struct tracing_data *tdata);
|
||||
int tracing_data_put(struct tracing_data *tdata);
|
||||
|
||||
|
||||
struct addr_location;
|
||||
|
|
|
@ -17,6 +17,8 @@ bool test_attr__enabled;
|
|||
bool perf_host = true;
|
||||
bool perf_guest = false;
|
||||
|
||||
char tracing_events_path[PATH_MAX + 1] = "/sys/kernel/debug/tracing/events";
|
||||
|
||||
void event_attr_init(struct perf_event_attr *attr)
|
||||
{
|
||||
if (!perf_host)
|
||||
|
@ -242,3 +244,28 @@ void get_term_dimensions(struct winsize *ws)
|
|||
ws->ws_row = 25;
|
||||
ws->ws_col = 80;
|
||||
}
|
||||
|
||||
static void set_tracing_events_path(const char *mountpoint)
|
||||
{
|
||||
snprintf(tracing_events_path, sizeof(tracing_events_path), "%s/%s",
|
||||
mountpoint, "tracing/events");
|
||||
}
|
||||
|
||||
const char *perf_debugfs_mount(const char *mountpoint)
|
||||
{
|
||||
const char *mnt;
|
||||
|
||||
mnt = debugfs_mount(mountpoint);
|
||||
if (!mnt)
|
||||
return NULL;
|
||||
|
||||
set_tracing_events_path(mnt);
|
||||
|
||||
return mnt;
|
||||
}
|
||||
|
||||
void perf_debugfs_set_path(const char *mntpt)
|
||||
{
|
||||
snprintf(debugfs_mountpoint, strlen(debugfs_mountpoint), "%s", mntpt);
|
||||
set_tracing_events_path(mntpt);
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
#ifndef GIT_COMPAT_UTIL_H
|
||||
#define GIT_COMPAT_UTIL_H
|
||||
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
|
||||
#ifndef FLEX_ARRAY
|
||||
/*
|
||||
* See if our compiler is known to support flexible array members.
|
||||
|
@ -73,10 +71,14 @@
|
|||
#include <linux/magic.h>
|
||||
#include "types.h"
|
||||
#include <sys/ttydefaults.h>
|
||||
#include <lk/debugfs.h>
|
||||
|
||||
extern const char *graph_line;
|
||||
extern const char *graph_dotted_line;
|
||||
extern char buildid_dir[];
|
||||
extern char tracing_events_path[];
|
||||
extern void perf_debugfs_set_path(const char *mountpoint);
|
||||
const char *perf_debugfs_mount(const char *mountpoint);
|
||||
|
||||
/* On most systems <limits.h> would have given us this, but
|
||||
* not on some systems (e.g. GNU/Hurd).
|
||||
|
@ -274,5 +276,4 @@ extern unsigned int page_size;
|
|||
|
||||
struct winsize;
|
||||
void get_term_dimensions(struct winsize *ws);
|
||||
|
||||
#endif
|
||||
#endif /* GIT_COMPAT_UTIL_H */
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
ifneq ($(O),)
|
||||
ifeq ($(origin O), command line)
|
||||
dummy := $(if $(shell test -d $(O) || echo $(O)),$(error O=$(O) does not exist),)
|
||||
ABSOLUTE_O := $(shell cd $(O) ; pwd)
|
||||
|
@ -7,9 +8,10 @@ ifeq ($(objtree),)
|
|||
objtree := $(O)
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq ($(OUTPUT),)
|
||||
# check that the output directory actually exists
|
||||
ifneq ($(OUTPUT),)
|
||||
OUTDIR := $(shell cd $(OUTPUT) && /bin/pwd)
|
||||
$(if $(OUTDIR),, $(error output directory "$(OUTPUT)" does not exist))
|
||||
endif
|
||||
|
@ -70,7 +72,7 @@ ifndef V
|
|||
QUIET_BISON = @echo ' ' BISON $@;
|
||||
|
||||
descend = \
|
||||
@echo ' ' DESCEND $(1); \
|
||||
+@echo ' ' DESCEND $(1); \
|
||||
mkdir -p $(OUTPUT)$(1) && \
|
||||
$(MAKE) $(COMMAND_O) subdir=$(if $(subdir),$(subdir)/$(1),$(1)) $(PRINT_DIR) -C $(1) $(2)
|
||||
endif
|
||||
|
|
|
@ -1,11 +1,22 @@
|
|||
# Makefile for vm tools
|
||||
#
|
||||
TARGETS=page-types slabinfo
|
||||
|
||||
LK_DIR = ../lib/lk
|
||||
LIBLK = $(LK_DIR)/liblk.a
|
||||
|
||||
CC = $(CROSS_COMPILE)gcc
|
||||
CFLAGS = -Wall -Wextra
|
||||
CFLAGS = -Wall -Wextra -I../lib/
|
||||
LDFLAGS = $(LIBLK)
|
||||
|
||||
$(TARGETS): liblk
|
||||
|
||||
liblk:
|
||||
make -C $(LK_DIR)
|
||||
|
||||
all: page-types slabinfo
|
||||
%: %.c
|
||||
$(CC) $(CFLAGS) -o $@ $^
|
||||
$(CC) $(CFLAGS) -o $@ $< $(LDFLAGS)
|
||||
|
||||
clean:
|
||||
$(RM) page-types slabinfo
|
||||
make -C ../lib/lk clean
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
#include <sys/statfs.h>
|
||||
#include "../../include/uapi/linux/magic.h"
|
||||
#include "../../include/uapi/linux/kernel-page-flags.h"
|
||||
|
||||
#include <lk/debugfs.h>
|
||||
|
||||
#ifndef MAX_PATH
|
||||
# define MAX_PATH 256
|
||||
|
@ -178,7 +178,7 @@ static int kpageflags_fd;
|
|||
static int opt_hwpoison;
|
||||
static int opt_unpoison;
|
||||
|
||||
static char hwpoison_debug_fs[MAX_PATH+1];
|
||||
static char *hwpoison_debug_fs;
|
||||
static int hwpoison_inject_fd;
|
||||
static int hwpoison_forget_fd;
|
||||
|
||||
|
@ -458,81 +458,6 @@ static uint64_t kpageflags_flags(uint64_t flags)
|
|||
return flags;
|
||||
}
|
||||
|
||||
/* verify that a mountpoint is actually a debugfs instance */
|
||||
static int debugfs_valid_mountpoint(const char *debugfs)
|
||||
{
|
||||
struct statfs st_fs;
|
||||
|
||||
if (statfs(debugfs, &st_fs) < 0)
|
||||
return -ENOENT;
|
||||
else if (st_fs.f_type != (long) DEBUGFS_MAGIC)
|
||||
return -ENOENT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* find the path to the mounted debugfs */
|
||||
static const char *debugfs_find_mountpoint(void)
|
||||
{
|
||||
const char *const *ptr;
|
||||
char type[100];
|
||||
FILE *fp;
|
||||
|
||||
ptr = debugfs_known_mountpoints;
|
||||
while (*ptr) {
|
||||
if (debugfs_valid_mountpoint(*ptr) == 0) {
|
||||
strcpy(hwpoison_debug_fs, *ptr);
|
||||
return hwpoison_debug_fs;
|
||||
}
|
||||
ptr++;
|
||||
}
|
||||
|
||||
/* give up and parse /proc/mounts */
|
||||
fp = fopen("/proc/mounts", "r");
|
||||
if (fp == NULL)
|
||||
perror("Can't open /proc/mounts for read");
|
||||
|
||||
while (fscanf(fp, "%*s %"
|
||||
STR(MAX_PATH)
|
||||
"s %99s %*s %*d %*d\n",
|
||||
hwpoison_debug_fs, type) == 2) {
|
||||
if (strcmp(type, "debugfs") == 0)
|
||||
break;
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
if (strcmp(type, "debugfs") != 0)
|
||||
return NULL;
|
||||
|
||||
return hwpoison_debug_fs;
|
||||
}
|
||||
|
||||
/* mount the debugfs somewhere if it's not mounted */
|
||||
|
||||
static void debugfs_mount(void)
|
||||
{
|
||||
const char *const *ptr;
|
||||
|
||||
/* see if it's already mounted */
|
||||
if (debugfs_find_mountpoint())
|
||||
return;
|
||||
|
||||
ptr = debugfs_known_mountpoints;
|
||||
while (*ptr) {
|
||||
if (mount(NULL, *ptr, "debugfs", 0, NULL) == 0) {
|
||||
/* save the mountpoint */
|
||||
strcpy(hwpoison_debug_fs, *ptr);
|
||||
break;
|
||||
}
|
||||
ptr++;
|
||||
}
|
||||
|
||||
if (*ptr == NULL) {
|
||||
perror("mount debugfs");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* page actions
|
||||
*/
|
||||
|
@ -541,7 +466,11 @@ static void prepare_hwpoison_fd(void)
|
|||
{
|
||||
char buf[MAX_PATH + 1];
|
||||
|
||||
debugfs_mount();
|
||||
hwpoison_debug_fs = debugfs_mount(NULL);
|
||||
if (!hwpoison_debug_fs) {
|
||||
perror("mount debugfs");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (opt_hwpoison && !hwpoison_inject_fd) {
|
||||
snprintf(buf, MAX_PATH, "%s/hwpoison/corrupt-pfn",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue