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:
Linus Torvalds 2013-04-30 07:41:01 -07:00
commit e0972916e8
130 changed files with 5678 additions and 1716 deletions

View file

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

View file

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

View file

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -6,7 +6,7 @@
*
*/
#include <libio.h>
#include <stddef.h>
#include <dwarf-regs.h>
#define NUM_GPRS 16

View file

@ -19,7 +19,7 @@
*
*/
#include <libio.h>
#include <stddef.h>
#include <dwarf-regs.h>
/*

View file

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

View file

@ -20,7 +20,7 @@
*
*/
#include <libio.h>
#include <stddef.h>
#include <dwarf-regs.h>
/*

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -2,6 +2,7 @@
fd=1
group_fd=-1
flags=0
cpu=*
type=0|1
size=96
config=0

View file

@ -2,6 +2,7 @@
fd=1
group_fd=-1
flags=0
cpu=*
type=0
size=96
config=0

View 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

View 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

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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

View file

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

View file

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

View file

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

View file

@ -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(&notes->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, &notes->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(&notes->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(&notes->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(&notes->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 = &notes->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, &notes->src->source, node) {
list_del(&pos->node);
disasm_line__free(pos);

View file

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

View file

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

View file

@ -1,5 +1,4 @@
#include <elf.h>
#include <newt.h>
#include <inttypes.h>
#include <sys/ttydefaults.h>
#include <string.h>

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -36,6 +36,7 @@ struct symbol_conf symbol_conf = {
.use_modules = true,
.try_vmlinux_path = true,
.annotate_src = true,
.demangle = true,
.symfs = "",
};

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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