diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-04-30 07:41:01 -0700 | 
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-04-30 07:41:01 -0700 | 
| commit | e0972916e8fe943f342b0dd1c9d43dbf5bc261c2 (patch) | |
| tree | 690c436f1f9b839c4ba34d17ab3efa63b97a2dce /tools | |
| parent | 1f889ec62c3f0d8913f3c32f9aff2a1e15099346 (diff) | |
| parent | 5ac2b5c2721501a8f5c5e1cd4116cbc31ace6886 (diff) | |
| download | olio-linux-3.10-e0972916e8fe943f342b0dd1c9d43dbf5bc261c2.tar.xz olio-linux-3.10-e0972916e8fe943f342b0dd1c9d43dbf5bc261c2.zip | |
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
  ...
Diffstat (limited to 'tools')
99 files changed, 3241 insertions, 1220 deletions
| diff --git a/tools/Makefile b/tools/Makefile index fa36565b209..6aaeb6cd867 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -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: diff --git a/tools/lib/lk/Makefile b/tools/lib/lk/Makefile new file mode 100644 index 00000000000..926cbf3efc7 --- /dev/null +++ b/tools/lib/lk/Makefile @@ -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 diff --git a/tools/perf/util/debugfs.c b/tools/lib/lk/debugfs.c index dd8b19319c0..099e7cd022e 100644 --- a/tools/perf/util/debugfs.c +++ b/tools/lib/lk/debugfs.c @@ -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); -} diff --git a/tools/lib/lk/debugfs.h b/tools/lib/lk/debugfs.h new file mode 100644 index 00000000000..935c59bdb44 --- /dev/null +++ b/tools/lib/lk/debugfs.h @@ -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__ */ diff --git a/tools/perf/Documentation/perf-annotate.txt b/tools/perf/Documentation/perf-annotate.txt index 5ad07ef417f..e9cd39a92dc 100644 --- a/tools/perf/Documentation/perf-annotate.txt +++ b/tools/perf/Documentation/perf-annotate.txt @@ -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] diff --git a/tools/perf/Documentation/perf-mem.txt b/tools/perf/Documentation/perf-mem.txt new file mode 100644 index 00000000000..888d51137fb --- /dev/null +++ b/tools/perf/Documentation/perf-mem.txt @@ -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] diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index 938e8904f64..d4da111ef53 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt @@ -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] diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index 02284a0067f..7d5f4f38aa5 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt @@ -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] diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt index faf4f4feebc..2fe87fb558f 100644 --- a/tools/perf/Documentation/perf-stat.txt +++ b/tools/perf/Documentation/perf-stat.txt @@ -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  -------- diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt index a414bc95fd5..9f1a2fe5475 100644 --- a/tools/perf/Documentation/perf-top.txt +++ b/tools/perf/Documentation/perf-top.txt @@ -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:: diff --git a/tools/perf/MANIFEST b/tools/perf/MANIFEST index 39d41068484..025de796067 100644 --- a/tools/perf/MANIFEST +++ b/tools/perf/MANIFEST @@ -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 diff --git a/tools/perf/Makefile b/tools/perf/Makefile index bb74c79cd16..b0f164b133d 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -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 diff --git a/tools/perf/arch/arm/util/dwarf-regs.c b/tools/perf/arch/arm/util/dwarf-regs.c index e8d5c551c69..33ec5b339da 100644 --- a/tools/perf/arch/arm/util/dwarf-regs.c +++ b/tools/perf/arch/arm/util/dwarf-regs.c @@ -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 { diff --git a/tools/perf/arch/powerpc/util/dwarf-regs.c b/tools/perf/arch/powerpc/util/dwarf-regs.c index 7cdd61d0e27..733151cdf46 100644 --- a/tools/perf/arch/powerpc/util/dwarf-regs.c +++ b/tools/perf/arch/powerpc/util/dwarf-regs.c @@ -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> diff --git a/tools/perf/arch/s390/util/dwarf-regs.c b/tools/perf/arch/s390/util/dwarf-regs.c index e19653e025f..0469df02ee6 100644 --- a/tools/perf/arch/s390/util/dwarf-regs.c +++ b/tools/perf/arch/s390/util/dwarf-regs.c @@ -6,7 +6,7 @@   *   */ -#include <libio.h> +#include <stddef.h>  #include <dwarf-regs.h>  #define NUM_GPRS 16 diff --git a/tools/perf/arch/sh/util/dwarf-regs.c b/tools/perf/arch/sh/util/dwarf-regs.c index a11edb007a6..0d0897f57a1 100644 --- a/tools/perf/arch/sh/util/dwarf-regs.c +++ b/tools/perf/arch/sh/util/dwarf-regs.c @@ -19,7 +19,7 @@   *   */ -#include <libio.h> +#include <stddef.h>  #include <dwarf-regs.h>  /* diff --git a/tools/perf/arch/sparc/util/dwarf-regs.c b/tools/perf/arch/sparc/util/dwarf-regs.c index 0ab88483720..92eda412fed 100644 --- a/tools/perf/arch/sparc/util/dwarf-regs.c +++ b/tools/perf/arch/sparc/util/dwarf-regs.c @@ -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 diff --git a/tools/perf/arch/x86/util/dwarf-regs.c b/tools/perf/arch/x86/util/dwarf-regs.c index a794d308192..be22dd46323 100644 --- a/tools/perf/arch/x86/util/dwarf-regs.c +++ b/tools/perf/arch/x86/util/dwarf-regs.c @@ -20,7 +20,7 @@   *   */ -#include <libio.h> +#include <stddef.h>  #include <dwarf-regs.h>  /* diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 2e6961ea318..db491e9a812 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -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()  	}; diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index d207a97a2db..2d0462d89a9 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -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;  	} diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c index 37a769d7f9f..533501e2b07 100644 --- a/tools/perf/builtin-kvm.c +++ b/tools/perf/builtin-kvm.c @@ -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" diff --git a/tools/perf/builtin-mem.c b/tools/perf/builtin-mem.c new file mode 100644 index 00000000000..a8ff6d264e5 --- /dev/null +++ b/tools/perf/builtin-mem.c @@ -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; +} diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index de38a034b12..e8a66f9a671 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -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" diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index f1a939ebc19..cdf58ecc04b 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -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: diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 96b5a7fee4b..bd0ca81eeac 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -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);  	} diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 138229439a9..2da2a6ca22b 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -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,  		}, diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 99848761f57..7e910bab109 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -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) { +	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(counter); +			read_counter_aggr(counter);  		} -	} else { +	} else	{  		list_for_each_entry(counter, &evsel_list->entries, node) {  			ps = counter->priv;  			memset(ps->res_stats, 0, sizeof(ps->res_stats)); -			read_counter_aggr(counter); +			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) { +	if (aggr_mode == AGGR_GLOBAL) {  		list_for_each_entry(counter, &evsel_list->entries, node) { -			read_counter(counter); -			perf_evsel__close_fd(counter, perf_evsel__nr_cpus(counter), 1); +			read_counter_aggr(counter); +			perf_evsel__close_fd(counter, perf_evsel__nr_cpus(counter), +					     thread_map__nr(evsel_list->threads));  		}  	} else {  		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); +			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", +	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); +		break; +	case AGGR_SOCKET: +		fprintf(output, "S%*d%s%*d%s",  			csv_output ? 0 : -5, -			cpu, +			id,  			csv_sep,  			csv_output ? 0 : 4,  			nr,  			csv_sep); -	else if (no_aggr) -		sprintf(cpustr, "CPU%*d%s", +			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; +	} +} + +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, cpustr, msecs, csv_sep, perf_evsel__name(evsel)); +	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); diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 72f6eb7b417..67bdb9f14ad 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -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; diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index d222d7fc7e9..ab3ed4af146 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -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: diff --git a/tools/perf/builtin.h b/tools/perf/builtin.h index 08143bd854c..b210d62907e 100644 --- a/tools/perf/builtin.h +++ b/tools/perf/builtin.h @@ -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 diff --git a/tools/perf/command-list.txt b/tools/perf/command-list.txt index 3e86bbd8c2d..0906fc401c5 100644 --- a/tools/perf/command-list.txt +++ b/tools/perf/command-list.txt @@ -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 diff --git a/tools/perf/config/feature-tests.mak b/tools/perf/config/feature-tests.mak index b4eabb44e38..708fb8e9822 100644 --- a/tools/perf/config/feature-tests.mak +++ b/tools/perf/config/feature-tests.mak @@ -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
\ No newline at end of file +endef diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 095b88207cd..85e1aed9520 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -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; diff --git a/tools/perf/perf.h b/tools/perf/perf.h index 74659ecf93e..32bd102c32b 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h @@ -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; diff --git a/tools/perf/tests/attr.c b/tools/perf/tests/attr.c index bdcceb886f7..038de3ecb8c 100644 --- a/tools/perf/tests/attr.c +++ b/tools/perf/tests/attr.c @@ -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);  } diff --git a/tools/perf/tests/attr.py b/tools/perf/tests/attr.py index 2f629ca485b..c9b4b6269b5 100644 --- a/tools/perf/tests/attr.py +++ b/tools/perf/tests/attr.py @@ -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) diff --git a/tools/perf/tests/attr/base-record b/tools/perf/tests/attr/base-record index 5bc3880f7be..b4fc835de60 100644 --- a/tools/perf/tests/attr/base-record +++ b/tools/perf/tests/attr/base-record @@ -2,6 +2,7 @@  fd=1  group_fd=-1  flags=0 +cpu=*  type=0|1  size=96  config=0 diff --git a/tools/perf/tests/attr/base-stat b/tools/perf/tests/attr/base-stat index 4bd79a82784..748ee949a20 100644 --- a/tools/perf/tests/attr/base-stat +++ b/tools/perf/tests/attr/base-stat @@ -2,6 +2,7 @@  fd=1  group_fd=-1  flags=0 +cpu=*  type=0  size=96  config=0 diff --git a/tools/perf/tests/attr/test-record-C0 b/tools/perf/tests/attr/test-record-C0 new file mode 100644 index 00000000000..d6a7e43f61b --- /dev/null +++ b/tools/perf/tests/attr/test-record-C0 @@ -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 diff --git a/tools/perf/tests/attr/test-stat-C0 b/tools/perf/tests/attr/test-stat-C0 new file mode 100644 index 00000000000..aa835950751 --- /dev/null +++ b/tools/perf/tests/attr/test-stat-C0 @@ -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 diff --git a/tools/perf/tests/bp_signal.c b/tools/perf/tests/bp_signal.c new file mode 100644 index 00000000000..68daa289e94 --- /dev/null +++ b/tools/perf/tests/bp_signal.c @@ -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; +} diff --git a/tools/perf/tests/bp_signal_overflow.c b/tools/perf/tests/bp_signal_overflow.c new file mode 100644 index 00000000000..fe7ed28815f --- /dev/null +++ b/tools/perf/tests/bp_signal_overflow.c @@ -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; +} diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index acb98e0e39f..0918ada4cc4 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -78,6 +78,22 @@ static struct test {  		.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,  	},  }; diff --git a/tools/perf/tests/evsel-roundtrip-name.c b/tools/perf/tests/evsel-roundtrip-name.c index 0fd99a9adb9..0197bda9c46 100644 --- a/tools/perf/tests/evsel-roundtrip-name.c +++ b/tools/perf/tests/evsel-roundtrip-name.c @@ -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; diff --git a/tools/perf/tests/hists_link.c b/tools/perf/tests/hists_link.c index 1be64a6c5da..89085a9615e 100644 --- a/tools/perf/tests/hists_link.c +++ b/tools/perf/tests/hists_link.c @@ -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; diff --git a/tools/perf/tests/mmap-basic.c b/tools/perf/tests/mmap-basic.c index cdd50755af5..5b1b5aba722 100644 --- a/tools/perf/tests/mmap-basic.c +++ b/tools/perf/tests/mmap-basic.c @@ -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]; diff --git a/tools/perf/tests/open-syscall-tp-fields.c b/tools/perf/tests/open-syscall-tp-fields.c index 1c52fdc1164..fc5b9fca8b4 100644 --- a/tools/perf/tests/open-syscall-tp-fields.c +++ b/tools/perf/tests/open-syscall-tp-fields.c @@ -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: diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index c5636f36fe3..88e2f44cb15 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c @@ -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; diff --git a/tools/perf/tests/perf-record.c b/tools/perf/tests/perf-record.c index 1e8e5128d0d..72d8881873b 100644 --- a/tools/perf/tests/perf-record.c +++ b/tools/perf/tests/perf-record.c @@ -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: diff --git a/tools/perf/tests/sw-clock.c b/tools/perf/tests/sw-clock.c new file mode 100644 index 00000000000..2e41e2d32cc --- /dev/null +++ b/tools/perf/tests/sw-clock.c @@ -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; +} diff --git a/tools/perf/tests/task-exit.c b/tools/perf/tests/task-exit.c new file mode 100644 index 00000000000..28fe5894b06 --- /dev/null +++ b/tools/perf/tests/task-exit.c @@ -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; +} diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index 5de0be1ff4b..dd7feae2d37 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h @@ -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 */ diff --git a/tools/perf/ui/browser.c b/tools/perf/ui/browser.c index 809ea4632a3..bbc782e364b 100644 --- a/tools/perf/ui/browser.c +++ b/tools/perf/ui/browser.c @@ -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);  } @@ -514,6 +513,12 @@ static struct ui_browser_colorset {  		.bg	  = "default",  	},  	{ +		.colorset = HE_COLORSET_ROOT, +		.name	  = "root", +		.fg	  = "white", +		.bg	  = "blue", +	}, +	{  		.name = NULL,  	}  }; diff --git a/tools/perf/ui/browser.h b/tools/perf/ui/browser.h index af70314605e..404ff66a3e3 100644 --- a/tools/perf/ui/browser.h +++ b/tools/perf/ui/browser.h @@ -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; diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c index 7dca1555c61..cc64d3f7fc3 100644 --- a/tools/perf/ui/browsers/annotate.c +++ b/tools/perf/ui/browsers/annotate.c @@ -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; - -	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; +	int i; -		next = disasm__get_next_ip_line(¬es->src->source, dl); -		while (offset < (s64)len && -		       (next == NULL || offset < next->offset)) { -			if (src_line) { -				percent += src_line[offset].percent; -			} else -				hits += h->addr[offset]; - -			++offset; -		} -		/* - 		 * If the percentage wasn't already calculated in - 		 * symbol__get_source_line, do it now: - 		 */ -		if (src_line == NULL && h->sum) -			percent = 100.0 * hits / h->sum; +	for (i = 0; i < nr_pcnt; i++) { +		if (a->percent[i] == b->percent[i]) +			continue; +		return a->percent[i] < b->percent[i];  	} - -	return percent; +	return 0;  } -static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_line *bdl) +static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_line *bdl, +				   int nr_events)  {  	struct rb_node **p = &root->rb_node;  	struct rb_node *parent = NULL; @@ -282,7 +285,8 @@ static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_l  	while (*p != NULL) {  		parent = *p;  		l = rb_entry(parent, struct browser_disasm_line, rb_node); -		if (bdl->percent < l->percent) + +		if (disasm__cmp(bdl, l, nr_events))  			p = &(*p)->rb_left;  		else  			p = &(*p)->rb_right; @@ -331,12 +335,13 @@ static void annotate_browser__set_rb_top(struct annotate_browser *browser,  }  static void annotate_browser__calc_percent(struct annotate_browser *browser, -					   int evidx) +					   struct perf_evsel *evsel)  {  	struct map_symbol *ms = browser->b.priv;  	struct symbol *sym = ms->sym;  	struct annotation *notes = symbol__annotation(sym); -	struct disasm_line *pos; +	struct disasm_line *pos, *next; +	s64 len = symbol__size(sym);  	browser->entries = RB_ROOT; @@ -344,12 +349,34 @@ static void annotate_browser__calc_percent(struct annotate_browser *browser,  	list_for_each_entry(pos, ¬es->src->source, node) {  		struct browser_disasm_line *bpos = disasm_line__browser(pos); -		bpos->percent = disasm_line__calc_percent(pos, sym, evidx); -		if (bpos->percent < 0.01) { +		const char *path = NULL; +		double max_percent = 0.0; +		int i; + +		if (pos->offset == -1) { +			RB_CLEAR_NODE(&bpos->rb_node); +			continue; +		} + +		next = disasm__get_next_ip_line(¬es->src->source, pos); + +		for (i = 0; i < browser->nr_events; i++) { +			bpos->percent[i] = disasm__calc_percent(notes, +						evsel->idx + i, +						pos->offset, +						next ? next->offset : len, +					        &path); + +			if (max_percent < bpos->percent[i]) +				max_percent = bpos->percent[i]; +		} + +		if (max_percent < 0.01) {  			RB_CLEAR_NODE(&bpos->rb_node);  			continue;  		} -		disasm_rb_tree__insert(&browser->entries, bpos); +		disasm_rb_tree__insert(&browser->entries, bpos, +				       browser->nr_events);  	}  	pthread_mutex_unlock(¬es->lock); @@ -401,7 +428,8 @@ static void annotate_browser__init_asm_mode(struct annotate_browser *browser)  	browser->b.nr_entries = browser->nr_asm_entries;  } -static bool annotate_browser__callq(struct annotate_browser *browser, int evidx, +static bool annotate_browser__callq(struct annotate_browser *browser, +				    struct perf_evsel *evsel,  				    struct hist_browser_timer *hbt)  {  	struct map_symbol *ms = browser->b.priv; @@ -432,7 +460,7 @@ static bool annotate_browser__callq(struct annotate_browser *browser, int evidx,  	}  	pthread_mutex_unlock(¬es->lock); -	symbol__tui_annotate(target, ms->map, evidx, hbt); +	symbol__tui_annotate(target, ms->map, evsel, hbt);  	ui_browser__show_title(&browser->b, sym->name);  	return true;  } @@ -615,7 +643,8 @@ static void annotate_browser__update_addr_width(struct annotate_browser *browser  		browser->addr_width += browser->jumps_width + 1;  } -static int annotate_browser__run(struct annotate_browser *browser, int evidx, +static int annotate_browser__run(struct annotate_browser *browser, +				 struct perf_evsel *evsel,  				 struct hist_browser_timer *hbt)  {  	struct rb_node *nd = NULL; @@ -628,7 +657,7 @@ static int annotate_browser__run(struct annotate_browser *browser, int evidx,  	if (ui_browser__show(&browser->b, sym->name, help) < 0)  		return -1; -	annotate_browser__calc_percent(browser, evidx); +	annotate_browser__calc_percent(browser, evsel);  	if (browser->curr_hot) {  		annotate_browser__set_rb_top(browser, browser->curr_hot); @@ -641,7 +670,7 @@ static int annotate_browser__run(struct annotate_browser *browser, int evidx,  		key = ui_browser__run(&browser->b, delay_secs);  		if (delay_secs != 0) { -			annotate_browser__calc_percent(browser, evidx); +			annotate_browser__calc_percent(browser, evsel);  			/*  			 * Current line focus got out of the list of most active  			 * lines, NULL it so that if TAB|UNTAB is pressed, we @@ -657,7 +686,7 @@ static int annotate_browser__run(struct annotate_browser *browser, int evidx,  				hbt->timer(hbt->arg);  			if (delay_secs != 0) -				symbol__annotate_decay_histogram(sym, evidx); +				symbol__annotate_decay_histogram(sym, evsel->idx);  			continue;  		case K_TAB:  			if (nd != NULL) { @@ -754,7 +783,7 @@ show_help:  					goto show_sup_ins;  				goto out;  			} else if (!(annotate_browser__jump(browser) || -				     annotate_browser__callq(browser, evidx, hbt))) { +				     annotate_browser__callq(browser, evsel, hbt))) {  show_sup_ins:  				ui_helpline__puts("Actions are only available for 'callq', 'retq' & jump instructions.");  			} @@ -776,10 +805,10 @@ out:  	return key;  } -int hist_entry__tui_annotate(struct hist_entry *he, int evidx, +int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,  			     struct hist_browser_timer *hbt)  { -	return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx, hbt); +	return symbol__tui_annotate(he->ms.sym, he->ms.map, evsel, hbt);  }  static void annotate_browser__mark_jump_targets(struct annotate_browser *browser, @@ -826,7 +855,8 @@ static inline int width_jumps(int n)  	return 1;  } -int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, +int symbol__tui_annotate(struct symbol *sym, struct map *map, +			 struct perf_evsel *evsel,  			 struct hist_browser_timer *hbt)  {  	struct disasm_line *pos, *n; @@ -847,6 +877,8 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,  		},  	};  	int ret = -1; +	int nr_pcnt = 1; +	size_t sizeof_bdl = sizeof(struct browser_disasm_line);  	if (sym == NULL)  		return -1; @@ -862,7 +894,12 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,  		return -1;  	} -	if (symbol__annotate(sym, map, sizeof(struct browser_disasm_line)) < 0) { +	if (perf_evsel__is_group_event(evsel)) { +		nr_pcnt = evsel->nr_members; +		sizeof_bdl += sizeof(double) * (nr_pcnt - 1); +	} + +	if (symbol__annotate(sym, map, sizeof_bdl) < 0) {  		ui__error("%s", ui_helpline__last_msg);  		goto out_free_offsets;  	} @@ -900,6 +937,7 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,  	browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size);  	browser.max_addr_width = hex_width(sym->end);  	browser.jumps_width = width_jumps(browser.max_jump_sources); +	browser.nr_events = nr_pcnt;  	browser.b.nr_entries = browser.nr_entries;  	browser.b.entries = ¬es->src->source,  	browser.b.width += 18; /* Percentage */ @@ -909,7 +947,7 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,  	annotate_browser__update_addr_width(&browser); -	ret = annotate_browser__run(&browser, evidx, hbt); +	ret = annotate_browser__run(&browser, evsel, hbt);  	list_for_each_entry_safe(pos, n, ¬es->src->source, node) {  		list_del(&pos->node);  		disasm_line__free(pos); diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index aa22704047d..d88a2d0acb6 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -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); diff --git a/tools/perf/ui/browsers/map.c b/tools/perf/ui/browsers/map.c index 98851d55a53..95c7cfb8f2c 100644 --- a/tools/perf/ui/browsers/map.c +++ b/tools/perf/ui/browsers/map.c @@ -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;  } diff --git a/tools/perf/ui/browsers/scripts.c b/tools/perf/ui/browsers/scripts.c index cbbd44b0d93..12f009e61e9 100644 --- a/tools/perf/ui/browsers/scripts.c +++ b/tools/perf/ui/browsers/scripts.c @@ -1,5 +1,4 @@  #include <elf.h> -#include <newt.h>  #include <inttypes.h>  #include <sys/ttydefaults.h>  #include <string.h> diff --git a/tools/perf/ui/gtk/annotate.c b/tools/perf/ui/gtk/annotate.c index 7d8dc581a54..f538794615d 100644 --- a/tools/perf/ui/gtk/annotate.c +++ b/tools/perf/ui/gtk/annotate.c @@ -1,6 +1,7 @@  #include "gtk.h"  #include "util/debug.h"  #include "util/annotate.h" +#include "util/evsel.h"  #include "ui/helpline.h" @@ -32,7 +33,7 @@ static int perf_gtk__get_percent(char *buf, size_t size, struct symbol *sym,  		return 0;  	symhist = annotation__histogram(symbol__annotation(sym), evidx); -	if (!symhist->addr[dl->offset]) +	if (!symbol_conf.event_group && !symhist->addr[dl->offset])  		return 0;  	percent = 100.0 * symhist->addr[dl->offset] / symhist->sum; @@ -85,7 +86,7 @@ static int perf_gtk__get_line(char *buf, size_t size, struct disasm_line *dl)  }  static int perf_gtk__annotate_symbol(GtkWidget *window, struct symbol *sym, -				struct map *map, int evidx, +				struct map *map, struct perf_evsel *evsel,  				struct hist_browser_timer *hbt __maybe_unused)  {  	struct disasm_line *pos, *n; @@ -118,10 +119,24 @@ static int perf_gtk__annotate_symbol(GtkWidget *window, struct symbol *sym,  	list_for_each_entry(pos, ¬es->src->source, node) {  		GtkTreeIter iter; +		int ret = 0;  		gtk_list_store_append(store, &iter); -		if (perf_gtk__get_percent(s, sizeof(s), sym, pos, evidx)) +		if (perf_evsel__is_group_event(evsel)) { +			for (i = 0; i < evsel->nr_members; i++) { +				ret += perf_gtk__get_percent(s + ret, +							     sizeof(s) - ret, +							     sym, pos, +							     evsel->idx + i); +				ret += scnprintf(s + ret, sizeof(s) - ret, " "); +			} +		} else { +			ret = perf_gtk__get_percent(s, sizeof(s), sym, pos, +						    evsel->idx); +		} + +		if (ret)  			gtk_list_store_set(store, &iter, ANN_COL__PERCENT, s, -1);  		if (perf_gtk__get_offset(s, sizeof(s), sym, map, pos))  			gtk_list_store_set(store, &iter, ANN_COL__OFFSET, s, -1); @@ -139,7 +154,8 @@ static int perf_gtk__annotate_symbol(GtkWidget *window, struct symbol *sym,  	return 0;  } -int symbol__gtk_annotate(struct symbol *sym, struct map *map, int evidx, +int symbol__gtk_annotate(struct symbol *sym, struct map *map, +			 struct perf_evsel *evsel,  			 struct hist_browser_timer *hbt)  {  	GtkWidget *window; @@ -206,7 +222,7 @@ int symbol__gtk_annotate(struct symbol *sym, struct map *map, int evidx,  	gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled_window,  				 tab_label); -	perf_gtk__annotate_symbol(scrolled_window, sym, map, evidx, hbt); +	perf_gtk__annotate_symbol(scrolled_window, sym, map, evsel, hbt);  	return 0;  } diff --git a/tools/perf/ui/gtk/hists.c b/tools/perf/ui/gtk/hists.c index 1e764a8ad25..6f259b3d14e 100644 --- a/tools/perf/ui/gtk/hists.c +++ b/tools/perf/ui/gtk/hists.c @@ -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) { diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c index d671e63aa35..4bf91b09d62 100644 --- a/tools/perf/ui/hist.c +++ b/tools/perf/ui/hist.c @@ -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) { diff --git a/tools/perf/ui/tui/setup.c b/tools/perf/ui/tui/setup.c index 81efa192e86..b9401482d11 100644 --- a/tools/perf/ui/tui/setup.c +++ b/tools/perf/ui/tui/setup.c @@ -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(); diff --git a/tools/perf/ui/ui.h b/tools/perf/ui/ui.h index d86359c9990..70cb0d4eb8a 100644 --- a/tools/perf/ui/ui.h +++ b/tools/perf/ui/ui.h @@ -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 diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index d33fe937e6f..d102716c43a 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -14,6 +14,7 @@  #include "symbol.h"  #include "debug.h"  #include "annotate.h" +#include "evsel.h"  #include <pthread.h>  #include <linux/bitops.h> @@ -602,8 +603,42 @@ struct disasm_line *disasm__get_next_ip_line(struct list_head *head, struct disa  	return NULL;  } +double disasm__calc_percent(struct annotation *notes, int evidx, s64 offset, +			    s64 end, const char **path) +{ +	struct source_line *src_line = notes->src->lines; +	double percent = 0.0; + +	if (src_line) { +		size_t sizeof_src_line = sizeof(*src_line) + +				sizeof(src_line->p) * (src_line->nr_pcnt - 1); + +		while (offset < end) { +			src_line = (void *)notes->src->lines + +					(sizeof_src_line * offset); + +			if (*path == NULL) +				*path = src_line->path; + +			percent += src_line->p[evidx].percent; +			offset++; +		} +	} else { +		struct sym_hist *h = annotation__histogram(notes, evidx); +		unsigned int hits = 0; + +		while (offset < end) +			hits += h->addr[offset++]; + +		if (h->sum) +			percent = 100.0 * hits / h->sum; +	} + +	return percent; +} +  static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 start, -		      int evidx, u64 len, int min_pcnt, int printed, +		      struct perf_evsel *evsel, u64 len, int min_pcnt, int printed,  		      int max_lines, struct disasm_line *queue)  {  	static const char *prev_line; @@ -611,34 +646,37 @@ static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 st  	if (dl->offset != -1) {  		const char *path = NULL; -		unsigned int hits = 0; -		double percent = 0.0; +		double percent, max_percent = 0.0; +		double *ppercents = &percent; +		int i, nr_percent = 1;  		const char *color;  		struct annotation *notes = symbol__annotation(sym); -		struct source_line *src_line = notes->src->lines; -		struct sym_hist *h = annotation__histogram(notes, evidx);  		s64 offset = dl->offset;  		const u64 addr = start + offset;  		struct disasm_line *next;  		next = disasm__get_next_ip_line(¬es->src->source, dl); -		while (offset < (s64)len && -		       (next == NULL || offset < next->offset)) { -			if (src_line) { -				if (path == NULL) -					path = src_line[offset].path; -				percent += src_line[offset].percent; -			} else -				hits += h->addr[offset]; - -			++offset; +		if (perf_evsel__is_group_event(evsel)) { +			nr_percent = evsel->nr_members; +			ppercents = calloc(nr_percent, sizeof(double)); +			if (ppercents == NULL) +				return -1;  		} -		if (src_line == NULL && h->sum) -			percent = 100.0 * hits / h->sum; +		for (i = 0; i < nr_percent; i++) { +			percent = disasm__calc_percent(notes, +					notes->src->lines ? i : evsel->idx + i, +					offset, +					next ? next->offset : (s64) len, +					&path); + +			ppercents[i] = percent; +			if (percent > max_percent) +				max_percent = percent; +		} -		if (percent < min_pcnt) +		if (max_percent < min_pcnt)  			return -1;  		if (max_lines && printed >= max_lines) @@ -648,12 +686,12 @@ static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 st  			list_for_each_entry_from(queue, ¬es->src->source, node) {  				if (queue == dl)  					break; -				disasm_line__print(queue, sym, start, evidx, len, +				disasm_line__print(queue, sym, start, evsel, len,  						    0, 0, 1, NULL);  			}  		} -		color = get_percent_color(percent); +		color = get_percent_color(max_percent);  		/*  		 * Also color the filename and line if needed, with @@ -669,25 +707,59 @@ static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 st  			}  		} -		color_fprintf(stdout, color, " %7.2f", percent); +		for (i = 0; i < nr_percent; i++) { +			percent = ppercents[i]; +			color = get_percent_color(percent); +			color_fprintf(stdout, color, " %7.2f", percent); +		} +  		printf(" :	");  		color_fprintf(stdout, PERF_COLOR_MAGENTA, "  %" PRIx64 ":", addr);  		color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", dl->line); + +		if (ppercents != &percent) +			free(ppercents); +  	} else if (max_lines && printed >= max_lines)  		return 1;  	else { +		int width = 8; +  		if (queue)  			return -1; +		if (perf_evsel__is_group_event(evsel)) +			width *= evsel->nr_members; +  		if (!*dl->line) -			printf("         :\n"); +			printf(" %*s:\n", width, " ");  		else -			printf("         :	%s\n", dl->line); +			printf(" %*s:	%s\n", width, " ", dl->line);  	}  	return 0;  } +/* + * symbol__parse_objdump_line() parses objdump output (with -d --no-show-raw) + * which looks like following + * + *  0000000000415500 <_init>: + *    415500:       sub    $0x8,%rsp + *    415504:       mov    0x2f5ad5(%rip),%rax        # 70afe0 <_DYNAMIC+0x2f8> + *    41550b:       test   %rax,%rax + *    41550e:       je     415515 <_init+0x15> + *    415510:       callq  416e70 <__gmon_start__@plt> + *    415515:       add    $0x8,%rsp + *    415519:       retq + * + * it will be parsed and saved into struct disasm_line as + *  <offset>       <name>  <ops.raw> + * + * The offset will be a relative offset from the start of the symbol and -1 + * means that it's not a disassembly line so should be treated differently. + * The ops.raw part will be parsed further according to type of the instruction. + */  static int symbol__parse_objdump_line(struct symbol *sym, struct map *map,  				      FILE *file, size_t privsize)  { @@ -858,7 +930,7 @@ static void insert_source_line(struct rb_root *root, struct source_line *src_lin  	struct source_line *iter;  	struct rb_node **p = &root->rb_node;  	struct rb_node *parent = NULL; -	int ret; +	int i, ret;  	while (*p != NULL) {  		parent = *p; @@ -866,7 +938,8 @@ static void insert_source_line(struct rb_root *root, struct source_line *src_lin  		ret = strcmp(iter->path, src_line->path);  		if (ret == 0) { -			iter->percent_sum += src_line->percent; +			for (i = 0; i < src_line->nr_pcnt; i++) +				iter->p[i].percent_sum += src_line->p[i].percent;  			return;  		} @@ -876,12 +949,26 @@ static void insert_source_line(struct rb_root *root, struct source_line *src_lin  			p = &(*p)->rb_right;  	} -	src_line->percent_sum = src_line->percent; +	for (i = 0; i < src_line->nr_pcnt; i++) +		src_line->p[i].percent_sum = src_line->p[i].percent;  	rb_link_node(&src_line->node, parent, p);  	rb_insert_color(&src_line->node, root);  } +static int cmp_source_line(struct source_line *a, struct source_line *b) +{ +	int i; + +	for (i = 0; i < a->nr_pcnt; i++) { +		if (a->p[i].percent_sum == b->p[i].percent_sum) +			continue; +		return a->p[i].percent_sum > b->p[i].percent_sum; +	} + +	return 0; +} +  static void __resort_source_line(struct rb_root *root, struct source_line *src_line)  {  	struct source_line *iter; @@ -892,7 +979,7 @@ static void __resort_source_line(struct rb_root *root, struct source_line *src_l  		parent = *p;  		iter = rb_entry(parent, struct source_line, node); -		if (src_line->percent_sum > iter->percent_sum) +		if (cmp_source_line(src_line, iter))  			p = &(*p)->rb_left;  		else  			p = &(*p)->rb_right; @@ -924,32 +1011,52 @@ static void symbol__free_source_line(struct symbol *sym, int len)  {  	struct annotation *notes = symbol__annotation(sym);  	struct source_line *src_line = notes->src->lines; +	size_t sizeof_src_line;  	int i; -	for (i = 0; i < len; i++) -		free(src_line[i].path); +	sizeof_src_line = sizeof(*src_line) + +			  (sizeof(src_line->p) * (src_line->nr_pcnt - 1)); -	free(src_line); +	for (i = 0; i < len; i++) { +		free(src_line->path); +		src_line = (void *)src_line + sizeof_src_line; +	} + +	free(notes->src->lines);  	notes->src->lines = NULL;  }  /* Get the filename:line for the colored entries */  static int symbol__get_source_line(struct symbol *sym, struct map *map, -				   int evidx, struct rb_root *root, int len, +				   struct perf_evsel *evsel, +				   struct rb_root *root, int len,  				   const char *filename)  {  	u64 start; -	int i; +	int i, k; +	int evidx = evsel->idx;  	char cmd[PATH_MAX * 2];  	struct source_line *src_line;  	struct annotation *notes = symbol__annotation(sym);  	struct sym_hist *h = annotation__histogram(notes, evidx);  	struct rb_root tmp_root = RB_ROOT; +	int nr_pcnt = 1; +	u64 h_sum = h->sum; +	size_t sizeof_src_line = sizeof(struct source_line); -	if (!h->sum) +	if (perf_evsel__is_group_event(evsel)) { +		for (i = 1; i < evsel->nr_members; i++) { +			h = annotation__histogram(notes, evidx + i); +			h_sum += h->sum; +		} +		nr_pcnt = evsel->nr_members; +		sizeof_src_line += (nr_pcnt - 1) * sizeof(src_line->p); +	} + +	if (!h_sum)  		return 0; -	src_line = notes->src->lines = calloc(len, sizeof(struct source_line)); +	src_line = notes->src->lines = calloc(len, sizeof_src_line);  	if (!notes->src->lines)  		return -1; @@ -960,29 +1067,41 @@ static int symbol__get_source_line(struct symbol *sym, struct map *map,  		size_t line_len;  		u64 offset;  		FILE *fp; +		double percent_max = 0.0; -		src_line[i].percent = 100.0 * h->addr[i] / h->sum; -		if (src_line[i].percent <= 0.5) -			continue; +		src_line->nr_pcnt = nr_pcnt; + +		for (k = 0; k < nr_pcnt; k++) { +			h = annotation__histogram(notes, evidx + k); +			src_line->p[k].percent = 100.0 * h->addr[i] / h->sum; + +			if (src_line->p[k].percent > percent_max) +				percent_max = src_line->p[k].percent; +		} + +		if (percent_max <= 0.5) +			goto next;  		offset = start + i;  		sprintf(cmd, "addr2line -e %s %016" PRIx64, filename, offset);  		fp = popen(cmd, "r");  		if (!fp) -			continue; +			goto next;  		if (getline(&path, &line_len, fp) < 0 || !line_len) -			goto next; +			goto next_close; -		src_line[i].path = malloc(sizeof(char) * line_len + 1); -		if (!src_line[i].path) -			goto next; +		src_line->path = malloc(sizeof(char) * line_len + 1); +		if (!src_line->path) +			goto next_close; -		strcpy(src_line[i].path, path); -		insert_source_line(&tmp_root, &src_line[i]); +		strcpy(src_line->path, path); +		insert_source_line(&tmp_root, src_line); -	next: +	next_close:  		pclose(fp); +	next: +		src_line = (void *)src_line + sizeof_src_line;  	}  	resort_source_line(root, &tmp_root); @@ -1004,24 +1123,33 @@ static void print_summary(struct rb_root *root, const char *filename)  	node = rb_first(root);  	while (node) { -		double percent; +		double percent, percent_max = 0.0;  		const char *color;  		char *path; +		int i;  		src_line = rb_entry(node, struct source_line, node); -		percent = src_line->percent_sum; -		color = get_percent_color(percent); +		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); + +	if (perf_evsel__is_group_event(evsel)) +		width *= evsel->nr_members; -	printf(" Percent |	Source code & Disassembly of %s\n", d_filename); -	printf("------------------------------------------------\n"); +	printf(" %-*.*s|	Source code & Disassembly of %s\n", +	       width, width, "Percent", d_filename); +	printf("-%-*.*s-------------------------------------\n", +	       width+namelen, width+namelen, graph_dotted_line);  	if (verbose) -		symbol__annotate_hits(sym, evidx); +		symbol__annotate_hits(sym, evsel);  	list_for_each_entry(pos, ¬es->src->source, node) {  		if (context && queue == NULL) { @@ -1068,7 +1204,7 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx,  			queue_len = 0;  		} -		switch (disasm_line__print(pos, sym, start, evidx, len, +		switch (disasm_line__print(pos, sym, start, evsel, len,  					    min_pcnt, printed, max_lines,  					    queue)) {  		case 0: @@ -1163,9 +1299,9 @@ size_t disasm__fprintf(struct list_head *head, FILE *fp)  	return printed;  } -int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx, -			 bool print_lines, bool full_paths, int min_pcnt, -			 int max_lines) +int symbol__tty_annotate(struct symbol *sym, struct map *map, +			 struct perf_evsel *evsel, bool print_lines, +			 bool full_paths, int min_pcnt, int max_lines)  {  	struct dso *dso = map->dso;  	const char *filename = dso->long_name; @@ -1178,12 +1314,12 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx,  	len = symbol__size(sym);  	if (print_lines) { -		symbol__get_source_line(sym, map, evidx, &source_line, +		symbol__get_source_line(sym, map, evsel, &source_line,  					len, filename);  		print_summary(&source_line, filename);  	} -	symbol__annotate_printf(sym, map, evidx, full_paths, +	symbol__annotate_printf(sym, map, evsel, full_paths,  				min_pcnt, max_lines, 0);  	if (print_lines)  		symbol__free_source_line(sym, len); diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index c422440fe61..af755156d27 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h @@ -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;  } diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index f817046e22b..beb8cf9f997 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c @@ -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); +} diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h index 161b00756a1..9bed02e5fb3 100644 --- a/tools/perf/util/cpumap.h +++ b/tools/perf/util/cpumap.h @@ -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; diff --git a/tools/perf/util/debugfs.h b/tools/perf/util/debugfs.h deleted file mode 100644 index 68f3e87ec57..00000000000 --- a/tools/perf/util/debugfs.h +++ /dev/null @@ -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__ */ diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 0d573ff4771..181389535c0 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -88,8 +88,10 @@ struct perf_sample {  	u64 id;  	u64 stream_id;  	u64 period; +	u64 weight;  	u32 cpu;  	u32 raw_size; +	u64 data_src;  	void *raw_data;  	struct ip_callchain *callchain;  	struct branch_stack *branch_stack; @@ -97,6 +99,13 @@ struct perf_sample {  	struct stack_dump user_stack;  }; +#define PERF_MEM_DATA_SRC_NONE \ +	(PERF_MEM_S(OP, NA) |\ +	 PERF_MEM_S(LVL, NA) |\ +	 PERF_MEM_S(SNOOP, NA) |\ +	 PERF_MEM_S(LOCK, NA) |\ +	 PERF_MEM_S(TLB, NA)) +  struct build_id_event {  	struct perf_event_header header;  	pid_t			 pid; diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index c8be0fbc514..f7c727801aa 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -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]); diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 2dd07bd60b4..0583d36252b 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -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, diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 9c82f98f26d..07b1a3ad3e2 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -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;  } diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 52021c3087d..3f156ccc1ac 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -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; diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index f4bfd79ef6a..326068a593a 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -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;  	} diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index f855941bebe..6b32721f829 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -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);  } diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 226a4ae2f93..14c2fe20aa6 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -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)  { diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index efdb38e65a9..b2ecad6ec46 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -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) diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index 5ac5892f232..77940680f1f 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h @@ -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); diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index c84f48cf967..6c8bb0fb189 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -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" diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 49a256e6e0a..aa04bf9c9ad 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -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" diff --git a/tools/perf/util/python-ext-sources b/tools/perf/util/python-ext-sources index 64536a993f4..f75ae1b9900 100644 --- a/tools/perf/util/python-ext-sources +++ b/tools/perf/util/python-ext-sources @@ -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 diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index bd85280bb6e..cf1fe01b7e8 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -1,5 +1,3 @@ -#define _FILE_OFFSET_BITS 64 -  #include <linux/kernel.h>  #include <byteswap.h> @@ -800,6 +798,12 @@ static void dump_sample(struct perf_evsel *evsel, union perf_event *event,  	if (sample_type & PERF_SAMPLE_STACK_USER)  		stack_user__printf(&sample->user_stack); + +	if (sample_type & PERF_SAMPLE_WEIGHT) +		printf("... weight: %" PRIu64 "\n", sample->weight); + +	if (sample_type & PERF_SAMPLE_DATA_SRC) +		printf(" . data_src: 0x%"PRIx64"\n", sample->data_src);  }  static struct machine * @@ -1365,18 +1369,6 @@ size_t perf_session__fprintf(struct perf_session *session, FILE *fp)  	return machine__fprintf(&session->machines.host, fp);  } -void perf_session__remove_thread(struct perf_session *session, -				 struct thread *th) -{ -	/* -	 * FIXME: This one makes no sense, we need to remove the thread from -	 * the machine it belongs to, perf_session can have many machines, so -	 * doing it always on ->machines.host is wrong.  Fix when auditing all -	 * the 'perf kvm' code. -	 */ -	machine__remove_thread(&session->machines.host, th); -} -  struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session,  					      unsigned int type)  { diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index b5c0847edfa..6b51d47acdb 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -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) diff --git a/tools/perf/util/setup.py b/tools/perf/util/setup.py index 73d51026978..6b0ed322907 100644 --- a/tools/perf/util/setup.py +++ b/tools/perf/util/setup.py @@ -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', diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index d41926cb9e3..5f52d492590 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -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;  		} diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index b13e56f6ccb..f24bdf64238 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -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, diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 54efcb5659a..4b12bf85032 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -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); diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index e6432d85b43..8cf3b5426a9 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -36,6 +36,7 @@ struct symbol_conf symbol_conf = {  	.use_modules	  = true,  	.try_vmlinux_path = true,  	.annotate_src	  = true, +	.demangle	  = true,  	.symfs            = "",  }; diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index b62ca37c4b7..5f720dc076d 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -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; diff --git a/tools/perf/util/thread_map.h b/tools/perf/util/thread_map.h index f718df8a3c5..0cd8b310808 100644 --- a/tools/perf/util/thread_map.h +++ b/tools/perf/util/thread_map.h @@ -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 */ diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c index a8d81c35ef6..3917eb9a847 100644 --- a/tools/perf/util/trace-event-info.c +++ b/tools/perf/util/trace-event-info.c @@ -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; +	} + +	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; +	} -	write_or_die("header_event", 13); -	record_file(path, 8); +	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,56 +376,90 @@ 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); +	return err; +} + +static void +put_tracepoints_path(struct tracepoint_path *tps) +{ +	while (tps) { +		struct tracepoint_path *t = tps; + +		tps = tps->next; +		free(t->name); +		free(t->system); +		free(t); +	}  }  static struct tracepoint_path * @@ -396,27 +474,17 @@ get_tracepoints_path(struct list_head *pattrs)  			continue;  		++nr_tracepoints;  		ppath->next = tracepoint_id_to_path(pos->attr.config); -		if (!ppath->next) -			die("%s\n", "No memory to alloc tracepoints list"); +		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;  } -static void -put_tracepoints_path(struct tracepoint_path *tps) -{ -	while (tps) { -		struct tracepoint_path *t = tps; - -		tps = tps->next; -		free(t->name); -		free(t->system); -		free(t); -	} -} -  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;  } diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c index 3aabcd687cd..4454835a9eb 100644 --- a/tools/perf/util/trace-event-parse.c +++ b/tools/perf/util/trace-event-parse.c @@ -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)  { diff --git a/tools/perf/util/trace-event-read.c b/tools/perf/util/trace-event-read.c index 3741572696a..af215c0d237 100644 --- a/tools/perf/util/trace-event-read.c +++ b/tools/perf/util/trace-event-read.c @@ -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(size + 1); +	if (buf == NULL) +		return -1; -	buf = malloc_or_die(size + 1); -	read_or_die(buf, size); +	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(size); +	if (buf == NULL) +		return -1; -	buf = malloc_or_die(size); -	read_or_die(buf, size); +	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); -		} -	} -} - -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"); +			ret = read_event_file(pevent, sys, size); +			if (ret) +				return ret;  		} -		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; -	calc_data_size = 1; -	repipe = __repipe; +	*ppevent = NULL; +	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;  } diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h index a55fd37ffea..1978c398ad8 100644 --- a/tools/perf/util/trace-event.h +++ b/tools/perf/util/trace-event.h @@ -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; diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index 805d1f52c5b..59d868add27 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c @@ -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); +} diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 09b4c26b71a..a45710b70a5 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -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 */ diff --git a/tools/scripts/Makefile.include b/tools/scripts/Makefile.include index 2964b96aa55..f03e681f889 100644 --- a/tools/scripts/Makefile.include +++ b/tools/scripts/Makefile.include @@ -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 diff --git a/tools/vm/Makefile b/tools/vm/Makefile index 8e30e5c40f8..24e9ddd93fa 100644 --- a/tools/vm/Makefile +++ b/tools/vm/Makefile @@ -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 diff --git a/tools/vm/page-types.c b/tools/vm/page-types.c index b76edf2f833..71c9c2511ee 100644 --- a/tools/vm/page-types.c +++ b/tools/vm/page-types.c @@ -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", |