diff options
Diffstat (limited to 'tools')
55 files changed, 3664 insertions, 1867 deletions
diff --git a/tools/perf/Documentation/Makefile b/tools/perf/Documentation/Makefile index 5457192e1b4..bdd3b7ecad0 100644 --- a/tools/perf/Documentation/Makefile +++ b/tools/perf/Documentation/Makefile @@ -35,7 +35,7 @@ man7dir=$(mandir)/man7  # DESTDIR=  ASCIIDOC=asciidoc -ASCIIDOC_EXTRA = +ASCIIDOC_EXTRA = --unsafe  MANPAGE_XSL = manpage-normal.xsl  XMLTO_EXTRA =  INSTALL?=install diff --git a/tools/perf/Documentation/examples.txt b/tools/perf/Documentation/examples.txt new file mode 100644 index 00000000000..8eb6c489fb1 --- /dev/null +++ b/tools/perf/Documentation/examples.txt @@ -0,0 +1,225 @@ + +		------------------------------ +		****** perf by examples ****** +		------------------------------ + +[ From an e-mail by Ingo Molnar, http://lkml.org/lkml/2009/8/4/346 ] + + +First, discovery/enumeration of available counters can be done via +'perf list': + +titan:~> perf list +  [...] +  kmem:kmalloc                             [Tracepoint event] +  kmem:kmem_cache_alloc                    [Tracepoint event] +  kmem:kmalloc_node                        [Tracepoint event] +  kmem:kmem_cache_alloc_node               [Tracepoint event] +  kmem:kfree                               [Tracepoint event] +  kmem:kmem_cache_free                     [Tracepoint event] +  kmem:mm_page_free_direct                 [Tracepoint event] +  kmem:mm_pagevec_free                     [Tracepoint event] +  kmem:mm_page_alloc                       [Tracepoint event] +  kmem:mm_page_alloc_zone_locked           [Tracepoint event] +  kmem:mm_page_pcpu_drain                  [Tracepoint event] +  kmem:mm_page_alloc_extfrag               [Tracepoint event] + +Then any (or all) of the above event sources can be activated and +measured. For example the page alloc/free properties of a 'hackbench +run' are: + + titan:~> perf stat -e kmem:mm_page_pcpu_drain -e kmem:mm_page_alloc + -e kmem:mm_pagevec_free -e kmem:mm_page_free_direct ./hackbench 10 + Time: 0.575 + + Performance counter stats for './hackbench 10': + +          13857  kmem:mm_page_pcpu_drain +          27576  kmem:mm_page_alloc +           6025  kmem:mm_pagevec_free +          20934  kmem:mm_page_free_direct + +    0.613972165  seconds time elapsed + +You can observe the statistical properties as well, by using the +'repeat the workload N times' feature of perf stat: + + titan:~> perf stat --repeat 5 -e kmem:mm_page_pcpu_drain -e +   kmem:mm_page_alloc -e kmem:mm_pagevec_free -e +   kmem:mm_page_free_direct ./hackbench 10 + Time: 0.627 + Time: 0.644 + Time: 0.564 + Time: 0.559 + Time: 0.626 + + Performance counter stats for './hackbench 10' (5 runs): + +          12920  kmem:mm_page_pcpu_drain    ( +-   3.359% ) +          25035  kmem:mm_page_alloc         ( +-   3.783% ) +           6104  kmem:mm_pagevec_free       ( +-   0.934% ) +          18376  kmem:mm_page_free_direct   ( +-   4.941% ) + +    0.643954516  seconds time elapsed   ( +-   2.363% ) + +Furthermore, these tracepoints can be used to sample the workload as +well. For example the page allocations done by a 'git gc' can be +captured the following way: + + titan:~/git> perf record -f -e kmem:mm_page_alloc -c 1 ./git gc + Counting objects: 1148, done. + Delta compression using up to 2 threads. + Compressing objects: 100% (450/450), done. + Writing objects: 100% (1148/1148), done. + Total 1148 (delta 690), reused 1148 (delta 690) + [ perf record: Captured and wrote 0.267 MB perf.data (~11679 samples) ] + +To check which functions generated page allocations: + + titan:~/git> perf report + # Samples: 10646 + # + # Overhead          Command               Shared Object + # ........  ...............  .......................... + # +    23.57%       git-repack  /lib64/libc-2.5.so +    21.81%              git  /lib64/libc-2.5.so +    14.59%              git  ./git +    11.79%       git-repack  ./git +     7.12%              git  /lib64/ld-2.5.so +     3.16%       git-repack  /lib64/libpthread-2.5.so +     2.09%       git-repack  /bin/bash +     1.97%               rm  /lib64/libc-2.5.so +     1.39%               mv  /lib64/ld-2.5.so +     1.37%               mv  /lib64/libc-2.5.so +     1.12%       git-repack  /lib64/ld-2.5.so +     0.95%               rm  /lib64/ld-2.5.so +     0.90%  git-update-serv  /lib64/libc-2.5.so +     0.73%  git-update-serv  /lib64/ld-2.5.so +     0.68%             perf  /lib64/libpthread-2.5.so +     0.64%       git-repack  /usr/lib64/libz.so.1.2.3 + +Or to see it on a more finegrained level: + +titan:~/git> perf report --sort comm,dso,symbol +# Samples: 10646 +# +# Overhead          Command               Shared Object  Symbol +# ........  ...............  ..........................  ...... +# +     9.35%       git-repack  ./git                       [.] insert_obj_hash +     9.12%              git  ./git                       [.] insert_obj_hash +     7.31%              git  /lib64/libc-2.5.so          [.] memcpy +     6.34%       git-repack  /lib64/libc-2.5.so          [.] _int_malloc +     6.24%       git-repack  /lib64/libc-2.5.so          [.] memcpy +     5.82%       git-repack  /lib64/libc-2.5.so          [.] __GI___fork +     5.47%              git  /lib64/libc-2.5.so          [.] _int_malloc +     2.99%              git  /lib64/libc-2.5.so          [.] memset + +Furthermore, call-graph sampling can be done too, of page +allocations - to see precisely what kind of page allocations there +are: + + titan:~/git> perf record -f -g -e kmem:mm_page_alloc -c 1 ./git gc + Counting objects: 1148, done. + Delta compression using up to 2 threads. + Compressing objects: 100% (450/450), done. + Writing objects: 100% (1148/1148), done. + Total 1148 (delta 690), reused 1148 (delta 690) + [ perf record: Captured and wrote 0.963 MB perf.data (~42069 samples) ] + + titan:~/git> perf report -g + # Samples: 10686 + # + # Overhead          Command               Shared Object + # ........  ...............  .......................... + # +    23.25%       git-repack  /lib64/libc-2.5.so +                | +                |--50.00%-- _int_free +                | +                |--37.50%-- __GI___fork +                |          make_child +                | +                |--12.50%-- ptmalloc_unlock_all2 +                |          make_child +                | +                 --6.25%-- __GI_strcpy +    21.61%              git  /lib64/libc-2.5.so +                | +                |--30.00%-- __GI_read +                |          | +                |           --83.33%-- git_config_from_file +                |                     git_config +                |                     | +   [...] + +Or you can observe the whole system's page allocations for 10 +seconds: + +titan:~/git> perf stat -a -e kmem:mm_page_pcpu_drain -e +kmem:mm_page_alloc -e kmem:mm_pagevec_free -e +kmem:mm_page_free_direct sleep 10 + + Performance counter stats for 'sleep 10': + +         171585  kmem:mm_page_pcpu_drain +         322114  kmem:mm_page_alloc +          73623  kmem:mm_pagevec_free +         254115  kmem:mm_page_free_direct + +   10.000591410  seconds time elapsed + +Or observe how fluctuating the page allocations are, via statistical +analysis done over ten 1-second intervals: + + titan:~/git> perf stat --repeat 10 -a -e kmem:mm_page_pcpu_drain -e +   kmem:mm_page_alloc -e kmem:mm_pagevec_free -e +   kmem:mm_page_free_direct sleep 1 + + Performance counter stats for 'sleep 1' (10 runs): + +          17254  kmem:mm_page_pcpu_drain    ( +-   3.709% ) +          34394  kmem:mm_page_alloc         ( +-   4.617% ) +           7509  kmem:mm_pagevec_free       ( +-   4.820% ) +          25653  kmem:mm_page_free_direct   ( +-   3.672% ) + +    1.058135029  seconds time elapsed   ( +-   3.089% ) + +Or you can annotate the recorded 'git gc' run on a per symbol basis +and check which instructions/source-code generated page allocations: + + titan:~/git> perf annotate __GI___fork + ------------------------------------------------ +  Percent |      Source code & Disassembly of libc-2.5.so + ------------------------------------------------ +          : +          : +          :      Disassembly of section .plt: +          :      Disassembly of section .text: +          : +          :      00000031a2e95560 <__fork>: + [...] +     0.00 :        31a2e95602:   b8 38 00 00 00          mov    $0x38,%eax +     0.00 :        31a2e95607:   0f 05                   syscall +    83.42 :        31a2e95609:   48 3d 00 f0 ff ff       cmp    $0xfffffffffffff000,%rax +     0.00 :        31a2e9560f:   0f 87 4d 01 00 00       ja     31a2e95762 <__fork+0x202> +     0.00 :        31a2e95615:   85 c0                   test   %eax,%eax + +( this shows that 83.42% of __GI___fork's page allocations come from +  the 0x38 system call it performs. ) + +etc. etc. - a lot more is possible. I could list a dozen of +other different usecases straight away - neither of which is +possible via /proc/vmstat. + +/proc/vmstat is not in the same league really, in terms of +expressive power of system analysis and performance +analysis. + +All that the above results needed were those new tracepoints +in include/tracing/events/kmem.h. + +	Ingo + + diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index 1dbc1eeb4c0..6be696b0a2b 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt @@ -29,13 +29,67 @@ OPTIONS  	Select the PMU event. Selection can be a symbolic event name  	(use 'perf list' to list all events) or a raw PMU  	event (eventsel+umask) in the form of rNNN where NNN is a -	 hexadecimal event descriptor. +	hexadecimal event descriptor.  -a:: -        system-wide collection +        System-wide collection.  -l:: -        scale counter values +        Scale counter values. + +-p:: +--pid=:: +	Record events on existing pid. + +-r:: +--realtime=:: +	Collect data with this RT SCHED_FIFO priority. +-A:: +--append:: +	Append to the output file to do incremental profiling. + +-f:: +--force:: +	Overwrite existing data file. + +-c:: +--count=:: +	Event period to sample. + +-o:: +--output=:: +	Output file name. + +-i:: +--inherit:: +	Child tasks inherit counters. +-F:: +--freq=:: +	Profile at this frequency. + +-m:: +--mmap-pages=:: +	Number of mmap data pages. + +-g:: +--call-graph:: +	Do call-graph (stack chain/backtrace) recording. + +-v:: +--verbose:: +	Be more verbose (show counter open errors, etc). + +-s:: +--stat:: +	Per thread counts. + +-d:: +--data:: +	Sample addresses. + +-n:: +--no-samples:: +	Don't sample.  SEE ALSO  -------- diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index 8aa3f8c8870..e72e9311078 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt @@ -24,6 +24,9 @@ OPTIONS  --dsos=::  	Only consider symbols in these dsos. CSV that understands  	file://filename entries. +-n +--show-nr-samples +	Show the number of samples for each symbol  -C::  --comms=::  	Only consider symbols in these comms. CSV that understands @@ -33,6 +36,18 @@ OPTIONS  	Only consider these symbols. CSV that understands  	file://filename entries. +-w:: +--field-width=:: +	Force each column width to the provided list, for large terminal +	readability. + +-t:: +--field-separator=:: + +	Use a special separator character and don't pad with spaces, replacing +	all occurances of this separator in symbol names (and other output) +	with a '.' character, that thus it's the only non valid separator. +  SEE ALSO  --------  linkperf:perf-stat[1] diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt index 0d74346d21a..484080dd5b6 100644 --- a/tools/perf/Documentation/perf-stat.txt +++ b/tools/perf/Documentation/perf-stat.txt @@ -40,7 +40,7 @@ OPTIONS  -a::          system-wide collection --S:: +-c::          scale counter values  EXAMPLES diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt index 539d0128972..4a7d558dc30 100644 --- a/tools/perf/Documentation/perf-top.txt +++ b/tools/perf/Documentation/perf-top.txt @@ -3,36 +3,122 @@ perf-top(1)  NAME  ---- -perf-top - Run a command and profile it +perf-top - System profiling tool.  SYNOPSIS  --------  [verse] -'perf top' [-e <EVENT> | --event=EVENT] [-l] [-a] <command> +'perf top' [-e <EVENT> | --event=EVENT] [<options>]  DESCRIPTION  ----------- -This command runs a command and gathers a performance counter profile -from it. +This command generates and displays a performance counter profile in realtime.  OPTIONS  ------- -<command>...:: -	Any command you can specify in a shell. +-a:: +--all-cpus:: +        System-wide collection.  (default) + +-c <count>:: +--count=<count>:: +	Event period to sample. + +-C <cpu>:: +--CPU=<cpu>:: +	CPU to profile. + +-d <seconds>:: +--delay=<seconds>:: +	Number of seconds to delay between refreshes. --e:: ---event=:: +-e <event>:: +--event=<event>::  	Select the PMU event. Selection can be a symbolic event name  	(use 'perf list' to list all events) or a raw PMU  	event (eventsel+umask) in the form of rNNN where NNN is a -	 hexadecimal event descriptor. +	hexadecimal event descriptor. --a:: -        system-wide collection +-E <entries>:: +--entries=<entries>:: +	Display this many functions. + +-f <count>:: +--count-filter=<count>:: +	Only display functions with more events than this. + +-F <freq>:: +--freq=<freq>:: +	Profile at this frequency. + +-i:: +--inherit:: +	Child tasks inherit counters, only makes sens with -p option. + +-k <path>:: +--vmlinux=<path>:: +	Path to vmlinux.  Required for annotation functionality. + +-m <pages>:: +--mmap-pages=<pages>:: +	Number of mmapped data pages. + +-p <pid>:: +--pid=<pid>:: +	Profile events on existing pid. + +-r <priority>:: +--realtime=<priority>:: +	Collect data with this RT SCHED_FIFO priority. + +-s <symbol>:: +--sym-annotate=<symbol>:: +        Annotate this symbol.  Requires -k option. + +-v:: +--verbose:: +	Be more verbose (show counter open errors, etc). + +-z:: +--zero:: +	Zero history across display updates. + +INTERACTIVE PROMPTING KEYS +-------------------------- + +[d]:: +	Display refresh delay. + +[e]:: +	Number of entries to display. + +[E]:: +	Event to display when multiple counters are active. + +[f]:: +	Profile display filter (>= hit count). + +[F]:: +	Annotation display filter (>= % of total). + +[s]:: +	Annotate symbol. + +[S]:: +	Stop annotation, return to full profile display. + +[w]:: +	Toggle between weighted sum and individual count[E]r profile. + +[z]:: +	Toggle event count zeroing across display updates. + +[qQ]:: +	Quit. + +Pressing any unmapped key displays a menu, and prompts for input. --l:: -        scale counter values  SEE ALSO  -------- diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 9c6d0ae3708..c045b4271e5 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -158,13 +158,15 @@ uname_P := $(shell sh -c 'uname -p 2>/dev/null || echo not')  uname_V := $(shell sh -c 'uname -v 2>/dev/null || echo not')  # If we're on a 64-bit kernel, use -m64 -ifneq ($(patsubst %64,%,$(uname_M)),$(uname_M)) -  M64 := -m64 +ifndef NO_64BIT +	ifneq ($(patsubst %64,%,$(uname_M)),$(uname_M)) +	  M64 := -m64 +	endif  endif  # CFLAGS and LDFLAGS are for the users to override from the command line. -CFLAGS = $(M64) -ggdb3 -Wall -Wstrict-prototypes -Wmissing-declarations -Wmissing-prototypes -std=gnu99 -Wdeclaration-after-statement -Werror -O6 +CFLAGS = $(M64) -ggdb3 -Wall -Wextra -Wstrict-prototypes -Wmissing-declarations -Wmissing-prototypes -std=gnu99 -Wdeclaration-after-statement -Werror -O6  LDFLAGS = -lpthread -lrt -lelf -lm  ALL_CFLAGS = $(CFLAGS)  ALL_LDFLAGS = $(LDFLAGS) @@ -223,7 +225,7 @@ SPARSE_FLAGS = -D__BIG_ENDIAN__ -D__powerpc__  # Those must not be GNU-specific; they are shared with perl/ which may  # be built by a different compiler. (Note that this is an artifact now  # but it still might be nice to keep that distinction.) -BASIC_CFLAGS = +BASIC_CFLAGS = -Iutil/include  BASIC_LDFLAGS =  # Guard against environment variables @@ -289,10 +291,11 @@ export PERL_PATH  LIB_FILE=libperf.a  LIB_H += ../../include/linux/perf_counter.h +LIB_H += ../../include/linux/rbtree.h +LIB_H += ../../include/linux/list.h +LIB_H += util/include/linux/list.h  LIB_H += perf.h  LIB_H += util/types.h -LIB_H += util/list.h -LIB_H += util/rbtree.h  LIB_H += util/levenshtein.h  LIB_H += util/parse-options.h  LIB_H += util/parse-events.h @@ -305,6 +308,7 @@ LIB_H += util/strlist.h  LIB_H += util/run-command.h  LIB_H += util/sigchain.h  LIB_H += util/symbol.h +LIB_H += util/module.h  LIB_H += util/color.h  LIB_OBJS += util/abspath.o @@ -328,6 +332,7 @@ LIB_OBJS += util/usage.o  LIB_OBJS += util/wrapper.o  LIB_OBJS += util/sigchain.o  LIB_OBJS += util/symbol.o +LIB_OBJS += util/module.o  LIB_OBJS += util/color.o  LIB_OBJS += util/pager.o  LIB_OBJS += util/header.o @@ -342,7 +347,6 @@ BUILTIN_OBJS += builtin-stat.o  BUILTIN_OBJS += builtin-top.o  PERFLIBS = $(LIB_FILE) -EXTLIBS =  #  # Platform specific tweaks @@ -371,6 +375,39 @@ ifeq ($(uname_S),Darwin)  	PTHREAD_LIBS =  endif +ifneq ($(shell sh -c "(echo '\#include <libelf.h>'; echo 'int main(void) { Elf * elf = elf_begin(0, ELF_C_READ_MMAP, 0); return (long)elf; }') | $(CC) -x c - $(ALL_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -o /dev/null $(ALL_LDFLAGS) > /dev/null 2>&1 && echo y"), y) +	msg := $(error No libelf.h/libelf found, please install libelf-dev/elfutils-libelf-devel); +endif + +ifdef NO_DEMANGLE +	BASIC_CFLAGS += -DNO_DEMANGLE +else +	has_bfd := $(shell sh -c "(echo '\#include <bfd.h>'; echo 'int main(void) { bfd_demangle(0, 0, 0); return 0; }') | $(CC) -x c - $(ALL_CFLAGS) -o /dev/null $(ALL_LDFLAGS) -lbfd > /dev/null 2>&1 && echo y") + +	ifeq ($(has_bfd),y) +		EXTLIBS += -lbfd +	else +		has_bfd_iberty := $(shell sh -c "(echo '\#include <bfd.h>'; echo 'int main(void) { bfd_demangle(0, 0, 0); return 0; }') | $(CC) -x c - $(ALL_CFLAGS) -o /dev/null $(ALL_LDFLAGS) -lbfd -liberty > /dev/null 2>&1 && echo y") +		ifeq ($(has_bfd_iberty),y) +			EXTLIBS += -lbfd -liberty +		else +			has_bfd_iberty_z := $(shell sh -c "(echo '\#include <bfd.h>'; echo 'int main(void) { bfd_demangle(0, 0, 0); return 0; }') | $(CC) -x c - $(ALL_CFLAGS) -o /dev/null $(ALL_LDFLAGS) -lbfd -liberty -lz > /dev/null 2>&1 && echo y") +			ifeq ($(has_bfd_iberty_z),y) +				EXTLIBS += -lbfd -liberty -lz +			else +				has_cplus_demangle := $(shell sh -c "(echo 'extern char *cplus_demangle(const char *, int);'; echo 'int main(void) { cplus_demangle(0, 0); return 0; }') | $(CC) -x c - $(ALL_CFLAGS) -o /dev/null $(ALL_LDFLAGS) -liberty > /dev/null 2>&1 && echo y") +				ifeq ($(has_cplus_demangle),y) +					EXTLIBS += -liberty +					BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE +				else +					msg := $(warning No bfd.h/libbfd found, install binutils-dev[el] to gain symbol demangling) +					BASIC_CFLAGS += -DNO_DEMANGLE +				endif +			endif +		endif +	endif +endif +  ifndef CC_LD_DYNPATH  	ifdef NO_R_TO_GCC_LINKER  		# Some gcc does not accept and pass -R to the linker to specify @@ -381,12 +418,6 @@ ifndef CC_LD_DYNPATH  	endif  endif -ifdef ZLIB_PATH -	BASIC_CFLAGS += -I$(ZLIB_PATH)/include -	EXTLIBS += -L$(ZLIB_PATH)/$(lib) $(CC_LD_DYNPATH)$(ZLIB_PATH)/$(lib) -endif -EXTLIBS += -lz -  ifdef NEEDS_SOCKET  	EXTLIBS += -lsocket  endif @@ -697,6 +728,9 @@ builtin-init-db.o: builtin-init-db.c PERF-CFLAGS  util/config.o: util/config.c PERF-CFLAGS  	$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< +util/rbtree.o: ../../lib/rbtree.c PERF-CFLAGS +	$(QUIET_CC)$(CC) -o util/rbtree.o -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< +  perf-%$X: %.o $(PERFLIBS)  	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 722c0f54e54..343e7b14bf0 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -10,9 +10,9 @@  #include "util/util.h"  #include "util/color.h" -#include "util/list.h" +#include <linux/list.h>  #include "util/cache.h" -#include "util/rbtree.h" +#include <linux/rbtree.h>  #include "util/symbol.h"  #include "util/string.h" @@ -25,10 +25,6 @@  #define SHOW_USER	2  #define SHOW_HV		4 -#define MIN_GREEN		0.5 -#define MIN_RED		5.0 - -  static char		const *input_name = "perf.data";  static char		*vmlinux = "vmlinux"; @@ -43,6 +39,10 @@ static int		dump_trace = 0;  static int		verbose; +static int		modules; + +static int		full_paths; +  static int		print_line;  static unsigned long	page_size; @@ -74,20 +74,12 @@ struct fork_event {  	u32 pid, ppid;  }; -struct period_event { -	struct perf_event_header header; -	u64 time; -	u64 id; -	u64 sample_period; -}; -  typedef union event_union {  	struct perf_event_header	header;  	struct ip_event			ip;  	struct mmap_event		mmap;  	struct comm_event		comm;  	struct fork_event		fork; -	struct period_event		period;  } event_t; @@ -160,7 +152,7 @@ static void dsos__fprintf(FILE *fp)  static struct symbol *vdso__find_symbol(struct dso *dso, u64 ip)  { -	return dso__find_symbol(kernel_dso, ip); +	return dso__find_symbol(dso, ip);  }  static int load_kernel(void) @@ -171,8 +163,8 @@ static int load_kernel(void)  	if (!kernel_dso)  		return -1; -	err = dso__load_kernel(kernel_dso, vmlinux, NULL, verbose); -	if (err) { +	err = dso__load_kernel(kernel_dso, vmlinux, NULL, verbose, modules); +	if (err <= 0) {  		dso__delete(kernel_dso);  		kernel_dso = NULL;  	} else @@ -203,7 +195,7 @@ static u64 map__map_ip(struct map *map, u64 ip)  	return ip - map->start + map->pgoff;  } -static u64 vdso__map_ip(struct map *map, u64 ip) +static u64 vdso__map_ip(struct map *map __used, u64 ip)  {  	return ip;  } @@ -600,7 +592,7 @@ static LIST_HEAD(hist_entry__sort_list);  static int sort_dimension__add(char *tok)  { -	int i; +	unsigned int i;  	for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) {  		struct sort_dimension *sd = &sort_dimensions[i]; @@ -988,6 +980,13 @@ process_fork_event(event_t *event, unsigned long offset, unsigned long head)  		(void *)(long)(event->header.size),  		event->fork.pid, event->fork.ppid); +	/* +	 * A thread clone will have the same PID for both +	 * parent and child. +	 */ +	if (thread == parent) +		return 0; +  	if (!thread || !parent || thread__fork(thread, parent)) {  		dprintf("problem processing PERF_EVENT_FORK, skipping event.\n");  		return -1; @@ -998,19 +997,6 @@ process_fork_event(event_t *event, unsigned long offset, unsigned long head)  }  static int -process_period_event(event_t *event, unsigned long offset, unsigned long head) -{ -	dprintf("%p [%p]: PERF_EVENT_PERIOD: time:%Ld, id:%Ld: period:%Ld\n", -		(void *)(offset + head), -		(void *)(long)(event->header.size), -		event->period.time, -		event->period.id, -		event->period.sample_period); - -	return 0; -} - -static int  process_event(event_t *event, unsigned long offset, unsigned long head)  {  	switch (event->header.type) { @@ -1025,9 +1011,6 @@ process_event(event_t *event, unsigned long offset, unsigned long head)  	case PERF_EVENT_FORK:  		return process_fork_event(event, offset, head); - -	case PERF_EVENT_PERIOD: -		return process_period_event(event, offset, head);  	/*  	 * We dont process them right now but they are fine:  	 */ @@ -1043,24 +1026,6 @@ process_event(event_t *event, unsigned long offset, unsigned long head)  	return 0;  } -static char *get_color(double percent) -{ -	char *color = PERF_COLOR_NORMAL; - -	/* -	 * We color high-overhead entries in red, mid-overhead -	 * entries in green - and keep the low overhead places -	 * normal: -	 */ -	if (percent >= MIN_RED) -		color = PERF_COLOR_RED; -	else { -		if (percent > MIN_GREEN) -			color = PERF_COLOR_GREEN; -	} -	return color; -} -  static int  parse_line(FILE *file, struct symbol *sym, u64 start, u64 len)  { @@ -1069,7 +1034,7 @@ parse_line(FILE *file, struct symbol *sym, u64 start, u64 len)  	static const char *prev_color;  	unsigned int offset;  	size_t line_len; -	u64 line_ip; +	s64 line_ip;  	int ret;  	char *c; @@ -1122,7 +1087,7 @@ parse_line(FILE *file, struct symbol *sym, u64 start, u64 len)  		} else if (sym->hist_sum)  			percent = 100.0 * hits / sym->hist_sum; -		color = get_color(percent); +		color = get_percent_color(percent);  		/*  		 * Also color the filename and line if needed, with @@ -1258,7 +1223,7 @@ static void print_summary(char *filename)  		sym_ext = rb_entry(node, struct sym_ext, node);  		percent = sym_ext->percent; -		color = get_color(percent); +		color = get_percent_color(percent);  		path = sym_ext->path;  		color_fprintf(stdout, color, " %7.2f %s", percent, path); @@ -1268,19 +1233,25 @@ static void print_summary(char *filename)  static void annotate_sym(struct dso *dso, struct symbol *sym)  { -	char *filename = dso->name; +	char *filename = dso->name, *d_filename;  	u64 start, end, len;  	char command[PATH_MAX*2];  	FILE *file;  	if (!filename)  		return; -	if (dso == kernel_dso) +	if (sym->module) +		filename = sym->module->path; +	else if (dso == kernel_dso)  		filename = vmlinux;  	start = sym->obj_start;  	if (!start)  		start = sym->start; +	if (full_paths) +		d_filename = filename; +	else +		d_filename = basename(filename);  	end = start + sym->end - sym->start + 1;  	len = sym->end - sym->start; @@ -1291,13 +1262,14 @@ static void annotate_sym(struct dso *dso, struct symbol *sym)  	}  	printf("\n\n------------------------------------------------\n"); -	printf(" Percent |	Source code & Disassembly of %s\n", filename); +	printf(" Percent |	Source code & Disassembly of %s\n", d_filename);  	printf("------------------------------------------------\n");  	if (verbose >= 2)  		printf("annotating [%p] %30s : [%p] %30s\n", dso, dso->name, sym, sym->name); -	sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s", (u64)start, (u64)end, filename); +	sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s|grep -v %s", +			(u64)start, (u64)end, filename, filename);  	if (verbose >= 3)  		printf("doing: %s\n", command); @@ -1428,7 +1400,7 @@ more:  	head += size; -	if (offset + head < stat.st_size) +	if (offset + head < (unsigned long)stat.st_size)  		goto more;  	rc = EXIT_SUCCESS; @@ -1472,8 +1444,12 @@ static const struct option options[] = {  	OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,  		    "dump raw trace in ASCII"),  	OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"), +	OPT_BOOLEAN('m', "modules", &modules, +		    "load module symbols - WARNING: use only with -k and LIVE kernel"),  	OPT_BOOLEAN('l', "print-line", &print_line,  		    "print matching source lines (may be slow)"), +	OPT_BOOLEAN('P', "full-paths", &full_paths, +		    "Don't shorten the displayed pathnames"),  	OPT_END()  }; @@ -1492,7 +1468,7 @@ static void setup_sorting(void)  	free(str);  } -int cmd_annotate(int argc, const char **argv, const char *prefix) +int cmd_annotate(int argc, const char **argv, const char *prefix __used)  {  	symbol__init(); diff --git a/tools/perf/builtin-help.c b/tools/perf/builtin-help.c index 0f32dc3f3c4..2599d86a733 100644 --- a/tools/perf/builtin-help.c +++ b/tools/perf/builtin-help.c @@ -3,6 +3,7 @@   *   * Builtin help command   */ +#include "perf.h"  #include "util/cache.h"  #include "builtin.h"  #include "util/exec_cmd.h" @@ -277,7 +278,7 @@ static struct cmdnames main_cmds, other_cmds;  void list_common_cmds_help(void)  { -	int i, longest = 0; +	unsigned int i, longest = 0;  	for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {  		if (longest < strlen(common_cmds[i].name)) @@ -415,9 +416,10 @@ static void show_html_page(const char *perf_cmd)  	open_html(page_path.buf);  } -int cmd_help(int argc, const char **argv, const char *prefix) +int cmd_help(int argc, const char **argv, const char *prefix __used)  {  	const char *alias; +  	load_command_list("perf-", &main_cmds, &other_cmds);  	perf_config(perf_help_config, NULL); diff --git a/tools/perf/builtin-list.c b/tools/perf/builtin-list.c index fe60e37c96e..d88c6961274 100644 --- a/tools/perf/builtin-list.c +++ b/tools/perf/builtin-list.c @@ -10,11 +10,12 @@  #include "perf.h" -#include "util/parse-options.h"  #include "util/parse-events.h" +#include "util/cache.h" -int cmd_list(int argc, const char **argv, const char *prefix) +int cmd_list(int argc __used, const char **argv __used, const char *prefix __used)  { +	setup_pager();  	print_events();  	return 0;  } diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index d18546f37d7..89a5ddcd1de 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -34,7 +34,9 @@ static int			output;  static const char		*output_name			= "perf.data";  static int			group				= 0;  static unsigned int		realtime_prio			= 0; +static int			raw_samples			= 0;  static int			system_wide			= 0; +static int			profile_cpu			= -1;  static pid_t			target_pid			= -1;  static int			inherit				= 1;  static int			force				= 0; @@ -43,6 +45,7 @@ static int			call_graph			= 0;  static int			verbose				= 0;  static int			inherit_stat			= 0;  static int			no_samples			= 0; +static int			sample_address			= 0;  static long			samples;  static struct timeval		last_read; @@ -202,46 +205,48 @@ static void sig_atexit(void)  	kill(getpid(), signr);  } -static void pid_synthesize_comm_event(pid_t pid, int full) +static pid_t pid_synthesize_comm_event(pid_t pid, int full)  {  	struct comm_event comm_ev;  	char filename[PATH_MAX];  	char bf[BUFSIZ]; -	int fd; -	size_t size; -	char *field, *sep; +	FILE *fp; +	size_t size = 0;  	DIR *tasks;  	struct dirent dirent, *next; +	pid_t tgid = 0; -	snprintf(filename, sizeof(filename), "/proc/%d/stat", pid); +	snprintf(filename, sizeof(filename), "/proc/%d/status", pid); -	fd = open(filename, O_RDONLY); -	if (fd < 0) { +	fp = fopen(filename, "r"); +	if (fp == NULL) {  		/*  		 * We raced with a task exiting - just return:  		 */  		if (verbose)  			fprintf(stderr, "couldn't open %s\n", filename); -		return; -	} -	if (read(fd, bf, sizeof(bf)) < 0) { -		fprintf(stderr, "couldn't read %s\n", filename); -		exit(EXIT_FAILURE); +		return 0;  	} -	close(fd); -	/* 9027 (cat) R 6747 9027 6747 34816 9027 ... */  	memset(&comm_ev, 0, sizeof(comm_ev)); -	field = strchr(bf, '('); -	if (field == NULL) -		goto out_failure; -	sep = strchr(++field, ')'); -	if (sep == NULL) -		goto out_failure; -	size = sep - field; -	memcpy(comm_ev.comm, field, size++); +	while (!comm_ev.comm[0] || !comm_ev.pid) { +		if (fgets(bf, sizeof(bf), fp) == NULL) +			goto out_failure; + +		if (memcmp(bf, "Name:", 5) == 0) { +			char *name = bf + 5; +			while (*name && isspace(*name)) +				++name; +			size = strlen(name) - 1; +			memcpy(comm_ev.comm, name, size++); +		} else if (memcmp(bf, "Tgid:", 5) == 0) { +			char *tgids = bf + 5; +			while (*tgids && isspace(*tgids)) +				++tgids; +			tgid = comm_ev.pid = atoi(tgids); +		} +	} -	comm_ev.pid = pid;  	comm_ev.header.type = PERF_EVENT_COMM;  	size = ALIGN(size, sizeof(u64));  	comm_ev.header.size = sizeof(comm_ev) - (sizeof(comm_ev.comm) - size); @@ -250,7 +255,7 @@ static void pid_synthesize_comm_event(pid_t pid, int full)  		comm_ev.tid = pid;  		write_output(&comm_ev, comm_ev.header.size); -		return; +		goto out_fclose;  	}  	snprintf(filename, sizeof(filename), "/proc/%d/task", pid); @@ -267,7 +272,10 @@ static void pid_synthesize_comm_event(pid_t pid, int full)  		write_output(&comm_ev, comm_ev.header.size);  	}  	closedir(tasks); -	return; + +out_fclose: +	fclose(fp); +	return tgid;  out_failure:  	fprintf(stderr, "couldn't get COMM and pgid, malformed %s\n", @@ -275,7 +283,7 @@ out_failure:  	exit(EXIT_FAILURE);  } -static void pid_synthesize_mmap_samples(pid_t pid) +static void pid_synthesize_mmap_samples(pid_t pid, pid_t tgid)  {  	char filename[PATH_MAX];  	FILE *fp; @@ -294,7 +302,7 @@ static void pid_synthesize_mmap_samples(pid_t pid)  	while (1) {  		char bf[BUFSIZ], *pbf = bf;  		struct mmap_event mmap_ev = { -			.header.type = PERF_EVENT_MMAP, +			.header = { .type = PERF_EVENT_MMAP },  		};  		int n;  		size_t size; @@ -313,6 +321,10 @@ static void pid_synthesize_mmap_samples(pid_t pid)  		if (*pbf == 'x') { /* vm_exec */  			char *execname = strchr(bf, '/'); +			/* Catch VDSO */ +			if (execname == NULL) +				execname = strstr(bf, "[vdso]"); +  			if (execname == NULL)  				continue; @@ -323,7 +335,7 @@ static void pid_synthesize_mmap_samples(pid_t pid)  			mmap_ev.len -= mmap_ev.start;  			mmap_ev.header.size = (sizeof(mmap_ev) -  					       (sizeof(mmap_ev.filename) - size)); -			mmap_ev.pid = pid; +			mmap_ev.pid = tgid;  			mmap_ev.tid = pid;  			write_output(&mmap_ev, mmap_ev.header.size); @@ -342,14 +354,14 @@ static void synthesize_all(void)  	while (!readdir_r(proc, &dirent, &next) && next) {  		char *end; -		pid_t pid; +		pid_t pid, tgid;  		pid = strtol(dirent.d_name, &end, 10);  		if (*end) /* only interested in proper numerical dirents */  			continue; -		pid_synthesize_comm_event(pid, 1); -		pid_synthesize_mmap_samples(pid); +		tgid = pid_synthesize_comm_event(pid, 1); +		pid_synthesize_mmap_samples(pid, tgid);  	}  	closedir(proc); @@ -387,7 +399,7 @@ static void create_counter(int counter, int cpu, pid_t pid)  				  PERF_FORMAT_TOTAL_TIME_RUNNING |  				  PERF_FORMAT_ID; -	attr->sample_type	= PERF_SAMPLE_IP | PERF_SAMPLE_TID; +	attr->sample_type	|= PERF_SAMPLE_IP | PERF_SAMPLE_TID;  	if (freq) {  		attr->sample_type	|= PERF_SAMPLE_PERIOD; @@ -401,9 +413,15 @@ static void create_counter(int counter, int cpu, pid_t pid)  	if (inherit_stat)  		attr->inherit_stat = 1; +	if (sample_address) +		attr->sample_type	|= PERF_SAMPLE_ADDR; +  	if (call_graph)  		attr->sample_type	|= PERF_SAMPLE_CALLCHAIN; +	if (raw_samples) +		attr->sample_type	|= PERF_SAMPLE_RAW; +  	attr->mmap		= track;  	attr->comm		= track;  	attr->inherit		= (cpu < 0) && inherit; @@ -417,6 +435,8 @@ try_again:  		if (err == EPERM)  			die("Permission error - are you root?\n"); +		else if (err ==  ENODEV && profile_cpu != -1) +			die("No such device - did you specify an out-of-range profile CPU?\n");  		/*  		 * If it's cycles then fall back to hrtimer @@ -516,10 +536,14 @@ static int __cmd_record(int argc, const char **argv)  	signal(SIGCHLD, sig_handler);  	signal(SIGINT, sig_handler); -	if (!stat(output_name, &st) && !force && !append_file) { -		fprintf(stderr, "Error, output file %s exists, use -A to append or -f to overwrite.\n", -				output_name); -		exit(-1); +	if (!stat(output_name, &st) && st.st_size) { +		if (!force && !append_file) { +			fprintf(stderr, "Error, output file %s exists, use -A to append or -f to overwrite.\n", +					output_name); +			exit(-1); +		} +	} else { +		append_file = 0;  	}  	flags = O_CREAT|O_RDWR; @@ -546,16 +570,22 @@ static int __cmd_record(int argc, const char **argv)  		if (pid == -1)  			pid = getpid(); -		open_counters(-1, pid); -	} else for (i = 0; i < nr_cpus; i++) -		open_counters(i, target_pid); +		open_counters(profile_cpu, pid); +	} else { +		if (profile_cpu != -1) { +			open_counters(profile_cpu, target_pid); +		} else { +			for (i = 0; i < nr_cpus; i++) +				open_counters(i, target_pid); +		} +	}  	if (file_new)  		perf_header__write(header, output);  	if (!system_wide) { -		pid_synthesize_comm_event(pid, 0); -		pid_synthesize_mmap_samples(pid); +		pid_t tgid = pid_synthesize_comm_event(pid, 0); +		pid_synthesize_mmap_samples(pid, tgid);  	} else  		synthesize_all(); @@ -623,10 +653,14 @@ static const struct option options[] = {  		    "record events on existing pid"),  	OPT_INTEGER('r', "realtime", &realtime_prio,  		    "collect data with this RT SCHED_FIFO priority"), +	OPT_BOOLEAN('R', "raw-samples", &raw_samples, +		    "collect raw sample records from all opened counters"),  	OPT_BOOLEAN('a', "all-cpus", &system_wide,  			    "system-wide collection from all CPUs"),  	OPT_BOOLEAN('A', "append", &append_file,  			    "append to the output file to do incremental profiling"), +	OPT_INTEGER('C', "profile_cpu", &profile_cpu, +			    "CPU to profile on"),  	OPT_BOOLEAN('f', "force", &force,  			"overwrite existing data file"),  	OPT_LONG('c', "count", &default_interval, @@ -645,16 +679,19 @@ static const struct option options[] = {  		    "be more verbose (show counter open errors, etc)"),  	OPT_BOOLEAN('s', "stat", &inherit_stat,  		    "per thread counts"), +	OPT_BOOLEAN('d', "data", &sample_address, +		    "Sample addresses"),  	OPT_BOOLEAN('n', "no-samples", &no_samples,  		    "don't sample"),  	OPT_END()  }; -int cmd_record(int argc, const char **argv, const char *prefix) +int cmd_record(int argc, const char **argv, const char *prefix __used)  {  	int counter; -	argc = parse_options(argc, argv, options, record_usage, 0); +	argc = parse_options(argc, argv, options, record_usage, +		PARSE_OPT_STOP_AT_NON_OPTION);  	if (!argc && target_pid == -1 && !system_wide)  		usage_with_options(record_usage, options); diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 135b7837e6b..b53a60fc12d 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -10,9 +10,9 @@  #include "util/util.h"  #include "util/color.h" -#include "util/list.h" +#include <linux/list.h>  #include "util/cache.h" -#include "util/rbtree.h" +#include <linux/rbtree.h>  #include "util/symbol.h"  #include "util/string.h"  #include "util/callchain.h" @@ -31,10 +31,12 @@  static char		const *input_name = "perf.data";  static char		*vmlinux = NULL; -static char		default_sort_order[] = "comm,dso"; +static char		default_sort_order[] = "comm,dso,symbol";  static char		*sort_order = default_sort_order; -static char		*dso_list_str, *comm_list_str, *sym_list_str; +static char		*dso_list_str, *comm_list_str, *sym_list_str, +			*col_width_list_str;  static struct strlist	*dso_list, *comm_list, *sym_list; +static char		*field_sep;  static int		input;  static int		show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV; @@ -46,7 +48,10 @@ static int		dump_trace = 0;  static int		verbose;  #define eprintf(x...)	do { if (verbose) fprintf(stderr, x); } while (0) +static int		modules; +  static int		full_paths; +static int		show_nr_samples;  static unsigned long	page_size;  static unsigned long	mmap_window = 32; @@ -56,8 +61,17 @@ static char		*parent_pattern = default_parent_pattern;  static regex_t		parent_regex;  static int		exclude_other = 1; + +static char		callchain_default_opt[] = "fractal,0.5"; +  static int		callchain; +static +struct callchain_param	callchain_param = { +	.mode	= CHAIN_GRAPH_REL, +	.min_percent = 0.5 +}; +  static u64		sample_type;  struct ip_event { @@ -85,13 +99,7 @@ struct comm_event {  struct fork_event {  	struct perf_event_header header;  	u32 pid, ppid; -}; - -struct period_event { -	struct perf_event_header header; -	u64 time; -	u64 id; -	u64 sample_period; +	u32 tid, ptid;  };  struct lost_event { @@ -104,7 +112,9 @@ struct read_event {  	struct perf_event_header header;  	u32 pid,tid;  	u64 value; -	u64 format[3]; +	u64 time_enabled; +	u64 time_running; +	u64 id;  };  typedef union event_union { @@ -113,14 +123,41 @@ typedef union event_union {  	struct mmap_event		mmap;  	struct comm_event		comm;  	struct fork_event		fork; -	struct period_event		period;  	struct lost_event		lost;  	struct read_event		read;  } event_t; +static int repsep_fprintf(FILE *fp, const char *fmt, ...) +{ +	int n; +	va_list ap; + +	va_start(ap, fmt); +	if (!field_sep) +		n = vfprintf(fp, fmt, ap); +	else { +		char *bf = NULL; +		n = vasprintf(&bf, fmt, ap); +		if (n > 0) { +			char *sep = bf; +			while (1) { +				sep = strchr(sep, *field_sep); +				if (sep == NULL) +					break; +				*sep = '.'; +			} +		} +		fputs(bf, fp); +		free(bf); +	} +	va_end(ap); +	return n; +} +  static LIST_HEAD(dsos);  static struct dso *kernel_dso;  static struct dso *vdso; +static struct dso *hypervisor_dso;  static void dsos__add(struct dso *dso)  { @@ -176,7 +213,7 @@ static void dsos__fprintf(FILE *fp)  static struct symbol *vdso__find_symbol(struct dso *dso, u64 ip)  { -	return dso__find_symbol(kernel_dso, ip); +	return dso__find_symbol(dso, ip);  }  static int load_kernel(void) @@ -187,8 +224,8 @@ static int load_kernel(void)  	if (!kernel_dso)  		return -1; -	err = dso__load_kernel(kernel_dso, vmlinux, NULL, verbose); -	if (err) { +	err = dso__load_kernel(kernel_dso, vmlinux, NULL, verbose, modules); +	if (err <= 0) {  		dso__delete(kernel_dso);  		kernel_dso = NULL;  	} else @@ -202,6 +239,11 @@ static int load_kernel(void)  	dsos__add(vdso); +	hypervisor_dso = dso__new("[hypervisor]", 0); +	if (!hypervisor_dso) +		return -1; +	dsos__add(hypervisor_dso); +  	return err;  } @@ -213,7 +255,7 @@ static int strcommon(const char *pathname)  {  	int n = 0; -	while (pathname[n] == cwd[n] && n < cwdlen) +	while (n < cwdlen && pathname[n] == cwd[n])  		++n;  	return n; @@ -233,7 +275,7 @@ static u64 map__map_ip(struct map *map, u64 ip)  	return ip - map->start + map->pgoff;  } -static u64 vdso__map_ip(struct map *map, u64 ip) +static u64 vdso__map_ip(struct map *map __used, u64 ip)  {  	return ip;  } @@ -343,12 +385,28 @@ static struct thread *thread__new(pid_t pid)  	return self;  } +static unsigned int dsos__col_width, +		    comms__col_width, +		    threads__col_width; +  static int thread__set_comm(struct thread *self, const char *comm)  {  	if (self->comm)  		free(self->comm);  	self->comm = strdup(comm); -	return self->comm ? 0 : -ENOMEM; +	if (!self->comm) +		return -ENOMEM; + +	if (!col_width_list_str && !field_sep && +	    (!comm_list || strlist__has_entry(comm_list, comm))) { +		unsigned int slen = strlen(comm); +		if (slen > comms__col_width) { +			comms__col_width = slen; +			threads__col_width = slen + 6; +		} +	} + +	return 0;  }  static size_t thread__fprintf(struct thread *self, FILE *fp) @@ -519,7 +577,9 @@ struct sort_entry {  	int64_t (*cmp)(struct hist_entry *, struct hist_entry *);  	int64_t (*collapse)(struct hist_entry *, struct hist_entry *); -	size_t	(*print)(FILE *fp, struct hist_entry *); +	size_t	(*print)(FILE *fp, struct hist_entry *, unsigned int width); +	unsigned int *width; +	bool	elide;  };  static int64_t cmp_null(void *l, void *r) @@ -541,15 +601,17 @@ sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)  }  static size_t -sort__thread_print(FILE *fp, struct hist_entry *self) +sort__thread_print(FILE *fp, struct hist_entry *self, unsigned int width)  { -	return fprintf(fp, "%16s:%5d", self->thread->comm ?: "", self->thread->pid); +	return repsep_fprintf(fp, "%*s:%5d", width - 6, +			      self->thread->comm ?: "", self->thread->pid);  }  static struct sort_entry sort_thread = { -	.header = "         Command:  Pid", +	.header = "Command:  Pid",  	.cmp	= sort__thread_cmp,  	.print	= sort__thread_print, +	.width	= &threads__col_width,  };  /* --sort comm */ @@ -573,16 +635,17 @@ sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)  }  static size_t -sort__comm_print(FILE *fp, struct hist_entry *self) +sort__comm_print(FILE *fp, struct hist_entry *self, unsigned int width)  { -	return fprintf(fp, "%16s", self->thread->comm); +	return repsep_fprintf(fp, "%*s", width, self->thread->comm);  }  static struct sort_entry sort_comm = { -	.header		= "         Command", +	.header		= "Command",  	.cmp		= sort__comm_cmp,  	.collapse	= sort__comm_collapse,  	.print		= sort__comm_print, +	.width		= &comms__col_width,  };  /* --sort dso */ @@ -600,18 +663,19 @@ sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)  }  static size_t -sort__dso_print(FILE *fp, struct hist_entry *self) +sort__dso_print(FILE *fp, struct hist_entry *self, unsigned int width)  {  	if (self->dso) -		return fprintf(fp, "%-25s", self->dso->name); +		return repsep_fprintf(fp, "%-*s", width, self->dso->name); -	return fprintf(fp, "%016llx         ", (u64)self->ip); +	return repsep_fprintf(fp, "%*llx", width, (u64)self->ip);  }  static struct sort_entry sort_dso = { -	.header = "Shared Object            ", +	.header = "Shared Object",  	.cmp	= sort__dso_cmp,  	.print	= sort__dso_print, +	.width	= &dsos__col_width,  };  /* --sort symbol */ @@ -631,18 +695,23 @@ sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)  }  static size_t -sort__sym_print(FILE *fp, struct hist_entry *self) +sort__sym_print(FILE *fp, struct hist_entry *self, unsigned int width __used)  {  	size_t ret = 0;  	if (verbose) -		ret += fprintf(fp, "%#018llx  ", (u64)self->ip); +		ret += repsep_fprintf(fp, "%#018llx %c ", (u64)self->ip, +				      dso__symtab_origin(self->dso)); +	ret += repsep_fprintf(fp, "[%c] ", self->level);  	if (self->sym) { -		ret += fprintf(fp, "[%c] %s", -			self->dso == kernel_dso ? 'k' : '.', self->sym->name); +		ret += repsep_fprintf(fp, "%s", self->sym->name); + +		if (self->sym->module) +			ret += repsep_fprintf(fp, "\t[%s]", +					     self->sym->module->name);  	} else { -		ret += fprintf(fp, "%#016llx", (u64)self->ip); +		ret += repsep_fprintf(fp, "%#016llx", (u64)self->ip);  	}  	return ret; @@ -669,19 +738,19 @@ sort__parent_cmp(struct hist_entry *left, struct hist_entry *right)  }  static size_t -sort__parent_print(FILE *fp, struct hist_entry *self) +sort__parent_print(FILE *fp, struct hist_entry *self, unsigned int width)  { -	size_t ret = 0; - -	ret += fprintf(fp, "%-20s", self->parent ? self->parent->name : "[other]"); - -	return ret; +	return repsep_fprintf(fp, "%-*s", width, +			      self->parent ? self->parent->name : "[other]");  } +static unsigned int parent_symbol__col_width; +  static struct sort_entry sort_parent = { -	.header = "Parent symbol       ", +	.header = "Parent symbol",  	.cmp	= sort__parent_cmp,  	.print	= sort__parent_print, +	.width	= &parent_symbol__col_width,  };  static int sort__need_collapse = 0; @@ -705,7 +774,7 @@ static LIST_HEAD(hist_entry__sort_list);  static int sort_dimension__add(char *tok)  { -	int i; +	unsigned int i;  	for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) {  		struct sort_dimension *sd = &sort_dimensions[i]; @@ -775,8 +844,146 @@ hist_entry__collapse(struct hist_entry *left, struct hist_entry *right)  	return cmp;  } +static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask) +{ +	int i; +	size_t ret = 0; + +	ret += fprintf(fp, "%s", "                "); + +	for (i = 0; i < depth; i++) +		if (depth_mask & (1 << i)) +			ret += fprintf(fp, "|          "); +		else +			ret += fprintf(fp, "           "); + +	ret += fprintf(fp, "\n"); + +	return ret; +} +static size_t +ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, int depth, +		       int depth_mask, int count, u64 total_samples, +		       int hits) +{ +	int i; +	size_t ret = 0; + +	ret += fprintf(fp, "%s", "                "); +	for (i = 0; i < depth; i++) { +		if (depth_mask & (1 << i)) +			ret += fprintf(fp, "|"); +		else +			ret += fprintf(fp, " "); +		if (!count && i == depth - 1) { +			double percent; + +			percent = hits * 100.0 / total_samples; +			ret += percent_color_fprintf(fp, "--%2.2f%%-- ", percent); +		} else +			ret += fprintf(fp, "%s", "          "); +	} +	if (chain->sym) +		ret += fprintf(fp, "%s\n", chain->sym->name); +	else +		ret += fprintf(fp, "%p\n", (void *)(long)chain->ip); + +	return ret; +} + +static struct symbol *rem_sq_bracket; +static struct callchain_list rem_hits; + +static void init_rem_hits(void) +{ +	rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6); +	if (!rem_sq_bracket) { +		fprintf(stderr, "Not enough memory to display remaining hits\n"); +		return; +	} + +	strcpy(rem_sq_bracket->name, "[...]"); +	rem_hits.sym = rem_sq_bracket; +} + +static size_t +callchain__fprintf_graph(FILE *fp, struct callchain_node *self, +			u64 total_samples, int depth, int depth_mask) +{ +	struct rb_node *node, *next; +	struct callchain_node *child; +	struct callchain_list *chain; +	int new_depth_mask = depth_mask; +	u64 new_total; +	u64 remaining; +	size_t ret = 0; +	int i; + +	if (callchain_param.mode == CHAIN_GRAPH_REL) +		new_total = self->children_hit; +	else +		new_total = total_samples; + +	remaining = new_total; + +	node = rb_first(&self->rb_root); +	while (node) { +		u64 cumul; + +		child = rb_entry(node, struct callchain_node, rb_node); +		cumul = cumul_hits(child); +		remaining -= cumul; + +		/* +		 * The depth mask manages the output of pipes that show +		 * the depth. We don't want to keep the pipes of the current +		 * level for the last child of this depth. +		 * Except if we have remaining filtered hits. They will +		 * supersede the last child +		 */ +		next = rb_next(node); +		if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining)) +			new_depth_mask &= ~(1 << (depth - 1)); + +		/* +		 * But we keep the older depth mask for the line seperator +		 * to keep the level link until we reach the last child +		 */ +		ret += ipchain__fprintf_graph_line(fp, depth, depth_mask); +		i = 0; +		list_for_each_entry(chain, &child->val, list) { +			if (chain->ip >= PERF_CONTEXT_MAX) +				continue; +			ret += ipchain__fprintf_graph(fp, chain, depth, +						      new_depth_mask, i++, +						      new_total, +						      cumul); +		} +		ret += callchain__fprintf_graph(fp, child, new_total, +						depth + 1, +						new_depth_mask | (1 << depth)); +		node = next; +	} + +	if (callchain_param.mode == CHAIN_GRAPH_REL && +		remaining && remaining != new_total) { + +		if (!rem_sq_bracket) +			return ret; + +		new_depth_mask &= ~(1 << (depth - 1)); + +		ret += ipchain__fprintf_graph(fp, &rem_hits, depth, +					      new_depth_mask, 0, new_total, +					      remaining); +	} + +	return ret; +} +  static size_t -callchain__fprintf(FILE *fp, struct callchain_node *self, u64 total_samples) +callchain__fprintf_flat(FILE *fp, struct callchain_node *self, +			u64 total_samples)  {  	struct callchain_list *chain;  	size_t ret = 0; @@ -784,11 +991,18 @@ callchain__fprintf(FILE *fp, struct callchain_node *self, u64 total_samples)  	if (!self)  		return 0; -	ret += callchain__fprintf(fp, self->parent, total_samples); +	ret += callchain__fprintf_flat(fp, self->parent, total_samples); -	list_for_each_entry(chain, &self->val, list) -		ret += fprintf(fp, "                %p\n", (void *)chain->ip); +	list_for_each_entry(chain, &self->val, list) { +		if (chain->ip >= PERF_CONTEXT_MAX) +			continue; +		if (chain->sym) +			ret += fprintf(fp, "                %s\n", chain->sym->name); +		else +			ret += fprintf(fp, "                %p\n", +					(void *)(long)chain->ip); +	}  	return ret;  } @@ -807,8 +1021,19 @@ hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self,  		chain = rb_entry(rb_node, struct callchain_node, rb_node);  		percent = chain->hit * 100.0 / total_samples; -		ret += fprintf(fp, "           %6.2f%%\n", percent); -		ret += callchain__fprintf(fp, chain, total_samples); +		switch (callchain_param.mode) { +		case CHAIN_FLAT: +			ret += percent_color_fprintf(fp, "           %6.2f%%\n", +						     percent); +			ret += callchain__fprintf_flat(fp, chain, total_samples); +			break; +		case CHAIN_GRAPH_ABS: /* Falldown */ +		case CHAIN_GRAPH_REL: +			ret += callchain__fprintf_graph(fp, chain, +							total_samples, 1, 1); +		default: +			break; +		}  		ret += fprintf(fp, "\n");  		rb_node = rb_next(rb_node);  	} @@ -826,33 +1051,26 @@ hist_entry__fprintf(FILE *fp, struct hist_entry *self, u64 total_samples)  	if (exclude_other && !self->parent)  		return 0; -	if (total_samples) { -		double percent = self->count * 100.0 / total_samples; -		char *color = PERF_COLOR_NORMAL; - -		/* -		 * We color high-overhead entries in red, mid-overhead -		 * entries in green - and keep the low overhead places -		 * normal: -		 */ -		if (percent >= 5.0) { -			color = PERF_COLOR_RED; -		} else { -			if (percent >= 0.5) -				color = PERF_COLOR_GREEN; -		} +	if (total_samples) +		ret = percent_color_fprintf(fp, +					    field_sep ? "%.2f" : "   %6.2f%%", +					(self->count * 100.0) / total_samples); +	else +		ret = fprintf(fp, field_sep ? "%lld" : "%12lld ", self->count); -		ret = color_fprintf(fp, color, "   %6.2f%%", -				(self->count * 100.0) / total_samples); -	} else -		ret = fprintf(fp, "%12Ld ", self->count); +	if (show_nr_samples) { +		if (field_sep) +			fprintf(fp, "%c%lld", *field_sep, self->count); +		else +			fprintf(fp, "%11lld", self->count); +	}  	list_for_each_entry(se, &hist_entry__sort_list, list) { -		if (exclude_other && (se == &sort_parent)) +		if (se->elide)  			continue; -		fprintf(fp, "  "); -		ret += se->print(fp, self); +		fprintf(fp, "%s", field_sep ?: "  "); +		ret += se->print(fp, self, se->width ? *se->width : 0);  	}  	ret += fprintf(fp, "\n"); @@ -867,6 +1085,18 @@ hist_entry__fprintf(FILE *fp, struct hist_entry *self, u64 total_samples)   *   */ +static void dso__calc_col_width(struct dso *self) +{ +	if (!col_width_list_str && !field_sep && +	    (!dso_list || strlist__has_entry(dso_list, self->name))) { +		unsigned int slen = strlen(self->name); +		if (slen > dsos__col_width) +			dsos__col_width = slen; +	} + +	self->slen_calculated = 1; +} +  static struct symbol *  resolve_symbol(struct thread *thread, struct map **mapp,  	       struct dso **dsop, u64 *ipp) @@ -886,6 +1116,14 @@ resolve_symbol(struct thread *thread, struct map **mapp,  	map = thread__find_map(thread, ip);  	if (map != NULL) { +		/* +		 * We have to do this here as we may have a dso +		 * with no symbol hit that has a name longer than +		 * the ones with symbols sampled. +		 */ +		if (!sort_dso.elide && !map->dso->slen_calculated) +			dso__calc_col_width(map->dso); +  		if (mapp)  			*mapp = map;  got_map: @@ -923,6 +1161,58 @@ static int call__match(struct symbol *sym)  	return 0;  } +static struct symbol ** +resolve_callchain(struct thread *thread, struct map *map __used, +		    struct ip_callchain *chain, struct hist_entry *entry) +{ +	u64 context = PERF_CONTEXT_MAX; +	struct symbol **syms = NULL; +	unsigned int i; + +	if (callchain) { +		syms = calloc(chain->nr, sizeof(*syms)); +		if (!syms) { +			fprintf(stderr, "Can't allocate memory for symbols\n"); +			exit(-1); +		} +	} + +	for (i = 0; i < chain->nr; i++) { +		u64 ip = chain->ips[i]; +		struct dso *dso = NULL; +		struct symbol *sym; + +		if (ip >= PERF_CONTEXT_MAX) { +			context = ip; +			continue; +		} + +		switch (context) { +		case PERF_CONTEXT_HV: +			dso = hypervisor_dso; +			break; +		case PERF_CONTEXT_KERNEL: +			dso = kernel_dso; +			break; +		default: +			break; +		} + +		sym = resolve_symbol(thread, NULL, &dso, &ip); + +		if (sym) { +			if (sort__has_parent && call__match(sym) && +			    !entry->parent) +				entry->parent = sym; +			if (!callchain) +				break; +			syms[i] = sym; +		} +	} + +	return syms; +} +  /*   * collect histogram counts   */ @@ -935,6 +1225,7 @@ hist_entry__add(struct thread *thread, struct map *map, struct dso *dso,  	struct rb_node **p = &hist.rb_node;  	struct rb_node *parent = NULL;  	struct hist_entry *he; +	struct symbol **syms = NULL;  	struct hist_entry entry = {  		.thread	= thread,  		.map	= map, @@ -948,36 +1239,8 @@ hist_entry__add(struct thread *thread, struct map *map, struct dso *dso,  	};  	int cmp; -	if (sort__has_parent && chain) { -		u64 context = PERF_CONTEXT_MAX; -		int i; - -		for (i = 0; i < chain->nr; i++) { -			u64 ip = chain->ips[i]; -			struct dso *dso = NULL; -			struct symbol *sym; - -			if (ip >= PERF_CONTEXT_MAX) { -				context = ip; -				continue; -			} - -			switch (context) { -			case PERF_CONTEXT_KERNEL: -				dso = kernel_dso; -				break; -			default: -				break; -			} - -			sym = resolve_symbol(thread, NULL, &dso, &ip); - -			if (sym && call__match(sym)) { -				entry.parent = sym; -				break; -			} -		} -	} +	if ((sort__has_parent || callchain) && chain) +		syms = resolve_callchain(thread, map, chain, &entry);  	while (*p != NULL) {  		parent = *p; @@ -987,8 +1250,10 @@ hist_entry__add(struct thread *thread, struct map *map, struct dso *dso,  		if (!cmp) {  			he->count += count; -			if (callchain) -				append_chain(&he->callchain, chain); +			if (callchain) { +				append_chain(&he->callchain, chain, syms); +				free(syms); +			}  			return 0;  		} @@ -1004,7 +1269,8 @@ hist_entry__add(struct thread *thread, struct map *map, struct dso *dso,  	*he = entry;  	if (callchain) {  		callchain_init(&he->callchain); -		append_chain(&he->callchain, chain); +		append_chain(&he->callchain, chain, syms); +		free(syms);  	}  	rb_link_node(&he->rb_node, parent, p);  	rb_insert_color(&he->rb_node, &hist); @@ -1076,14 +1342,15 @@ static void collapse__resort(void)  static struct rb_root output_hists; -static void output__insert_entry(struct hist_entry *he) +static void output__insert_entry(struct hist_entry *he, u64 min_callchain_hits)  {  	struct rb_node **p = &output_hists.rb_node;  	struct rb_node *parent = NULL;  	struct hist_entry *iter;  	if (callchain) -		sort_chain_to_rbtree(&he->sorted_chain, &he->callchain); +		callchain_param.sort(&he->sorted_chain, &he->callchain, +				      min_callchain_hits, &callchain_param);  	while (*p != NULL) {  		parent = *p; @@ -1099,11 +1366,14 @@ static void output__insert_entry(struct hist_entry *he)  	rb_insert_color(&he->rb_node, &output_hists);  } -static void output__resort(void) +static void output__resort(u64 total_samples)  {  	struct rb_node *next;  	struct hist_entry *n;  	struct rb_root *tree = &hist; +	u64 min_callchain_hits; + +	min_callchain_hits = total_samples * (callchain_param.min_percent / 100);  	if (sort__need_collapse)  		tree = &collapse_hists; @@ -1115,7 +1385,7 @@ static void output__resort(void)  		next = rb_next(&n->rb_node);  		rb_erase(&n->rb_node, tree); -		output__insert_entry(n); +		output__insert_entry(n, min_callchain_hits);  	}  } @@ -1125,35 +1395,69 @@ static size_t output__fprintf(FILE *fp, u64 total_samples)  	struct sort_entry *se;  	struct rb_node *nd;  	size_t ret = 0; +	unsigned int width; +	char *col_width = col_width_list_str; -	fprintf(fp, "\n"); -	fprintf(fp, "#\n"); -	fprintf(fp, "# (%Ld samples)\n", (u64)total_samples); +	init_rem_hits(); + +	fprintf(fp, "# Samples: %Ld\n", (u64)total_samples);  	fprintf(fp, "#\n");  	fprintf(fp, "# Overhead"); +	if (show_nr_samples) { +		if (field_sep) +			fprintf(fp, "%cSamples", *field_sep); +		else +			fputs("  Samples  ", fp); +	}  	list_for_each_entry(se, &hist_entry__sort_list, list) { -		if (exclude_other && (se == &sort_parent)) +		if (se->elide)  			continue; -		fprintf(fp, "  %s", se->header); +		if (field_sep) { +			fprintf(fp, "%c%s", *field_sep, se->header); +			continue; +		} +		width = strlen(se->header); +		if (se->width) { +			if (col_width_list_str) { +				if (col_width) { +					*se->width = atoi(col_width); +					col_width = strchr(col_width, ','); +					if (col_width) +						++col_width; +				} +			} +			width = *se->width = max(*se->width, width); +		} +		fprintf(fp, "  %*s", width, se->header);  	}  	fprintf(fp, "\n"); +	if (field_sep) +		goto print_entries; +  	fprintf(fp, "# ........"); +	if (show_nr_samples) +		fprintf(fp, " ..........");  	list_for_each_entry(se, &hist_entry__sort_list, list) { -		int i; +		unsigned int i; -		if (exclude_other && (se == &sort_parent)) +		if (se->elide)  			continue;  		fprintf(fp, "  "); -		for (i = 0; i < strlen(se->header); i++) +		if (se->width) +			width = *se->width; +		else +			width = strlen(se->header); +		for (i = 0; i < width; i++)  			fprintf(fp, ".");  	}  	fprintf(fp, "\n");  	fprintf(fp, "#\n"); +print_entries:  	for (nd = rb_first(&output_hists); nd; nd = rb_next(nd)) {  		pos = rb_entry(nd, struct hist_entry, rb_node);  		ret += hist_entry__fprintf(fp, pos, total_samples); @@ -1162,11 +1466,13 @@ static size_t output__fprintf(FILE *fp, u64 total_samples)  	if (sort_order == default_sort_order &&  			parent_pattern == default_parent_pattern) {  		fprintf(fp, "#\n"); -		fprintf(fp, "# (For more details, try: perf report --sort comm,dso,symbol)\n"); +		fprintf(fp, "# (For a higher level overview, try: perf report --sort comm,dso)\n");  		fprintf(fp, "#\n");  	}  	fprintf(fp, "\n"); +	free(rem_sq_bracket); +  	return ret;  } @@ -1213,22 +1519,23 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head)  	struct map *map = NULL;  	void *more_data = event->ip.__more_data;  	struct ip_callchain *chain = NULL; +	int cpumode;  	if (sample_type & PERF_SAMPLE_PERIOD) {  		period = *(u64 *)more_data;  		more_data += sizeof(u64);  	} -	dprintf("%p [%p]: PERF_EVENT_SAMPLE (IP, %d): %d: %p period: %Ld\n", +	dprintf("%p [%p]: PERF_EVENT_SAMPLE (IP, %d): %d/%d: %p period: %Ld\n",  		(void *)(offset + head),  		(void *)(long)(event->header.size),  		event->header.misc, -		event->ip.pid, +		event->ip.pid, event->ip.tid,  		(void *)(long)ip,  		(long long)period);  	if (sample_type & PERF_SAMPLE_CALLCHAIN) { -		int i; +		unsigned int i;  		chain = (void *)more_data; @@ -1256,7 +1563,9 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head)  	if (comm_list && !strlist__has_entry(comm_list, thread->comm))  		return 0; -	if (event->header.misc & PERF_EVENT_MISC_KERNEL) { +	cpumode = event->header.misc & PERF_EVENT_MISC_CPUMODE_MASK; + +	if (cpumode == PERF_EVENT_MISC_KERNEL) {  		show = SHOW_KERNEL;  		level = 'k'; @@ -1264,7 +1573,7 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head)  		dprintf(" ...... dso: %s\n", dso->name); -	} else if (event->header.misc & PERF_EVENT_MISC_USER) { +	} else if (cpumode == PERF_EVENT_MISC_USER) {  		show = SHOW_USER;  		level = '.'; @@ -1272,16 +1581,20 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head)  	} else {  		show = SHOW_HV;  		level = 'H'; + +		dso = hypervisor_dso; +  		dprintf(" ...... dso: [hypervisor]\n");  	}  	if (show & show_mask) {  		struct symbol *sym = resolve_symbol(thread, &map, &dso, &ip); -		if (dso_list && dso && dso->name && !strlist__has_entry(dso_list, dso->name)) +		if (dso_list && (!dso || !dso->name || +				 !strlist__has_entry(dso_list, dso->name)))  			return 0; -		if (sym_list && sym && !strlist__has_entry(sym_list, sym->name)) +		if (sym_list && (!sym || !strlist__has_entry(sym_list, sym->name)))  			return 0;  		if (hist_entry__add(thread, map, dso, sym, ip, chain, level, period)) { @@ -1300,10 +1613,11 @@ process_mmap_event(event_t *event, unsigned long offset, unsigned long head)  	struct thread *thread = threads__findnew(event->mmap.pid);  	struct map *map = map__new(&event->mmap); -	dprintf("%p [%p]: PERF_EVENT_MMAP %d: [%p(%p) @ %p]: %s\n", +	dprintf("%p [%p]: PERF_EVENT_MMAP %d/%d: [%p(%p) @ %p]: %s\n",  		(void *)(offset + head),  		(void *)(long)(event->header.size),  		event->mmap.pid, +		event->mmap.tid,  		(void *)(long)event->mmap.start,  		(void *)(long)event->mmap.len,  		(void *)(long)event->mmap.pgoff, @@ -1341,15 +1655,27 @@ process_comm_event(event_t *event, unsigned long offset, unsigned long head)  }  static int -process_fork_event(event_t *event, unsigned long offset, unsigned long head) +process_task_event(event_t *event, unsigned long offset, unsigned long head)  {  	struct thread *thread = threads__findnew(event->fork.pid);  	struct thread *parent = threads__findnew(event->fork.ppid); -	dprintf("%p [%p]: PERF_EVENT_FORK: %d:%d\n", +	dprintf("%p [%p]: PERF_EVENT_%s: (%d:%d):(%d:%d)\n",  		(void *)(offset + head),  		(void *)(long)(event->header.size), -		event->fork.pid, event->fork.ppid); +		event->header.type == PERF_EVENT_FORK ? "FORK" : "EXIT", +		event->fork.pid, event->fork.tid, +		event->fork.ppid, event->fork.ptid); + +	/* +	 * A thread clone will have the same PID for both +	 * parent and child. +	 */ +	if (thread == parent) +		return 0; + +	if (event->header.type == PERF_EVENT_EXIT) +		return 0;  	if (!thread || !parent || thread__fork(thread, parent)) {  		dprintf("problem processing PERF_EVENT_FORK, skipping event.\n"); @@ -1361,19 +1687,6 @@ process_fork_event(event_t *event, unsigned long offset, unsigned long head)  }  static int -process_period_event(event_t *event, unsigned long offset, unsigned long head) -{ -	dprintf("%p [%p]: PERF_EVENT_PERIOD: time:%Ld, id:%Ld: period:%Ld\n", -		(void *)(offset + head), -		(void *)(long)(event->header.size), -		event->period.time, -		event->period.id, -		event->period.sample_period); - -	return 0; -} - -static int  process_lost_event(event_t *event, unsigned long offset, unsigned long head)  {  	dprintf("%p [%p]: PERF_EVENT_LOST: id:%Ld: lost:%Ld\n", @@ -1423,14 +1736,37 @@ static void trace_event(event_t *event)  	dprintf(".\n");  } +static struct perf_header	*header; + +static struct perf_counter_attr *perf_header__find_attr(u64 id) +{ +	int i; + +	for (i = 0; i < header->attrs; i++) { +		struct perf_header_attr *attr = header->attr[i]; +		int j; + +		for (j = 0; j < attr->ids; j++) { +			if (attr->id[j] == id) +				return &attr->attr; +		} +	} + +	return NULL; +} +  static int  process_read_event(event_t *event, unsigned long offset, unsigned long head)  { -	dprintf("%p [%p]: PERF_EVENT_READ: %d %d %Lu\n", +	struct perf_counter_attr *attr = perf_header__find_attr(event->read.id); + +	dprintf("%p [%p]: PERF_EVENT_READ: %d %d %s %Lu\n",  			(void *)(offset + head),  			(void *)(long)(event->header.size),  			event->read.pid,  			event->read.tid, +			attr ? __event_name(attr->type, attr->config) +			     : "FAIL",  			event->read.value);  	return 0; @@ -1452,10 +1788,8 @@ process_event(event_t *event, unsigned long offset, unsigned long head)  		return process_comm_event(event, offset, head);  	case PERF_EVENT_FORK: -		return process_fork_event(event, offset, head); - -	case PERF_EVENT_PERIOD: -		return process_period_event(event, offset, head); +	case PERF_EVENT_EXIT: +		return process_task_event(event, offset, head);  	case PERF_EVENT_LOST:  		return process_lost_event(event, offset, head); @@ -1478,8 +1812,6 @@ process_event(event_t *event, unsigned long offset, unsigned long head)  	return 0;  } -static struct perf_header	*header; -  static u64 perf_header__sample_type(void)  {  	u64 sample_type = 0; @@ -1534,9 +1866,26 @@ static int __cmd_report(void)  	sample_type = perf_header__sample_type(); -	if (sort__has_parent && !(sample_type & PERF_SAMPLE_CALLCHAIN)) { -		fprintf(stderr, "selected --sort parent, but no callchain data\n"); -		exit(-1); +	if (!(sample_type & PERF_SAMPLE_CALLCHAIN)) { +		if (sort__has_parent) { +			fprintf(stderr, "selected --sort parent, but no" +					" callchain data. Did you call" +					" perf record without -g?\n"); +			exit(-1); +		} +		if (callchain) { +			fprintf(stderr, "selected -c but no callchain data." +					" Did you call perf record without" +					" -g?\n"); +			exit(-1); +		} +	} else if (callchain_param.mode != CHAIN_NONE && !callchain) { +			callchain = 1; +			if (register_callchain_param(&callchain_param) < 0) { +				fprintf(stderr, "Can't register callchain" +						" params\n"); +				exit(-1); +			}  	}  	if (load_kernel() < 0) { @@ -1619,7 +1968,7 @@ more:  	if (offset + head >= header->data_offset + header->data_size)  		goto done; -	if (offset + head < stat.st_size) +	if (offset + head < (unsigned long)stat.st_size)  		goto more;  done: @@ -1643,12 +1992,65 @@ done:  		dsos__fprintf(stdout);  	collapse__resort(); -	output__resort(); +	output__resort(total);  	output__fprintf(stdout, total);  	return rc;  } +static int +parse_callchain_opt(const struct option *opt __used, const char *arg, +		    int unset __used) +{ +	char *tok; +	char *endptr; + +	callchain = 1; + +	if (!arg) +		return 0; + +	tok = strtok((char *)arg, ","); +	if (!tok) +		return -1; + +	/* get the output mode */ +	if (!strncmp(tok, "graph", strlen(arg))) +		callchain_param.mode = CHAIN_GRAPH_ABS; + +	else if (!strncmp(tok, "flat", strlen(arg))) +		callchain_param.mode = CHAIN_FLAT; + +	else if (!strncmp(tok, "fractal", strlen(arg))) +		callchain_param.mode = CHAIN_GRAPH_REL; + +	else if (!strncmp(tok, "none", strlen(arg))) { +		callchain_param.mode = CHAIN_NONE; +		callchain = 0; + +		return 0; +	} + +	else +		return -1; + +	/* get the min percentage */ +	tok = strtok(NULL, ","); +	if (!tok) +		goto setup; + +	callchain_param.min_percent = strtod(tok, &endptr); +	if (tok == endptr) +		return -1; + +setup: +	if (register_callchain_param(&callchain_param) < 0) { +		fprintf(stderr, "Can't register callchain params\n"); +		return -1; +	} +	return 0; +} +  static const char * const report_usage[] = {  	"perf report [<options>] <command>",  	NULL @@ -1662,6 +2064,10 @@ static const struct option options[] = {  	OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,  		    "dump raw trace in ASCII"),  	OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"), +	OPT_BOOLEAN('m', "modules", &modules, +		    "load module symbols - WARNING: use only with -k and LIVE kernel"), +	OPT_BOOLEAN('n', "show-nr-samples", &show_nr_samples, +		    "Show a column with the number of samples"),  	OPT_STRING('s', "sort", &sort_order, "key[,key2...]",  		   "sort by key(s): pid, comm, dso, symbol, parent"),  	OPT_BOOLEAN('P', "full-paths", &full_paths, @@ -1670,13 +2076,21 @@ static const struct option options[] = {  		   "regex filter to identify parent, see: '--sort parent'"),  	OPT_BOOLEAN('x', "exclude-other", &exclude_other,  		    "Only display entries with parent-match"), -	OPT_BOOLEAN('c', "callchain", &callchain, "Display callchains"), +	OPT_CALLBACK_DEFAULT('g', "call-graph", NULL, "output_type,min_percent", +		     "Display callchains using output_type and min percent threshold. " +		     "Default: fractal,0.5", &parse_callchain_opt, callchain_default_opt),  	OPT_STRING('d', "dsos", &dso_list_str, "dso[,dso...]",  		   "only consider symbols in these dsos"),  	OPT_STRING('C', "comms", &comm_list_str, "comm[,comm...]",  		   "only consider symbols in these comms"),  	OPT_STRING('S', "symbols", &sym_list_str, "symbol[,symbol...]",  		   "only consider these symbols"), +	OPT_STRING('w', "column-widths", &col_width_list_str, +		   "width[,width...]", +		   "don't try to adjust column width, use these fixed values"), +	OPT_STRING('t', "field-separator", &field_sep, "separator", +		   "separator for columns, no spaces will be added between " +		   "columns '.' is reserved."),  	OPT_END()  }; @@ -1696,7 +2110,8 @@ static void setup_sorting(void)  }  static void setup_list(struct strlist **list, const char *list_str, -		       const char *list_name) +		       struct sort_entry *se, const char *list_name, +		       FILE *fp)  {  	if (list_str) {  		*list = strlist__new(true, list_str); @@ -1705,10 +2120,15 @@ static void setup_list(struct strlist **list, const char *list_str,  				list_name);  			exit(129);  		} +		if (strlist__nr_entries(*list) == 1) { +			fprintf(fp, "# %s: %s\n", list_name, +				strlist__entry(*list, 0)->s); +			se->elide = true; +		}  	}  } -int cmd_report(int argc, const char **argv, const char *prefix) +int cmd_report(int argc, const char **argv, const char *prefix __used)  {  	symbol__init(); @@ -1718,9 +2138,10 @@ int cmd_report(int argc, const char **argv, const char *prefix)  	setup_sorting(); -	if (parent_pattern != default_parent_pattern) +	if (parent_pattern != default_parent_pattern) {  		sort_dimension__add("parent"); -	else +		sort_parent.elide = 1; +	} else  		exclude_other = 0;  	/* @@ -1729,11 +2150,17 @@ int cmd_report(int argc, const char **argv, const char *prefix)  	if (argc)  		usage_with_options(report_usage, options); -	setup_list(&dso_list, dso_list_str, "dso"); -	setup_list(&comm_list, comm_list_str, "comm"); -	setup_list(&sym_list, sym_list_str, "symbol"); -  	setup_pager(); +	setup_list(&dso_list, dso_list_str, &sort_dso, "dso", stdout); +	setup_list(&comm_list, comm_list_str, &sort_comm, "comm", stdout); +	setup_list(&sym_list, sym_list_str, &sort_sym, "symbol", stdout); + +	if (field_sep && *field_sep == '.') { +		fputs("'.' is the only non valid --field-separator argument\n", +		      stderr); +		exit(129); +	} +  	return __cmd_report();  } diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 2e03524a1de..b4b06c7903e 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -64,7 +64,7 @@ static struct perf_counter_attr default_attrs[] = {  static int			system_wide			=  0;  static int			verbose				=  0; -static int			nr_cpus				=  0; +static unsigned int		nr_cpus				=  0;  static int			run_idx				=  0;  static int			run_count			=  1; @@ -96,6 +96,10 @@ static u64			walltime_nsecs_noise;  static u64			runtime_cycles_avg;  static u64			runtime_cycles_noise; +#define MATCH_EVENT(t, c, counter)			\ +	(attrs[counter].type == PERF_TYPE_##t &&	\ +	 attrs[counter].config == PERF_COUNT_##c) +  #define ERR_PERF_OPEN \  "Error: counter %d, sys_perf_counter_open() syscall returned with %d (%s)\n" @@ -108,7 +112,8 @@ static void create_perf_stat_counter(int counter, int pid)  				    PERF_FORMAT_TOTAL_TIME_RUNNING;  	if (system_wide) { -		int cpu; +		unsigned int cpu; +  		for (cpu = 0; cpu < nr_cpus; cpu++) {  			fd[cpu][counter] = sys_perf_counter_open(attr, -1, cpu, -1, 0);  			if (fd[cpu][counter] < 0 && verbose) @@ -132,13 +137,8 @@ static void create_perf_stat_counter(int counter, int pid)   */  static inline int nsec_counter(int counter)  { -	if (attrs[counter].type != PERF_TYPE_SOFTWARE) -		return 0; - -	if (attrs[counter].config == PERF_COUNT_SW_CPU_CLOCK) -		return 1; - -	if (attrs[counter].config == PERF_COUNT_SW_TASK_CLOCK) +	if (MATCH_EVENT(SOFTWARE, SW_CPU_CLOCK, counter) || +	    MATCH_EVENT(SOFTWARE, SW_TASK_CLOCK, counter))  		return 1;  	return 0; @@ -150,8 +150,8 @@ static inline int nsec_counter(int counter)  static void read_counter(int counter)  {  	u64 *count, single_count[3]; -	ssize_t res; -	int cpu, nv; +	unsigned int cpu; +	size_t res, nv;  	int scaled;  	count = event_res[run_idx][counter]; @@ -165,6 +165,7 @@ static void read_counter(int counter)  		res = read(fd[cpu][counter], single_count, nv * sizeof(u64));  		assert(res == nv * sizeof(u64)); +  		close(fd[cpu][counter]);  		fd[cpu][counter] = -1; @@ -192,15 +193,13 @@ static void read_counter(int counter)  	/*  	 * Save the full runtime - to allow normalization during printout:  	 */ -	if (attrs[counter].type == PERF_TYPE_SOFTWARE && -		attrs[counter].config == PERF_COUNT_SW_TASK_CLOCK) +	if (MATCH_EVENT(SOFTWARE, SW_TASK_CLOCK, counter))  		runtime_nsecs[run_idx] = count[0]; -	if (attrs[counter].type == PERF_TYPE_HARDWARE && -		attrs[counter].config == PERF_COUNT_HW_CPU_CYCLES) +	if (MATCH_EVENT(HARDWARE, HW_CPU_CYCLES, counter))  		runtime_cycles[run_idx] = count[0];  } -static int run_perf_stat(int argc, const char **argv) +static int run_perf_stat(int argc __used, const char **argv)  {  	unsigned long long t0, t1;  	int status = 0; @@ -240,7 +239,8 @@ static int run_perf_stat(int argc, const char **argv)  		/*  		 * Wait until the parent tells us to go.  		 */ -		read(go_pipe[0], &buf, 1); +		if (read(go_pipe[0], &buf, 1) == -1) +			perror("unable to read pipe");  		execvp(argv[0], (char **)argv); @@ -253,7 +253,8 @@ static int run_perf_stat(int argc, const char **argv)  	 */  	close(child_ready_pipe[1]);  	close(go_pipe[0]); -	read(child_ready_pipe[0], &buf, 1); +	if (read(child_ready_pipe[0], &buf, 1) == -1) +		perror("unable to read pipe");  	close(child_ready_pipe[0]);  	for (counter = 0; counter < nr_counters; counter++) @@ -290,9 +291,7 @@ static void nsec_printout(int counter, u64 *count, u64 *noise)  	fprintf(stderr, " %14.6f  %-24s", msecs, event_name(counter)); -	if (attrs[counter].type == PERF_TYPE_SOFTWARE && -		attrs[counter].config == PERF_COUNT_SW_TASK_CLOCK) { - +	if (MATCH_EVENT(SOFTWARE, SW_TASK_CLOCK, counter)) {  		if (walltime_nsecs_avg)  			fprintf(stderr, " # %10.3f CPUs ",  				(double)count[0] / (double)walltime_nsecs_avg); @@ -305,9 +304,7 @@ static void abs_printout(int counter, u64 *count, u64 *noise)  	fprintf(stderr, " %14Ld  %-24s", count[0], event_name(counter));  	if (runtime_cycles_avg && -		attrs[counter].type == PERF_TYPE_HARDWARE && -			attrs[counter].config == PERF_COUNT_HW_INSTRUCTIONS) { - +	    MATCH_EVENT(HARDWARE, HW_INSTRUCTIONS, counter)) {  		fprintf(stderr, " # %10.3f IPC  ",  			(double)count[0] / (double)runtime_cycles_avg);  	} else { @@ -390,7 +387,7 @@ static void calc_avg(void)  				event_res_avg[j]+1, event_res[i][j]+1);  			update_avg("counter/2", j,  				event_res_avg[j]+2, event_res[i][j]+2); -			if (event_scaled[i][j] != -1) +			if (event_scaled[i][j] != (u64)-1)  				update_avg("scaled", j,  					event_scaled_avg + j, event_scaled[i]+j);  			else @@ -499,7 +496,7 @@ static const struct option options[] = {  		    "stat events on existing pid"),  	OPT_BOOLEAN('a', "all-cpus", &system_wide,  		    "system-wide collection from all CPUs"), -	OPT_BOOLEAN('S', "scale", &scale, +	OPT_BOOLEAN('c', "scale", &scale,  		    "scale/normalize counters"),  	OPT_BOOLEAN('v', "verbose", &verbose,  		    "be more verbose (show counter open errors, etc)"), @@ -510,11 +507,12 @@ static const struct option options[] = {  	OPT_END()  }; -int cmd_stat(int argc, const char **argv, const char *prefix) +int cmd_stat(int argc, const char **argv, const char *prefix __used)  {  	int status; -	argc = parse_options(argc, argv, options, stat_usage, 0); +	argc = parse_options(argc, argv, options, stat_usage, +		PARSE_OPT_STOP_AT_NON_OPTION);  	if (!argc)  		usage_with_options(stat_usage, options);  	if (run_count <= 0 || run_count > MAX_RUN) @@ -528,7 +526,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix)  	nr_cpus = sysconf(_SC_NPROCESSORS_ONLN);  	assert(nr_cpus <= MAX_NR_CPUS); -	assert(nr_cpus >= 0); +	assert((int)nr_cpus >= 0);  	/*  	 * We dont want to block the signals - that would cause diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index cf0d21f1ae1..7de28ce9ca2 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -23,7 +23,7 @@  #include "util/symbol.h"  #include "util/color.h"  #include "util/util.h" -#include "util/rbtree.h" +#include <linux/rbtree.h>  #include "util/parse-options.h"  #include "util/parse-events.h" @@ -31,6 +31,8 @@  #include <fcntl.h>  #include <stdio.h> +#include <termios.h> +#include <unistd.h>  #include <errno.h>  #include <time.h> @@ -54,10 +56,11 @@ static int			system_wide			=  0;  static int			default_interval		= 100000; -static u64			count_filter			=  5; +static int			count_filter			=  5;  static int			print_entries			= 15;  static int			target_pid			= -1; +static int			inherit				=  0;  static int			profile_cpu			= -1;  static int			nr_cpus				=  0;  static unsigned int		realtime_prio			=  0; @@ -66,16 +69,30 @@ static unsigned int		page_size;  static unsigned int		mmap_pages			= 16;  static int			freq				=  0;  static int			verbose				=  0; - -static char			*sym_filter; -static unsigned long		filter_start; -static unsigned long		filter_end; +static char			*vmlinux			=  NULL;  static int			delay_secs			=  2;  static int			zero;  static int			dump_symtab;  /* + * Source + */ + +struct source_line { +	u64			eip; +	unsigned long		count[MAX_COUNTERS]; +	char			*line; +	struct source_line	*next; +}; + +static char			*sym_filter			=  NULL; +struct sym_entry		*sym_filter_entry		=  NULL; +static int			sym_pcnt_filter			=  5; +static int			sym_counter			=  0; +static int			display_weighted		= -1; + +/*   * Symbols   */ @@ -89,9 +106,237 @@ struct sym_entry {  	unsigned long		snap_count;  	double			weight;  	int			skip; +	struct source_line	*source; +	struct source_line	*lines; +	struct source_line	**lines_tail; +	pthread_mutex_t		source_lock;  }; -struct sym_entry		*sym_filter_entry; +/* + * Source functions + */ + +static void parse_source(struct sym_entry *syme) +{ +	struct symbol *sym; +	struct module *module; +	struct section *section = NULL; +	FILE *file; +	char command[PATH_MAX*2], *path = vmlinux; +	u64 start, end, len; + +	if (!syme) +		return; + +	if (syme->lines) { +		pthread_mutex_lock(&syme->source_lock); +		goto out_assign; +	} + +	sym = (struct symbol *)(syme + 1); +	module = sym->module; + +	if (module) +		path = module->path; +	if (!path) +		return; + +	start = sym->obj_start; +	if (!start) +		start = sym->start; + +	if (module) { +		section = module->sections->find_section(module->sections, ".text"); +		if (section) +			start -= section->vma; +	} + +	end = start + sym->end - sym->start + 1; +	len = sym->end - sym->start; + +	sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s", start, end, path); + +	file = popen(command, "r"); +	if (!file) +		return; + +	pthread_mutex_lock(&syme->source_lock); +	syme->lines_tail = &syme->lines; +	while (!feof(file)) { +		struct source_line *src; +		size_t dummy = 0; +		char *c; + +		src = malloc(sizeof(struct source_line)); +		assert(src != NULL); +		memset(src, 0, sizeof(struct source_line)); + +		if (getline(&src->line, &dummy, file) < 0) +			break; +		if (!src->line) +			break; + +		c = strchr(src->line, '\n'); +		if (c) +			*c = 0; + +		src->next = NULL; +		*syme->lines_tail = src; +		syme->lines_tail = &src->next; + +		if (strlen(src->line)>8 && src->line[8] == ':') { +			src->eip = strtoull(src->line, NULL, 16); +			if (section) +				src->eip += section->vma; +		} +		if (strlen(src->line)>8 && src->line[16] == ':') { +			src->eip = strtoull(src->line, NULL, 16); +			if (section) +				src->eip += section->vma; +		} +	} +	pclose(file); +out_assign: +	sym_filter_entry = syme; +	pthread_mutex_unlock(&syme->source_lock); +} + +static void __zero_source_counters(struct sym_entry *syme) +{ +	int i; +	struct source_line *line; + +	line = syme->lines; +	while (line) { +		for (i = 0; i < nr_counters; i++) +			line->count[i] = 0; +		line = line->next; +	} +} + +static void record_precise_ip(struct sym_entry *syme, int counter, u64 ip) +{ +	struct source_line *line; + +	if (syme != sym_filter_entry) +		return; + +	if (pthread_mutex_trylock(&syme->source_lock)) +		return; + +	if (!syme->source) +		goto out_unlock; + +	for (line = syme->lines; line; line = line->next) { +		if (line->eip == ip) { +			line->count[counter]++; +			break; +		} +		if (line->eip > ip) +			break; +	} +out_unlock: +	pthread_mutex_unlock(&syme->source_lock); +} + +static void lookup_sym_source(struct sym_entry *syme) +{ +	struct symbol *symbol = (struct symbol *)(syme + 1); +	struct source_line *line; +	char pattern[PATH_MAX]; +	char *idx; + +	sprintf(pattern, "<%s>:", symbol->name); + +	if (symbol->module) { +		idx = strstr(pattern, "\t"); +		if (idx) +			*idx = 0; +	} + +	pthread_mutex_lock(&syme->source_lock); +	for (line = syme->lines; line; line = line->next) { +		if (strstr(line->line, pattern)) { +			syme->source = line; +			break; +		} +	} +	pthread_mutex_unlock(&syme->source_lock); +} + +static void show_lines(struct source_line *queue, int count, int total) +{ +	int i; +	struct source_line *line; + +	line = queue; +	for (i = 0; i < count; i++) { +		float pcnt = 100.0*(float)line->count[sym_counter]/(float)total; + +		printf("%8li %4.1f%%\t%s\n", line->count[sym_counter], pcnt, line->line); +		line = line->next; +	} +} + +#define TRACE_COUNT     3 + +static void show_details(struct sym_entry *syme) +{ +	struct symbol *symbol; +	struct source_line *line; +	struct source_line *line_queue = NULL; +	int displayed = 0; +	int line_queue_count = 0, total = 0, more = 0; + +	if (!syme) +		return; + +	if (!syme->source) +		lookup_sym_source(syme); + +	if (!syme->source) +		return; + +	symbol = (struct symbol *)(syme + 1); +	printf("Showing %s for %s\n", event_name(sym_counter), symbol->name); +	printf("  Events  Pcnt (>=%d%%)\n", sym_pcnt_filter); + +	pthread_mutex_lock(&syme->source_lock); +	line = syme->source; +	while (line) { +		total += line->count[sym_counter]; +		line = line->next; +	} + +	line = syme->source; +	while (line) { +		float pcnt = 0.0; + +		if (!line_queue_count) +			line_queue = line; +		line_queue_count++; + +		if (line->count[sym_counter]) +			pcnt = 100.0 * line->count[sym_counter] / (float)total; +		if (pcnt >= (float)sym_pcnt_filter) { +			if (displayed <= print_entries) +				show_lines(line_queue, line_queue_count, total); +			else more++; +			displayed += line_queue_count; +			line_queue_count = 0; +			line_queue = NULL; +		} else if (line_queue_count > TRACE_COUNT) { +			line_queue = line_queue->next; +			line_queue_count--; +		} + +		line->count[sym_counter] = zero ? 0 : line->count[sym_counter] * 7 / 8; +		line = line->next; +	} +	pthread_mutex_unlock(&syme->source_lock); +	if (more) +		printf("%d lines not displayed, maybe increase display entries [e]\n", more); +}  struct dso			*kernel_dso; @@ -110,6 +355,9 @@ static double sym_weight(const struct sym_entry *sym)  	double weight = sym->snap_count;  	int counter; +	if (!display_weighted) +		return weight; +  	for (counter = 1; counter < nr_counters-1; counter++)  		weight *= sym->count[counter]; @@ -157,7 +405,7 @@ static void rb_insert_active_sym(struct rb_root *tree, struct sym_entry *se)  static void print_sym_table(void)  {  	int printed = 0, j; -	int counter; +	int counter, snap = !display_weighted ? sym_counter : 0;  	float samples_per_sec = samples/delay_secs;  	float ksamples_per_sec = (samples-userspace_samples)/delay_secs;  	float sum_ksamples = 0.0; @@ -173,7 +421,7 @@ static void print_sym_table(void)  	pthread_mutex_unlock(&active_symbols_lock);  	list_for_each_entry_safe_from(syme, n, &active_symbols, node) { -		syme->snap_count = syme->count[0]; +		syme->snap_count = syme->count[snap];  		if (syme->snap_count != 0) {  			syme->weight = sym_weight(syme);  			rb_insert_active_sym(&tmp, syme); @@ -193,7 +441,7 @@ static void print_sym_table(void)  		samples_per_sec,  		100.0 - (100.0*((samples_per_sec-ksamples_per_sec)/samples_per_sec))); -	if (nr_counters == 1) { +	if (nr_counters == 1 || !display_weighted) {  		printf("%Ld", (u64)attrs[0].sample_period);  		if (freq)  			printf("Hz "); @@ -201,7 +449,9 @@ static void print_sym_table(void)  			printf(" ");  	} -	for (counter = 0; counter < nr_counters; counter++) { +	if (!display_weighted) +		printf("%s", event_name(sym_counter)); +	else for (counter = 0; counter < nr_counters; counter++) {  		if (counter)  			printf("/"); @@ -226,6 +476,11 @@ static void print_sym_table(void)  	printf("------------------------------------------------------------------------------\n\n"); +	if (sym_filter_entry) { +		show_details(sym_filter_entry); +		return; +	} +  	if (nr_counters == 1)  		printf("             samples    pcnt");  	else @@ -238,59 +493,300 @@ static void print_sym_table(void)  	for (nd = rb_first(&tmp); nd; nd = rb_next(nd)) {  		struct sym_entry *syme = rb_entry(nd, struct sym_entry, rb_node);  		struct symbol *sym = (struct symbol *)(syme + 1); -		char *color = PERF_COLOR_NORMAL;  		double pcnt; -		if (++printed > print_entries || syme->snap_count < count_filter) +		if (++printed > print_entries || (int)syme->snap_count < count_filter)  			continue;  		pcnt = 100.0 - (100.0 * ((sum_ksamples - syme->snap_count) /  					 sum_ksamples)); -		/* -		 * We color high-overhead entries in red, mid-overhead -		 * entries in green - and keep the low overhead places -		 * normal: -		 */ -		if (pcnt >= 5.0) { -			color = PERF_COLOR_RED; -		} else { -			if (pcnt >= 0.5) -				color = PERF_COLOR_GREEN; -		} - -		if (nr_counters == 1) +		if (nr_counters == 1 || !display_weighted)  			printf("%20.2f - ", syme->weight);  		else  			printf("%9.1f %10ld - ", syme->weight, syme->snap_count); -		color_fprintf(stdout, color, "%4.1f%%", pcnt); -		printf(" - %016llx : %s\n", sym->start, sym->name); +		percent_color_fprintf(stdout, "%4.1f%%", pcnt); +		printf(" - %016llx : %s", sym->start, sym->name); +		if (sym->module) +			printf("\t[%s]", sym->module->name); +		printf("\n"); +	} +} + +static void prompt_integer(int *target, const char *msg) +{ +	char *buf = malloc(0), *p; +	size_t dummy = 0; +	int tmp; + +	fprintf(stdout, "\n%s: ", msg); +	if (getline(&buf, &dummy, stdin) < 0) +		return; + +	p = strchr(buf, '\n'); +	if (p) +		*p = 0; + +	p = buf; +	while(*p) { +		if (!isdigit(*p)) +			goto out_free; +		p++; +	} +	tmp = strtoul(buf, NULL, 10); +	*target = tmp; +out_free: +	free(buf); +} + +static void prompt_percent(int *target, const char *msg) +{ +	int tmp = 0; + +	prompt_integer(&tmp, msg); +	if (tmp >= 0 && tmp <= 100) +		*target = tmp; +} + +static void prompt_symbol(struct sym_entry **target, const char *msg) +{ +	char *buf = malloc(0), *p; +	struct sym_entry *syme = *target, *n, *found = NULL; +	size_t dummy = 0; + +	/* zero counters of active symbol */ +	if (syme) { +		pthread_mutex_lock(&syme->source_lock); +		__zero_source_counters(syme); +		*target = NULL; +		pthread_mutex_unlock(&syme->source_lock); +	} + +	fprintf(stdout, "\n%s: ", msg); +	if (getline(&buf, &dummy, stdin) < 0) +		goto out_free; + +	p = strchr(buf, '\n'); +	if (p) +		*p = 0; + +	pthread_mutex_lock(&active_symbols_lock); +	syme = list_entry(active_symbols.next, struct sym_entry, node); +	pthread_mutex_unlock(&active_symbols_lock); + +	list_for_each_entry_safe_from(syme, n, &active_symbols, node) { +		struct symbol *sym = (struct symbol *)(syme + 1); + +		if (!strcmp(buf, sym->name)) { +			found = syme; +			break; +		} +	} + +	if (!found) { +		fprintf(stderr, "Sorry, %s is not active.\n", sym_filter); +		sleep(1); +		return; +	} else +		parse_source(found); + +out_free: +	free(buf); +} + +static void print_mapped_keys(void) +{ +	char *name = NULL; + +	if (sym_filter_entry) { +		struct symbol *sym = (struct symbol *)(sym_filter_entry+1); +		name = sym->name; +	} + +	fprintf(stdout, "\nMapped keys:\n"); +	fprintf(stdout, "\t[d]     display refresh delay.             \t(%d)\n", delay_secs); +	fprintf(stdout, "\t[e]     display entries (lines).           \t(%d)\n", print_entries); + +	if (nr_counters > 1) +		fprintf(stdout, "\t[E]     active event counter.              \t(%s)\n", event_name(sym_counter)); + +	fprintf(stdout, "\t[f]     profile display filter (count).    \t(%d)\n", count_filter); + +	if (vmlinux) { +		fprintf(stdout, "\t[F]     annotate display filter (percent). \t(%d%%)\n", sym_pcnt_filter); +		fprintf(stdout, "\t[s]     annotate symbol.                   \t(%s)\n", name?: "NULL"); +		fprintf(stdout, "\t[S]     stop annotation.\n"); +	} + +	if (nr_counters > 1) +		fprintf(stdout, "\t[w]     toggle display weighted/count[E]r. \t(%d)\n", display_weighted ? 1 : 0); + +	fprintf(stdout, "\t[z]     toggle sample zeroing.             \t(%d)\n", zero ? 1 : 0); +	fprintf(stdout, "\t[qQ]    quit.\n"); +} + +static int key_mapped(int c) +{ +	switch (c) { +		case 'd': +		case 'e': +		case 'f': +		case 'z': +		case 'q': +		case 'Q': +			return 1; +		case 'E': +		case 'w': +			return nr_counters > 1 ? 1 : 0; +		case 'F': +		case 's': +		case 'S': +			return vmlinux ? 1 : 0; +	} + +	return 0; +} + +static void handle_keypress(int c) +{ +	if (!key_mapped(c)) { +		struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; +		struct termios tc, save; + +		print_mapped_keys(); +		fprintf(stdout, "\nEnter selection, or unmapped key to continue: "); +		fflush(stdout); + +		tcgetattr(0, &save); +		tc = save; +		tc.c_lflag &= ~(ICANON | ECHO); +		tc.c_cc[VMIN] = 0; +		tc.c_cc[VTIME] = 0; +		tcsetattr(0, TCSANOW, &tc); + +		poll(&stdin_poll, 1, -1); +		c = getc(stdin); + +		tcsetattr(0, TCSAFLUSH, &save); +		if (!key_mapped(c)) +			return; +	} + +	switch (c) { +		case 'd': +			prompt_integer(&delay_secs, "Enter display delay"); +			break; +		case 'e': +			prompt_integer(&print_entries, "Enter display entries (lines)"); +			break; +		case 'E': +			if (nr_counters > 1) { +				int i; + +				fprintf(stderr, "\nAvailable events:"); +				for (i = 0; i < nr_counters; i++) +					fprintf(stderr, "\n\t%d %s", i, event_name(i)); + +				prompt_integer(&sym_counter, "Enter details event counter"); + +				if (sym_counter >= nr_counters) { +					fprintf(stderr, "Sorry, no such event, using %s.\n", event_name(0)); +					sym_counter = 0; +					sleep(1); +				} +			} else sym_counter = 0; +			break; +		case 'f': +			prompt_integer(&count_filter, "Enter display event count filter"); +			break; +		case 'F': +			prompt_percent(&sym_pcnt_filter, "Enter details display event filter (percent)"); +			break; +		case 'q': +		case 'Q': +			printf("exiting.\n"); +			exit(0); +		case 's': +			prompt_symbol(&sym_filter_entry, "Enter details symbol"); +			break; +		case 'S': +			if (!sym_filter_entry) +				break; +			else { +				struct sym_entry *syme = sym_filter_entry; + +				pthread_mutex_lock(&syme->source_lock); +				sym_filter_entry = NULL; +				__zero_source_counters(syme); +				pthread_mutex_unlock(&syme->source_lock); +			} +			break; +		case 'w': +			display_weighted = ~display_weighted; +			break; +		case 'z': +			zero = ~zero; +			break;  	}  } -static void *display_thread(void *arg) +static void *display_thread(void *arg __used)  {  	struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; -	int delay_msecs = delay_secs * 1000; +	struct termios tc, save; +	int delay_msecs, c; + +	tcgetattr(0, &save); +	tc = save; +	tc.c_lflag &= ~(ICANON | ECHO); +	tc.c_cc[VMIN] = 0; +	tc.c_cc[VTIME] = 0; -	printf("PerfTop refresh period: %d seconds\n", delay_secs); +repeat: +	delay_msecs = delay_secs * 1000; +	tcsetattr(0, TCSANOW, &tc); +	/* trash return*/ +	getc(stdin);  	do {  		print_sym_table();  	} while (!poll(&stdin_poll, 1, delay_msecs) == 1); -	printf("key pressed - exiting.\n"); -	exit(0); +	c = getc(stdin); +	tcsetattr(0, TCSAFLUSH, &save); + +	handle_keypress(c); +	goto repeat;  	return NULL;  } +/* Tag samples to be skipped. */ +static const char *skip_symbols[] = { +	"default_idle", +	"cpu_idle", +	"enter_idle", +	"exit_idle", +	"mwait_idle", +	"mwait_idle_with_hints", +	"ppc64_runlatch_off", +	"pseries_dedicated_idle_sleep", +	NULL +}; +  static int symbol_filter(struct dso *self, struct symbol *sym)  { -	static int filter_match;  	struct sym_entry *syme;  	const char *name = sym->name; +	int i; + +	/* +	 * ppc64 uses function descriptors and appends a '.' to the +	 * start of every instruction address. Remove it. +	 */ +	if (name[0] == '.') +		name++;  	if (!strcmp(name, "_text") ||  	    !strcmp(name, "_etext") || @@ -302,37 +798,17 @@ static int symbol_filter(struct dso *self, struct symbol *sym)  		return 1;  	syme = dso__sym_priv(self, sym); -	/* Tag samples to be skipped. */ -	if (!strcmp("default_idle", name) || -	    !strcmp("cpu_idle", name) || -	    !strcmp("enter_idle", name) || -	    !strcmp("exit_idle", name) || -	    !strcmp("mwait_idle", name)) -		syme->skip = 1; +	pthread_mutex_init(&syme->source_lock, NULL); +	if (!sym_filter_entry && sym_filter && !strcmp(name, sym_filter)) +		sym_filter_entry = syme; -	if (filter_match == 1) { -		filter_end = sym->start; -		filter_match = -1; -		if (filter_end - filter_start > 10000) { -			fprintf(stderr, -				"hm, too large filter symbol <%s> - skipping.\n", -				sym_filter); -			fprintf(stderr, "symbol filter start: %016lx\n", -				filter_start); -			fprintf(stderr, "                end: %016lx\n", -				filter_end); -			filter_end = filter_start = 0; -			sym_filter = NULL; -			sleep(1); +	for (i = 0; skip_symbols[i]; i++) { +		if (!strcmp(skip_symbols[i], name)) { +			syme->skip = 1; +			break;  		}  	} -	if (filter_match == 0 && sym_filter && !strcmp(name, sym_filter)) { -		filter_match = 1; -		filter_start = sym->start; -	} - -  	return 0;  } @@ -340,12 +816,13 @@ static int parse_symbols(void)  {  	struct rb_node *node;  	struct symbol  *sym; +	int modules = vmlinux ? 1 : 0;  	kernel_dso = dso__new("[kernel]", sizeof(struct sym_entry));  	if (kernel_dso == NULL)  		return -1; -	if (dso__load_kernel(kernel_dso, NULL, symbol_filter, 1) != 0) +	if (dso__load_kernel(kernel_dso, vmlinux, symbol_filter, verbose, modules) <= 0)  		goto out_delete_dso;  	node = rb_first(&kernel_dso->syms); @@ -367,8 +844,6 @@ out_delete_dso:  	return -1;  } -#define TRACE_COUNT     3 -  /*   * Binary search in the histogram table and record the hit:   */ @@ -381,6 +856,7 @@ static void record_ip(u64 ip, int counter)  		if (!syme->skip) {  			syme->count[counter]++; +			record_precise_ip(syme, counter, ip);  			pthread_mutex_lock(&active_symbols_lock);  			if (list_empty(&syme->node) || !syme->node.next)  				__list_insert_active_sym(syme); @@ -407,7 +883,7 @@ static void process_event(u64 ip, int counter, int user)  struct mmap_data {  	int			counter;  	void			*base; -	unsigned int		mask; +	int			mask;  	unsigned int		prev;  }; @@ -538,7 +1014,7 @@ int group_fd;  static void start_counter(int i, int counter)  {  	struct perf_counter_attr *attr; -	unsigned int cpu; +	int cpu;  	cpu = profile_cpu;  	if (target_pid == -1 && profile_cpu == -1) @@ -548,6 +1024,7 @@ static void start_counter(int i, int counter)  	attr->sample_type	= PERF_SAMPLE_IP | PERF_SAMPLE_TID;  	attr->freq		= freq; +	attr->inherit		= (cpu < 0) && inherit;  try_again:  	fd[i][counter] = sys_perf_counter_open(attr, target_pid, cpu, group_fd, 0); @@ -661,6 +1138,7 @@ static const struct option options[] = {  			    "system-wide collection from all CPUs"),  	OPT_INTEGER('C', "CPU", &profile_cpu,  		    "CPU to profile on"), +	OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"),  	OPT_INTEGER('m', "mmap-pages", &mmap_pages,  		    "number of mmap data pages"),  	OPT_INTEGER('r', "realtime", &realtime_prio, @@ -673,9 +1151,11 @@ static const struct option options[] = {  		    "only display functions with more events than this"),  	OPT_BOOLEAN('g', "group", &group,  			    "put the counters into a counter group"), -	OPT_STRING('s', "sym-filter", &sym_filter, "pattern", -		    "only display symbols matchig this pattern"), -	OPT_BOOLEAN('z', "zero", &group, +	OPT_BOOLEAN('i', "inherit", &inherit, +		    "child tasks inherit counters"), +	OPT_STRING('s', "sym-annotate", &sym_filter, "symbol name", +		    "symbol to annotate - requires -k option"), +	OPT_BOOLEAN('z', "zero", &zero,  		    "zero history across updates"),  	OPT_INTEGER('F', "freq", &freq,  		    "profile at this frequency"), @@ -686,10 +1166,12 @@ static const struct option options[] = {  	OPT_END()  }; -int cmd_top(int argc, const char **argv, const char *prefix) +int cmd_top(int argc, const char **argv, const char *prefix __used)  {  	int counter; +	symbol__init(); +  	page_size = sysconf(_SC_PAGE_SIZE);  	argc = parse_options(argc, argv, options, top_usage, 0); @@ -715,6 +1197,7 @@ int cmd_top(int argc, const char **argv, const char *prefix)  		delay_secs = 1;  	parse_symbols(); +	parse_source(sym_filter_entry);  	/*  	 * Fill in the ones not specifically initialized via -c: diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 4eb72593370..31982ad064b 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -12,6 +12,8 @@  #include "util/cache.h"  #include "util/quote.h"  #include "util/run-command.h" +#include "util/parse-events.h" +#include "util/string.h"  const char perf_usage_string[] =  	"perf [--version] [--help] COMMAND [ARGS]"; @@ -25,6 +27,8 @@ struct pager_config {  	int val;  }; +static char debugfs_mntpt[MAXPATHLEN]; +  static int pager_command_config(const char *var, const char *value, void *data)  {  	struct pager_config *c = data; @@ -56,6 +60,15 @@ static void commit_pager_choice(void) {  	}  } +static void set_debugfs_path(void) +{ +	char *path; + +	path = getenv(PERF_DEBUGFS_ENVIRONMENT); +	snprintf(debugfs_path, MAXPATHLEN, "%s/%s", path ?: debugfs_mntpt, +		 "tracing/events"); +} +  static int handle_options(const char*** argv, int* argc, int* envchanged)  {  	int handled = 0; @@ -122,6 +135,22 @@ static int handle_options(const char*** argv, int* argc, int* envchanged)  			setenv(PERF_WORK_TREE_ENVIRONMENT, cmd + 12, 1);  			if (envchanged)  				*envchanged = 1; +		} else if (!strcmp(cmd, "--debugfs-dir")) { +			if (*argc < 2) { +				fprintf(stderr, "No directory given for --debugfs-dir.\n"); +				usage(perf_usage_string); +			} +			strncpy(debugfs_mntpt, (*argv)[1], MAXPATHLEN); +			debugfs_mntpt[MAXPATHLEN - 1] = '\0'; +			if (envchanged) +				*envchanged = 1; +			(*argv)++; +			(*argc)--; +		} else if (!prefixcmp(cmd, "--debugfs-dir=")) { +			strncpy(debugfs_mntpt, cmd + 14, MAXPATHLEN); +			debugfs_mntpt[MAXPATHLEN - 1] = '\0'; +			if (envchanged) +				*envchanged = 1;  		} else {  			fprintf(stderr, "Unknown option: %s\n", cmd);  			usage(perf_usage_string); @@ -228,9 +257,7 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)  	if (use_pager == -1 && p->option & USE_PAGER)  		use_pager = 1;  	commit_pager_choice(); - -	if (p->option & NEED_WORK_TREE) -		/* setup_work_tree() */; +	set_debugfs_path();  	status = p->fn(argc, argv, prefix);  	if (status) @@ -266,7 +293,7 @@ static void handle_internal_command(int argc, const char **argv)  		{ "annotate", cmd_annotate, 0 },  		{ "version", cmd_version, 0 },  	}; -	int i; +	unsigned int i;  	static const char ext[] = STRIP_EXTENSION;  	if (sizeof(ext) > 1) { @@ -349,6 +376,49 @@ static int run_argv(int *argcp, const char ***argv)  	return done_alias;  } +/* mini /proc/mounts parser: searching for "^blah /mount/point debugfs" */ +static void get_debugfs_mntpt(void) +{ +	FILE *file; +	char fs_type[100]; +	char debugfs[MAXPATHLEN]; + +	/* +	 * try the standard location +	 */ +	if (valid_debugfs_mount("/sys/kernel/debug/") == 0) { +		strcpy(debugfs_mntpt, "/sys/kernel/debug/"); +		return; +	} + +	/* +	 * try the sane location +	 */ +	if (valid_debugfs_mount("/debug/") == 0) { +		strcpy(debugfs_mntpt, "/debug/"); +		return; +	} + +	/* +	 * give up and parse /proc/mounts +	 */ +	file = fopen("/proc/mounts", "r"); +	if (file == NULL) +		return; + +	while (fscanf(file, "%*s %" +		      STR(MAXPATHLEN) +		      "s %99s %*s %*d %*d\n", +		      debugfs, fs_type) == 2) { +		if (strcmp(fs_type, "debugfs") == 0) +			break; +	} +	fclose(file); +	if (strcmp(fs_type, "debugfs") == 0) { +		strncpy(debugfs_mntpt, debugfs, MAXPATHLEN); +		debugfs_mntpt[MAXPATHLEN - 1] = '\0'; +	} +}  int main(int argc, const char **argv)  { @@ -357,7 +427,8 @@ int main(int argc, const char **argv)  	cmd = perf_extract_argv0_path(argv[0]);  	if (!cmd)  		cmd = "perf-help"; - +	/* get debugfs mount point from /proc/mounts */ +	get_debugfs_mntpt();  	/*  	 * "perf-xxxx" is the same as "perf xxxx", but we obviously:  	 * @@ -380,6 +451,7 @@ int main(int argc, const char **argv)  	argc--;  	handle_options(&argv, &argc, NULL);  	commit_pager_choice(); +	set_debugfs_path();  	if (argc > 0) {  		if (!prefixcmp(argv[0], "--"))  			argv[0] += 2; diff --git a/tools/perf/perf.h b/tools/perf/perf.h index 8f729aedc1a..e5148e2b613 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h @@ -1,7 +1,13 @@  #ifndef _PERF_PERF_H  #define _PERF_PERF_H -#if defined(__x86_64__) || defined(__i386__) +#if defined(__i386__) +#include "../../arch/x86/include/asm/unistd.h" +#define rmb()		asm volatile("lock; addl $0,0(%%esp)" ::: "memory") +#define cpu_relax()	asm volatile("rep; nop" ::: "memory"); +#endif + +#if defined(__x86_64__)  #include "../../arch/x86/include/asm/unistd.h"  #define rmb()		asm volatile("lfence" ::: "memory")  #define cpu_relax()	asm volatile("rep; nop" ::: "memory"); @@ -29,6 +35,12 @@  #define cpu_relax()	asm volatile("" ::: "memory")  #endif +#ifdef __hppa__ +#include "../../arch/parisc/include/asm/unistd.h" +#define rmb()		asm volatile("" ::: "memory") +#define cpu_relax()	asm volatile("" ::: "memory"); +#endif +  #include <time.h>  #include <unistd.h>  #include <sys/types.h> @@ -62,6 +74,8 @@ static inline unsigned long long rdclock(void)  #define __user  #define asmlinkage +#define __used		__attribute__((__unused__)) +  #define unlikely(x)	__builtin_expect(!!(x), 0)  #define min(x, y) ({				\  	typeof(x) _min1 = (x);			\ diff --git a/tools/perf/util/alias.c b/tools/perf/util/alias.c index 9b3dd2b428d..b8144e80bb1 100644 --- a/tools/perf/util/alias.c +++ b/tools/perf/util/alias.c @@ -3,7 +3,7 @@  static const char *alias_key;  static char *alias_val; -static int alias_lookup_cb(const char *k, const char *v, void *cb) +static int alias_lookup_cb(const char *k, const char *v, void *cb __used)  {  	if (!prefixcmp(k, "alias.") && !strcmp(k+6, alias_key)) {  		if (!v) diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h index 393d6146d13..4b50c412b9c 100644 --- a/tools/perf/util/cache.h +++ b/tools/perf/util/cache.h @@ -3,6 +3,7 @@  #include "util.h"  #include "strbuf.h" +#include "../perf.h"  #define PERF_DIR_ENVIRONMENT "PERF_DIR"  #define PERF_WORK_TREE_ENVIRONMENT "PERF_WORK_TREE" @@ -17,6 +18,7 @@  #define PERFATTRIBUTES_FILE ".perfattributes"  #define INFOATTRIBUTES_FILE "info/attributes"  #define ATTRIBUTE_MACRO_PREFIX "[attr]" +#define PERF_DEBUGFS_ENVIRONMENT "PERF_DEBUGFS_DIR"  typedef int (*config_fn_t)(const char *, const char *, void *);  extern int perf_default_config(const char *, const char *, void *); diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index ad3c2857896..01147341164 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c @@ -4,52 +4,157 @@   * Handle the callchains from the stream in an ad-hoc radix tree and then   * sort them in an rbtree.   * + * Using a radix for code path provides a fast retrieval and factorizes + * memory use. Also that lets us use the paths in a hierarchical graph view. + *   */  #include <stdlib.h>  #include <stdio.h>  #include <stdbool.h>  #include <errno.h> +#include <math.h>  #include "callchain.h" +#define chain_for_each_child(child, parent)	\ +	list_for_each_entry(child, &parent->children, brothers) -static void rb_insert_callchain(struct rb_root *root, struct callchain_node *chain) +static void +rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, +		    enum chain_mode mode)  {  	struct rb_node **p = &root->rb_node;  	struct rb_node *parent = NULL;  	struct callchain_node *rnode; +	u64 chain_cumul = cumul_hits(chain);  	while (*p) { +		u64 rnode_cumul; +  		parent = *p;  		rnode = rb_entry(parent, struct callchain_node, rb_node); +		rnode_cumul = cumul_hits(rnode); -		if (rnode->hit < chain->hit) -			p = &(*p)->rb_left; -		else -			p = &(*p)->rb_right; +		switch (mode) { +		case CHAIN_FLAT: +			if (rnode->hit < chain->hit) +				p = &(*p)->rb_left; +			else +				p = &(*p)->rb_right; +			break; +		case CHAIN_GRAPH_ABS: /* Falldown */ +		case CHAIN_GRAPH_REL: +			if (rnode_cumul < chain_cumul) +				p = &(*p)->rb_left; +			else +				p = &(*p)->rb_right; +			break; +		default: +			break; +		}  	}  	rb_link_node(&chain->rb_node, parent, p);  	rb_insert_color(&chain->rb_node, root);  } +static void +__sort_chain_flat(struct rb_root *rb_root, struct callchain_node *node, +		  u64 min_hit) +{ +	struct callchain_node *child; + +	chain_for_each_child(child, node) +		__sort_chain_flat(rb_root, child, min_hit); + +	if (node->hit && node->hit >= min_hit) +		rb_insert_callchain(rb_root, node, CHAIN_FLAT); +} +  /*   * Once we get every callchains from the stream, we can now   * sort them by hit   */ -void sort_chain_to_rbtree(struct rb_root *rb_root, struct callchain_node *node) +static void +sort_chain_flat(struct rb_root *rb_root, struct callchain_node *node, +		u64 min_hit, struct callchain_param *param __used) +{ +	__sort_chain_flat(rb_root, node, min_hit); +} + +static void __sort_chain_graph_abs(struct callchain_node *node, +				   u64 min_hit)  {  	struct callchain_node *child; -	list_for_each_entry(child, &node->children, brothers) -		sort_chain_to_rbtree(rb_root, child); +	node->rb_root = RB_ROOT; + +	chain_for_each_child(child, node) { +		__sort_chain_graph_abs(child, min_hit); +		if (cumul_hits(child) >= min_hit) +			rb_insert_callchain(&node->rb_root, child, +					    CHAIN_GRAPH_ABS); +	} +} + +static void +sort_chain_graph_abs(struct rb_root *rb_root, struct callchain_node *chain_root, +		     u64 min_hit, struct callchain_param *param __used) +{ +	__sort_chain_graph_abs(chain_root, min_hit); +	rb_root->rb_node = chain_root->rb_root.rb_node; +} + +static void __sort_chain_graph_rel(struct callchain_node *node, +				   double min_percent) +{ +	struct callchain_node *child; +	u64 min_hit; + +	node->rb_root = RB_ROOT; +	min_hit = ceil(node->children_hit * min_percent); + +	chain_for_each_child(child, node) { +		__sort_chain_graph_rel(child, min_percent); +		if (cumul_hits(child) >= min_hit) +			rb_insert_callchain(&node->rb_root, child, +					    CHAIN_GRAPH_REL); +	} +} + +static void +sort_chain_graph_rel(struct rb_root *rb_root, struct callchain_node *chain_root, +		     u64 min_hit __used, struct callchain_param *param) +{ +	__sort_chain_graph_rel(chain_root, param->min_percent / 100.0); +	rb_root->rb_node = chain_root->rb_root.rb_node; +} -	if (node->hit) -		rb_insert_callchain(rb_root, node); +int register_callchain_param(struct callchain_param *param) +{ +	switch (param->mode) { +	case CHAIN_GRAPH_ABS: +		param->sort = sort_chain_graph_abs; +		break; +	case CHAIN_GRAPH_REL: +		param->sort = sort_chain_graph_rel; +		break; +	case CHAIN_FLAT: +		param->sort = sort_chain_flat; +		break; +	default: +		return -1; +	} +	return 0;  } -static struct callchain_node *create_child(struct callchain_node *parent) +/* + * Create a child for a parent. If inherit_children, then the new child + * will become the new parent of it's parent children + */ +static struct callchain_node * +create_child(struct callchain_node *parent, bool inherit_children)  {  	struct callchain_node *new; @@ -61,91 +166,150 @@ static struct callchain_node *create_child(struct callchain_node *parent)  	new->parent = parent;  	INIT_LIST_HEAD(&new->children);  	INIT_LIST_HEAD(&new->val); + +	if (inherit_children) { +		struct callchain_node *next; + +		list_splice(&parent->children, &new->children); +		INIT_LIST_HEAD(&parent->children); + +		chain_for_each_child(next, new) +			next->parent = new; +	}  	list_add_tail(&new->brothers, &parent->children);  	return new;  } +/* + * Fill the node with callchain values + */  static void -fill_node(struct callchain_node *node, struct ip_callchain *chain, int start) +fill_node(struct callchain_node *node, struct ip_callchain *chain, +	  int start, struct symbol **syms)  { -	int i; +	unsigned int i;  	for (i = start; i < chain->nr; i++) {  		struct callchain_list *call; -		call = malloc(sizeof(*chain)); +		call = malloc(sizeof(*call));  		if (!call) {  			perror("not enough memory for the code path tree");  			return;  		}  		call->ip = chain->ips[i]; +		call->sym = syms[i];  		list_add_tail(&call->list, &node->val);  	} -	node->val_nr = i - start; +	node->val_nr = chain->nr - start; +	if (!node->val_nr) +		printf("Warning: empty node in callchain tree\n");  } -static void add_child(struct callchain_node *parent, struct ip_callchain *chain) +static void +add_child(struct callchain_node *parent, struct ip_callchain *chain, +	  int start, struct symbol **syms)  {  	struct callchain_node *new; -	new = create_child(parent); -	fill_node(new, chain, parent->val_nr); +	new = create_child(parent, false); +	fill_node(new, chain, start, syms); +	new->children_hit = 0;  	new->hit = 1;  } +/* + * Split the parent in two parts (a new child is created) and + * give a part of its callchain to the created child. + * Then create another child to host the given callchain of new branch + */  static void  split_add_child(struct callchain_node *parent, struct ip_callchain *chain, -		struct callchain_list *to_split, int idx) +		struct callchain_list *to_split, int idx_parents, int idx_local, +		struct symbol **syms)  {  	struct callchain_node *new; +	struct list_head *old_tail; +	unsigned int idx_total = idx_parents + idx_local;  	/* split */ -	new = create_child(parent); -	list_move_tail(&to_split->list, &new->val); +	new = create_child(parent, true); + +	/* split the callchain and move a part to the new child */ +	old_tail = parent->val.prev; +	list_del_range(&to_split->list, old_tail); +	new->val.next = &to_split->list; +	new->val.prev = old_tail; +	to_split->list.prev = &new->val; +	old_tail->next = &new->val; + +	/* split the hits */  	new->hit = parent->hit; -	parent->hit = 0; -	parent->val_nr = idx; +	new->children_hit = parent->children_hit; +	parent->children_hit = cumul_hits(new); +	new->val_nr = parent->val_nr - idx_local; +	parent->val_nr = idx_local; -	/* create the new one */ -	add_child(parent, chain); +	/* create a new child for the new branch if any */ +	if (idx_total < chain->nr) { +		parent->hit = 0; +		add_child(parent, chain, idx_total, syms); +		parent->children_hit++; +	} else { +		parent->hit = 1; +	}  }  static int  __append_chain(struct callchain_node *root, struct ip_callchain *chain, -		int start); +	       unsigned int start, struct symbol **syms); -static int -__append_chain_children(struct callchain_node *root, struct ip_callchain *chain) +static void +__append_chain_children(struct callchain_node *root, struct ip_callchain *chain, +			struct symbol **syms, unsigned int start)  {  	struct callchain_node *rnode;  	/* lookup in childrens */ -	list_for_each_entry(rnode, &root->children, brothers) { -		int ret = __append_chain(rnode, chain, root->val_nr); +	chain_for_each_child(rnode, root) { +		unsigned int ret = __append_chain(rnode, chain, start, syms); +  		if (!ret) -			return 0; +			goto inc_children_hit;  	} -	return -1; +	/* nothing in children, add to the current node */ +	add_child(root, chain, start, syms); + +inc_children_hit: +	root->children_hit++;  }  static int  __append_chain(struct callchain_node *root, struct ip_callchain *chain, -		int start) +	       unsigned int start, struct symbol **syms)  {  	struct callchain_list *cnode; -	int i = start; +	unsigned int i = start;  	bool found = false; -	/* lookup in the current node */ +	/* +	 * Lookup in the current node +	 * If we have a symbol, then compare the start to match +	 * anywhere inside a function. +	 */  	list_for_each_entry(cnode, &root->val, list) { -		if (cnode->ip != chain->ips[i++]) +		if (i == chain->nr) +			break; +		if (cnode->sym && syms[i]) { +			if (cnode->sym->start != syms[i]->start) +				break; +		} else if (cnode->ip != chain->ips[i])  			break;  		if (!found)  			found = true; -		if (i == chain->nr) -			break; +		i++;  	}  	/* matches not, relay on the parent */ @@ -153,22 +317,27 @@ __append_chain(struct callchain_node *root, struct ip_callchain *chain,  		return -1;  	/* we match only a part of the node. Split it and add the new chain */ -	if (i < root->val_nr) { -		split_add_child(root, chain, cnode, i); +	if (i - start < root->val_nr) { +		split_add_child(root, chain, cnode, start, i - start, syms);  		return 0;  	}  	/* we match 100% of the path, increment the hit */ -	if (i == root->val_nr) { +	if (i - start == root->val_nr && i == chain->nr) {  		root->hit++;  		return 0;  	} -	return __append_chain_children(root, chain); +	/* We match the node and still have a part remaining */ +	__append_chain_children(root, chain, syms, i); + +	return 0;  } -void append_chain(struct callchain_node *root, struct ip_callchain *chain) +void append_chain(struct callchain_node *root, struct ip_callchain *chain, +		  struct symbol **syms)  { -	if (__append_chain_children(root, chain) == -1) -		add_child(root, chain); +	if (!chain->nr) +		return; +	__append_chain_children(root, chain, syms, 0);  } diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index fa1cd2f71fd..a926ae4f5a1 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h @@ -2,22 +2,43 @@  #define __PERF_CALLCHAIN_H  #include "../perf.h" -#include "list.h" -#include "rbtree.h" +#include <linux/list.h> +#include <linux/rbtree.h> +#include "symbol.h" +enum chain_mode { +	CHAIN_NONE, +	CHAIN_FLAT, +	CHAIN_GRAPH_ABS, +	CHAIN_GRAPH_REL +};  struct callchain_node {  	struct callchain_node	*parent;  	struct list_head	brothers; -	struct list_head 	children; -	struct list_head 	val; -	struct rb_node		rb_node; -	int			val_nr; -	int			hit; +	struct list_head	children; +	struct list_head	val; +	struct rb_node		rb_node; /* to sort nodes in an rbtree */ +	struct rb_root		rb_root; /* sorted tree of children */ +	unsigned int		val_nr; +	u64			hit; +	u64			children_hit; +}; + +struct callchain_param; + +typedef void (*sort_chain_func_t)(struct rb_root *, struct callchain_node *, +				 u64, struct callchain_param *); + +struct callchain_param { +	enum chain_mode 	mode; +	double			min_percent; +	sort_chain_func_t	sort;  };  struct callchain_list { -	unsigned long		ip; +	u64			ip; +	struct symbol		*sym;  	struct list_head	list;  }; @@ -28,6 +49,12 @@ static inline void callchain_init(struct callchain_node *node)  	INIT_LIST_HEAD(&node->val);  } -void append_chain(struct callchain_node *root, struct ip_callchain *chain); -void sort_chain_to_rbtree(struct rb_root *rb_root, struct callchain_node *node); +static inline u64 cumul_hits(struct callchain_node *node) +{ +	return node->hit + node->children_hit; +} + +int register_callchain_param(struct callchain_param *param); +void append_chain(struct callchain_node *root, struct ip_callchain *chain, +		  struct symbol **syms);  #endif diff --git a/tools/perf/util/color.c b/tools/perf/util/color.c index 9a8c20ccc53..90a044d1fe7 100644 --- a/tools/perf/util/color.c +++ b/tools/perf/util/color.c @@ -11,7 +11,8 @@ static int parse_color(const char *name, int len)  	};  	char *end;  	int i; -	for (i = 0; i < ARRAY_SIZE(color_names); i++) { + +	for (i = 0; i < (int)ARRAY_SIZE(color_names); i++) {  		const char *str = color_names[i];  		if (!strncasecmp(name, str, len) && !str[len])  			return i - 1; @@ -28,7 +29,8 @@ static int parse_attr(const char *name, int len)  	static const char * const attr_names[] = {  		"bold", "dim", "ul", "blink", "reverse"  	}; -	int i; +	unsigned int i; +  	for (i = 0; i < ARRAY_SIZE(attr_names); i++) {  		const char *str = attr_names[i];  		if (!strncasecmp(name, str, len) && !str[len]) @@ -222,10 +224,12 @@ int color_fwrite_lines(FILE *fp, const char *color,  {  	if (!*color)  		return fwrite(buf, count, 1, fp) != 1; +  	while (count) {  		char *p = memchr(buf, '\n', count); +  		if (p != buf && (fputs(color, fp) < 0 || -				fwrite(buf, p ? p - buf : count, 1, fp) != 1 || +				fwrite(buf, p ? (size_t)(p - buf) : count, 1, fp) != 1 ||  				fputs(PERF_COLOR_RESET, fp) < 0))  			return -1;  		if (!p) @@ -238,4 +242,31 @@ int color_fwrite_lines(FILE *fp, const char *color,  	return 0;  } +char *get_percent_color(double percent) +{ +	char *color = PERF_COLOR_NORMAL; +	/* +	 * We color high-overhead entries in red, mid-overhead +	 * entries in green - and keep the low overhead places +	 * normal: +	 */ +	if (percent >= MIN_RED) +		color = PERF_COLOR_RED; +	else { +		if (percent > MIN_GREEN) +			color = PERF_COLOR_GREEN; +	} +	return color; +} + +int percent_color_fprintf(FILE *fp, const char *fmt, double percent) +{ +	int r; +	char *color; + +	color = get_percent_color(percent); +	r = color_fprintf(fp, color, fmt, percent); + +	return r; +} diff --git a/tools/perf/util/color.h b/tools/perf/util/color.h index 5abfd379582..706cec50bd2 100644 --- a/tools/perf/util/color.h +++ b/tools/perf/util/color.h @@ -15,6 +15,9 @@  #define PERF_COLOR_CYAN		"\033[36m"  #define PERF_COLOR_BG_RED	"\033[41m" +#define MIN_GREEN	0.5 +#define MIN_RED		5.0 +  /*   * This variable stores the value of color.ui   */ @@ -32,5 +35,7 @@ void color_parse_mem(const char *value, int len, const char *var, char *dst);  int color_fprintf(FILE *fp, const char *color, const char *fmt, ...);  int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...);  int color_fwrite_lines(FILE *fp, const char *color, size_t count, const char *buf); +int percent_color_fprintf(FILE *fp, const char *fmt, double percent); +char *get_percent_color(double percent);  #endif /* COLOR_H */ diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c index 3dd13faa6a2..780df541006 100644 --- a/tools/perf/util/config.c +++ b/tools/perf/util/config.c @@ -47,10 +47,12 @@ static int get_next_char(void)  static char *parse_value(void)  {  	static char value[1024]; -	int quote = 0, comment = 0, len = 0, space = 0; +	int quote = 0, comment = 0, space = 0; +	size_t len = 0;  	for (;;) {  		int c = get_next_char(); +  		if (len >= sizeof(value) - 1)  			return NULL;  		if (c == '\n') { @@ -353,13 +355,13 @@ int perf_config_string(const char **dest, const char *var, const char *value)  	return 0;  } -static int perf_default_core_config(const char *var, const char *value) +static int perf_default_core_config(const char *var __used, const char *value __used)  {  	/* Add other config variables here and to Documentation/config.txt. */  	return 0;  } -int perf_default_config(const char *var, const char *value, void *dummy) +int perf_default_config(const char *var, const char *value, void *dummy __used)  {  	if (!prefixcmp(var, "core."))  		return perf_default_core_config(var, value); @@ -471,10 +473,10 @@ static int matches(const char* key, const char* value)  		  !regexec(store.value_regex, value, 0, NULL, 0)));  } -static int store_aux(const char* key, const char* value, void *cb) +static int store_aux(const char* key, const char* value, void *cb __used)  { +	int section_len;  	const char *ep; -	size_t section_len;  	switch (store.state) {  	case KEY_SEEN: @@ -551,7 +553,7 @@ static int store_write_section(int fd, const char* key)  		strbuf_addf(&sb, "[%.*s]\n", store.baselen, key);  	} -	success = write_in_full(fd, sb.buf, sb.len) == sb.len; +	success = (write_in_full(fd, sb.buf, sb.len) == (ssize_t)sb.len);  	strbuf_release(&sb);  	return success; @@ -599,7 +601,7 @@ static int store_write_pair(int fd, const char* key, const char* value)  		}  	strbuf_addf(&sb, "%s\n", quote); -	success = write_in_full(fd, sb.buf, sb.len) == sb.len; +	success = (write_in_full(fd, sb.buf, sb.len) == (ssize_t)sb.len);  	strbuf_release(&sb);  	return success; @@ -741,7 +743,7 @@ int perf_config_set_multivar(const char* key, const char* value,  	} else {  		struct stat st;  		char* contents; -		size_t contents_sz, copy_begin, copy_end; +		ssize_t contents_sz, copy_begin, copy_end;  		int i, new_line = 0;  		if (value_regex == NULL) diff --git a/tools/perf/util/exec_cmd.c b/tools/perf/util/exec_cmd.c index d3929226315..34a35286738 100644 --- a/tools/perf/util/exec_cmd.c +++ b/tools/perf/util/exec_cmd.c @@ -1,6 +1,9 @@  #include "cache.h"  #include "exec_cmd.h"  #include "quote.h" + +#include <string.h> +  #define MAX_ARGS	32  extern char **environ; @@ -51,7 +54,7 @@ const char *perf_extract_argv0_path(const char *argv0)  		slash--;  	if (slash >= argv0) { -		argv0_path = strndup(argv0, slash - argv0); +		argv0_path = xstrndup(argv0, slash - argv0);  		return slash + 1;  	} diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 450384b3bbe..b92a457ca32 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -185,6 +185,8 @@ static void do_read(int fd, void *buf, size_t size)  		if (ret < 0)  			die("failed to read"); +		if (ret == 0) +			die("failed to read: missing data");  		size -= ret;  		buf += ret; @@ -213,9 +215,10 @@ struct perf_header *perf_header__read(int fd)  	for (i = 0; i < nr_attrs; i++) {  		struct perf_header_attr *attr; -		off_t tmp = lseek(fd, 0, SEEK_CUR); +		off_t tmp;  		do_read(fd, &f_attr, sizeof(f_attr)); +		tmp = lseek(fd, 0, SEEK_CUR);  		attr = perf_header_attr__new(&f_attr.attr); diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index b5ef53ad4c7..bf280449fcf 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h @@ -16,7 +16,7 @@ struct perf_header {  	int frozen;  	int attrs, size;  	struct perf_header_attr **attr; -	off_t attr_offset; +	s64 attr_offset;  	u64 data_offset;  	u64 data_size;  }; diff --git a/tools/perf/util/help.c b/tools/perf/util/help.c index 17a00e0df2c..fbb00978b2e 100644 --- a/tools/perf/util/help.c +++ b/tools/perf/util/help.c @@ -26,7 +26,7 @@ static int term_columns(void)  	return 80;  } -void add_cmdname(struct cmdnames *cmds, const char *name, int len) +void add_cmdname(struct cmdnames *cmds, const char *name, size_t len)  {  	struct cmdname *ent = malloc(sizeof(*ent) + len + 1); @@ -40,7 +40,8 @@ void add_cmdname(struct cmdnames *cmds, const char *name, int len)  static void clean_cmdnames(struct cmdnames *cmds)  { -	int i; +	unsigned int i; +  	for (i = 0; i < cmds->cnt; ++i)  		free(cmds->names[i]);  	free(cmds->names); @@ -57,7 +58,7 @@ static int cmdname_compare(const void *a_, const void *b_)  static void uniq(struct cmdnames *cmds)  { -	int i, j; +	unsigned int i, j;  	if (!cmds->cnt)  		return; @@ -71,7 +72,7 @@ static void uniq(struct cmdnames *cmds)  void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes)  { -	int ci, cj, ei; +	size_t ci, cj, ei;  	int cmp;  	ci = cj = ei = 0; @@ -106,8 +107,9 @@ static void pretty_print_string_list(struct cmdnames *cmds, int longest)  		printf("  ");  		for (j = 0; j < cols; j++) { -			int n = j * rows + i; -			int size = space; +			unsigned int n = j * rows + i; +			unsigned int size = space; +  			if (n >= cmds->cnt)  				break;  			if (j == cols-1 || n + rows >= cmds->cnt) @@ -208,7 +210,7 @@ void load_command_list(const char *prefix,  void list_commands(const char *title, struct cmdnames *main_cmds,  		   struct cmdnames *other_cmds)  { -	int i, longest = 0; +	unsigned int i, longest = 0;  	for (i = 0; i < main_cmds->cnt; i++)  		if (longest < main_cmds->names[i]->len) @@ -239,7 +241,8 @@ void list_commands(const char *title, struct cmdnames *main_cmds,  int is_in_cmdlist(struct cmdnames *c, const char *s)  { -	int i; +	unsigned int i; +  	for (i = 0; i < c->cnt; i++)  		if (!strcmp(s, c->names[i]->name))  			return 1; @@ -271,7 +274,8 @@ static int levenshtein_compare(const void *p1, const void *p2)  static void add_cmd_list(struct cmdnames *cmds, struct cmdnames *old)  { -	int i; +	unsigned int i; +  	ALLOC_GROW(cmds->names, cmds->cnt + old->cnt, cmds->alloc);  	for (i = 0; i < old->cnt; i++) @@ -283,7 +287,7 @@ static void add_cmd_list(struct cmdnames *cmds, struct cmdnames *old)  const char *help_unknown_cmd(const char *cmd)  { -	int i, n = 0, best_similarity = 0; +	unsigned int i, n = 0, best_similarity = 0;  	struct cmdnames main_cmds, other_cmds;  	memset(&main_cmds, 0, sizeof(main_cmds)); @@ -345,7 +349,7 @@ const char *help_unknown_cmd(const char *cmd)  	exit(1);  } -int cmd_version(int argc, const char **argv, const char *prefix) +int cmd_version(int argc __used, const char **argv __used, const char *prefix __used)  {  	printf("perf version %s\n", perf_version_string);  	return 0; diff --git a/tools/perf/util/help.h b/tools/perf/util/help.h index 56bc15406ff..7128783637b 100644 --- a/tools/perf/util/help.h +++ b/tools/perf/util/help.h @@ -2,8 +2,8 @@  #define HELP_H  struct cmdnames { -	int alloc; -	int cnt; +	size_t alloc; +	size_t cnt;  	struct cmdname {  		size_t len; /* also used for similarity index in help.c */  		char name[FLEX_ARRAY]; @@ -19,7 +19,7 @@ static inline void mput_char(char c, unsigned int num)  void load_command_list(const char *prefix,  		struct cmdnames *main_cmds,  		struct cmdnames *other_cmds); -void add_cmdname(struct cmdnames *cmds, const char *name, int len); +void add_cmdname(struct cmdnames *cmds, const char *name, size_t len);  /* Here we require that excludes is a sorted list. */  void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes);  int is_in_cmdlist(struct cmdnames *c, const char *s); diff --git a/tools/perf/util/include/asm/system.h b/tools/perf/util/include/asm/system.h new file mode 100644 index 00000000000..710cecca972 --- /dev/null +++ b/tools/perf/util/include/asm/system.h @@ -0,0 +1 @@ +/* Empty */ diff --git a/tools/perf/util/include/linux/kernel.h b/tools/perf/util/include/linux/kernel.h new file mode 100644 index 00000000000..a6b87390cb5 --- /dev/null +++ b/tools/perf/util/include/linux/kernel.h @@ -0,0 +1,29 @@ +#ifndef PERF_LINUX_KERNEL_H_ +#define PERF_LINUX_KERNEL_H_ + +#ifndef offsetof +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#endif + +#ifndef container_of +/** + * container_of - cast a member of a structure out to the containing structure + * @ptr:	the pointer to the member. + * @type:	the type of the container struct this is embedded in. + * @member:	the name of the member within the struct. + * + */ +#define container_of(ptr, type, member) ({			\ +	const typeof(((type *)0)->member) * __mptr = (ptr);	\ +	(type *)((char *)__mptr - offsetof(type, member)); }) +#endif + +#ifndef max +#define max(x, y) ({				\ +	typeof(x) _max1 = (x);			\ +	typeof(y) _max2 = (y);			\ +	(void) (&_max1 == &_max2);		\ +	_max1 > _max2 ? _max1 : _max2; }) +#endif + +#endif diff --git a/tools/perf/util/include/linux/list.h b/tools/perf/util/include/linux/list.h new file mode 100644 index 00000000000..dbe4b814382 --- /dev/null +++ b/tools/perf/util/include/linux/list.h @@ -0,0 +1,18 @@ +#include "../../../../include/linux/list.h" + +#ifndef PERF_LIST_H +#define PERF_LIST_H +/** + * list_del_range - deletes range of entries from list. + * @begin: first element in the range to delete from the list. + * @end: last element in the range to delete from the list. + * Note: list_empty on the range of entries does not return true after this, + * the entries is in an undefined state. + */ +static inline void list_del_range(struct list_head *begin, +				  struct list_head *end) +{ +	begin->prev->next = end->next; +	end->next->prev = begin->prev; +} +#endif diff --git a/tools/perf/util/include/linux/module.h b/tools/perf/util/include/linux/module.h new file mode 100644 index 00000000000..b43e2dc21e0 --- /dev/null +++ b/tools/perf/util/include/linux/module.h @@ -0,0 +1,6 @@ +#ifndef PERF_LINUX_MODULE_H +#define PERF_LINUX_MODULE_H + +#define EXPORT_SYMBOL(name) + +#endif diff --git a/tools/perf/util/include/linux/poison.h b/tools/perf/util/include/linux/poison.h new file mode 100644 index 00000000000..fef6dbc9ce1 --- /dev/null +++ b/tools/perf/util/include/linux/poison.h @@ -0,0 +1 @@ +#include "../../../../include/linux/poison.h" diff --git a/tools/perf/util/include/linux/prefetch.h b/tools/perf/util/include/linux/prefetch.h new file mode 100644 index 00000000000..7841e485d8c --- /dev/null +++ b/tools/perf/util/include/linux/prefetch.h @@ -0,0 +1,6 @@ +#ifndef PERF_LINUX_PREFETCH_H +#define PERF_LINUX_PREFETCH_H + +static inline void prefetch(void *a __attribute__((unused))) { } + +#endif diff --git a/tools/perf/util/include/linux/rbtree.h b/tools/perf/util/include/linux/rbtree.h new file mode 100644 index 00000000000..7a243a14303 --- /dev/null +++ b/tools/perf/util/include/linux/rbtree.h @@ -0,0 +1 @@ +#include "../../../../include/linux/rbtree.h" diff --git a/tools/perf/util/list.h b/tools/perf/util/list.h deleted file mode 100644 index e2548e8072c..00000000000 --- a/tools/perf/util/list.h +++ /dev/null @@ -1,603 +0,0 @@ -#ifndef _LINUX_LIST_H -#define _LINUX_LIST_H -/* -  Copyright (C) Cast of dozens, comes from the Linux kernel - -  This program is free software; you can redistribute it and/or modify it -  under the terms of version 2 of the GNU General Public License as -  published by the Free Software Foundation. -*/ - -#include <stddef.h> - -/* - * These are non-NULL pointers that will result in page faults - * under normal circumstances, used to verify that nobody uses - * non-initialized list entries. - */ -#define LIST_POISON1 ((void *)0x00100100) -#define LIST_POISON2 ((void *)0x00200200) - -/** - * container_of - cast a member of a structure out to the containing structure - * @ptr:	the pointer to the member. - * @type:	the type of the container struct this is embedded in. - * @member:	the name of the member within the struct. - * - */ -#define container_of(ptr, type, member) ({			\ -        const typeof( ((type *)0)->member ) *__mptr = (ptr);	\ -        (type *)( (char *)__mptr - offsetof(type,member) );}) - -/* - * Simple doubly linked list implementation. - * - * Some of the internal functions ("__xxx") are useful when - * manipulating whole lists rather than single entries, as - * sometimes we already know the next/prev entries and we can - * generate better code by using them directly rather than - * using the generic single-entry routines. - */ - -struct list_head { -	struct list_head *next, *prev; -}; - -#define LIST_HEAD_INIT(name) { &(name), &(name) } - -#define LIST_HEAD(name) \ -	struct list_head name = LIST_HEAD_INIT(name) - -static inline void INIT_LIST_HEAD(struct list_head *list) -{ -	list->next = list; -	list->prev = list; -} - -/* - * Insert a new entry between two known consecutive entries. - * - * This is only for internal list manipulation where we know - * the prev/next entries already! - */ -static inline void __list_add(struct list_head *new, -			      struct list_head *prev, -			      struct list_head *next) -{ -	next->prev = new; -	new->next = next; -	new->prev = prev; -	prev->next = new; -} - -/** - * list_add - add a new entry - * @new: new entry to be added - * @head: list head to add it after - * - * Insert a new entry after the specified head. - * This is good for implementing stacks. - */ -static inline void list_add(struct list_head *new, struct list_head *head) -{ -	__list_add(new, head, head->next); -} - -/** - * list_add_tail - add a new entry - * @new: new entry to be added - * @head: list head to add it before - * - * Insert a new entry before the specified head. - * This is useful for implementing queues. - */ -static inline void list_add_tail(struct list_head *new, struct list_head *head) -{ -	__list_add(new, head->prev, head); -} - -/* - * Delete a list entry by making the prev/next entries - * point to each other. - * - * This is only for internal list manipulation where we know - * the prev/next entries already! - */ -static inline void __list_del(struct list_head * prev, struct list_head * next) -{ -	next->prev = prev; -	prev->next = next; -} - -/** - * list_del - deletes entry from list. - * @entry: the element to delete from the list. - * Note: list_empty on entry does not return true after this, the entry is - * in an undefined state. - */ -static inline void list_del(struct list_head *entry) -{ -	__list_del(entry->prev, entry->next); -	entry->next = LIST_POISON1; -	entry->prev = LIST_POISON2; -} - -/** - * list_del_range - deletes range of entries from list. - * @beging: first element in the range to delete from the list. - * @beging: first element in the range to delete from the list. - * Note: list_empty on the range of entries does not return true after this, - * the entries is in an undefined state. - */ -static inline void list_del_range(struct list_head *begin, -				  struct list_head *end) -{ -	begin->prev->next = end->next; -	end->next->prev = begin->prev; -} - -/** - * list_replace - replace old entry by new one - * @old : the element to be replaced - * @new : the new element to insert - * Note: if 'old' was empty, it will be overwritten. - */ -static inline void list_replace(struct list_head *old, -				struct list_head *new) -{ -	new->next = old->next; -	new->next->prev = new; -	new->prev = old->prev; -	new->prev->next = new; -} - -static inline void list_replace_init(struct list_head *old, -					struct list_head *new) -{ -	list_replace(old, new); -	INIT_LIST_HEAD(old); -} - -/** - * list_del_init - deletes entry from list and reinitialize it. - * @entry: the element to delete from the list. - */ -static inline void list_del_init(struct list_head *entry) -{ -	__list_del(entry->prev, entry->next); -	INIT_LIST_HEAD(entry); -} - -/** - * list_move - delete from one list and add as another's head - * @list: the entry to move - * @head: the head that will precede our entry - */ -static inline void list_move(struct list_head *list, struct list_head *head) -{ -        __list_del(list->prev, list->next); -        list_add(list, head); -} - -/** - * list_move_tail - delete from one list and add as another's tail - * @list: the entry to move - * @head: the head that will follow our entry - */ -static inline void list_move_tail(struct list_head *list, -				  struct list_head *head) -{ -        __list_del(list->prev, list->next); -        list_add_tail(list, head); -} - -/** - * list_is_last - tests whether @list is the last entry in list @head - * @list: the entry to test - * @head: the head of the list - */ -static inline int list_is_last(const struct list_head *list, -				const struct list_head *head) -{ -	return list->next == head; -} - -/** - * list_empty - tests whether a list is empty - * @head: the list to test. - */ -static inline int list_empty(const struct list_head *head) -{ -	return head->next == head; -} - -/** - * list_empty_careful - tests whether a list is empty and not being modified - * @head: the list to test - * - * Description: - * tests whether a list is empty _and_ checks that no other CPU might be - * in the process of modifying either member (next or prev) - * - * NOTE: using list_empty_careful() without synchronization - * can only be safe if the only activity that can happen - * to the list entry is list_del_init(). Eg. it cannot be used - * if another CPU could re-list_add() it. - */ -static inline int list_empty_careful(const struct list_head *head) -{ -	struct list_head *next = head->next; -	return (next == head) && (next == head->prev); -} - -static inline void __list_splice(struct list_head *list, -				 struct list_head *head) -{ -	struct list_head *first = list->next; -	struct list_head *last = list->prev; -	struct list_head *at = head->next; - -	first->prev = head; -	head->next = first; - -	last->next = at; -	at->prev = last; -} - -/** - * list_splice - join two lists - * @list: the new list to add. - * @head: the place to add it in the first list. - */ -static inline void list_splice(struct list_head *list, struct list_head *head) -{ -	if (!list_empty(list)) -		__list_splice(list, head); -} - -/** - * list_splice_init - join two lists and reinitialise the emptied list. - * @list: the new list to add. - * @head: the place to add it in the first list. - * - * The list at @list is reinitialised - */ -static inline void list_splice_init(struct list_head *list, -				    struct list_head *head) -{ -	if (!list_empty(list)) { -		__list_splice(list, head); -		INIT_LIST_HEAD(list); -	} -} - -/** - * list_entry - get the struct for this entry - * @ptr:	the &struct list_head pointer. - * @type:	the type of the struct this is embedded in. - * @member:	the name of the list_struct within the struct. - */ -#define list_entry(ptr, type, member) \ -	container_of(ptr, type, member) - -/** - * list_first_entry - get the first element from a list - * @ptr:       the list head to take the element from. - * @type:      the type of the struct this is embedded in. - * @member:    the name of the list_struct within the struct. - * - * Note, that list is expected to be not empty. - */ -#define list_first_entry(ptr, type, member) \ -	list_entry((ptr)->next, type, member) - -/** - * list_for_each	-	iterate over a list - * @pos:	the &struct list_head to use as a loop cursor. - * @head:	the head for your list. - */ -#define list_for_each(pos, head) \ -	for (pos = (head)->next; pos != (head); \ -        	pos = pos->next) - -/** - * __list_for_each	-	iterate over a list - * @pos:	the &struct list_head to use as a loop cursor. - * @head:	the head for your list. - * - * This variant differs from list_for_each() in that it's the - * simplest possible list iteration code, no prefetching is done. - * Use this for code that knows the list to be very short (empty - * or 1 entry) most of the time. - */ -#define __list_for_each(pos, head) \ -	for (pos = (head)->next; pos != (head); pos = pos->next) - -/** - * list_for_each_prev	-	iterate over a list backwards - * @pos:	the &struct list_head to use as a loop cursor. - * @head:	the head for your list. - */ -#define list_for_each_prev(pos, head) \ -	for (pos = (head)->prev; pos != (head); \ -        	pos = pos->prev) - -/** - * list_for_each_safe - iterate over a list safe against removal of list entry - * @pos:	the &struct list_head to use as a loop cursor. - * @n:		another &struct list_head to use as temporary storage - * @head:	the head for your list. - */ -#define list_for_each_safe(pos, n, head) \ -	for (pos = (head)->next, n = pos->next; pos != (head); \ -		pos = n, n = pos->next) - -/** - * list_for_each_entry	-	iterate over list of given type - * @pos:	the type * to use as a loop cursor. - * @head:	the head for your list. - * @member:	the name of the list_struct within the struct. - */ -#define list_for_each_entry(pos, head, member)				\ -	for (pos = list_entry((head)->next, typeof(*pos), member);	\ -	     &pos->member != (head); 	\ -	     pos = list_entry(pos->member.next, typeof(*pos), member)) - -/** - * list_for_each_entry_reverse - iterate backwards over list of given type. - * @pos:	the type * to use as a loop cursor. - * @head:	the head for your list. - * @member:	the name of the list_struct within the struct. - */ -#define list_for_each_entry_reverse(pos, head, member)			\ -	for (pos = list_entry((head)->prev, typeof(*pos), member);	\ -	     &pos->member != (head); 	\ -	     pos = list_entry(pos->member.prev, typeof(*pos), member)) - -/** - * list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue - * @pos:	the type * to use as a start point - * @head:	the head of the list - * @member:	the name of the list_struct within the struct. - * - * Prepares a pos entry for use as a start point in list_for_each_entry_continue. - */ -#define list_prepare_entry(pos, head, member) \ -	((pos) ? : list_entry(head, typeof(*pos), member)) - -/** - * list_for_each_entry_continue - continue iteration over list of given type - * @pos:	the type * to use as a loop cursor. - * @head:	the head for your list. - * @member:	the name of the list_struct within the struct. - * - * Continue to iterate over list of given type, continuing after - * the current position. - */ -#define list_for_each_entry_continue(pos, head, member) 		\ -	for (pos = list_entry(pos->member.next, typeof(*pos), member);	\ -	     &pos->member != (head);	\ -	     pos = list_entry(pos->member.next, typeof(*pos), member)) - -/** - * list_for_each_entry_from - iterate over list of given type from the current point - * @pos:	the type * to use as a loop cursor. - * @head:	the head for your list. - * @member:	the name of the list_struct within the struct. - * - * Iterate over list of given type, continuing from current position. - */ -#define list_for_each_entry_from(pos, head, member) 			\ -	for (; &pos->member != (head);	\ -	     pos = list_entry(pos->member.next, typeof(*pos), member)) - -/** - * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry - * @pos:	the type * to use as a loop cursor. - * @n:		another type * to use as temporary storage - * @head:	the head for your list. - * @member:	the name of the list_struct within the struct. - */ -#define list_for_each_entry_safe(pos, n, head, member)			\ -	for (pos = list_entry((head)->next, typeof(*pos), member),	\ -		n = list_entry(pos->member.next, typeof(*pos), member);	\ -	     &pos->member != (head); 					\ -	     pos = n, n = list_entry(n->member.next, typeof(*n), member)) - -/** - * list_for_each_entry_safe_continue - * @pos:	the type * to use as a loop cursor. - * @n:		another type * to use as temporary storage - * @head:	the head for your list. - * @member:	the name of the list_struct within the struct. - * - * Iterate over list of given type, continuing after current point, - * safe against removal of list entry. - */ -#define list_for_each_entry_safe_continue(pos, n, head, member) 		\ -	for (pos = list_entry(pos->member.next, typeof(*pos), member), 		\ -		n = list_entry(pos->member.next, typeof(*pos), member);		\ -	     &pos->member != (head);						\ -	     pos = n, n = list_entry(n->member.next, typeof(*n), member)) - -/** - * list_for_each_entry_safe_from - * @pos:	the type * to use as a loop cursor. - * @n:		another type * to use as temporary storage - * @head:	the head for your list. - * @member:	the name of the list_struct within the struct. - * - * Iterate over list of given type from current point, safe against - * removal of list entry. - */ -#define list_for_each_entry_safe_from(pos, n, head, member) 			\ -	for (n = list_entry(pos->member.next, typeof(*pos), member);		\ -	     &pos->member != (head);						\ -	     pos = n, n = list_entry(n->member.next, typeof(*n), member)) - -/** - * list_for_each_entry_safe_reverse - * @pos:	the type * to use as a loop cursor. - * @n:		another type * to use as temporary storage - * @head:	the head for your list. - * @member:	the name of the list_struct within the struct. - * - * Iterate backwards over list of given type, safe against removal - * of list entry. - */ -#define list_for_each_entry_safe_reverse(pos, n, head, member)		\ -	for (pos = list_entry((head)->prev, typeof(*pos), member),	\ -		n = list_entry(pos->member.prev, typeof(*pos), member);	\ -	     &pos->member != (head); 					\ -	     pos = n, n = list_entry(n->member.prev, typeof(*n), member)) - -/* - * Double linked lists with a single pointer list head. - * Mostly useful for hash tables where the two pointer list head is - * too wasteful. - * You lose the ability to access the tail in O(1). - */ - -struct hlist_head { -	struct hlist_node *first; -}; - -struct hlist_node { -	struct hlist_node *next, **pprev; -}; - -#define HLIST_HEAD_INIT { .first = NULL } -#define HLIST_HEAD(name) struct hlist_head name = {  .first = NULL } -#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL) -static inline void INIT_HLIST_NODE(struct hlist_node *h) -{ -	h->next = NULL; -	h->pprev = NULL; -} - -static inline int hlist_unhashed(const struct hlist_node *h) -{ -	return !h->pprev; -} - -static inline int hlist_empty(const struct hlist_head *h) -{ -	return !h->first; -} - -static inline void __hlist_del(struct hlist_node *n) -{ -	struct hlist_node *next = n->next; -	struct hlist_node **pprev = n->pprev; -	*pprev = next; -	if (next) -		next->pprev = pprev; -} - -static inline void hlist_del(struct hlist_node *n) -{ -	__hlist_del(n); -	n->next = LIST_POISON1; -	n->pprev = LIST_POISON2; -} - -static inline void hlist_del_init(struct hlist_node *n) -{ -	if (!hlist_unhashed(n)) { -		__hlist_del(n); -		INIT_HLIST_NODE(n); -	} -} - -static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h) -{ -	struct hlist_node *first = h->first; -	n->next = first; -	if (first) -		first->pprev = &n->next; -	h->first = n; -	n->pprev = &h->first; -} - -/* next must be != NULL */ -static inline void hlist_add_before(struct hlist_node *n, -					struct hlist_node *next) -{ -	n->pprev = next->pprev; -	n->next = next; -	next->pprev = &n->next; -	*(n->pprev) = n; -} - -static inline void hlist_add_after(struct hlist_node *n, -					struct hlist_node *next) -{ -	next->next = n->next; -	n->next = next; -	next->pprev = &n->next; - -	if(next->next) -		next->next->pprev  = &next->next; -} - -#define hlist_entry(ptr, type, member) container_of(ptr,type,member) - -#define hlist_for_each(pos, head) \ -	for (pos = (head)->first; pos; \ -	     pos = pos->next) - -#define hlist_for_each_safe(pos, n, head) \ -	for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \ -	     pos = n) - -/** - * hlist_for_each_entry	- iterate over list of given type - * @tpos:	the type * to use as a loop cursor. - * @pos:	the &struct hlist_node to use as a loop cursor. - * @head:	the head for your list. - * @member:	the name of the hlist_node within the struct. - */ -#define hlist_for_each_entry(tpos, pos, head, member)			 \ -	for (pos = (head)->first;					 \ -	     pos && 			 \ -		({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ -	     pos = pos->next) - -/** - * hlist_for_each_entry_continue - iterate over a hlist continuing after current point - * @tpos:	the type * to use as a loop cursor. - * @pos:	the &struct hlist_node to use as a loop cursor. - * @member:	the name of the hlist_node within the struct. - */ -#define hlist_for_each_entry_continue(tpos, pos, member)		 \ -	for (pos = (pos)->next;						 \ -	     pos && 			 \ -		({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ -	     pos = pos->next) - -/** - * hlist_for_each_entry_from - iterate over a hlist continuing from current point - * @tpos:	the type * to use as a loop cursor. - * @pos:	the &struct hlist_node to use as a loop cursor. - * @member:	the name of the hlist_node within the struct. - */ -#define hlist_for_each_entry_from(tpos, pos, member)			 \ -	for (; pos && 			 \ -		({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ -	     pos = pos->next) - -/** - * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry - * @tpos:	the type * to use as a loop cursor. - * @pos:	the &struct hlist_node to use as a loop cursor. - * @n:		another &struct hlist_node to use as temporary storage - * @head:	the head for your list. - * @member:	the name of the hlist_node within the struct. - */ -#define hlist_for_each_entry_safe(tpos, pos, n, head, member) 		 \ -	for (pos = (head)->first;					 \ -	     pos && ({ n = pos->next; 1; }) && 				 \ -		({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ -	     pos = n) - -#endif diff --git a/tools/perf/util/module.c b/tools/perf/util/module.c new file mode 100644 index 00000000000..ddabe925d65 --- /dev/null +++ b/tools/perf/util/module.c @@ -0,0 +1,509 @@ +#include "util.h" +#include "../perf.h" +#include "string.h" +#include "module.h" + +#include <libelf.h> +#include <gelf.h> +#include <elf.h> +#include <dirent.h> +#include <sys/utsname.h> + +static unsigned int crc32(const char *p, unsigned int len) +{ +	int i; +	unsigned int crc = 0; + +	while (len--) { +		crc ^= *p++; +		for (i = 0; i < 8; i++) +			crc = (crc >> 1) ^ ((crc & 1) ? 0xedb88320 : 0); +	} +	return crc; +} + +/* module section methods */ + +struct sec_dso *sec_dso__new_dso(const char *name) +{ +	struct sec_dso *self = malloc(sizeof(*self) + strlen(name) + 1); + +	if (self != NULL) { +		strcpy(self->name, name); +		self->secs = RB_ROOT; +		self->find_section = sec_dso__find_section; +	} + +	return self; +} + +static void sec_dso__delete_section(struct section *self) +{ +	free(((void *)self)); +} + +void sec_dso__delete_sections(struct sec_dso *self) +{ +	struct section *pos; +	struct rb_node *next = rb_first(&self->secs); + +	while (next) { +		pos = rb_entry(next, struct section, rb_node); +		next = rb_next(&pos->rb_node); +		rb_erase(&pos->rb_node, &self->secs); +		sec_dso__delete_section(pos); +	} +} + +void sec_dso__delete_self(struct sec_dso *self) +{ +	sec_dso__delete_sections(self); +	free(self); +} + +static void sec_dso__insert_section(struct sec_dso *self, struct section *sec) +{ +	struct rb_node **p = &self->secs.rb_node; +	struct rb_node *parent = NULL; +	const u64 hash = sec->hash; +	struct section *s; + +	while (*p != NULL) { +		parent = *p; +		s = rb_entry(parent, struct section, rb_node); +		if (hash < s->hash) +			p = &(*p)->rb_left; +		else +			p = &(*p)->rb_right; +	} +	rb_link_node(&sec->rb_node, parent, p); +	rb_insert_color(&sec->rb_node, &self->secs); +} + +struct section *sec_dso__find_section(struct sec_dso *self, const char *name) +{ +	struct rb_node *n; +	u64 hash; +	int len; + +	if (self == NULL) +		return NULL; + +	len = strlen(name); +	hash = crc32(name, len); + +	n = self->secs.rb_node; + +	while (n) { +		struct section *s = rb_entry(n, struct section, rb_node); + +		if (hash < s->hash) +			n = n->rb_left; +		else if (hash > s->hash) +			n = n->rb_right; +		else { +			if (!strcmp(name, s->name)) +				return s; +			else +				n = rb_next(&s->rb_node); +		} +	} + +	return NULL; +} + +static size_t sec_dso__fprintf_section(struct section *self, FILE *fp) +{ +	return fprintf(fp, "name:%s vma:%llx path:%s\n", +		       self->name, self->vma, self->path); +} + +size_t sec_dso__fprintf(struct sec_dso *self, FILE *fp) +{ +	size_t ret = fprintf(fp, "dso: %s\n", self->name); + +	struct rb_node *nd; +	for (nd = rb_first(&self->secs); nd; nd = rb_next(nd)) { +		struct section *pos = rb_entry(nd, struct section, rb_node); +		ret += sec_dso__fprintf_section(pos, fp); +	} + +	return ret; +} + +static struct section *section__new(const char *name, const char *path) +{ +	struct section *self = calloc(1, sizeof(*self)); + +	if (!self) +		goto out_failure; + +	self->name = calloc(1, strlen(name) + 1); +	if (!self->name) +		goto out_failure; + +	self->path = calloc(1, strlen(path) + 1); +	if (!self->path) +		goto out_failure; + +	strcpy(self->name, name); +	strcpy(self->path, path); +	self->hash = crc32(self->name, strlen(name)); + +	return self; + +out_failure: +	if (self) { +		if (self->name) +			free(self->name); +		if (self->path) +			free(self->path); +		free(self); +	} + +	return NULL; +} + +/* module methods */ + +struct mod_dso *mod_dso__new_dso(const char *name) +{ +	struct mod_dso *self = malloc(sizeof(*self) + strlen(name) + 1); + +	if (self != NULL) { +		strcpy(self->name, name); +		self->mods = RB_ROOT; +		self->find_module = mod_dso__find_module; +	} + +	return self; +} + +static void mod_dso__delete_module(struct module *self) +{ +	free(((void *)self)); +} + +void mod_dso__delete_modules(struct mod_dso *self) +{ +	struct module *pos; +	struct rb_node *next = rb_first(&self->mods); + +	while (next) { +		pos = rb_entry(next, struct module, rb_node); +		next = rb_next(&pos->rb_node); +		rb_erase(&pos->rb_node, &self->mods); +		mod_dso__delete_module(pos); +	} +} + +void mod_dso__delete_self(struct mod_dso *self) +{ +	mod_dso__delete_modules(self); +	free(self); +} + +static void mod_dso__insert_module(struct mod_dso *self, struct module *mod) +{ +	struct rb_node **p = &self->mods.rb_node; +	struct rb_node *parent = NULL; +	const u64 hash = mod->hash; +	struct module *m; + +	while (*p != NULL) { +		parent = *p; +		m = rb_entry(parent, struct module, rb_node); +		if (hash < m->hash) +			p = &(*p)->rb_left; +		else +			p = &(*p)->rb_right; +	} +	rb_link_node(&mod->rb_node, parent, p); +	rb_insert_color(&mod->rb_node, &self->mods); +} + +struct module *mod_dso__find_module(struct mod_dso *self, const char *name) +{ +	struct rb_node *n; +	u64 hash; +	int len; + +	if (self == NULL) +		return NULL; + +	len = strlen(name); +	hash = crc32(name, len); + +	n = self->mods.rb_node; + +	while (n) { +		struct module *m = rb_entry(n, struct module, rb_node); + +		if (hash < m->hash) +			n = n->rb_left; +		else if (hash > m->hash) +			n = n->rb_right; +		else { +			if (!strcmp(name, m->name)) +				return m; +			else +				n = rb_next(&m->rb_node); +		} +	} + +	return NULL; +} + +static size_t mod_dso__fprintf_module(struct module *self, FILE *fp) +{ +	return fprintf(fp, "name:%s path:%s\n", self->name, self->path); +} + +size_t mod_dso__fprintf(struct mod_dso *self, FILE *fp) +{ +	struct rb_node *nd; +	size_t ret; + +	ret = fprintf(fp, "dso: %s\n", self->name); + +	for (nd = rb_first(&self->mods); nd; nd = rb_next(nd)) { +		struct module *pos = rb_entry(nd, struct module, rb_node); + +		ret += mod_dso__fprintf_module(pos, fp); +	} + +	return ret; +} + +static struct module *module__new(const char *name, const char *path) +{ +	struct module *self = calloc(1, sizeof(*self)); + +	if (!self) +		goto out_failure; + +	self->name = calloc(1, strlen(name) + 1); +	if (!self->name) +		goto out_failure; + +	self->path = calloc(1, strlen(path) + 1); +	if (!self->path) +		goto out_failure; + +	strcpy(self->name, name); +	strcpy(self->path, path); +	self->hash = crc32(self->name, strlen(name)); + +	return self; + +out_failure: +	if (self) { +		if (self->name) +			free(self->name); +		if (self->path) +			free(self->path); +		free(self); +	} + +	return NULL; +} + +static int mod_dso__load_sections(struct module *mod) +{ +	int count = 0, path_len; +	struct dirent *entry; +	char *line = NULL; +	char *dir_path; +	DIR *dir; +	size_t n; + +	path_len = strlen("/sys/module/"); +	path_len += strlen(mod->name); +	path_len += strlen("/sections/"); + +	dir_path = calloc(1, path_len + 1); +	if (dir_path == NULL) +		goto out_failure; + +	strcat(dir_path, "/sys/module/"); +	strcat(dir_path, mod->name); +	strcat(dir_path, "/sections/"); + +	dir = opendir(dir_path); +	if (dir == NULL) +		goto out_free; + +	while ((entry = readdir(dir))) { +		struct section *section; +		char *path, *vma; +		int line_len; +		FILE *file; + +		if (!strcmp(".", entry->d_name) || !strcmp("..", entry->d_name)) +			continue; + +		path = calloc(1, path_len + strlen(entry->d_name) + 1); +		if (path == NULL) +			break; +		strcat(path, dir_path); +		strcat(path, entry->d_name); + +		file = fopen(path, "r"); +		if (file == NULL) { +			free(path); +			break; +		} + +		line_len = getline(&line, &n, file); +		if (line_len < 0) { +			free(path); +			fclose(file); +			break; +		} + +		if (!line) { +			free(path); +			fclose(file); +			break; +		} + +		line[--line_len] = '\0'; /* \n */ + +		vma = strstr(line, "0x"); +		if (!vma) { +			free(path); +			fclose(file); +			break; +		} +		vma += 2; + +		section = section__new(entry->d_name, path); +		if (!section) { +			fprintf(stderr, "load_sections: allocation error\n"); +			free(path); +			fclose(file); +			break; +		} + +		hex2u64(vma, §ion->vma); +		sec_dso__insert_section(mod->sections, section); + +		free(path); +		fclose(file); +		count++; +	} + +	closedir(dir); +	free(line); +	free(dir_path); + +	return count; + +out_free: +	free(dir_path); + +out_failure: +	return count; +} + +static int mod_dso__load_module_paths(struct mod_dso *self) +{ +	struct utsname uts; +	int count = 0, len; +	char *line = NULL; +	FILE *file; +	char *path; +	size_t n; + +	if (uname(&uts) < 0) +		goto out_failure; + +	len = strlen("/lib/modules/"); +	len += strlen(uts.release); +	len += strlen("/modules.dep"); + +	path = calloc(1, len); +	if (path == NULL) +		goto out_failure; + +	strcat(path, "/lib/modules/"); +	strcat(path, uts.release); +	strcat(path, "/modules.dep"); + +	file = fopen(path, "r"); +	free(path); +	if (file == NULL) +		goto out_failure; + +	while (!feof(file)) { +		char *path, *name, *tmp; +		struct module *module; +		int line_len, len; + +		line_len = getline(&line, &n, file); +		if (line_len < 0) +			break; + +		if (!line) +			goto out_failure; + +		line[--line_len] = '\0'; /* \n */ + +		path = strtok(line, ":"); +		if (!path) +			goto out_failure; + +		name = strdup(path); +		name = strtok(name, "/"); + +		tmp = name; + +		while (tmp) { +			tmp = strtok(NULL, "/"); +			if (tmp) +				name = tmp; +		} +		name = strsep(&name, "."); + +		/* Quirk: replace '-' with '_' in sound modules */ +		for (len = strlen(name); len; len--) { +			if (*(name+len) == '-') +				*(name+len) = '_'; +		} + +		module = module__new(name, path); +		if (!module) { +			fprintf(stderr, "load_module_paths: allocation error\n"); +			goto out_failure; +		} +		mod_dso__insert_module(self, module); + +		module->sections = sec_dso__new_dso("sections"); +		if (!module->sections) { +			fprintf(stderr, "load_module_paths: allocation error\n"); +			goto out_failure; +		} + +		module->active = mod_dso__load_sections(module); + +		if (module->active > 0) +			count++; +	} + +	free(line); +	fclose(file); + +	return count; + +out_failure: +	return -1; +} + +int mod_dso__load_modules(struct mod_dso *dso) +{ +	int err; + +	err = mod_dso__load_module_paths(dso); + +	return err; +} diff --git a/tools/perf/util/module.h b/tools/perf/util/module.h new file mode 100644 index 00000000000..8a592ef641c --- /dev/null +++ b/tools/perf/util/module.h @@ -0,0 +1,53 @@ +#ifndef _PERF_MODULE_ +#define _PERF_MODULE_ 1 + +#include <linux/types.h> +#include "../types.h" +#include <linux/list.h> +#include <linux/rbtree.h> + +struct section { +	struct rb_node	rb_node; +	u64		hash; +	u64		vma; +	char		*name; +	char		*path; +}; + +struct sec_dso { +	struct list_head node; +	struct rb_root	 secs; +	struct section    *(*find_section)(struct sec_dso *, const char *name); +	char		 name[0]; +}; + +struct module { +	struct rb_node	rb_node; +	u64		hash; +	char		*name; +	char		*path; +	struct sec_dso	*sections; +	int		active; +}; + +struct mod_dso { +	struct list_head node; +	struct rb_root	 mods; +	struct module    *(*find_module)(struct mod_dso *, const char *name); +	char		 name[0]; +}; + +struct sec_dso *sec_dso__new_dso(const char *name); +void sec_dso__delete_sections(struct sec_dso *self); +void sec_dso__delete_self(struct sec_dso *self); +size_t sec_dso__fprintf(struct sec_dso *self, FILE *fp); +struct section *sec_dso__find_section(struct sec_dso *self, const char *name); + +struct mod_dso *mod_dso__new_dso(const char *name); +void mod_dso__delete_modules(struct mod_dso *self); +void mod_dso__delete_self(struct mod_dso *self); +size_t mod_dso__fprintf(struct mod_dso *self, FILE *fp); +struct module *mod_dso__find_module(struct mod_dso *self, const char *name); +int mod_dso__load_modules(struct mod_dso *dso); + +#endif /* _PERF_MODULE_ */ diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 4d042f104cd..04417840878 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -5,6 +5,7 @@  #include "parse-events.h"  #include "exec_cmd.h"  #include "string.h" +#include "cache.h"  extern char *strcasestr(const char *haystack, const char *needle); @@ -19,6 +20,8 @@ struct event_symbol {  	char	*alias;  }; +char debugfs_path[MAXPATHLEN]; +  #define CHW(x) .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_##x  #define CSW(x) .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_##x @@ -71,8 +74,8 @@ static char *sw_event_names[] = {  #define MAX_ALIASES 8  static char *hw_cache[][MAX_ALIASES] = { - { "L1-d$",	"l1-d",		"l1d",		"L1-data",		}, - { "L1-i$",	"l1-i",		"l1i",		"L1-instruction",	}, + { "L1-dcache",	"l1-d",		"l1d",		"L1-data",		}, + { "L1-icache",	"l1-i",		"l1i",		"L1-instruction",	},   { "LLC",	"L2"							},   { "dTLB",	"d-tlb",	"Data-TLB",				},   { "iTLB",	"i-tlb",	"Instruction-TLB",			}, @@ -110,6 +113,104 @@ static unsigned long hw_cache_stat[C(MAX)] = {   [C(BPU)]	= (CACHE_READ),  }; +#define for_each_subsystem(sys_dir, sys_dirent, sys_next, file, st)	       \ +	while (!readdir_r(sys_dir, &sys_dirent, &sys_next) && sys_next)	       \ +	if (snprintf(file, MAXPATHLEN, "%s/%s", debugfs_path,	       	       \ +			sys_dirent.d_name) &&		       		       \ +	   (!stat(file, &st)) && (S_ISDIR(st.st_mode)) &&		       \ +	   (strcmp(sys_dirent.d_name, ".")) &&				       \ +	   (strcmp(sys_dirent.d_name, ".."))) + +static int tp_event_has_id(struct dirent *sys_dir, struct dirent *evt_dir) +{ +	char evt_path[MAXPATHLEN]; +	int fd; + +	snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", debugfs_path, +			sys_dir->d_name, evt_dir->d_name); +	fd = open(evt_path, O_RDONLY); +	if (fd < 0) +		return -EINVAL; +	close(fd); + +	return 0; +} + +#define for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next, file, st)    \ +	while (!readdir_r(evt_dir, &evt_dirent, &evt_next) && evt_next)        \ +	if (snprintf(file, MAXPATHLEN, "%s/%s/%s", debugfs_path,	       \ +		     sys_dirent.d_name, evt_dirent.d_name) &&		       \ +	   (!stat(file, &st)) && (S_ISDIR(st.st_mode)) &&		       \ +	   (strcmp(evt_dirent.d_name, ".")) &&				       \ +	   (strcmp(evt_dirent.d_name, "..")) &&				       \ +	   (!tp_event_has_id(&sys_dirent, &evt_dirent))) + +#define MAX_EVENT_LENGTH 30 + +int valid_debugfs_mount(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; +} + +static char *tracepoint_id_to_name(u64 config) +{ +	static char tracepoint_name[2 * MAX_EVENT_LENGTH]; +	DIR *sys_dir, *evt_dir; +	struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent; +	struct stat st; +	char id_buf[4]; +	int fd; +	u64 id; +	char evt_path[MAXPATHLEN]; + +	if (valid_debugfs_mount(debugfs_path)) +		return "unkown"; + +	sys_dir = opendir(debugfs_path); +	if (!sys_dir) +		goto cleanup; + +	for_each_subsystem(sys_dir, sys_dirent, sys_next, evt_path, st) { +		evt_dir = opendir(evt_path); +		if (!evt_dir) +			goto cleanup; +		for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next, +								evt_path, st) { +			snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", +				 debugfs_path, sys_dirent.d_name, +				 evt_dirent.d_name); +			fd = open(evt_path, O_RDONLY); +			if (fd < 0) +				continue; +			if (read(fd, id_buf, sizeof(id_buf)) < 0) { +				close(fd); +				continue; +			} +			close(fd); +			id = atoll(id_buf); +			if (id == config) { +				closedir(evt_dir); +				closedir(sys_dir); +				snprintf(tracepoint_name, 2 * MAX_EVENT_LENGTH, +					"%s:%s", sys_dirent.d_name, +					evt_dirent.d_name); +				return tracepoint_name; +			} +		} +		closedir(evt_dir); +	} + +cleanup: +	closedir(sys_dir); +	return "unkown"; +} +  static int is_cache_op_valid(u8 cache_type, u8 cache_op)  {  	if (hw_cache_stat[cache_type] & COP(cache_op)) @@ -138,9 +239,15 @@ char *event_name(int counter)  {  	u64 config = attrs[counter].config;  	int type = attrs[counter].type; + +	return __event_name(type, config); +} + +char *__event_name(int type, u64 config) +{  	static char buf[32]; -	if (attrs[counter].type == PERF_TYPE_RAW) { +	if (type == PERF_TYPE_RAW) {  		sprintf(buf, "raw 0x%llx", config);  		return buf;  	} @@ -177,6 +284,9 @@ char *event_name(int counter)  			return sw_event_names[config];  		return "unknown-software"; +	case PERF_TYPE_TRACEPOINT: +		return tracepoint_id_to_name(config); +  	default:  		break;  	} @@ -184,16 +294,20 @@ char *event_name(int counter)  	return "unknown";  } -static int parse_aliases(const char *str, char *names[][MAX_ALIASES], int size) +static int parse_aliases(const char **str, char *names[][MAX_ALIASES], int size)  {  	int i, j; +	int n, longest = -1;  	for (i = 0; i < size; i++) { -		for (j = 0; j < MAX_ALIASES; j++) { -			if (!names[i][j]) -				break; -			if (strcasestr(str, names[i][j])) -				return i; +		for (j = 0; j < MAX_ALIASES && names[i][j]; j++) { +			n = strlen(names[i][j]); +			if (n > longest && !strncasecmp(*str, names[i][j], n)) +				longest = n; +		} +		if (longest > 0) { +			*str += longest; +			return i;  		}  	} @@ -201,30 +315,53 @@ static int parse_aliases(const char *str, char *names[][MAX_ALIASES], int size)  }  static int -parse_generic_hw_symbols(const char *str, struct perf_counter_attr *attr) +parse_generic_hw_event(const char **str, struct perf_counter_attr *attr)  { -	int cache_type = -1, cache_op = 0, cache_result = 0; +	const char *s = *str; +	int cache_type = -1, cache_op = -1, cache_result = -1; -	cache_type = parse_aliases(str, hw_cache, PERF_COUNT_HW_CACHE_MAX); +	cache_type = parse_aliases(&s, hw_cache, PERF_COUNT_HW_CACHE_MAX);  	/*  	 * No fallback - if we cannot get a clear cache type  	 * then bail out:  	 */  	if (cache_type == -1) -		return -EINVAL; +		return 0; + +	while ((cache_op == -1 || cache_result == -1) && *s == '-') { +		++s; + +		if (cache_op == -1) { +			cache_op = parse_aliases(&s, hw_cache_op, +						PERF_COUNT_HW_CACHE_OP_MAX); +			if (cache_op >= 0) { +				if (!is_cache_op_valid(cache_type, cache_op)) +					return 0; +				continue; +			} +		} + +		if (cache_result == -1) { +			cache_result = parse_aliases(&s, hw_cache_result, +						PERF_COUNT_HW_CACHE_RESULT_MAX); +			if (cache_result >= 0) +				continue; +		} + +		/* +		 * Can't parse this as a cache op or result, so back up +		 * to the '-'. +		 */ +		--s; +		break; +	} -	cache_op = parse_aliases(str, hw_cache_op, PERF_COUNT_HW_CACHE_OP_MAX);  	/*  	 * Fall back to reads:  	 */  	if (cache_op == -1)  		cache_op = PERF_COUNT_HW_CACHE_OP_READ; -	if (!is_cache_op_valid(cache_type, cache_op)) -		return -EINVAL; - -	cache_result = parse_aliases(str, hw_cache_result, -					PERF_COUNT_HW_CACHE_RESULT_MAX);  	/*  	 * Fall back to accesses:  	 */ @@ -234,93 +371,212 @@ parse_generic_hw_symbols(const char *str, struct perf_counter_attr *attr)  	attr->config = cache_type | (cache_op << 8) | (cache_result << 16);  	attr->type = PERF_TYPE_HW_CACHE; -	return 0; +	*str = s; +	return 1; +} + +static int parse_tracepoint_event(const char **strp, +				    struct perf_counter_attr *attr) +{ +	const char *evt_name; +	char *flags; +	char sys_name[MAX_EVENT_LENGTH]; +	char id_buf[4]; +	int fd; +	unsigned int sys_length, evt_length; +	u64 id; +	char evt_path[MAXPATHLEN]; + +	if (valid_debugfs_mount(debugfs_path)) +		return 0; + +	evt_name = strchr(*strp, ':'); +	if (!evt_name) +		return 0; + +	sys_length = evt_name - *strp; +	if (sys_length >= MAX_EVENT_LENGTH) +		return 0; + +	strncpy(sys_name, *strp, sys_length); +	sys_name[sys_length] = '\0'; +	evt_name = evt_name + 1; + +	flags = strchr(evt_name, ':'); +	if (flags) { +		*flags = '\0'; +		flags++; +		if (!strncmp(flags, "record", strlen(flags))) +			attr->sample_type |= PERF_SAMPLE_RAW; +	} + +	evt_length = strlen(evt_name); +	if (evt_length >= MAX_EVENT_LENGTH) +		return 0; + +	snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", debugfs_path, +		 sys_name, evt_name); +	fd = open(evt_path, O_RDONLY); +	if (fd < 0) +		return 0; + +	if (read(fd, id_buf, sizeof(id_buf)) < 0) { +		close(fd); +		return 0; +	} +	close(fd); +	id = atoll(id_buf); +	attr->config = id; +	attr->type = PERF_TYPE_TRACEPOINT; +	*strp = evt_name + evt_length; +	return 1;  }  static int check_events(const char *str, unsigned int i)  { -	if (!strncmp(str, event_symbols[i].symbol, -		     strlen(event_symbols[i].symbol))) -		return 1; +	int n; -	if (strlen(event_symbols[i].alias)) -		if (!strncmp(str, event_symbols[i].alias, -			     strlen(event_symbols[i].alias))) -			return 1; +	n = strlen(event_symbols[i].symbol); +	if (!strncmp(str, event_symbols[i].symbol, n)) +		return n; + +	n = strlen(event_symbols[i].alias); +	if (n) +		if (!strncmp(str, event_symbols[i].alias, n)) +			return n;  	return 0;  } -/* - * Each event can have multiple symbolic names. - * Symbolic names are (almost) exactly matched. - */ -static int parse_event_symbols(const char *str, struct perf_counter_attr *attr) +static int +parse_symbolic_event(const char **strp, struct perf_counter_attr *attr)  { -	u64 config, id; -	int type; +	const char *str = *strp;  	unsigned int i; -	const char *sep, *pstr; +	int n; -	if (str[0] == 'r' && hex2u64(str + 1, &config) > 0) { -		attr->type = PERF_TYPE_RAW; -		attr->config = config; +	for (i = 0; i < ARRAY_SIZE(event_symbols); i++) { +		n = check_events(str, i); +		if (n > 0) { +			attr->type = event_symbols[i].type; +			attr->config = event_symbols[i].config; +			*strp = str + n; +			return 1; +		} +	} +	return 0; +} + +static int parse_raw_event(const char **strp, struct perf_counter_attr *attr) +{ +	const char *str = *strp; +	u64 config; +	int n; +	if (*str != 'r')  		return 0; +	n = hex2u64(str + 1, &config); +	if (n > 0) { +		*strp = str + n + 1; +		attr->type = PERF_TYPE_RAW; +		attr->config = config; +		return 1;  	} +	return 0; +} -	pstr = str; -	sep = strchr(pstr, ':'); -	if (sep) { -		type = atoi(pstr); -		pstr = sep + 1; -		id = atoi(pstr); -		sep = strchr(pstr, ':'); -		if (sep) { -			pstr = sep + 1; -			if (strchr(pstr, 'k')) -				attr->exclude_user = 1; -			if (strchr(pstr, 'u')) -				attr->exclude_kernel = 1; +static int +parse_numeric_event(const char **strp, struct perf_counter_attr *attr) +{ +	const char *str = *strp; +	char *endp; +	unsigned long type; +	u64 config; + +	type = strtoul(str, &endp, 0); +	if (endp > str && type < PERF_TYPE_MAX && *endp == ':') { +		str = endp + 1; +		config = strtoul(str, &endp, 0); +		if (endp > str) { +			attr->type = type; +			attr->config = config; +			*strp = endp; +			return 1;  		} -		attr->type = type; -		attr->config = id; +	} +	return 0; +} + +static int +parse_event_modifier(const char **strp, struct perf_counter_attr *attr) +{ +	const char *str = *strp; +	int eu = 1, ek = 1, eh = 1; +	if (*str++ != ':')  		return 0; +	while (*str) { +		if (*str == 'u') +			eu = 0; +		else if (*str == 'k') +			ek = 0; +		else if (*str == 'h') +			eh = 0; +		else +			break; +		++str; +	} +	if (str >= *strp + 2) { +		*strp = str; +		attr->exclude_user   = eu; +		attr->exclude_kernel = ek; +		attr->exclude_hv     = eh; +		return 1;  	} +	return 0; +} -	for (i = 0; i < ARRAY_SIZE(event_symbols); i++) { -		if (check_events(str, i)) { -			attr->type = event_symbols[i].type; -			attr->config = event_symbols[i].config; +/* + * Each event can have multiple symbolic names. + * Symbolic names are (almost) exactly matched. + */ +static int parse_event_symbols(const char **str, struct perf_counter_attr *attr) +{ +	if (!(parse_tracepoint_event(str, attr) || +	      parse_raw_event(str, attr) || +	      parse_numeric_event(str, attr) || +	      parse_symbolic_event(str, attr) || +	      parse_generic_hw_event(str, attr))) +		return 0; -			return 0; -		} -	} +	parse_event_modifier(str, attr); -	return parse_generic_hw_symbols(str, attr); +	return 1;  } -int parse_events(const struct option *opt, const char *str, int unset) +int parse_events(const struct option *opt __used, const char *str, int unset __used)  {  	struct perf_counter_attr attr; -	int ret; -	memset(&attr, 0, sizeof(attr)); -again: -	if (nr_counters == MAX_COUNTERS) -		return -1; +	for (;;) { +		if (nr_counters == MAX_COUNTERS) +			return -1; + +		memset(&attr, 0, sizeof(attr)); +		if (!parse_event_symbols(&str, &attr)) +			return -1; -	ret = parse_event_symbols(str, &attr); -	if (ret < 0) -		return ret; +		if (!(*str == 0 || *str == ',' || isspace(*str))) +			return -1; -	attrs[nr_counters] = attr; -	nr_counters++; +		attrs[nr_counters] = attr; +		nr_counters++; -	str = strstr(str, ","); -	if (str) { -		str++; -		goto again; +		if (*str == 0) +			break; +		if (*str == ',') +			++str; +		while (isspace(*str)) +			++str;  	}  	return 0; @@ -335,12 +591,48 @@ static const char * const event_type_descriptors[] = {  };  /* + * Print the events from <debugfs_mount_point>/tracing/events + */ + +static void print_tracepoint_events(void) +{ +	DIR *sys_dir, *evt_dir; +	struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent; +	struct stat st; +	char evt_path[MAXPATHLEN]; + +	if (valid_debugfs_mount(debugfs_path)) +		return; + +	sys_dir = opendir(debugfs_path); +	if (!sys_dir) +		goto cleanup; + +	for_each_subsystem(sys_dir, sys_dirent, sys_next, evt_path, st) { +		evt_dir = opendir(evt_path); +		if (!evt_dir) +			goto cleanup; +		for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next, +								evt_path, st) { +			snprintf(evt_path, MAXPATHLEN, "%s:%s", +				 sys_dirent.d_name, evt_dirent.d_name); +			fprintf(stderr, "  %-40s [%s]\n", evt_path, +				event_type_descriptors[PERF_TYPE_TRACEPOINT+1]); +		} +		closedir(evt_dir); +	} + +cleanup: +	closedir(sys_dir); +} + +/*   * Print the help text for the event symbols:   */  void print_events(void)  {  	struct event_symbol *syms = event_symbols; -	unsigned int i, type, prev_type = -1; +	unsigned int i, type, op, prev_type = -1;  	char name[40];  	fprintf(stderr, "\n"); @@ -348,7 +640,7 @@ void print_events(void)  	for (i = 0; i < ARRAY_SIZE(event_symbols); i++, syms++) {  		type = syms->type + 1; -		if (type > ARRAY_SIZE(event_type_descriptors)) +		if (type >= ARRAY_SIZE(event_type_descriptors))  			type = 0;  		if (type != prev_type) @@ -365,9 +657,26 @@ void print_events(void)  	}  	fprintf(stderr, "\n"); +	for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) { +		for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) { +			/* skip invalid cache type */ +			if (!is_cache_op_valid(type, op)) +				continue; + +			for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) { +				fprintf(stderr, "  %-40s [%s]\n", +					event_cache_name(type, op, i), +					event_type_descriptors[4]); +			} +		} +	} + +	fprintf(stderr, "\n");  	fprintf(stderr, "  %-40s [raw hardware event descriptor]\n",  		"rNNN");  	fprintf(stderr, "\n"); +	print_tracepoint_events(); +  	exit(129);  } diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index e3d552908e6..192a962e3a0 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h @@ -3,11 +3,14 @@   * Parse symbolic events/counts passed in as options:   */ +struct option; +  extern int			nr_counters;  extern struct perf_counter_attr attrs[MAX_COUNTERS];  extern char *event_name(int ctr); +extern char *__event_name(int type, u64 config);  extern int parse_events(const struct option *opt, const char *str, int unset); @@ -15,3 +18,6 @@ extern int parse_events(const struct option *opt, const char *str, int unset);  extern void print_events(void); +extern char debugfs_path[]; +extern int valid_debugfs_mount(const char *debugfs); + diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c index b3affb1658d..1bf67190c82 100644 --- a/tools/perf/util/parse-options.c +++ b/tools/perf/util/parse-options.c @@ -20,7 +20,8 @@ static int get_arg(struct parse_opt_ctx_t *p, const struct option *opt,  	if (p->opt) {  		*arg = p->opt;  		p->opt = NULL; -	} else if (p->argc == 1 && (opt->flags & PARSE_OPT_LASTARG_DEFAULT)) { +	} else if ((opt->flags & PARSE_OPT_LASTARG_DEFAULT) && (p->argc == 1 || +		    **(p->argv + 1) == '-')) {  		*arg = (const char *)opt->defval;  	} else if (p->argc > 1) {  		p->argc--; @@ -485,7 +486,7 @@ int parse_options_usage(const char * const *usagestr,  } -int parse_opt_verbosity_cb(const struct option *opt, const char *arg, +int parse_opt_verbosity_cb(const struct option *opt, const char *arg __used,  			   int unset)  {  	int *target = opt->value; diff --git a/tools/perf/util/parse-options.h b/tools/perf/util/parse-options.h index a1039a6ce0e..8aa3464c709 100644 --- a/tools/perf/util/parse-options.h +++ b/tools/perf/util/parse-options.h @@ -90,21 +90,22 @@ struct option {  	intptr_t defval;  }; -#define OPT_END()                   { OPTION_END } -#define OPT_ARGUMENT(l, h)          { OPTION_ARGUMENT, 0, (l), NULL, NULL, (h) } -#define OPT_GROUP(h)                { OPTION_GROUP, 0, NULL, NULL, NULL, (h) } -#define OPT_BIT(s, l, v, h, b)      { OPTION_BIT, (s), (l), (v), NULL, (h), 0, NULL, (b) } -#define OPT_BOOLEAN(s, l, v, h)     { OPTION_BOOLEAN, (s), (l), (v), NULL, (h) } -#define OPT_SET_INT(s, l, v, h, i)  { OPTION_SET_INT, (s), (l), (v), NULL, (h), 0, NULL, (i) } -#define OPT_SET_PTR(s, l, v, h, p)  { OPTION_SET_PTR, (s), (l), (v), NULL, (h), 0, NULL, (p) } -#define OPT_INTEGER(s, l, v, h)     { OPTION_INTEGER, (s), (l), (v), NULL, (h) } -#define OPT_LONG(s, l, v, h)        { OPTION_LONG, (s), (l), (v), NULL, (h) } -#define OPT_STRING(s, l, v, a, h)   { OPTION_STRING,  (s), (l), (v), (a), (h) } +#define OPT_END()                   { .type = OPTION_END } +#define OPT_ARGUMENT(l, h)          { .type = OPTION_ARGUMENT, .long_name = (l), .help = (h) } +#define OPT_GROUP(h)                { .type = OPTION_GROUP, .help = (h) } +#define OPT_BIT(s, l, v, h, b)      { .type = OPTION_BIT, .short_name = (s), .long_name = (l), .value = (v), .help = (h), .defval = (b) } +#define OPT_BOOLEAN(s, l, v, h)     { .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), .value = (v), .help = (h) } +#define OPT_SET_INT(s, l, v, h, i)  { .type = OPTION_SET_INT, .short_name = (s), .long_name = (l), .value = (v), .help = (h), .defval = (i) } +#define OPT_SET_PTR(s, l, v, h, p)  { .type = OPTION_SET_PTR, .short_name = (s), .long_name = (l), .value = (v), .help = (h), .defval = (p) } +#define OPT_INTEGER(s, l, v, h)     { .type = OPTION_INTEGER, .short_name = (s), .long_name = (l), .value = (v), .help = (h) } +#define OPT_LONG(s, l, v, h)        { .type = OPTION_LONG, .short_name = (s), .long_name = (l), .value = (v), .help = (h) } +#define OPT_STRING(s, l, v, a, h)   { .type = OPTION_STRING,  .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h) }  #define OPT_DATE(s, l, v, h) \ -	{ OPTION_CALLBACK, (s), (l), (v), "time",(h), 0, \ -	  parse_opt_approxidate_cb } +	{ .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), .argh = "time", .help = (h), .callback = parse_opt_approxidate_cb }  #define OPT_CALLBACK(s, l, v, a, h, f) \ -	{ OPTION_CALLBACK, (s), (l), (v), (a), (h), 0, (f) } +	{ .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h), .callback = (f) } +#define OPT_CALLBACK_DEFAULT(s, l, v, a, h, f, d) \ +	{ .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h), .callback = (f), .defval = (intptr_t)d, .flags = PARSE_OPT_LASTARG_DEFAULT }  /* parse_options() will filter out the processed options and leave the   * non-option argments in argv[]. diff --git a/tools/perf/util/quote.c b/tools/perf/util/quote.c index f18c5212bc9..2726fe40eb5 100644 --- a/tools/perf/util/quote.c +++ b/tools/perf/util/quote.c @@ -162,12 +162,16 @@ static inline int sq_must_quote(char c)  	return sq_lookup[(unsigned char)c] + quote_path_fully > 0;  } -/* returns the longest prefix not needing a quote up to maxlen if positive. -   This stops at the first \0 because it's marked as a character needing an -   escape */ -static size_t next_quote_pos(const char *s, ssize_t maxlen) +/* + * Returns the longest prefix not needing a quote up to maxlen if + * positive. + * This stops at the first \0 because it's marked as a character + * needing an escape. + */ +static ssize_t next_quote_pos(const char *s, ssize_t maxlen)  { -	size_t len; +	ssize_t len; +  	if (maxlen < 0) {  		for (len = 0; !sq_must_quote(s[len]); len++);  	} else { @@ -192,22 +196,22 @@ static size_t next_quote_pos(const char *s, ssize_t maxlen)  static size_t quote_c_style_counted(const char *name, ssize_t maxlen,                                      struct strbuf *sb, FILE *fp, int no_dq)  { -#undef EMIT -#define EMIT(c)                                 \ -	do {                                        \ -		if (sb) strbuf_addch(sb, (c));          \ -		if (fp) fputc((c), fp);                 \ -		count++;                                \ +#define EMIT(c)							\ +	do {							\ +		if (sb) strbuf_addch(sb, (c));			\ +		if (fp) fputc((c), fp);				\ +		count++;					\  	} while (0) -#define EMITBUF(s, l)                           \ -	do {                                        \ -		int __ret;				\ -		if (sb) strbuf_add(sb, (s), (l));       \ -		if (fp) __ret = fwrite((s), (l), 1, fp);        \ -		count += (l);                           \ + +#define EMITBUF(s, l)						\ +	do {							\ +		int __ret;					\ +		if (sb) strbuf_add(sb, (s), (l));		\ +		if (fp) __ret = fwrite((s), (l), 1, fp);	\ +		count += (l);					\  	} while (0) -	size_t len, count = 0; +	ssize_t len, count = 0;  	const char *p = name;  	for (;;) { @@ -273,8 +277,8 @@ void write_name_quoted(const char *name, FILE *fp, int terminator)  	fputc(terminator, fp);  } -extern void write_name_quotedpfx(const char *pfx, size_t pfxlen, -                                 const char *name, FILE *fp, int terminator) +void write_name_quotedpfx(const char *pfx, ssize_t pfxlen, +			  const char *name, FILE *fp, int terminator)  {  	int needquote = 0; @@ -306,7 +310,7 @@ char *quote_path_relative(const char *in, int len,  		len = strlen(in);  	/* "../" prefix itself does not need quoting, but "in" might. */ -	needquote = next_quote_pos(in, len) < len; +	needquote = (next_quote_pos(in, len) < len);  	strbuf_setlen(out, 0);  	strbuf_grow(out, len); @@ -314,7 +318,7 @@ char *quote_path_relative(const char *in, int len,  		strbuf_addch(out, '"');  	if (prefix) {  		int off = 0; -		while (prefix[off] && off < len && prefix[off] == in[off]) +		while (off < len && prefix[off] && prefix[off] == in[off])  			if (prefix[off] == '/') {  				prefix += off + 1;  				in += off + 1; diff --git a/tools/perf/util/quote.h b/tools/perf/util/quote.h index 5dfad89816d..a5454a1d1c1 100644 --- a/tools/perf/util/quote.h +++ b/tools/perf/util/quote.h @@ -53,7 +53,7 @@ extern size_t quote_c_style(const char *name, struct strbuf *, FILE *, int no_dq  extern void quote_two_c_style(struct strbuf *, const char *, const char *, int);  extern void write_name_quoted(const char *name, FILE *, int terminator); -extern void write_name_quotedpfx(const char *pfx, size_t pfxlen, +extern void write_name_quotedpfx(const char *pfx, ssize_t pfxlen,                                   const char *name, FILE *, int terminator);  /* quote path as relative to the given prefix */ diff --git a/tools/perf/util/rbtree.c b/tools/perf/util/rbtree.c deleted file mode 100644 index b15ba9c7cb3..00000000000 --- a/tools/perf/util/rbtree.c +++ /dev/null @@ -1,383 +0,0 @@ -/* -  Red Black Trees -  (C) 1999  Andrea Arcangeli <andrea@suse.de> -  (C) 2002  David Woodhouse <dwmw2@infradead.org> -   -  This program is free software; you can redistribute it and/or modify -  it under the terms of the GNU General Public License as published by -  the Free Software Foundation; either version 2 of the License, or -  (at your option) any later version. - -  This program is distributed in the hope that it will be useful, -  but WITHOUT ANY WARRANTY; without even the implied warranty of -  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the -  GNU General Public License for more details. - -  You should have received a copy of the GNU General Public License -  along with this program; if not, write to the Free Software -  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA - -  linux/lib/rbtree.c -*/ - -#include "rbtree.h" - -static void __rb_rotate_left(struct rb_node *node, struct rb_root *root) -{ -	struct rb_node *right = node->rb_right; -	struct rb_node *parent = rb_parent(node); - -	if ((node->rb_right = right->rb_left)) -		rb_set_parent(right->rb_left, node); -	right->rb_left = node; - -	rb_set_parent(right, parent); - -	if (parent) -	{ -		if (node == parent->rb_left) -			parent->rb_left = right; -		else -			parent->rb_right = right; -	} -	else -		root->rb_node = right; -	rb_set_parent(node, right); -} - -static void __rb_rotate_right(struct rb_node *node, struct rb_root *root) -{ -	struct rb_node *left = node->rb_left; -	struct rb_node *parent = rb_parent(node); - -	if ((node->rb_left = left->rb_right)) -		rb_set_parent(left->rb_right, node); -	left->rb_right = node; - -	rb_set_parent(left, parent); - -	if (parent) -	{ -		if (node == parent->rb_right) -			parent->rb_right = left; -		else -			parent->rb_left = left; -	} -	else -		root->rb_node = left; -	rb_set_parent(node, left); -} - -void rb_insert_color(struct rb_node *node, struct rb_root *root) -{ -	struct rb_node *parent, *gparent; - -	while ((parent = rb_parent(node)) && rb_is_red(parent)) -	{ -		gparent = rb_parent(parent); - -		if (parent == gparent->rb_left) -		{ -			{ -				register struct rb_node *uncle = gparent->rb_right; -				if (uncle && rb_is_red(uncle)) -				{ -					rb_set_black(uncle); -					rb_set_black(parent); -					rb_set_red(gparent); -					node = gparent; -					continue; -				} -			} - -			if (parent->rb_right == node) -			{ -				register struct rb_node *tmp; -				__rb_rotate_left(parent, root); -				tmp = parent; -				parent = node; -				node = tmp; -			} - -			rb_set_black(parent); -			rb_set_red(gparent); -			__rb_rotate_right(gparent, root); -		} else { -			{ -				register struct rb_node *uncle = gparent->rb_left; -				if (uncle && rb_is_red(uncle)) -				{ -					rb_set_black(uncle); -					rb_set_black(parent); -					rb_set_red(gparent); -					node = gparent; -					continue; -				} -			} - -			if (parent->rb_left == node) -			{ -				register struct rb_node *tmp; -				__rb_rotate_right(parent, root); -				tmp = parent; -				parent = node; -				node = tmp; -			} - -			rb_set_black(parent); -			rb_set_red(gparent); -			__rb_rotate_left(gparent, root); -		} -	} - -	rb_set_black(root->rb_node); -} - -static void __rb_erase_color(struct rb_node *node, struct rb_node *parent, -			     struct rb_root *root) -{ -	struct rb_node *other; - -	while ((!node || rb_is_black(node)) && node != root->rb_node) -	{ -		if (parent->rb_left == node) -		{ -			other = parent->rb_right; -			if (rb_is_red(other)) -			{ -				rb_set_black(other); -				rb_set_red(parent); -				__rb_rotate_left(parent, root); -				other = parent->rb_right; -			} -			if ((!other->rb_left || rb_is_black(other->rb_left)) && -			    (!other->rb_right || rb_is_black(other->rb_right))) -			{ -				rb_set_red(other); -				node = parent; -				parent = rb_parent(node); -			} -			else -			{ -				if (!other->rb_right || rb_is_black(other->rb_right)) -				{ -					rb_set_black(other->rb_left); -					rb_set_red(other); -					__rb_rotate_right(other, root); -					other = parent->rb_right; -				} -				rb_set_color(other, rb_color(parent)); -				rb_set_black(parent); -				rb_set_black(other->rb_right); -				__rb_rotate_left(parent, root); -				node = root->rb_node; -				break; -			} -		} -		else -		{ -			other = parent->rb_left; -			if (rb_is_red(other)) -			{ -				rb_set_black(other); -				rb_set_red(parent); -				__rb_rotate_right(parent, root); -				other = parent->rb_left; -			} -			if ((!other->rb_left || rb_is_black(other->rb_left)) && -			    (!other->rb_right || rb_is_black(other->rb_right))) -			{ -				rb_set_red(other); -				node = parent; -				parent = rb_parent(node); -			} -			else -			{ -				if (!other->rb_left || rb_is_black(other->rb_left)) -				{ -					rb_set_black(other->rb_right); -					rb_set_red(other); -					__rb_rotate_left(other, root); -					other = parent->rb_left; -				} -				rb_set_color(other, rb_color(parent)); -				rb_set_black(parent); -				rb_set_black(other->rb_left); -				__rb_rotate_right(parent, root); -				node = root->rb_node; -				break; -			} -		} -	} -	if (node) -		rb_set_black(node); -} - -void rb_erase(struct rb_node *node, struct rb_root *root) -{ -	struct rb_node *child, *parent; -	int color; - -	if (!node->rb_left) -		child = node->rb_right; -	else if (!node->rb_right) -		child = node->rb_left; -	else -	{ -		struct rb_node *old = node, *left; - -		node = node->rb_right; -		while ((left = node->rb_left) != NULL) -			node = left; -		child = node->rb_right; -		parent = rb_parent(node); -		color = rb_color(node); - -		if (child) -			rb_set_parent(child, parent); -		if (parent == old) { -			parent->rb_right = child; -			parent = node; -		} else -			parent->rb_left = child; - -		node->rb_parent_color = old->rb_parent_color; -		node->rb_right = old->rb_right; -		node->rb_left = old->rb_left; - -		if (rb_parent(old)) -		{ -			if (rb_parent(old)->rb_left == old) -				rb_parent(old)->rb_left = node; -			else -				rb_parent(old)->rb_right = node; -		} else -			root->rb_node = node; - -		rb_set_parent(old->rb_left, node); -		if (old->rb_right) -			rb_set_parent(old->rb_right, node); -		goto color; -	} - -	parent = rb_parent(node); -	color = rb_color(node); - -	if (child) -		rb_set_parent(child, parent); -	if (parent) -	{ -		if (parent->rb_left == node) -			parent->rb_left = child; -		else -			parent->rb_right = child; -	} -	else -		root->rb_node = child; - - color: -	if (color == RB_BLACK) -		__rb_erase_color(child, parent, root); -} - -/* - * This function returns the first node (in sort order) of the tree. - */ -struct rb_node *rb_first(const struct rb_root *root) -{ -	struct rb_node	*n; - -	n = root->rb_node; -	if (!n) -		return NULL; -	while (n->rb_left) -		n = n->rb_left; -	return n; -} - -struct rb_node *rb_last(const struct rb_root *root) -{ -	struct rb_node	*n; - -	n = root->rb_node; -	if (!n) -		return NULL; -	while (n->rb_right) -		n = n->rb_right; -	return n; -} - -struct rb_node *rb_next(const struct rb_node *node) -{ -	struct rb_node *parent; - -	if (rb_parent(node) == node) -		return NULL; - -	/* If we have a right-hand child, go down and then left as far -	   as we can. */ -	if (node->rb_right) { -		node = node->rb_right;  -		while (node->rb_left) -			node=node->rb_left; -		return (struct rb_node *)node; -	} - -	/* No right-hand children.  Everything down and left is -	   smaller than us, so any 'next' node must be in the general -	   direction of our parent. Go up the tree; any time the -	   ancestor is a right-hand child of its parent, keep going -	   up. First time it's a left-hand child of its parent, said -	   parent is our 'next' node. */ -	while ((parent = rb_parent(node)) && node == parent->rb_right) -		node = parent; - -	return parent; -} - -struct rb_node *rb_prev(const struct rb_node *node) -{ -	struct rb_node *parent; - -	if (rb_parent(node) == node) -		return NULL; - -	/* If we have a left-hand child, go down and then right as far -	   as we can. */ -	if (node->rb_left) { -		node = node->rb_left;  -		while (node->rb_right) -			node=node->rb_right; -		return (struct rb_node *)node; -	} - -	/* No left-hand children. Go up till we find an ancestor which -	   is a right-hand child of its parent */ -	while ((parent = rb_parent(node)) && node == parent->rb_left) -		node = parent; - -	return parent; -} - -void rb_replace_node(struct rb_node *victim, struct rb_node *new, -		     struct rb_root *root) -{ -	struct rb_node *parent = rb_parent(victim); - -	/* Set the surrounding nodes to point to the replacement */ -	if (parent) { -		if (victim == parent->rb_left) -			parent->rb_left = new; -		else -			parent->rb_right = new; -	} else { -		root->rb_node = new; -	} -	if (victim->rb_left) -		rb_set_parent(victim->rb_left, new); -	if (victim->rb_right) -		rb_set_parent(victim->rb_right, new); - -	/* Copy the pointers/colour from the victim to the replacement */ -	*new = *victim; -} diff --git a/tools/perf/util/rbtree.h b/tools/perf/util/rbtree.h deleted file mode 100644 index 6bdc488a47f..00000000000 --- a/tools/perf/util/rbtree.h +++ /dev/null @@ -1,171 +0,0 @@ -/* -  Red Black Trees -  (C) 1999  Andrea Arcangeli <andrea@suse.de> -   -  This program is free software; you can redistribute it and/or modify -  it under the terms of the GNU General Public License as published by -  the Free Software Foundation; either version 2 of the License, or -  (at your option) any later version. - -  This program is distributed in the hope that it will be useful, -  but WITHOUT ANY WARRANTY; without even the implied warranty of -  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the -  GNU General Public License for more details. - -  You should have received a copy of the GNU General Public License -  along with this program; if not, write to the Free Software -  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA - -  linux/include/linux/rbtree.h - -  To use rbtrees you'll have to implement your own insert and search cores. -  This will avoid us to use callbacks and to drop drammatically performances. -  I know it's not the cleaner way,  but in C (not in C++) to get -  performances and genericity... - -  Some example of insert and search follows here. The search is a plain -  normal search over an ordered tree. The insert instead must be implemented -  int two steps: as first thing the code must insert the element in -  order as a red leaf in the tree, then the support library function -  rb_insert_color() must be called. Such function will do the -  not trivial work to rebalance the rbtree if necessary. - ------------------------------------------------------------------------ -static inline struct page * rb_search_page_cache(struct inode * inode, -						 unsigned long offset) -{ -	struct rb_node * n = inode->i_rb_page_cache.rb_node; -	struct page * page; - -	while (n) -	{ -		page = rb_entry(n, struct page, rb_page_cache); - -		if (offset < page->offset) -			n = n->rb_left; -		else if (offset > page->offset) -			n = n->rb_right; -		else -			return page; -	} -	return NULL; -} - -static inline struct page * __rb_insert_page_cache(struct inode * inode, -						   unsigned long offset, -						   struct rb_node * node) -{ -	struct rb_node ** p = &inode->i_rb_page_cache.rb_node; -	struct rb_node * parent = NULL; -	struct page * page; - -	while (*p) -	{ -		parent = *p; -		page = rb_entry(parent, struct page, rb_page_cache); - -		if (offset < page->offset) -			p = &(*p)->rb_left; -		else if (offset > page->offset) -			p = &(*p)->rb_right; -		else -			return page; -	} - -	rb_link_node(node, parent, p); - -	return NULL; -} - -static inline struct page * rb_insert_page_cache(struct inode * inode, -						 unsigned long offset, -						 struct rb_node * node) -{ -	struct page * ret; -	if ((ret = __rb_insert_page_cache(inode, offset, node))) -		goto out; -	rb_insert_color(node, &inode->i_rb_page_cache); - out: -	return ret; -} ------------------------------------------------------------------------ -*/ - -#ifndef	_LINUX_RBTREE_H -#define	_LINUX_RBTREE_H - -#include <stddef.h> - -/** - * container_of - cast a member of a structure out to the containing structure - * @ptr:	the pointer to the member. - * @type:	the type of the container struct this is embedded in. - * @member:	the name of the member within the struct. - * - */ -#define container_of(ptr, type, member) ({			\ -	const typeof( ((type *)0)->member ) *__mptr = (ptr);	\ -	(type *)( (char *)__mptr - offsetof(type,member) );}) - -struct rb_node -{ -	unsigned long  rb_parent_color; -#define	RB_RED		0 -#define	RB_BLACK	1 -	struct rb_node *rb_right; -	struct rb_node *rb_left; -} __attribute__((aligned(sizeof(long)))); -    /* The alignment might seem pointless, but allegedly CRIS needs it */ - -struct rb_root -{ -	struct rb_node *rb_node; -}; - - -#define rb_parent(r)   ((struct rb_node *)((r)->rb_parent_color & ~3)) -#define rb_color(r)   ((r)->rb_parent_color & 1) -#define rb_is_red(r)   (!rb_color(r)) -#define rb_is_black(r) rb_color(r) -#define rb_set_red(r)  do { (r)->rb_parent_color &= ~1; } while (0) -#define rb_set_black(r)  do { (r)->rb_parent_color |= 1; } while (0) - -static inline void rb_set_parent(struct rb_node *rb, struct rb_node *p) -{ -	rb->rb_parent_color = (rb->rb_parent_color & 3) | (unsigned long)p; -} -static inline void rb_set_color(struct rb_node *rb, int color) -{ -	rb->rb_parent_color = (rb->rb_parent_color & ~1) | color; -} - -#define RB_ROOT	(struct rb_root) { NULL, } -#define	rb_entry(ptr, type, member) container_of(ptr, type, member) - -#define RB_EMPTY_ROOT(root)	((root)->rb_node == NULL) -#define RB_EMPTY_NODE(node)	(rb_parent(node) == node) -#define RB_CLEAR_NODE(node)	(rb_set_parent(node, node)) - -extern void rb_insert_color(struct rb_node *, struct rb_root *); -extern void rb_erase(struct rb_node *, struct rb_root *); - -/* Find logical next and previous nodes in a tree */ -extern struct rb_node *rb_next(const struct rb_node *); -extern struct rb_node *rb_prev(const struct rb_node *); -extern struct rb_node *rb_first(const struct rb_root *); -extern struct rb_node *rb_last(const struct rb_root *); - -/* Fast replacement of a single node without remove/rebalance/add/rebalance */ -extern void rb_replace_node(struct rb_node *victim, struct rb_node *new,  -			    struct rb_root *root); - -static inline void rb_link_node(struct rb_node * node, struct rb_node * parent, -				struct rb_node ** rb_link) -{ -	node->rb_parent_color = (unsigned long )parent; -	node->rb_left = node->rb_right = NULL; - -	*rb_link = node; -} - -#endif	/* _LINUX_RBTREE_H */ diff --git a/tools/perf/util/strbuf.c b/tools/perf/util/strbuf.c index 464e7ca898c..5249d5a1b0c 100644 --- a/tools/perf/util/strbuf.c +++ b/tools/perf/util/strbuf.c @@ -16,7 +16,7 @@ int prefixcmp(const char *str, const char *prefix)   */  char strbuf_slopbuf[1]; -void strbuf_init(struct strbuf *sb, size_t hint) +void strbuf_init(struct strbuf *sb, ssize_t hint)  {  	sb->alloc = sb->len = 0;  	sb->buf = strbuf_slopbuf; @@ -92,7 +92,8 @@ void strbuf_ltrim(struct strbuf *sb)  void strbuf_tolower(struct strbuf *sb)  { -	int i; +	unsigned int i; +  	for (i = 0; i < sb->len; i++)  		sb->buf[i] = tolower(sb->buf[i]);  } @@ -264,7 +265,7 @@ size_t strbuf_fread(struct strbuf *sb, size_t size, FILE *f)  	return res;  } -ssize_t strbuf_read(struct strbuf *sb, int fd, size_t hint) +ssize_t strbuf_read(struct strbuf *sb, int fd, ssize_t hint)  {  	size_t oldlen = sb->len;  	size_t oldalloc = sb->alloc; @@ -293,7 +294,7 @@ ssize_t strbuf_read(struct strbuf *sb, int fd, size_t hint)  #define STRBUF_MAXLINK (2*PATH_MAX) -int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint) +int strbuf_readlink(struct strbuf *sb, const char *path, ssize_t hint)  {  	size_t oldalloc = sb->alloc; @@ -301,7 +302,7 @@ int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint)  		hint = 32;  	while (hint < STRBUF_MAXLINK) { -		int len; +		ssize_t len;  		strbuf_grow(sb, hint);  		len = readlink(path, sb->buf, hint); @@ -343,7 +344,7 @@ int strbuf_getline(struct strbuf *sb, FILE *fp, int term)  	return 0;  } -int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint) +int strbuf_read_file(struct strbuf *sb, const char *path, ssize_t hint)  {  	int fd, len; diff --git a/tools/perf/util/strbuf.h b/tools/perf/util/strbuf.h index 9ee908a3ec5..d2aa86c014c 100644 --- a/tools/perf/util/strbuf.h +++ b/tools/perf/util/strbuf.h @@ -50,7 +50,7 @@ struct strbuf {  #define STRBUF_INIT  { 0, 0, strbuf_slopbuf }  /*----- strbuf life cycle -----*/ -extern void strbuf_init(struct strbuf *, size_t); +extern void strbuf_init(struct strbuf *buf, ssize_t hint);  extern void strbuf_release(struct strbuf *);  extern char *strbuf_detach(struct strbuf *, size_t *);  extern void strbuf_attach(struct strbuf *, void *, size_t, size_t); @@ -61,7 +61,7 @@ static inline void strbuf_swap(struct strbuf *a, struct strbuf *b) {  }  /*----- strbuf size related -----*/ -static inline size_t strbuf_avail(const struct strbuf *sb) { +static inline ssize_t strbuf_avail(const struct strbuf *sb) {  	return sb->alloc ? sb->alloc - sb->len - 1 : 0;  } @@ -122,9 +122,9 @@ extern void strbuf_addf(struct strbuf *sb, const char *fmt, ...);  extern size_t strbuf_fread(struct strbuf *, size_t, FILE *);  /* XXX: if read fails, any partial read is undone */ -extern ssize_t strbuf_read(struct strbuf *, int fd, size_t hint); -extern int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint); -extern int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint); +extern ssize_t strbuf_read(struct strbuf *, int fd, ssize_t hint); +extern int strbuf_read_file(struct strbuf *sb, const char *path, ssize_t hint); +extern int strbuf_readlink(struct strbuf *sb, const char *path, ssize_t hint);  extern int strbuf_getline(struct strbuf *, FILE *, int); diff --git a/tools/perf/util/string.h b/tools/perf/util/string.h index 3dca2f654cd..bf39dfadfd2 100644 --- a/tools/perf/util/string.h +++ b/tools/perf/util/string.h @@ -5,4 +5,7 @@  int hex2u64(const char *ptr, u64 *val); +#define _STR(x) #x +#define STR(x) _STR(x) +  #endif diff --git a/tools/perf/util/strlist.c b/tools/perf/util/strlist.c index 025a78edfff..7ad38171dc2 100644 --- a/tools/perf/util/strlist.c +++ b/tools/perf/util/strlist.c @@ -64,6 +64,7 @@ int strlist__add(struct strlist *self, const char *new_entry)  	rb_link_node(&sn->rb_node, parent, p);  	rb_insert_color(&sn->rb_node, &self->entries); +	++self->nr_entries;  	return 0;  } @@ -155,8 +156,9 @@ struct strlist *strlist__new(bool dupstr, const char *slist)  	struct strlist *self = malloc(sizeof(*self));  	if (self != NULL) { -		self->entries = RB_ROOT; -		self->dupstr = dupstr; +		self->entries	 = RB_ROOT; +		self->dupstr	 = dupstr; +		self->nr_entries = 0;  		if (slist && strlist__parse_list(self, slist) != 0)  			goto out_error;  	} @@ -182,3 +184,17 @@ void strlist__delete(struct strlist *self)  		free(self);  	}  } + +struct str_node *strlist__entry(const struct strlist *self, unsigned int idx) +{ +	struct rb_node *nd; + +	for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { +		struct str_node *pos = rb_entry(nd, struct str_node, rb_node); + +		if (!idx--) +			return pos; +	} + +	return NULL; +} diff --git a/tools/perf/util/strlist.h b/tools/perf/util/strlist.h index 2fb117fb4b6..921818e44a5 100644 --- a/tools/perf/util/strlist.h +++ b/tools/perf/util/strlist.h @@ -1,7 +1,7 @@  #ifndef STRLIST_H_  #define STRLIST_H_ -#include "rbtree.h" +#include <linux/rbtree.h>  #include <stdbool.h>  struct str_node { @@ -11,7 +11,8 @@ struct str_node {  struct strlist {  	struct rb_root entries; -	bool dupstr; +	unsigned int   nr_entries; +	bool	       dupstr;  };  struct strlist *strlist__new(bool dupstr, const char *slist); @@ -21,11 +22,17 @@ void strlist__remove(struct strlist *self, struct str_node *sn);  int strlist__load(struct strlist *self, const char *filename);  int strlist__add(struct strlist *self, const char *str); +struct str_node *strlist__entry(const struct strlist *self, unsigned int idx);  bool strlist__has_entry(struct strlist *self, const char *entry);  static inline bool strlist__empty(const struct strlist *self)  { -	return rb_first(&self->entries) == NULL; +	return self->nr_entries == 0; +} + +static inline unsigned int strlist__nr_entries(const struct strlist *self) +{ +	return self->nr_entries;  }  int strlist__parse_list(struct strlist *self, const char *s); diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 78c2efde01b..5c0f42e6b33 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -9,6 +9,16 @@  const char *sym_hist_filter; +enum dso_origin { +	DSO__ORIG_KERNEL = 0, +	DSO__ORIG_JAVA_JIT, +	DSO__ORIG_FEDORA, +	DSO__ORIG_UBUNTU, +	DSO__ORIG_BUILDID, +	DSO__ORIG_DSO, +	DSO__ORIG_NOT_FOUND, +}; +  static struct symbol *symbol__new(u64 start, u64 len,  				  const char *name, unsigned int priv_size,  				  u64 obj_start, int verbose) @@ -35,7 +45,7 @@ static struct symbol *symbol__new(u64 start, u64 len,  		self = ((void *)self) + priv_size;  	}  	self->start = start; -	self->end   = start + len - 1; +	self->end   = len ? start + len - 1 : start;  	memcpy(self->name, name, namelen);  	return self; @@ -48,8 +58,12 @@ static void symbol__delete(struct symbol *self, unsigned int priv_size)  static size_t symbol__fprintf(struct symbol *self, FILE *fp)  { -	return fprintf(fp, " %llx-%llx %s\n", +	if (!self->module) +		return fprintf(fp, " %llx-%llx %s\n",  		       self->start, self->end, self->name); +	else +		return fprintf(fp, " %llx-%llx %s \t[%s]\n", +		       self->start, self->end, self->name, self->module->name);  }  struct dso *dso__new(const char *name, unsigned int sym_priv_size) @@ -61,6 +75,8 @@ struct dso *dso__new(const char *name, unsigned int sym_priv_size)  		self->syms = RB_ROOT;  		self->sym_priv_size = sym_priv_size;  		self->find_symbol = dso__find_symbol; +		self->slen_calculated = 0; +		self->origin = DSO__ORIG_NOT_FOUND;  	}  	return self; @@ -146,6 +162,7 @@ static int dso__load_kallsyms(struct dso *self, symbol_filter_t filter, int verb  	char *line = NULL;  	size_t n;  	FILE *file = fopen("/proc/kallsyms", "r"); +	int count = 0;  	if (file == NULL)  		goto out_failure; @@ -188,8 +205,10 @@ static int dso__load_kallsyms(struct dso *self, symbol_filter_t filter, int verb  		if (filter && filter(self, sym))  			symbol__delete(sym, self->sym_priv_size); -		else +		else {  			dso__insert_symbol(self, sym); +			count++; +		}  	}  	/* @@ -212,7 +231,7 @@ static int dso__load_kallsyms(struct dso *self, symbol_filter_t filter, int verb  	free(line);  	fclose(file); -	return 0; +	return count;  out_delete_line:  	free(line); @@ -307,6 +326,26 @@ static inline int elf_sym__is_function(const GElf_Sym *sym)  	       sym->st_size != 0;  } +static inline int elf_sym__is_label(const GElf_Sym *sym) +{ +	return elf_sym__type(sym) == STT_NOTYPE && +		sym->st_name != 0 && +		sym->st_shndx != SHN_UNDEF && +		sym->st_shndx != SHN_ABS; +} + +static inline const char *elf_sec__name(const GElf_Shdr *shdr, +					const Elf_Data *secstrs) +{ +	return secstrs->d_buf + shdr->sh_name; +} + +static inline int elf_sec__is_text(const GElf_Shdr *shdr, +					const Elf_Data *secstrs) +{ +	return strstr(elf_sec__name(shdr, secstrs), "text") != NULL; +} +  static inline const char *elf_sym__name(const GElf_Sym *sym,  					const Elf_Data *symstrs)  { @@ -346,36 +385,61 @@ static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep,  	     idx < nr_entries; \  	     ++idx, pos = gelf_getrela(reldata, idx, &pos_mem)) -static int dso__synthesize_plt_symbols(struct  dso *self, Elf *elf, -				       GElf_Ehdr *ehdr, Elf_Scn *scn_dynsym, -				       GElf_Shdr *shdr_dynsym, -				       size_t dynsym_idx, int verbose) +/* + * We need to check if we have a .dynsym, so that we can handle the + * .plt, synthesizing its symbols, that aren't on the symtabs (be it + * .dynsym or .symtab). + * And always look at the original dso, not at debuginfo packages, that + * have the PLT data stripped out (shdr_rel_plt.sh_type == SHT_NOBITS). + */ +static int dso__synthesize_plt_symbols(struct  dso *self, int verbose)  {  	uint32_t nr_rel_entries, idx;  	GElf_Sym sym;  	u64 plt_offset;  	GElf_Shdr shdr_plt;  	struct symbol *f; -	GElf_Shdr shdr_rel_plt; +	GElf_Shdr shdr_rel_plt, shdr_dynsym;  	Elf_Data *reldata, *syms, *symstrs; -	Elf_Scn *scn_plt_rel, *scn_symstrs; +	Elf_Scn *scn_plt_rel, *scn_symstrs, *scn_dynsym; +	size_t dynsym_idx; +	GElf_Ehdr ehdr;  	char sympltname[1024]; -	int nr = 0, symidx; +	Elf *elf; +	int nr = 0, symidx, fd, err = 0; -	scn_plt_rel = elf_section_by_name(elf, ehdr, &shdr_rel_plt, +	fd = open(self->name, O_RDONLY); +	if (fd < 0) +		goto out; + +	elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); +	if (elf == NULL) +		goto out_close; + +	if (gelf_getehdr(elf, &ehdr) == NULL) +		goto out_elf_end; + +	scn_dynsym = elf_section_by_name(elf, &ehdr, &shdr_dynsym, +					 ".dynsym", &dynsym_idx); +	if (scn_dynsym == NULL) +		goto out_elf_end; + +	scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt,  					  ".rela.plt", NULL);  	if (scn_plt_rel == NULL) { -		scn_plt_rel = elf_section_by_name(elf, ehdr, &shdr_rel_plt, +		scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt,  						  ".rel.plt", NULL);  		if (scn_plt_rel == NULL) -			return 0; +			goto out_elf_end;  	} +	err = -1; +  	if (shdr_rel_plt.sh_link != dynsym_idx) -		return 0; +		goto out_elf_end; -	if (elf_section_by_name(elf, ehdr, &shdr_plt, ".plt", NULL) == NULL) -		return 0; +	if (elf_section_by_name(elf, &ehdr, &shdr_plt, ".plt", NULL) == NULL) +		goto out_elf_end;  	/*  	 * Fetch the relocation section to find the indexes to the GOT @@ -383,19 +447,19 @@ static int dso__synthesize_plt_symbols(struct  dso *self, Elf *elf,  	 */  	reldata = elf_getdata(scn_plt_rel, NULL);  	if (reldata == NULL) -		return -1; +		goto out_elf_end;  	syms = elf_getdata(scn_dynsym, NULL);  	if (syms == NULL) -		return -1; +		goto out_elf_end; -	scn_symstrs = elf_getscn(elf, shdr_dynsym->sh_link); +	scn_symstrs = elf_getscn(elf, shdr_dynsym.sh_link);  	if (scn_symstrs == NULL) -		return -1; +		goto out_elf_end;  	symstrs = elf_getdata(scn_symstrs, NULL);  	if (symstrs == NULL) -		return -1; +		goto out_elf_end;  	nr_rel_entries = shdr_rel_plt.sh_size / shdr_rel_plt.sh_entsize;  	plt_offset = shdr_plt.sh_offset; @@ -414,7 +478,7 @@ static int dso__synthesize_plt_symbols(struct  dso *self, Elf *elf,  			f = symbol__new(plt_offset, shdr_plt.sh_entsize,  					sympltname, self->sym_priv_size, 0, verbose);  			if (!f) -				return -1; +				goto out_elf_end;  			dso__insert_symbol(self, f);  			++nr; @@ -432,25 +496,31 @@ static int dso__synthesize_plt_symbols(struct  dso *self, Elf *elf,  			f = symbol__new(plt_offset, shdr_plt.sh_entsize,  					sympltname, self->sym_priv_size, 0, verbose);  			if (!f) -				return -1; +				goto out_elf_end;  			dso__insert_symbol(self, f);  			++nr;  		} -	} else { -		/* -		 * TODO: There are still one more shdr_rel_plt.sh_type -		 * I have to investigate, but probably should be ignored. -		 */  	} -	return nr; +	err = 0; +out_elf_end: +	elf_end(elf); +out_close: +	close(fd); + +	if (err == 0) +		return nr; +out: +	fprintf(stderr, "%s: problems reading %s PLT info.\n", +		__func__, self->name); +	return 0;  }  static int dso__load_sym(struct dso *self, int fd, const char *name, -			 symbol_filter_t filter, int verbose) +			 symbol_filter_t filter, int verbose, struct module *mod)  { -	Elf_Data *symstrs; +	Elf_Data *symstrs, *secstrs;  	uint32_t nr_syms;  	int err = -1;  	uint32_t index; @@ -458,10 +528,9 @@ static int dso__load_sym(struct dso *self, int fd, const char *name,  	GElf_Shdr shdr;  	Elf_Data *syms;  	GElf_Sym sym; -	Elf_Scn *sec, *sec_dynsym; +	Elf_Scn *sec, *sec_strndx;  	Elf *elf; -	size_t dynsym_idx; -	int nr = 0; +	int nr = 0, kernel = !strcmp("[kernel]", self->name);  	elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);  	if (elf == NULL) { @@ -477,32 +546,11 @@ static int dso__load_sym(struct dso *self, int fd, const char *name,  		goto out_elf_end;  	} -	/* -	 * We need to check if we have a .dynsym, so that we can handle the -	 * .plt, synthesizing its symbols, that aren't on the symtabs (be it -	 * .dynsym or .symtab) -	 */ -	sec_dynsym = elf_section_by_name(elf, &ehdr, &shdr, -					 ".dynsym", &dynsym_idx); -	if (sec_dynsym != NULL) { -		nr = dso__synthesize_plt_symbols(self, elf, &ehdr, -						 sec_dynsym, &shdr, -						 dynsym_idx, verbose); -		if (nr < 0) -			goto out_elf_end; -	} - -	/* -	 * But if we have a full .symtab (that is a superset of .dynsym) we -	 * should add the symbols not in the .dynsyn -	 */  	sec = elf_section_by_name(elf, &ehdr, &shdr, ".symtab", NULL);  	if (sec == NULL) { -		if (sec_dynsym == NULL) +		sec = elf_section_by_name(elf, &ehdr, &shdr, ".dynsym", NULL); +		if (sec == NULL)  			goto out_elf_end; - -		sec = sec_dynsym; -		gelf_getshdr(sec, &shdr);  	}  	syms = elf_getdata(sec, NULL); @@ -517,17 +565,34 @@ static int dso__load_sym(struct dso *self, int fd, const char *name,  	if (symstrs == NULL)  		goto out_elf_end; +	sec_strndx = elf_getscn(elf, ehdr.e_shstrndx); +	if (sec_strndx == NULL) +		goto out_elf_end; + +	secstrs = elf_getdata(sec_strndx, NULL); +	if (secstrs == NULL) +		goto out_elf_end; +  	nr_syms = shdr.sh_size / shdr.sh_entsize;  	memset(&sym, 0, sizeof(sym)); -	self->prelinked = elf_section_by_name(elf, &ehdr, &shdr, -					      ".gnu.prelink_undo", -					      NULL) != NULL; +	if (!kernel) { +		self->adjust_symbols = (ehdr.e_type == ET_EXEC || +				elf_section_by_name(elf, &ehdr, &shdr, +						     ".gnu.prelink_undo", +						     NULL) != NULL); +	} else self->adjust_symbols = 0; +  	elf_symtab__for_each_symbol(syms, nr_syms, index, sym) {  		struct symbol *f; +		const char *name; +		char *demangled;  		u64 obj_start; +		struct section *section = NULL; +		int is_label = elf_sym__is_label(&sym); +		const char *section_name; -		if (!elf_sym__is_function(&sym)) +		if (!is_label && !elf_sym__is_function(&sym))  			continue;  		sec = elf_getscn(elf, sym.st_shndx); @@ -535,9 +600,14 @@ static int dso__load_sym(struct dso *self, int fd, const char *name,  			goto out_elf_end;  		gelf_getshdr(sec, &shdr); + +		if (is_label && !elf_sec__is_text(&shdr, secstrs)) +			continue; + +		section_name = elf_sec__name(&shdr, secstrs);  		obj_start = sym.st_value; -		if (self->prelinked) { +		if (self->adjust_symbols) {  			if (verbose >= 2)  				printf("adjusting symbol: st_value: %Lx sh_addr: %Lx sh_offset: %Lx\n",  					(u64)sym.st_value, (u64)shdr.sh_addr, (u64)shdr.sh_offset); @@ -545,15 +615,36 @@ static int dso__load_sym(struct dso *self, int fd, const char *name,  			sym.st_value -= shdr.sh_addr - shdr.sh_offset;  		} -		f = symbol__new(sym.st_value, sym.st_size, -				elf_sym__name(&sym, symstrs), +		if (mod) { +			section = mod->sections->find_section(mod->sections, section_name); +			if (section) +				sym.st_value += section->vma; +			else { +				fprintf(stderr, "dso__load_sym() module %s lookup of %s failed\n", +					mod->name, section_name); +				goto out_elf_end; +			} +		} +		/* +		 * We need to figure out if the object was created from C++ sources +		 * DWARF DW_compile_unit has this, but we don't always have access +		 * to it... +		 */ +		name = elf_sym__name(&sym, symstrs); +		demangled = bfd_demangle(NULL, name, DMGL_PARAMS | DMGL_ANSI); +		if (demangled != NULL) +			name = demangled; + +		f = symbol__new(sym.st_value, sym.st_size, name,  				self->sym_priv_size, obj_start, verbose); +		free(demangled);  		if (!f)  			goto out_elf_end;  		if (filter && filter(self, f))  			symbol__delete(f, self->sym_priv_size);  		else { +			f->module = mod;  			dso__insert_symbol(self, f);  			nr++;  		} @@ -566,44 +657,135 @@ out_close:  	return err;  } +#define BUILD_ID_SIZE 128 + +static char *dso__read_build_id(struct dso *self, int verbose) +{ +	int i; +	GElf_Ehdr ehdr; +	GElf_Shdr shdr; +	Elf_Data *build_id_data; +	Elf_Scn *sec; +	char *build_id = NULL, *bid; +	unsigned char *raw; +	Elf *elf; +	int fd = open(self->name, O_RDONLY); + +	if (fd < 0) +		goto out; + +	elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); +	if (elf == NULL) { +		if (verbose) +			fprintf(stderr, "%s: cannot read %s ELF file.\n", +				__func__, self->name); +		goto out_close; +	} + +	if (gelf_getehdr(elf, &ehdr) == NULL) { +		if (verbose) +			fprintf(stderr, "%s: cannot get elf header.\n", __func__); +		goto out_elf_end; +	} + +	sec = elf_section_by_name(elf, &ehdr, &shdr, ".note.gnu.build-id", NULL); +	if (sec == NULL) +		goto out_elf_end; + +	build_id_data = elf_getdata(sec, NULL); +	if (build_id_data == NULL) +		goto out_elf_end; +	build_id = malloc(BUILD_ID_SIZE); +	if (build_id == NULL) +		goto out_elf_end; +	raw = build_id_data->d_buf + 16; +	bid = build_id; + +	for (i = 0; i < 20; ++i) { +		sprintf(bid, "%02x", *raw); +		++raw; +		bid += 2; +	} +	if (verbose >= 2) +		printf("%s(%s): %s\n", __func__, self->name, build_id); +out_elf_end: +	elf_end(elf); +out_close: +	close(fd); +out: +	return build_id; +} + +char dso__symtab_origin(const struct dso *self) +{ +	static const char origin[] = { +		[DSO__ORIG_KERNEL] =   'k', +		[DSO__ORIG_JAVA_JIT] = 'j', +		[DSO__ORIG_FEDORA] =   'f', +		[DSO__ORIG_UBUNTU] =   'u', +		[DSO__ORIG_BUILDID] =  'b', +		[DSO__ORIG_DSO] =      'd', +	}; + +	if (self == NULL || self->origin == DSO__ORIG_NOT_FOUND) +		return '!'; +	return origin[self->origin]; +} +  int dso__load(struct dso *self, symbol_filter_t filter, int verbose)  { -	int size = strlen(self->name) + sizeof("/usr/lib/debug%s.debug"); -	char *name = malloc(size); -	int variant = 0; +	int size = PATH_MAX; +	char *name = malloc(size), *build_id = NULL;  	int ret = -1;  	int fd;  	if (!name)  		return -1; -	self->prelinked = 0; +	self->adjust_symbols = 0; + +	if (strncmp(self->name, "/tmp/perf-", 10) == 0) { +		ret = dso__load_perf_map(self, filter, verbose); +		self->origin = ret > 0 ? DSO__ORIG_JAVA_JIT : +					 DSO__ORIG_NOT_FOUND; +		return ret; +	} -	if (strncmp(self->name, "/tmp/perf-", 10) == 0) -		return dso__load_perf_map(self, filter, verbose); +	self->origin = DSO__ORIG_FEDORA - 1;  more:  	do { -		switch (variant) { -		case 0: /* Fedora */ +		self->origin++; +		switch (self->origin) { +		case DSO__ORIG_FEDORA:  			snprintf(name, size, "/usr/lib/debug%s.debug", self->name);  			break; -		case 1: /* Ubuntu */ +		case DSO__ORIG_UBUNTU:  			snprintf(name, size, "/usr/lib/debug%s", self->name);  			break; -		case 2: /* Sane people */ +		case DSO__ORIG_BUILDID: +			build_id = dso__read_build_id(self, verbose); +			if (build_id != NULL) { +				snprintf(name, size, +					 "/usr/lib/debug/.build-id/%.2s/%s.debug", +					build_id, build_id + 2); +				free(build_id); +				break; +			} +			self->origin++; +			/* Fall thru */ +		case DSO__ORIG_DSO:  			snprintf(name, size, "%s", self->name);  			break;  		default:  			goto out;  		} -		variant++;  		fd = open(name, O_RDONLY);  	} while (fd < 0); -	ret = dso__load_sym(self, fd, name, filter, verbose); +	ret = dso__load_sym(self, fd, name, filter, verbose, NULL);  	close(fd);  	/* @@ -612,11 +794,98 @@ more:  	if (!ret)  		goto more; +	if (ret > 0) { +		int nr_plt = dso__synthesize_plt_symbols(self, verbose); +		if (nr_plt > 0) +			ret += nr_plt; +	}  out:  	free(name); +	if (ret < 0 && strstr(self->name, " (deleted)") != NULL) +		return 0;  	return ret;  } +static int dso__load_module(struct dso *self, struct mod_dso *mods, const char *name, +			     symbol_filter_t filter, int verbose) +{ +	struct module *mod = mod_dso__find_module(mods, name); +	int err = 0, fd; + +	if (mod == NULL || !mod->active) +		return err; + +	fd = open(mod->path, O_RDONLY); + +	if (fd < 0) +		return err; + +	err = dso__load_sym(self, fd, name, filter, verbose, mod); +	close(fd); + +	return err; +} + +int dso__load_modules(struct dso *self, symbol_filter_t filter, int verbose) +{ +	struct mod_dso *mods = mod_dso__new_dso("modules"); +	struct module *pos; +	struct rb_node *next; +	int err; + +	err = mod_dso__load_modules(mods); + +	if (err <= 0) +		return err; + +	/* +	 * Iterate over modules, and load active symbols. +	 */ +	next = rb_first(&mods->mods); +	while (next) { +		pos = rb_entry(next, struct module, rb_node); +		err = dso__load_module(self, mods, pos->name, filter, verbose); + +		if (err < 0) +			break; + +		next = rb_next(&pos->rb_node); +	} + +	if (err < 0) { +		mod_dso__delete_modules(mods); +		mod_dso__delete_self(mods); +	} + +	return err; +} + +static inline void dso__fill_symbol_holes(struct dso *self) +{ +	struct symbol *prev = NULL; +	struct rb_node *nd; + +	for (nd = rb_last(&self->syms); nd; nd = rb_prev(nd)) { +		struct symbol *pos = rb_entry(nd, struct symbol, rb_node); + +		if (prev) { +			u64 hole = 0; +			int alias = pos->start == prev->start; + +			if (!alias) +				hole = prev->start - pos->end - 1; + +			if (hole || alias) { +				if (alias) +					pos->end = prev->end; +				else if (hole) +					pos->end = prev->start - 1; +			} +		} +		prev = pos; +	} +} +  static int dso__load_vmlinux(struct dso *self, const char *vmlinux,  			     symbol_filter_t filter, int verbose)  { @@ -625,23 +894,33 @@ static int dso__load_vmlinux(struct dso *self, const char *vmlinux,  	if (fd < 0)  		return -1; -	err = dso__load_sym(self, fd, vmlinux, filter, verbose); +	err = dso__load_sym(self, fd, vmlinux, filter, verbose, NULL); + +	if (err > 0) +		dso__fill_symbol_holes(self); +  	close(fd);  	return err;  }  int dso__load_kernel(struct dso *self, const char *vmlinux, -		     symbol_filter_t filter, int verbose) +		     symbol_filter_t filter, int verbose, int modules)  {  	int err = -1; -	if (vmlinux) +	if (vmlinux) {  		err = dso__load_vmlinux(self, vmlinux, filter, verbose); +		if (err > 0 && modules) +			err = dso__load_modules(self, filter, verbose); +	} -	if (err < 0) +	if (err <= 0)  		err = dso__load_kallsyms(self, filter, verbose); +	if (err > 0) +		self->origin = DSO__ORIG_KERNEL; +  	return err;  } diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 2c48ace8203..b53bf0125c1 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -3,8 +3,33 @@  #include <linux/types.h>  #include "types.h" -#include "list.h" -#include "rbtree.h" +#include <linux/list.h> +#include <linux/rbtree.h> +#include "module.h" + +#ifdef HAVE_CPLUS_DEMANGLE +extern char *cplus_demangle(const char *, int); + +static inline char *bfd_demangle(void __used *v, const char *c, int i) +{ +	return cplus_demangle(c, i); +} +#else +#ifdef NO_DEMANGLE +static inline char *bfd_demangle(void __used *v, const char __used *c, +				 int __used i) +{ +	return NULL; +} +#else +#include <bfd.h> +#endif +#endif + +#ifndef DMGL_PARAMS +#define DMGL_PARAMS      (1 << 0)       /* Include function args */ +#define DMGL_ANSI        (1 << 1)       /* Include const, volatile, etc */ +#endif  struct symbol {  	struct rb_node	rb_node; @@ -13,6 +38,7 @@ struct symbol {  	u64		obj_start;  	u64		hist_sum;  	u64		*hist; +	struct module	*module;  	void		*priv;  	char		name[0];  }; @@ -22,7 +48,9 @@ struct dso {  	struct rb_root	 syms;  	struct symbol    *(*find_symbol)(struct dso *, u64 ip);  	unsigned int	 sym_priv_size; -	unsigned char	 prelinked; +	unsigned char	 adjust_symbols; +	unsigned char	 slen_calculated; +	unsigned char	 origin;  	char		 name[0];  }; @@ -41,10 +69,12 @@ static inline void *dso__sym_priv(struct dso *self, struct symbol *sym)  struct symbol *dso__find_symbol(struct dso *self, u64 ip);  int dso__load_kernel(struct dso *self, const char *vmlinux, -		     symbol_filter_t filter, int verbose); +		     symbol_filter_t filter, int verbose, int modules); +int dso__load_modules(struct dso *self, symbol_filter_t filter, int verbose);  int dso__load(struct dso *self, symbol_filter_t filter, int verbose);  size_t dso__fprintf(struct dso *self, FILE *fp); +char dso__symtab_origin(const struct dso *self);  void symbol__init(void);  #endif /* _PERF_SYMBOL_ */ diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index b4be6071c10..68fe157d72f 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -50,6 +50,7 @@  #include <unistd.h>  #include <stdio.h>  #include <sys/stat.h> +#include <sys/statfs.h>  #include <fcntl.h>  #include <stddef.h>  #include <stdlib.h> @@ -80,6 +81,7 @@  #include <netdb.h>  #include <pwd.h>  #include <inttypes.h> +#include "../../../include/linux/magic.h"  #ifndef NO_ICONV  #include <iconv.h> diff --git a/tools/perf/util/wrapper.c b/tools/perf/util/wrapper.c index 6350d65f6d9..4574ac28396 100644 --- a/tools/perf/util/wrapper.c +++ b/tools/perf/util/wrapper.c @@ -7,7 +7,7 @@   * There's no pack memory to release - but stay close to the Git   * version so wrap this away:   */ -static inline void release_pack_memory(size_t size, int flag) +static inline void release_pack_memory(size_t size __used, int flag __used)  {  } @@ -59,7 +59,8 @@ void *xmemdupz(const void *data, size_t len)  char *xstrndup(const char *str, size_t len)  {  	char *p = memchr(str, '\0', len); -	return xmemdupz(str, p ? p - str : len); + +	return xmemdupz(str, p ? (size_t)(p - str) : len);  }  void *xrealloc(void *ptr, size_t size)  |