diff options
Diffstat (limited to 'tools/perf')
78 files changed, 3255 insertions, 1401 deletions
diff --git a/tools/perf/Documentation/perf-bench.txt b/tools/perf/Documentation/perf-bench.txt index a3dbadb26ef..7065cd6fbdf 100644 --- a/tools/perf/Documentation/perf-bench.txt +++ b/tools/perf/Documentation/perf-bench.txt @@ -12,7 +12,7 @@ SYNOPSIS  DESCRIPTION  ----------- -This 'perf bench' command is general framework for benchmark suites. +This 'perf bench' command is a general framework for benchmark suites.  COMMON OPTIONS  -------------- @@ -45,14 +45,20 @@ SUBSYSTEM  'sched'::  	Scheduler and IPC mechanisms. +'mem':: +	Memory access performance. + +'all':: +	All benchmark subsystems. +  SUITES FOR 'sched'  ~~~~~~~~~~~~~~~~~~  *messaging*::  Suite for evaluating performance of scheduler and IPC mechanisms.  Based on hackbench by Rusty Russell. -Options of *pipe* -^^^^^^^^^^^^^^^^^ +Options of *messaging* +^^^^^^^^^^^^^^^^^^^^^^  -p::  --pipe::  Use pipe() instead of socketpair() @@ -115,6 +121,72 @@ Example of *pipe*                  59004 ops/sec  --------------------- +SUITES FOR 'mem' +~~~~~~~~~~~~~~~~ +*memcpy*:: +Suite for evaluating performance of simple memory copy in various ways. + +Options of *memcpy* +^^^^^^^^^^^^^^^^^^^ +-l:: +--length:: +Specify length of memory to copy (default: 1MB). +Available units are B, KB, MB, GB and TB (case insensitive). + +-r:: +--routine:: +Specify routine to copy (default: default). +Available routines are depend on the architecture. +On x86-64, x86-64-unrolled, x86-64-movsq and x86-64-movsb are supported. + +-i:: +--iterations:: +Repeat memcpy invocation this number of times. + +-c:: +--cycle:: +Use perf's cpu-cycles event instead of gettimeofday syscall. + +-o:: +--only-prefault:: +Show only the result with page faults before memcpy. + +-n:: +--no-prefault:: +Show only the result without page faults before memcpy. + +*memset*:: +Suite for evaluating performance of simple memory set in various ways. + +Options of *memset* +^^^^^^^^^^^^^^^^^^^ +-l:: +--length:: +Specify length of memory to set (default: 1MB). +Available units are B, KB, MB, GB and TB (case insensitive). + +-r:: +--routine:: +Specify routine to set (default: default). +Available routines are depend on the architecture. +On x86-64, x86-64-unrolled, x86-64-stosq and x86-64-stosb are supported. + +-i:: +--iterations:: +Repeat memset invocation this number of times. + +-c:: +--cycle:: +Use perf's cpu-cycles event instead of gettimeofday syscall. + +-o:: +--only-prefault:: +Show only the result with page faults before memset. + +-n:: +--no-prefault:: +Show only the result without page faults before memset. +  SEE ALSO  --------  linkperf:perf[1] diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index 2d89f02719b..495210a612c 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt @@ -57,7 +57,7 @@ OPTIONS  -s::  --sort=:: -	Sort by key(s): pid, comm, dso, symbol, parent. +	Sort by key(s): pid, comm, dso, symbol, parent, srcline.  -p::  --parent=<regex>:: diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt index 4a5680cb242..5b80d84d6b4 100644 --- a/tools/perf/Documentation/perf-top.txt +++ b/tools/perf/Documentation/perf-top.txt @@ -112,7 +112,7 @@ Default is to monitor all CPUS.  -s::  --sort:: -	Sort by key(s): pid, comm, dso, symbol, parent +	Sort by key(s): pid, comm, dso, symbol, parent, srcline.  -n::  --show-nr-samples:: diff --git a/tools/perf/Documentation/perfconfig.example b/tools/perf/Documentation/perfconfig.example index 42c6fd2ae85..767ea2436e1 100644 --- a/tools/perf/Documentation/perfconfig.example +++ b/tools/perf/Documentation/perfconfig.example @@ -19,3 +19,11 @@  	# Default, disable using /dev/null  	dir = /root/.debug + +[annotate] + +	# Defaults +	hide_src_code = false +	use_offset = true +	jump_arrows = true +	show_nr_jumps = false diff --git a/tools/perf/MANIFEST b/tools/perf/MANIFEST index 5476bc0a1ea..b4b572e8c10 100644 --- a/tools/perf/MANIFEST +++ b/tools/perf/MANIFEST @@ -1,4 +1,6 @@  tools/perf +tools/scripts +tools/lib/traceevent  include/linux/const.h  include/linux/perf_event.h  include/linux/rbtree.h diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 1d3d513beb9..75d74e5db8d 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -80,7 +80,7 @@ ifeq ("$(origin DEBUG)", "command line")    PERF_DEBUG = $(DEBUG)  endif  ifndef PERF_DEBUG -  CFLAGS_OPTIMIZE = -O6 +  CFLAGS_OPTIMIZE = -O6 -D_FORTIFY_SOURCE=2  endif  ifdef PARSER_DEBUG @@ -89,7 +89,7 @@ ifdef PARSER_DEBUG  	PARSER_DEBUG_CFLAGS := -DPARSER_DEBUG  endif -CFLAGS = -fno-omit-frame-pointer -ggdb3 -Wall -Wextra -std=gnu99 $(CFLAGS_WERROR) $(CFLAGS_OPTIMIZE) -D_FORTIFY_SOURCE=2 $(EXTRA_WARNINGS) $(EXTRA_CFLAGS) $(PARSER_DEBUG_CFLAGS) +CFLAGS = -fno-omit-frame-pointer -ggdb3 -Wall -Wextra -std=gnu99 $(CFLAGS_WERROR) $(CFLAGS_OPTIMIZE) $(EXTRA_WARNINGS) $(EXTRA_CFLAGS) $(PARSER_DEBUG_CFLAGS)  EXTLIBS = -lpthread -lrt -lelf -lm  ALL_CFLAGS = $(CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE  ALL_LDFLAGS = $(LDFLAGS) @@ -155,7 +155,7 @@ endif  ### --- END CONFIGURATION SECTION --- -BASIC_CFLAGS = -Iutil/include -Iarch/$(ARCH)/include -I$(OUTPUT)/util -I$(TRACE_EVENT_DIR) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE +BASIC_CFLAGS = -Iutil/include -Iarch/$(ARCH)/include -I$(OUTPUT)util -I$(TRACE_EVENT_DIR) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE  BASIC_LDFLAGS =  # Guard against environment variables @@ -503,6 +503,7 @@ else  		LIB_OBJS += $(OUTPUT)ui/progress.o  		LIB_OBJS += $(OUTPUT)ui/util.o  		LIB_OBJS += $(OUTPUT)ui/tui/setup.o +		LIB_OBJS += $(OUTPUT)ui/tui/util.o  		LIB_H += ui/browser.h  		LIB_H += ui/browsers/map.h  		LIB_H += ui/helpline.h @@ -522,13 +523,18 @@ else  		msg := $(warning GTK2 not found, disables GTK2 support. Please install gtk2-devel or libgtk2.0-dev);  		BASIC_CFLAGS += -DNO_GTK2_SUPPORT  	else +		ifeq ($(call try-cc,$(SOURCE_GTK2_INFOBAR),$(FLAGS_GTK2)),y) +			BASIC_CFLAGS += -DHAVE_GTK_INFO_BAR +		endif  		BASIC_CFLAGS += $(shell pkg-config --cflags gtk+-2.0)  		EXTLIBS += $(shell pkg-config --libs gtk+-2.0)  		LIB_OBJS += $(OUTPUT)ui/gtk/browser.o  		LIB_OBJS += $(OUTPUT)ui/gtk/setup.o +		LIB_OBJS += $(OUTPUT)ui/gtk/util.o  		# Make sure that it'd be included only once.  		ifneq ($(findstring -DNO_NEWT_SUPPORT,$(BASIC_CFLAGS)),)  			LIB_OBJS += $(OUTPUT)ui/setup.o +			LIB_OBJS += $(OUTPUT)ui/util.o  		endif  	endif  endif diff --git a/tools/perf/bench/mem-memcpy.c b/tools/perf/bench/mem-memcpy.c index 71557225bf9..02dad5d3359 100644 --- a/tools/perf/bench/mem-memcpy.c +++ b/tools/perf/bench/mem-memcpy.c @@ -24,21 +24,21 @@  static const char	*length_str	= "1MB";  static const char	*routine	= "default";  static int		iterations	= 1; -static bool		use_clock; -static int		clock_fd; +static bool		use_cycle; +static int		cycle_fd;  static bool		only_prefault;  static bool		no_prefault;  static const struct option options[] = {  	OPT_STRING('l', "length", &length_str, "1MB",  		    "Specify length of memory to copy. " -		    "available unit: B, MB, GB (upper and lower)"), +		    "Available units: B, KB, MB, GB and TB (upper and lower)"),  	OPT_STRING('r', "routine", &routine, "default",  		    "Specify routine to copy"),  	OPT_INTEGER('i', "iterations", &iterations,  		    "repeat memcpy() invocation this number of times"), -	OPT_BOOLEAN('c', "clock", &use_clock, -		    "Use CPU clock for measuring"), +	OPT_BOOLEAN('c', "cycle", &use_cycle, +		    "Use cycles event instead of gettimeofday() for measuring"),  	OPT_BOOLEAN('o', "only-prefault", &only_prefault,  		    "Show only the result with page faults before memcpy()"),  	OPT_BOOLEAN('n', "no-prefault", &no_prefault, @@ -76,27 +76,27 @@ static const char * const bench_mem_memcpy_usage[] = {  	NULL  }; -static struct perf_event_attr clock_attr = { +static struct perf_event_attr cycle_attr = {  	.type		= PERF_TYPE_HARDWARE,  	.config		= PERF_COUNT_HW_CPU_CYCLES  }; -static void init_clock(void) +static void init_cycle(void)  { -	clock_fd = sys_perf_event_open(&clock_attr, getpid(), -1, -1, 0); +	cycle_fd = sys_perf_event_open(&cycle_attr, getpid(), -1, -1, 0); -	if (clock_fd < 0 && errno == ENOSYS) +	if (cycle_fd < 0 && errno == ENOSYS)  		die("No CONFIG_PERF_EVENTS=y kernel support configured?\n");  	else -		BUG_ON(clock_fd < 0); +		BUG_ON(cycle_fd < 0);  } -static u64 get_clock(void) +static u64 get_cycle(void)  {  	int ret;  	u64 clk; -	ret = read(clock_fd, &clk, sizeof(u64)); +	ret = read(cycle_fd, &clk, sizeof(u64));  	BUG_ON(ret != sizeof(u64));  	return clk; @@ -119,9 +119,9 @@ static void alloc_mem(void **dst, void **src, size_t length)  		die("memory allocation failed - maybe length is too large?\n");  } -static u64 do_memcpy_clock(memcpy_t fn, size_t len, bool prefault) +static u64 do_memcpy_cycle(memcpy_t fn, size_t len, bool prefault)  { -	u64 clock_start = 0ULL, clock_end = 0ULL; +	u64 cycle_start = 0ULL, cycle_end = 0ULL;  	void *src = NULL, *dst = NULL;  	int i; @@ -130,14 +130,14 @@ static u64 do_memcpy_clock(memcpy_t fn, size_t len, bool prefault)  	if (prefault)  		fn(dst, src, len); -	clock_start = get_clock(); +	cycle_start = get_cycle();  	for (i = 0; i < iterations; ++i)  		fn(dst, src, len); -	clock_end = get_clock(); +	cycle_end = get_cycle();  	free(src);  	free(dst); -	return clock_end - clock_start; +	return cycle_end - cycle_start;  }  static double do_memcpy_gettimeofday(memcpy_t fn, size_t len, bool prefault) @@ -182,17 +182,17 @@ int bench_mem_memcpy(int argc, const char **argv,  	int i;  	size_t len;  	double result_bps[2]; -	u64 result_clock[2]; +	u64 result_cycle[2];  	argc = parse_options(argc, argv, options,  			     bench_mem_memcpy_usage, 0); -	if (use_clock) -		init_clock(); +	if (use_cycle) +		init_cycle();  	len = (size_t)perf_atoll((char *)length_str); -	result_clock[0] = result_clock[1] = 0ULL; +	result_cycle[0] = result_cycle[1] = 0ULL;  	result_bps[0] = result_bps[1] = 0.0;  	if ((s64)len <= 0) { @@ -223,11 +223,11 @@ int bench_mem_memcpy(int argc, const char **argv,  	if (!only_prefault && !no_prefault) {  		/* show both of results */ -		if (use_clock) { -			result_clock[0] = -				do_memcpy_clock(routines[i].fn, len, false); -			result_clock[1] = -				do_memcpy_clock(routines[i].fn, len, true); +		if (use_cycle) { +			result_cycle[0] = +				do_memcpy_cycle(routines[i].fn, len, false); +			result_cycle[1] = +				do_memcpy_cycle(routines[i].fn, len, true);  		} else {  			result_bps[0] =  				do_memcpy_gettimeofday(routines[i].fn, @@ -237,9 +237,9 @@ int bench_mem_memcpy(int argc, const char **argv,  						len, true);  		}  	} else { -		if (use_clock) { -			result_clock[pf] = -				do_memcpy_clock(routines[i].fn, +		if (use_cycle) { +			result_cycle[pf] = +				do_memcpy_cycle(routines[i].fn,  						len, only_prefault);  		} else {  			result_bps[pf] = @@ -251,12 +251,12 @@ int bench_mem_memcpy(int argc, const char **argv,  	switch (bench_format) {  	case BENCH_FORMAT_DEFAULT:  		if (!only_prefault && !no_prefault) { -			if (use_clock) { -				printf(" %14lf Clock/Byte\n", -					(double)result_clock[0] +			if (use_cycle) { +				printf(" %14lf Cycle/Byte\n", +					(double)result_cycle[0]  					/ (double)len); -				printf(" %14lf Clock/Byte (with prefault)\n", -					(double)result_clock[1] +				printf(" %14lf Cycle/Byte (with prefault)\n", +					(double)result_cycle[1]  					/ (double)len);  			} else {  				print_bps(result_bps[0]); @@ -265,9 +265,9 @@ int bench_mem_memcpy(int argc, const char **argv,  				printf(" (with prefault)\n");  			}  		} else { -			if (use_clock) { -				printf(" %14lf Clock/Byte", -					(double)result_clock[pf] +			if (use_cycle) { +				printf(" %14lf Cycle/Byte", +					(double)result_cycle[pf]  					/ (double)len);  			} else  				print_bps(result_bps[pf]); @@ -277,17 +277,17 @@ int bench_mem_memcpy(int argc, const char **argv,  		break;  	case BENCH_FORMAT_SIMPLE:  		if (!only_prefault && !no_prefault) { -			if (use_clock) { +			if (use_cycle) {  				printf("%lf %lf\n", -					(double)result_clock[0] / (double)len, -					(double)result_clock[1] / (double)len); +					(double)result_cycle[0] / (double)len, +					(double)result_cycle[1] / (double)len);  			} else {  				printf("%lf %lf\n",  					result_bps[0], result_bps[1]);  			}  		} else { -			if (use_clock) { -				printf("%lf\n", (double)result_clock[pf] +			if (use_cycle) { +				printf("%lf\n", (double)result_cycle[pf]  					/ (double)len);  			} else  				printf("%lf\n", result_bps[pf]); diff --git a/tools/perf/bench/mem-memset.c b/tools/perf/bench/mem-memset.c index e9079185bd7..350cc955726 100644 --- a/tools/perf/bench/mem-memset.c +++ b/tools/perf/bench/mem-memset.c @@ -24,21 +24,21 @@  static const char	*length_str	= "1MB";  static const char	*routine	= "default";  static int		iterations	= 1; -static bool		use_clock; -static int		clock_fd; +static bool		use_cycle; +static int		cycle_fd;  static bool		only_prefault;  static bool		no_prefault;  static const struct option options[] = {  	OPT_STRING('l', "length", &length_str, "1MB", -		    "Specify length of memory to copy. " -		    "available unit: B, MB, GB (upper and lower)"), +		    "Specify length of memory to set. " +		    "Available units: B, KB, MB, GB and TB (upper and lower)"),  	OPT_STRING('r', "routine", &routine, "default", -		    "Specify routine to copy"), +		    "Specify routine to set"),  	OPT_INTEGER('i', "iterations", &iterations,  		    "repeat memset() invocation this number of times"), -	OPT_BOOLEAN('c', "clock", &use_clock, -		    "Use CPU clock for measuring"), +	OPT_BOOLEAN('c', "cycle", &use_cycle, +		    "Use cycles event instead of gettimeofday() for measuring"),  	OPT_BOOLEAN('o', "only-prefault", &only_prefault,  		    "Show only the result with page faults before memset()"),  	OPT_BOOLEAN('n', "no-prefault", &no_prefault, @@ -76,27 +76,27 @@ static const char * const bench_mem_memset_usage[] = {  	NULL  }; -static struct perf_event_attr clock_attr = { +static struct perf_event_attr cycle_attr = {  	.type		= PERF_TYPE_HARDWARE,  	.config		= PERF_COUNT_HW_CPU_CYCLES  }; -static void init_clock(void) +static void init_cycle(void)  { -	clock_fd = sys_perf_event_open(&clock_attr, getpid(), -1, -1, 0); +	cycle_fd = sys_perf_event_open(&cycle_attr, getpid(), -1, -1, 0); -	if (clock_fd < 0 && errno == ENOSYS) +	if (cycle_fd < 0 && errno == ENOSYS)  		die("No CONFIG_PERF_EVENTS=y kernel support configured?\n");  	else -		BUG_ON(clock_fd < 0); +		BUG_ON(cycle_fd < 0);  } -static u64 get_clock(void) +static u64 get_cycle(void)  {  	int ret;  	u64 clk; -	ret = read(clock_fd, &clk, sizeof(u64)); +	ret = read(cycle_fd, &clk, sizeof(u64));  	BUG_ON(ret != sizeof(u64));  	return clk; @@ -115,9 +115,9 @@ static void alloc_mem(void **dst, size_t length)  		die("memory allocation failed - maybe length is too large?\n");  } -static u64 do_memset_clock(memset_t fn, size_t len, bool prefault) +static u64 do_memset_cycle(memset_t fn, size_t len, bool prefault)  { -	u64 clock_start = 0ULL, clock_end = 0ULL; +	u64 cycle_start = 0ULL, cycle_end = 0ULL;  	void *dst = NULL;  	int i; @@ -126,13 +126,13 @@ static u64 do_memset_clock(memset_t fn, size_t len, bool prefault)  	if (prefault)  		fn(dst, -1, len); -	clock_start = get_clock(); +	cycle_start = get_cycle();  	for (i = 0; i < iterations; ++i)  		fn(dst, i, len); -	clock_end = get_clock(); +	cycle_end = get_cycle();  	free(dst); -	return clock_end - clock_start; +	return cycle_end - cycle_start;  }  static double do_memset_gettimeofday(memset_t fn, size_t len, bool prefault) @@ -176,17 +176,17 @@ int bench_mem_memset(int argc, const char **argv,  	int i;  	size_t len;  	double result_bps[2]; -	u64 result_clock[2]; +	u64 result_cycle[2];  	argc = parse_options(argc, argv, options,  			     bench_mem_memset_usage, 0); -	if (use_clock) -		init_clock(); +	if (use_cycle) +		init_cycle();  	len = (size_t)perf_atoll((char *)length_str); -	result_clock[0] = result_clock[1] = 0ULL; +	result_cycle[0] = result_cycle[1] = 0ULL;  	result_bps[0] = result_bps[1] = 0.0;  	if ((s64)len <= 0) { @@ -217,11 +217,11 @@ int bench_mem_memset(int argc, const char **argv,  	if (!only_prefault && !no_prefault) {  		/* show both of results */ -		if (use_clock) { -			result_clock[0] = -				do_memset_clock(routines[i].fn, len, false); -			result_clock[1] = -				do_memset_clock(routines[i].fn, len, true); +		if (use_cycle) { +			result_cycle[0] = +				do_memset_cycle(routines[i].fn, len, false); +			result_cycle[1] = +				do_memset_cycle(routines[i].fn, len, true);  		} else {  			result_bps[0] =  				do_memset_gettimeofday(routines[i].fn, @@ -231,9 +231,9 @@ int bench_mem_memset(int argc, const char **argv,  						len, true);  		}  	} else { -		if (use_clock) { -			result_clock[pf] = -				do_memset_clock(routines[i].fn, +		if (use_cycle) { +			result_cycle[pf] = +				do_memset_cycle(routines[i].fn,  						len, only_prefault);  		} else {  			result_bps[pf] = @@ -245,12 +245,12 @@ int bench_mem_memset(int argc, const char **argv,  	switch (bench_format) {  	case BENCH_FORMAT_DEFAULT:  		if (!only_prefault && !no_prefault) { -			if (use_clock) { -				printf(" %14lf Clock/Byte\n", -					(double)result_clock[0] +			if (use_cycle) { +				printf(" %14lf Cycle/Byte\n", +					(double)result_cycle[0]  					/ (double)len); -				printf(" %14lf Clock/Byte (with prefault)\n ", -					(double)result_clock[1] +				printf(" %14lf Cycle/Byte (with prefault)\n ", +					(double)result_cycle[1]  					/ (double)len);  			} else {  				print_bps(result_bps[0]); @@ -259,9 +259,9 @@ int bench_mem_memset(int argc, const char **argv,  				printf(" (with prefault)\n");  			}  		} else { -			if (use_clock) { -				printf(" %14lf Clock/Byte", -					(double)result_clock[pf] +			if (use_cycle) { +				printf(" %14lf Cycle/Byte", +					(double)result_cycle[pf]  					/ (double)len);  			} else  				print_bps(result_bps[pf]); @@ -271,17 +271,17 @@ int bench_mem_memset(int argc, const char **argv,  		break;  	case BENCH_FORMAT_SIMPLE:  		if (!only_prefault && !no_prefault) { -			if (use_clock) { +			if (use_cycle) {  				printf("%lf %lf\n", -					(double)result_clock[0] / (double)len, -					(double)result_clock[1] / (double)len); +					(double)result_cycle[0] / (double)len, +					(double)result_cycle[1] / (double)len);  			} else {  				printf("%lf %lf\n",  					result_bps[0], result_bps[1]);  			}  		} else { -			if (use_clock) { -				printf("%lf\n", (double)result_clock[pf] +			if (use_cycle) { +				printf("%lf\n", (double)result_cycle[pf]  					/ (double)len);  			} else  				printf("%lf\n", result_bps[pf]); diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 806e0a28663..67522cf8740 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -215,7 +215,7 @@ static int __cmd_annotate(struct perf_annotate *ann)  	}  	if (total_nr_samples == 0) { -		ui__warning("The %s file has no samples!\n", session->filename); +		ui__error("The %s file has no samples!\n", session->filename);  		goto out_delete;  	}  out_delete: diff --git a/tools/perf/builtin-bench.c b/tools/perf/builtin-bench.c index b0e74ab2d7a..1f310021644 100644 --- a/tools/perf/builtin-bench.c +++ b/tools/perf/builtin-bench.c @@ -33,7 +33,7 @@ struct bench_suite {  };  						\  /* sentinel: easy for help */ -#define suite_all { "all", "test all suite (pseudo suite)", NULL } +#define suite_all { "all", "Test all benchmark suites", NULL }  static struct bench_suite sched_suites[] = {  	{ "messaging", @@ -75,7 +75,7 @@ static struct bench_subsys subsystems[] = {  	  "memory access performance",  	  mem_suites },  	{ "all",		/* sentinel: easy for help */ -	  "test all subsystem (pseudo subsystem)", +	  "all benchmark subsystem",  	  NULL },  	{ NULL,  	  NULL, diff --git a/tools/perf/builtin-evlist.c b/tools/perf/builtin-evlist.c index e52d77ec708..0dd5a058f76 100644 --- a/tools/perf/builtin-evlist.c +++ b/tools/perf/builtin-evlist.c @@ -60,7 +60,7 @@ static int __cmd_evlist(const char *input_name, struct perf_attr_details *detail  	list_for_each_entry(pos, &session->evlist->entries, node) {  		bool first = true; -		printf("%s", event_name(pos)); +		printf("%s", perf_evsel__name(pos));  		if (details->verbose || details->freq) {  			comma_printf(&first, " sample_freq=%" PRIu64, @@ -116,7 +116,7 @@ static const char * const evlist_usage[] = {  int cmd_evlist(int argc, const char **argv, const char *prefix __used)  {  	struct perf_attr_details details = { .verbose = false, }; -	const char *input_name; +	const char *input_name = NULL;  	const struct option options[] = {  		OPT_STRING('i', "input", &input_name, "file",  			    "Input file name"), diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index 547af48deb4..ce35015f2dc 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c @@ -57,6 +57,11 @@ static unsigned long nr_allocs, nr_cross_allocs;  #define PATH_SYS_NODE	"/sys/devices/system/node" +struct perf_kmem { +	struct perf_tool    tool; +	struct perf_session *session; +}; +  static void init_cpunode_map(void)  {  	FILE *fp; @@ -278,14 +283,16 @@ static void process_free_event(void *data,  	s_alloc->alloc_cpu = -1;  } -static void process_raw_event(union perf_event *raw_event __used, void *data, +static void process_raw_event(struct perf_tool *tool, +			      union perf_event *raw_event __used, void *data,  			      int cpu, u64 timestamp, struct thread *thread)  { +	struct perf_kmem *kmem = container_of(tool, struct perf_kmem, tool);  	struct event_format *event;  	int type; -	type = trace_parse_common_type(data); -	event = trace_find_event(type); +	type = trace_parse_common_type(kmem->session->pevent, data); +	event = pevent_find_event(kmem->session->pevent, type);  	if (!strcmp(event->name, "kmalloc") ||  	    !strcmp(event->name, "kmem_cache_alloc")) { @@ -306,7 +313,7 @@ static void process_raw_event(union perf_event *raw_event __used, void *data,  	}  } -static int process_sample_event(struct perf_tool *tool __used, +static int process_sample_event(struct perf_tool *tool,  				union perf_event *event,  				struct perf_sample *sample,  				struct perf_evsel *evsel __used, @@ -322,16 +329,18 @@ static int process_sample_event(struct perf_tool *tool __used,  	dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); -	process_raw_event(event, sample->raw_data, sample->cpu, +	process_raw_event(tool, event, sample->raw_data, sample->cpu,  			  sample->time, thread);  	return 0;  } -static struct perf_tool perf_kmem = { -	.sample			= process_sample_event, -	.comm			= perf_event__process_comm, -	.ordered_samples	= true, +static struct perf_kmem perf_kmem = { +	.tool = { +		.sample			= process_sample_event, +		.comm			= perf_event__process_comm, +		.ordered_samples	= true, +	},  };  static double fragmentation(unsigned long n_req, unsigned long n_alloc) @@ -486,11 +495,15 @@ static void sort_result(void)  static int __cmd_kmem(void)  {  	int err = -EINVAL; -	struct perf_session *session = perf_session__new(input_name, O_RDONLY, -							 0, false, &perf_kmem); +	struct perf_session *session; + +	session = perf_session__new(input_name, O_RDONLY, 0, false, +				    &perf_kmem.tool);  	if (session == NULL)  		return -ENOMEM; +	perf_kmem.session = session; +  	if (perf_session__create_kernel_maps(session) < 0)  		goto out_delete; @@ -498,7 +511,7 @@ static int __cmd_kmem(void)  		goto out_delete;  	setup_pager(); -	err = perf_session__process_events(session, &perf_kmem); +	err = perf_session__process_events(session, &perf_kmem.tool);  	if (err != 0)  		goto out_delete;  	sort_result(); diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index fd53319de20..b3c42854886 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -724,8 +724,8 @@ process_raw_event(void *data, int cpu, u64 timestamp, struct thread *thread)  	struct event_format *event;  	int type; -	type = trace_parse_common_type(data); -	event = trace_find_event(type); +	type = trace_parse_common_type(session->pevent, data); +	event = pevent_find_event(session->pevent, type);  	if (!strcmp(event->name, "lock_acquire"))  		process_lock_acquire_event(data, event, cpu, timestamp, thread); diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index e5cb08427e1..f5a6452931e 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -264,8 +264,8 @@ try_again:  			}  			if (err == ENOENT) { -				ui__warning("The %s event is not supported.\n", -					    event_name(pos)); +				ui__error("The %s event is not supported.\n", +					  perf_evsel__name(pos));  				exit(EXIT_FAILURE);  			} @@ -858,8 +858,8 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)  		usage_with_options(record_usage, record_options);  	if (rec->force && rec->append_file) { -		fprintf(stderr, "Can't overwrite and append at the same time." -				" You need to choose between -f and -A"); +		ui__error("Can't overwrite and append at the same time." +			  " You need to choose between -f and -A");  		usage_with_options(record_usage, record_options);  	} else if (rec->append_file) {  		rec->write_mode = WRITE_APPEND; @@ -868,8 +868,8 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)  	}  	if (nr_cgroups && !rec->opts.target.system_wide) { -		fprintf(stderr, "cgroup monitoring only available in" -			" system-wide mode\n"); +		ui__error("cgroup monitoring only available in" +			  " system-wide mode\n");  		usage_with_options(record_usage, record_options);  	} @@ -905,7 +905,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)  		int saved_errno = errno;  		perf_target__strerror(&rec->opts.target, err, errbuf, BUFSIZ); -		ui__warning("%s", errbuf); +		ui__error("%s", errbuf);  		err = -saved_errno;  		goto out_free_fd; @@ -916,7 +916,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)  		usage_with_options(record_usage, record_options);  	list_for_each_entry(pos, &evsel_list->entries, node) { -		if (perf_header__push_event(pos->attr.config, event_name(pos))) +		if (perf_header__push_event(pos->attr.config, perf_evsel__name(pos)))  			goto out_free_fd;  	} @@ -933,7 +933,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)  	else if (rec->opts.freq) {  		rec->opts.default_interval = rec->opts.freq;  	} else { -		fprintf(stderr, "frequency and count are zero, aborting\n"); +		ui__error("frequency and count are zero, aborting\n");  		err = -EINVAL;  		goto out_free_fd;  	} diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index d58e41445d0..69b1c118515 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -69,7 +69,7 @@ static int perf_report__add_branch_hist_entry(struct perf_tool *tool,  	if ((sort__has_parent || symbol_conf.use_callchain)  	    && sample->callchain) { -		err = machine__resolve_callchain(machine, evsel, al->thread, +		err = machine__resolve_callchain(machine, al->thread,  						 sample->callchain, &parent);  		if (err)  			return err; @@ -140,7 +140,7 @@ static int perf_evsel__add_hist_entry(struct perf_evsel *evsel,  	struct hist_entry *he;  	if ((sort__has_parent || symbol_conf.use_callchain) && sample->callchain) { -		err = machine__resolve_callchain(machine, evsel, al->thread, +		err = machine__resolve_callchain(machine, al->thread,  						 sample->callchain, &parent);  		if (err)  			return err; @@ -152,7 +152,7 @@ static int perf_evsel__add_hist_entry(struct perf_evsel *evsel,  	if (symbol_conf.use_callchain) {  		err = callchain_append(he->callchain, -				       &evsel->hists.callchain_cursor, +				       &callchain_cursor,  				       sample->period);  		if (err)  			return err; @@ -162,7 +162,7 @@ static int perf_evsel__add_hist_entry(struct perf_evsel *evsel,  	 * so we don't allocated the extra space needed because the stdio  	 * code will not use it.  	 */ -	if (al->sym != NULL && use_browser > 0) { +	if (he->ms.sym != NULL && use_browser > 0) {  		struct annotation *notes = symbol__annotation(he->ms.sym);  		assert(evsel != NULL); @@ -230,7 +230,7 @@ static int process_read_event(struct perf_tool *tool,  	struct perf_report *rep = container_of(tool, struct perf_report, tool);  	if (rep->show_threads) { -		const char *name = evsel ? event_name(evsel) : "unknown"; +		const char *name = evsel ? perf_evsel__name(evsel) : "unknown";  		perf_read_values_add_value(&rep->show_threads_values,  					   event->read.pid, event->read.tid,  					   event->read.id, @@ -239,25 +239,26 @@ static int process_read_event(struct perf_tool *tool,  	}  	dump_printf(": %d %d %s %" PRIu64 "\n", event->read.pid, event->read.tid, -		    evsel ? event_name(evsel) : "FAIL", +		    evsel ? perf_evsel__name(evsel) : "FAIL",  		    event->read.value);  	return 0;  } +/* For pipe mode, sample_type is not currently set */  static int perf_report__setup_sample_type(struct perf_report *rep)  {  	struct perf_session *self = rep->session; -	if (!(self->sample_type & PERF_SAMPLE_CALLCHAIN)) { +	if (!self->fd_pipe && !(self->sample_type & PERF_SAMPLE_CALLCHAIN)) {  		if (sort__has_parent) { -			ui__warning("Selected --sort parent, but no " +			ui__error("Selected --sort parent, but no "  				    "callchain data. Did you call "  				    "'perf record' without -g?\n");  			return -EINVAL;  		}  		if (symbol_conf.use_callchain) { -			ui__warning("Selected -g but no callchain data. Did " +			ui__error("Selected -g but no callchain data. Did "  				    "you call 'perf record' without -g?\n");  			return -1;  		} @@ -266,17 +267,16 @@ static int perf_report__setup_sample_type(struct perf_report *rep)  		   !symbol_conf.use_callchain) {  			symbol_conf.use_callchain = true;  			if (callchain_register_param(&callchain_param) < 0) { -				ui__warning("Can't register callchain " -					    "params.\n"); +				ui__error("Can't register callchain params.\n");  				return -EINVAL;  			}  	}  	if (sort__branch_mode == 1) { -		if (!(self->sample_type & PERF_SAMPLE_BRANCH_STACK)) { -			fprintf(stderr, "selected -b but no branch data." -					" Did you call perf record without" -					" -b?\n"); +		if (!self->fd_pipe && +		    !(self->sample_type & PERF_SAMPLE_BRANCH_STACK)) { +			ui__error("Selected -b but no branch data. " +				  "Did you call perf record without -b?\n");  			return -1;  		}  	} @@ -316,7 +316,7 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist,  	list_for_each_entry(pos, &evlist->entries, node) {  		struct hists *hists = &pos->hists; -		const char *evname = event_name(pos); +		const char *evname = perf_evsel__name(pos);  		hists__fprintf_nr_sample_events(hists, evname, stdout);  		hists__fprintf(hists, NULL, false, true, 0, 0, stdout); @@ -420,7 +420,7 @@ static int __cmd_report(struct perf_report *rep)  	}  	if (nr_samples == 0) { -		ui__warning("The %s file has no samples!\n", session->filename); +		ui__error("The %s file has no samples!\n", session->filename);  		goto out_delete;  	} diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index b125e07eb39..7a9ad2b1ee7 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -43,6 +43,11 @@ static u64			sleep_measurement_overhead;  static unsigned long		nr_tasks; +struct perf_sched { +	struct perf_tool    tool; +	struct perf_session *session; +}; +  struct sched_atom;  struct task_desc { @@ -1597,11 +1602,13 @@ static int perf_sched__process_tracepoint_sample(struct perf_tool *tool,  						 struct perf_evsel *evsel,  						 struct machine *machine)  { +	struct perf_sched *sched = container_of(tool, struct perf_sched, tool); +	struct pevent *pevent = sched->session->pevent;  	struct thread *thread = machine__findnew_thread(machine, sample->pid);  	if (thread == NULL) {  		pr_debug("problem processing %s event, skipping it.\n", -			 evsel->name); +			 perf_evsel__name(evsel));  		return -1;  	} @@ -1612,7 +1619,8 @@ static int perf_sched__process_tracepoint_sample(struct perf_tool *tool,  		tracepoint_handler f = evsel->handler.func;  		if (evsel->handler.data == NULL) -			evsel->handler.data = trace_find_event(evsel->attr.config); +			evsel->handler.data = pevent_find_event(pevent, +							  evsel->attr.config);  		f(tool, evsel->handler.data, sample, machine, thread);  	} @@ -1620,12 +1628,14 @@ static int perf_sched__process_tracepoint_sample(struct perf_tool *tool,  	return 0;  } -static struct perf_tool perf_sched = { -	.sample			= perf_sched__process_tracepoint_sample, -	.comm			= perf_event__process_comm, -	.lost			= perf_event__process_lost, -	.fork			= perf_event__process_task, -	.ordered_samples	= true, +static struct perf_sched perf_sched = { +	.tool = { +		.sample		 = perf_sched__process_tracepoint_sample, +		.comm		 = perf_event__process_comm, +		.lost		 = perf_event__process_lost, +		.fork		 = perf_event__process_task, +		.ordered_samples = true, +	},  };  static void read_events(bool destroy, struct perf_session **psession) @@ -1640,16 +1650,20 @@ static void read_events(bool destroy, struct perf_session **psession)  		{ "sched:sched_process_exit", process_sched_exit_event, },  		{ "sched:sched_migrate_task", process_sched_migrate_task_event, },  	}; -	struct perf_session *session = perf_session__new(input_name, O_RDONLY, -							 0, false, &perf_sched); +	struct perf_session *session; + +	session = perf_session__new(input_name, O_RDONLY, 0, false, +				    &perf_sched.tool);  	if (session == NULL)  		die("No Memory"); -	err = perf_evlist__set_tracepoints_handlers_array(session->evlist, handlers); +	perf_sched.session = session; + +	err = perf_session__set_tracepoints_handlers(session, handlers);  	assert(err == 0);  	if (perf_session__has_traces(session, "record -R")) { -		err = perf_session__process_events(session, &perf_sched); +		err = perf_session__process_events(session, &perf_sched.tool);  		if (err)  			die("Failed to process events, error %d", err); diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 8e395a538eb..1e60ab70b2b 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -28,6 +28,11 @@ static bool			system_wide;  static const char		*cpu_list;  static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); +struct perf_script { +	struct perf_tool    tool; +	struct perf_session *session; +}; +  enum perf_output_field {  	PERF_OUTPUT_COMM            = 1U << 0,  	PERF_OUTPUT_TID             = 1U << 1, @@ -137,10 +142,11 @@ static const char *output_field2str(enum perf_output_field field)  #define PRINT_FIELD(x)  (output[attr->type].fields & PERF_OUTPUT_##x) -static int perf_event_attr__check_stype(struct perf_event_attr *attr, -				  u64 sample_type, const char *sample_msg, -				  enum perf_output_field field) +static int perf_evsel__check_stype(struct perf_evsel *evsel, +				   u64 sample_type, const char *sample_msg, +				   enum perf_output_field field)  { +	struct perf_event_attr *attr = &evsel->attr;  	int type = attr->type;  	const char *evname; @@ -148,7 +154,7 @@ static int perf_event_attr__check_stype(struct perf_event_attr *attr,  		return 0;  	if (output[type].user_set) { -		evname = __event_name(attr->type, attr->config); +		evname = perf_evsel__name(evsel);  		pr_err("Samples for '%s' event do not have %s attribute set. "  		       "Cannot print '%s' field.\n",  		       evname, sample_msg, output_field2str(field)); @@ -157,7 +163,7 @@ static int perf_event_attr__check_stype(struct perf_event_attr *attr,  	/* user did not ask for it explicitly so remove from the default list */  	output[type].fields &= ~field; -	evname = __event_name(attr->type, attr->config); +	evname = perf_evsel__name(evsel);  	pr_debug("Samples for '%s' event do not have %s attribute set. "  		 "Skipping '%s' field.\n",  		 evname, sample_msg, output_field2str(field)); @@ -175,8 +181,8 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel,  		return -EINVAL;  	if (PRINT_FIELD(IP)) { -		if (perf_event_attr__check_stype(attr, PERF_SAMPLE_IP, "IP", -					   PERF_OUTPUT_IP)) +		if (perf_evsel__check_stype(evsel, PERF_SAMPLE_IP, "IP", +					    PERF_OUTPUT_IP))  			return -EINVAL;  		if (!no_callchain && @@ -185,8 +191,8 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel,  	}  	if (PRINT_FIELD(ADDR) && -		perf_event_attr__check_stype(attr, PERF_SAMPLE_ADDR, "ADDR", -				       PERF_OUTPUT_ADDR)) +		perf_evsel__check_stype(evsel, PERF_SAMPLE_ADDR, "ADDR", +					PERF_OUTPUT_ADDR))  		return -EINVAL;  	if (PRINT_FIELD(SYM) && !PRINT_FIELD(IP) && !PRINT_FIELD(ADDR)) { @@ -208,18 +214,18 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel,  	}  	if ((PRINT_FIELD(PID) || PRINT_FIELD(TID)) && -		perf_event_attr__check_stype(attr, PERF_SAMPLE_TID, "TID", -				       PERF_OUTPUT_TID|PERF_OUTPUT_PID)) +		perf_evsel__check_stype(evsel, PERF_SAMPLE_TID, "TID", +					PERF_OUTPUT_TID|PERF_OUTPUT_PID))  		return -EINVAL;  	if (PRINT_FIELD(TIME) && -		perf_event_attr__check_stype(attr, PERF_SAMPLE_TIME, "TIME", -				       PERF_OUTPUT_TIME)) +		perf_evsel__check_stype(evsel, PERF_SAMPLE_TIME, "TIME", +					PERF_OUTPUT_TIME))  		return -EINVAL;  	if (PRINT_FIELD(CPU) && -		perf_event_attr__check_stype(attr, PERF_SAMPLE_CPU, "CPU", -				       PERF_OUTPUT_CPU)) +		perf_evsel__check_stype(evsel, PERF_SAMPLE_CPU, "CPU", +					PERF_OUTPUT_CPU))  		return -EINVAL;  	return 0; @@ -256,11 +262,13 @@ static int perf_session__check_output_opt(struct perf_session *session)  	return 0;  } -static void print_sample_start(struct perf_sample *sample, +static void print_sample_start(struct pevent *pevent, +			       struct perf_sample *sample,  			       struct thread *thread, -			       struct perf_event_attr *attr) +			       struct perf_evsel *evsel)  {  	int type; +	struct perf_event_attr *attr = &evsel->attr;  	struct event_format *event;  	const char *evname = NULL;  	unsigned long secs; @@ -300,12 +308,18 @@ static void print_sample_start(struct perf_sample *sample,  	if (PRINT_FIELD(EVNAME)) {  		if (attr->type == PERF_TYPE_TRACEPOINT) { -			type = trace_parse_common_type(sample->raw_data); -			event = trace_find_event(type); +			/* +			 * XXX Do we really need this here? +			 * perf_evlist__set_tracepoint_names should have done +			 * this already +			 */ +			type = trace_parse_common_type(pevent, +						       sample->raw_data); +			event = pevent_find_event(pevent, type);  			if (event)  				evname = event->name;  		} else -			evname = __event_name(attr->type, attr->config); +			evname = perf_evsel__name(evsel);  		printf("%s: ", evname ? evname : "[unknown]");  	} @@ -387,7 +401,7 @@ static void print_sample_bts(union perf_event *event,  			printf(" ");  		else  			printf("\n"); -		perf_event__print_ip(event, sample, machine, evsel, +		perf_event__print_ip(event, sample, machine,  				     PRINT_FIELD(SYM), PRINT_FIELD(DSO),  				     PRINT_FIELD(SYMOFFSET));  	} @@ -402,6 +416,7 @@ static void print_sample_bts(union perf_event *event,  }  static void process_event(union perf_event *event __unused, +			  struct pevent *pevent,  			  struct perf_sample *sample,  			  struct perf_evsel *evsel,  			  struct machine *machine, @@ -412,7 +427,7 @@ static void process_event(union perf_event *event __unused,  	if (output[attr->type].fields == 0)  		return; -	print_sample_start(sample, thread, attr); +	print_sample_start(pevent, sample, thread, evsel);  	if (is_bts_event(attr)) {  		print_sample_bts(event, sample, evsel, machine, thread); @@ -420,7 +435,7 @@ static void process_event(union perf_event *event __unused,  	}  	if (PRINT_FIELD(TRACE)) -		print_trace_event(sample->cpu, sample->raw_data, +		print_trace_event(pevent, sample->cpu, sample->raw_data,  				  sample->raw_size);  	if (PRINT_FIELD(ADDR)) @@ -431,7 +446,7 @@ static void process_event(union perf_event *event __unused,  			printf(" ");  		else  			printf("\n"); -		perf_event__print_ip(event, sample, machine, evsel, +		perf_event__print_ip(event, sample, machine,  				     PRINT_FIELD(SYM), PRINT_FIELD(DSO),  				     PRINT_FIELD(SYMOFFSET));  	} @@ -451,7 +466,8 @@ static int default_stop_script(void)  	return 0;  } -static int default_generate_script(const char *outfile __unused) +static int default_generate_script(struct pevent *pevent __unused, +				   const char *outfile __unused)  {  	return 0;  } @@ -489,6 +505,7 @@ static int process_sample_event(struct perf_tool *tool __used,  				struct machine *machine)  {  	struct addr_location al; +	struct perf_script *scr = container_of(tool, struct perf_script, tool);  	struct thread *thread = machine__findnew_thread(machine, event->ip.tid);  	if (thread == NULL) { @@ -520,24 +537,27 @@ static int process_sample_event(struct perf_tool *tool __used,  	if (cpu_list && !test_bit(sample->cpu, cpu_bitmap))  		return 0; -	scripting_ops->process_event(event, sample, evsel, machine, thread); +	scripting_ops->process_event(event, scr->session->pevent, +				     sample, evsel, machine, thread);  	evsel->hists.stats.total_period += sample->period;  	return 0;  } -static struct perf_tool perf_script = { -	.sample		 = process_sample_event, -	.mmap		 = perf_event__process_mmap, -	.comm		 = perf_event__process_comm, -	.exit		 = perf_event__process_task, -	.fork		 = perf_event__process_task, -	.attr		 = perf_event__process_attr, -	.event_type	 = perf_event__process_event_type, -	.tracing_data	 = perf_event__process_tracing_data, -	.build_id	 = perf_event__process_build_id, -	.ordered_samples = true, -	.ordering_requires_timestamps = true, +static struct perf_script perf_script = { +	.tool = { +		.sample		 = process_sample_event, +		.mmap		 = perf_event__process_mmap, +		.comm		 = perf_event__process_comm, +		.exit		 = perf_event__process_task, +		.fork		 = perf_event__process_task, +		.attr		 = perf_event__process_attr, +		.event_type	 = perf_event__process_event_type, +		.tracing_data	 = perf_event__process_tracing_data, +		.build_id	 = perf_event__process_build_id, +		.ordered_samples = true, +		.ordering_requires_timestamps = true, +	},  };  extern volatile int session_done; @@ -553,7 +573,7 @@ static int __cmd_script(struct perf_session *session)  	signal(SIGINT, sig_handler); -	ret = perf_session__process_events(session, &perf_script); +	ret = perf_session__process_events(session, &perf_script.tool);  	if (debug_mode)  		pr_err("Misordered timestamps: %" PRIu64 "\n", nr_unordered); @@ -1335,10 +1355,13 @@ int cmd_script(int argc, const char **argv, const char *prefix __used)  	if (!script_name)  		setup_pager(); -	session = perf_session__new(input_name, O_RDONLY, 0, false, &perf_script); +	session = perf_session__new(input_name, O_RDONLY, 0, false, +				    &perf_script.tool);  	if (session == NULL)  		return -ENOMEM; +	perf_script.session = session; +  	if (cpu_list) {  		if (perf_session__cpu_bitmap(session, cpu_list, cpu_bitmap))  			return -1; @@ -1384,7 +1407,8 @@ int cmd_script(int argc, const char **argv, const char *prefix __used)  			return -1;  		} -		err = scripting_ops->generate_script("perf-script"); +		err = scripting_ops->generate_script(session->pevent, +						     "perf-script");  		goto out;  	} diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 62ae30d34fa..861f0aec77a 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -391,7 +391,7 @@ static int read_counter_aggr(struct perf_evsel *counter)  	if (verbose) {  		fprintf(output, "%s: %" PRIu64 " %" PRIu64 " %" PRIu64 "\n", -			event_name(counter), count[0], count[1], count[2]); +			perf_evsel__name(counter), count[0], count[1], count[2]);  	}  	/* @@ -496,7 +496,7 @@ static int run_perf_stat(int argc __used, const char **argv)  			    errno == ENXIO) {  				if (verbose)  					ui__warning("%s event is not supported by the kernel.\n", -						    event_name(counter)); +						    perf_evsel__name(counter));  				counter->supported = false;  				continue;  			} @@ -594,7 +594,7 @@ static void nsec_printout(int cpu, struct perf_evsel *evsel, double avg)  			csv_output ? 0 : -4,  			evsel_list->cpus->map[cpu], csv_sep); -	fprintf(output, fmt, cpustr, msecs, csv_sep, event_name(evsel)); +	fprintf(output, fmt, cpustr, msecs, csv_sep, perf_evsel__name(evsel));  	if (evsel->cgrp)  		fprintf(output, "%s%s", csv_sep, evsel->cgrp->name); @@ -792,7 +792,7 @@ static void abs_printout(int cpu, struct perf_evsel *evsel, double avg)  	else  		cpu = 0; -	fprintf(output, fmt, cpustr, avg, csv_sep, event_name(evsel)); +	fprintf(output, fmt, cpustr, avg, csv_sep, perf_evsel__name(evsel));  	if (evsel->cgrp)  		fprintf(output, "%s%s", csv_sep, evsel->cgrp->name); @@ -908,7 +908,7 @@ static void print_counter_aggr(struct perf_evsel *counter)  			counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED,  			csv_sep,  			csv_output ? 0 : -24, -			event_name(counter)); +			perf_evsel__name(counter));  		if (counter->cgrp)  			fprintf(output, "%s%s", csv_sep, counter->cgrp->name); @@ -961,7 +961,7 @@ static void print_counter(struct perf_evsel *counter)  				counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED,  				csv_sep,  				csv_output ? 0 : -24, -				event_name(counter)); +				perf_evsel__name(counter));  			if (counter->cgrp)  				fprintf(output, "%s%s", @@ -1129,7 +1129,7 @@ static int add_default_attributes(void)  		return 0;  	if (!evsel_list->nr_entries) { -		if (perf_evlist__add_attrs_array(evsel_list, default_attrs) < 0) +		if (perf_evlist__add_default_attrs(evsel_list, default_attrs) < 0)  			return -1;  	} @@ -1139,21 +1139,21 @@ static int add_default_attributes(void)  		return 0;  	/* Append detailed run extra attributes: */ -	if (perf_evlist__add_attrs_array(evsel_list, detailed_attrs) < 0) +	if (perf_evlist__add_default_attrs(evsel_list, detailed_attrs) < 0)  		return -1;  	if (detailed_run < 2)  		return 0;  	/* Append very detailed run extra attributes: */ -	if (perf_evlist__add_attrs_array(evsel_list, very_detailed_attrs) < 0) +	if (perf_evlist__add_default_attrs(evsel_list, very_detailed_attrs) < 0)  		return -1;  	if (detailed_run < 3)  		return 0;  	/* Append very, very detailed run extra attributes: */ -	return perf_evlist__add_attrs_array(evsel_list, very_very_detailed_attrs); +	return perf_evlist__add_default_attrs(evsel_list, very_very_detailed_attrs);  }  int cmd_stat(int argc, const char **argv, const char *prefix __used) @@ -1179,6 +1179,12 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used)  		fprintf(stderr, "cannot use both --output and --log-fd\n");  		usage_with_options(stat_usage, options);  	} + +	if (output_fd < 0) { +		fprintf(stderr, "argument to --log-fd must be a > 0\n"); +		usage_with_options(stat_usage, options); +	} +  	if (!output) {  		struct timespec tm;  		mode = append_file ? "a" : "w"; @@ -1190,7 +1196,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used)  		}  		clock_gettime(CLOCK_REALTIME, &tm);  		fprintf(output, "# started on %s\n", ctime(&tm.tv_sec)); -	} else if (output_fd != 2) { +	} else if (output_fd > 0) {  		mode = append_file ? "a" : "w";  		output = fdopen(output_fd, mode);  		if (!output) { diff --git a/tools/perf/builtin-test.c b/tools/perf/builtin-test.c index 5a8727c0875..5ce30305462 100644 --- a/tools/perf/builtin-test.c +++ b/tools/perf/builtin-test.c @@ -583,7 +583,7 @@ static int test__basic_mmap(void)  		if (nr_events[evsel->idx] != expected_nr_events[evsel->idx]) {  			pr_debug("expected %d %s events, got %d\n",  				 expected_nr_events[evsel->idx], -				 event_name(evsel), nr_events[evsel->idx]); +				 perf_evsel__name(evsel), nr_events[evsel->idx]);  			goto out_munmap;  		}  	} diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 6031dce0429..e3cab5f088f 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -245,7 +245,7 @@ static void perf_top__show_details(struct perf_top *top)  	if (notes->src == NULL)  		goto out_unlock; -	printf("Showing %s for %s\n", event_name(top->sym_evsel), symbol->name); +	printf("Showing %s for %s\n", perf_evsel__name(top->sym_evsel), symbol->name);  	printf("  Events  Pcnt (>=%d%%)\n", top->sym_pcnt_filter);  	more = symbol__annotate_printf(symbol, he->ms.map, top->sym_evsel->idx, @@ -408,7 +408,7 @@ static void perf_top__print_mapped_keys(struct perf_top *top)  	fprintf(stdout, "\t[e]     display entries (lines).           \t(%d)\n", top->print_entries);  	if (top->evlist->nr_entries > 1) -		fprintf(stdout, "\t[E]     active event counter.              \t(%s)\n", event_name(top->sym_evsel)); +		fprintf(stdout, "\t[E]     active event counter.              \t(%s)\n", perf_evsel__name(top->sym_evsel));  	fprintf(stdout, "\t[f]     profile display filter (count).    \t(%d)\n", top->count_filter); @@ -503,13 +503,13 @@ static void perf_top__handle_keypress(struct perf_top *top, int c)  				fprintf(stderr, "\nAvailable events:");  				list_for_each_entry(top->sym_evsel, &top->evlist->entries, node) -					fprintf(stderr, "\n\t%d %s", top->sym_evsel->idx, event_name(top->sym_evsel)); +					fprintf(stderr, "\n\t%d %s", top->sym_evsel->idx, perf_evsel__name(top->sym_evsel));  				prompt_integer(&counter, "Enter details event counter");  				if (counter >= top->evlist->nr_entries) {  					top->sym_evsel = list_entry(top->evlist->entries.next, struct perf_evsel, node); -					fprintf(stderr, "Sorry, no such event, using %s.\n", event_name(top->sym_evsel)); +					fprintf(stderr, "Sorry, no such event, using %s.\n", perf_evsel__name(top->sym_evsel));  					sleep(1);  					break;  				} @@ -774,7 +774,7 @@ static void perf_event__process_sample(struct perf_tool *tool,  		if ((sort__has_parent || symbol_conf.use_callchain) &&  		    sample->callchain) { -			err = machine__resolve_callchain(machine, evsel, al.thread, +			err = machine__resolve_callchain(machine, al.thread,  							 sample->callchain, &parent);  			if (err)  				return; @@ -787,7 +787,7 @@ static void perf_event__process_sample(struct perf_tool *tool,  		}  		if (symbol_conf.use_callchain) { -			err = callchain_append(he->callchain, &evsel->hists.callchain_cursor, +			err = callchain_append(he->callchain, &callchain_cursor,  					       sample->period);  			if (err)  				return; @@ -953,22 +953,22 @@ try_again:  				attr->config = PERF_COUNT_SW_CPU_CLOCK;  				if (counter->name) {  					free(counter->name); -					counter->name = strdup(event_name(counter)); +					counter->name = NULL;  				}  				goto try_again;  			}  			if (err == ENOENT) { -				ui__warning("The %s event is not supported.\n", -					    event_name(counter)); +				ui__error("The %s event is not supported.\n", +					  perf_evsel__name(counter));  				goto out_err;  			} else if (err == EMFILE) { -				ui__warning("Too many events are opened.\n" +				ui__error("Too many events are opened.\n"  					    "Try again after reducing the number of events\n");  				goto out_err;  			} -			ui__warning("The sys_perf_event_open() syscall " +			ui__error("The sys_perf_event_open() syscall "  				    "returned with %d (%s).  /bin/dmesg "  				    "may provide additional information.\n"  				    "No CONFIG_PERF_EVENTS=y kernel support " @@ -978,7 +978,7 @@ try_again:  	}  	if (perf_evlist__mmap(evlist, top->mmap_pages, false) < 0) { -		ui__warning("Failed to mmap with %d (%s)\n", +		ui__error("Failed to mmap with %d (%s)\n",  			    errno, strerror(errno));  		goto out_err;  	} @@ -994,12 +994,12 @@ static int perf_top__setup_sample_type(struct perf_top *top)  {  	if (!top->sort_has_symbols) {  		if (symbol_conf.use_callchain) { -			ui__warning("Selected -g but \"sym\" not present in --sort/-s."); +			ui__error("Selected -g but \"sym\" not present in --sort/-s.");  			return -EINVAL;  		}  	} else if (!top->dont_use_callchains && callchain_param.mode != CHAIN_NONE) {  		if (callchain_register_param(&callchain_param) < 0) { -			ui__warning("Can't register callchain params.\n"); +			ui__error("Can't register callchain params.\n");  			return -EINVAL;  		}  	} @@ -1041,7 +1041,7 @@ static int __cmd_top(struct perf_top *top)  	if (pthread_create(&thread, NULL, (use_browser > 0 ? display_thread_tui :  							    display_thread), top)) { -		printf("Could not create display thread.\n"); +		ui__error("Could not create display thread.\n");  		exit(-1);  	} @@ -1050,7 +1050,7 @@ static int __cmd_top(struct perf_top *top)  		param.sched_priority = top->realtime_prio;  		if (sched_setscheduler(0, SCHED_FIFO, ¶m)) { -			printf("Could not set realtime priority.\n"); +			ui__error("Could not set realtime priority.\n");  			exit(-1);  		}  	} @@ -1274,7 +1274,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __used)  		int saved_errno = errno;  		perf_target__strerror(&top.target, status, errbuf, BUFSIZ); -		ui__warning("%s", errbuf); +		ui__error("%s", errbuf);  		status = -saved_errno;  		goto out_delete_evlist; @@ -1288,7 +1288,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __used)  	if (!top.evlist->nr_entries &&  	    perf_evlist__add_default(top.evlist) < 0) { -		pr_err("Not enough memory for event selector list\n"); +		ui__error("Not enough memory for event selector list\n");  		return -ENOMEM;  	} @@ -1305,7 +1305,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __used)  	else if (top.freq) {  		top.default_interval = top.freq;  	} else { -		fprintf(stderr, "frequency and count are zero, aborting\n"); +		ui__error("frequency and count are zero, aborting\n");  		exit(EXIT_FAILURE);  	} diff --git a/tools/perf/config/feature-tests.mak b/tools/perf/config/feature-tests.mak index d9084e03ce5..6c18785a641 100644 --- a/tools/perf/config/feature-tests.mak +++ b/tools/perf/config/feature-tests.mak @@ -78,6 +78,19 @@ int main(int argc, char *argv[])          return 0;  }  endef + +define SOURCE_GTK2_INFOBAR +#pragma GCC diagnostic ignored \"-Wstrict-prototypes\" +#include <gtk/gtk.h> +#pragma GCC diagnostic error \"-Wstrict-prototypes\" + +int main(void) +{ +	gtk_info_bar_new(); + +	return 0; +} +endef  endif  ifndef NO_LIBPERL diff --git a/tools/perf/design.txt b/tools/perf/design.txt index bd0bb1b1279..67e5d0cace8 100644 --- a/tools/perf/design.txt +++ b/tools/perf/design.txt @@ -409,14 +409,15 @@ Counters can be enabled and disabled in two ways: via ioctl and via  prctl.  When a counter is disabled, it doesn't count or generate  events but does continue to exist and maintain its count value. -An individual counter or counter group can be enabled with +An individual counter can be enabled with -	ioctl(fd, PERF_EVENT_IOC_ENABLE); +	ioctl(fd, PERF_EVENT_IOC_ENABLE, 0);  or disabled with -	ioctl(fd, PERF_EVENT_IOC_DISABLE); +	ioctl(fd, PERF_EVENT_IOC_DISABLE, 0); +For a counter group, pass PERF_IOC_FLAG_GROUP as the third argument.  Enabling or disabling the leader of a group enables or disables the  whole group; that is, while the group leader is disabled, none of the  counters in the group will count.  Enabling or disabling a member of a diff --git a/tools/perf/perf.h b/tools/perf/perf.h index 14f1034f14f..f960ccb2edc 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h @@ -227,7 +227,7 @@ struct perf_record_opts {  	unsigned int freq;  	unsigned int mmap_pages;  	unsigned int user_freq; -	int	     branch_stack; +	u64          branch_stack;  	u64	     default_interval;  	u64	     user_interval;  }; diff --git a/tools/perf/ui/browser.c b/tools/perf/ui/browser.c index cde4d0f0ddb..1818a531f1d 100644 --- a/tools/perf/ui/browser.c +++ b/tools/perf/ui/browser.c @@ -35,16 +35,16 @@ int ui_browser__set_color(struct ui_browser *browser, int color)  	return ret;  } -void ui_browser__set_percent_color(struct ui_browser *self, +void ui_browser__set_percent_color(struct ui_browser *browser,  				   double percent, bool current)  { -	 int color = ui_browser__percent_color(self, percent, current); -	 ui_browser__set_color(self, color); +	 int color = ui_browser__percent_color(browser, percent, current); +	 ui_browser__set_color(browser, color);  } -void ui_browser__gotorc(struct ui_browser *self, int y, int x) +void ui_browser__gotorc(struct ui_browser *browser, int y, int x)  { -	SLsmg_gotorc(self->y + y, self->x + x); +	SLsmg_gotorc(browser->y + y, browser->x + x);  }  static struct list_head * @@ -73,23 +73,23 @@ ui_browser__list_head_filter_prev_entries(struct ui_browser *browser,  	return NULL;  } -void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whence) +void ui_browser__list_head_seek(struct ui_browser *browser, off_t offset, int whence)  { -	struct list_head *head = self->entries; +	struct list_head *head = browser->entries;  	struct list_head *pos; -	if (self->nr_entries == 0) +	if (browser->nr_entries == 0)  		return;  	switch (whence) {  	case SEEK_SET: -		pos = ui_browser__list_head_filter_entries(self, head->next); +		pos = ui_browser__list_head_filter_entries(browser, head->next);  		break;  	case SEEK_CUR: -		pos = self->top; +		pos = browser->top;  		break;  	case SEEK_END: -		pos = ui_browser__list_head_filter_prev_entries(self, head->prev); +		pos = ui_browser__list_head_filter_prev_entries(browser, head->prev);  		break;  	default:  		return; @@ -99,18 +99,18 @@ void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whenc  	if (offset > 0) {  		while (offset-- != 0) -			pos = ui_browser__list_head_filter_entries(self, pos->next); +			pos = ui_browser__list_head_filter_entries(browser, pos->next);  	} else {  		while (offset++ != 0) -			pos = ui_browser__list_head_filter_prev_entries(self, pos->prev); +			pos = ui_browser__list_head_filter_prev_entries(browser, pos->prev);  	} -	self->top = pos; +	browser->top = pos;  } -void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence) +void ui_browser__rb_tree_seek(struct ui_browser *browser, off_t offset, int whence)  { -	struct rb_root *root = self->entries; +	struct rb_root *root = browser->entries;  	struct rb_node *nd;  	switch (whence) { @@ -118,7 +118,7 @@ void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence)  		nd = rb_first(root);  		break;  	case SEEK_CUR: -		nd = self->top; +		nd = browser->top;  		break;  	case SEEK_END:  		nd = rb_last(root); @@ -135,23 +135,23 @@ void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence)  			nd = rb_prev(nd);  	} -	self->top = nd; +	browser->top = nd;  } -unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self) +unsigned int ui_browser__rb_tree_refresh(struct ui_browser *browser)  {  	struct rb_node *nd;  	int row = 0; -	if (self->top == NULL) -                self->top = rb_first(self->entries); +	if (browser->top == NULL) +                browser->top = rb_first(browser->entries); -	nd = self->top; +	nd = browser->top;  	while (nd != NULL) { -		ui_browser__gotorc(self, row, 0); -		self->write(self, nd, row); -		if (++row == self->height) +		ui_browser__gotorc(browser, row, 0); +		browser->write(browser, nd, row); +		if (++row == browser->height)  			break;  		nd = rb_next(nd);  	} @@ -159,17 +159,17 @@ unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self)  	return row;  } -bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row) +bool ui_browser__is_current_entry(struct ui_browser *browser, unsigned row)  { -	return self->top_idx + row == self->index; +	return browser->top_idx + row == browser->index;  } -void ui_browser__refresh_dimensions(struct ui_browser *self) +void ui_browser__refresh_dimensions(struct ui_browser *browser)  { -	self->width = SLtt_Screen_Cols - 1; -	self->height = SLtt_Screen_Rows - 2; -	self->y = 1; -	self->x = 0; +	browser->width = SLtt_Screen_Cols - 1; +	browser->height = SLtt_Screen_Rows - 2; +	browser->y = 1; +	browser->x = 0;  }  void ui_browser__handle_resize(struct ui_browser *browser) @@ -225,10 +225,10 @@ bool ui_browser__dialog_yesno(struct ui_browser *browser, const char *text)  	return key == K_ENTER || toupper(key) == 'Y';  } -void ui_browser__reset_index(struct ui_browser *self) +void ui_browser__reset_index(struct ui_browser *browser)  { -	self->index = self->top_idx = 0; -	self->seek(self, 0, SEEK_SET); +	browser->index = browser->top_idx = 0; +	browser->seek(browser, 0, SEEK_SET);  }  void __ui_browser__show_title(struct ui_browser *browser, const char *title) @@ -245,26 +245,26 @@ void ui_browser__show_title(struct ui_browser *browser, const char *title)  	pthread_mutex_unlock(&ui__lock);  } -int ui_browser__show(struct ui_browser *self, const char *title, +int ui_browser__show(struct ui_browser *browser, const char *title,  		     const char *helpline, ...)  {  	int err;  	va_list ap; -	ui_browser__refresh_dimensions(self); +	ui_browser__refresh_dimensions(browser);  	pthread_mutex_lock(&ui__lock); -	__ui_browser__show_title(self, title); +	__ui_browser__show_title(browser, title); -	self->title = title; -	free(self->helpline); -	self->helpline = NULL; +	browser->title = title; +	free(browser->helpline); +	browser->helpline = NULL;  	va_start(ap, helpline); -	err = vasprintf(&self->helpline, helpline, ap); +	err = vasprintf(&browser->helpline, helpline, ap);  	va_end(ap);  	if (err > 0) -		ui_helpline__push(self->helpline); +		ui_helpline__push(browser->helpline);  	pthread_mutex_unlock(&ui__lock);  	return err ? 0 : -1;  } @@ -350,7 +350,7 @@ void ui_browser__update_nr_entries(struct ui_browser *browser, u32 nr_entries)  	browser->seek(browser, browser->top_idx, SEEK_SET);  } -int ui_browser__run(struct ui_browser *self, int delay_secs) +int ui_browser__run(struct ui_browser *browser, int delay_secs)  {  	int err, key; @@ -358,7 +358,7 @@ int ui_browser__run(struct ui_browser *self, int delay_secs)  		off_t offset;  		pthread_mutex_lock(&ui__lock); -		err = __ui_browser__refresh(self); +		err = __ui_browser__refresh(browser);  		SLsmg_refresh();  		pthread_mutex_unlock(&ui__lock);  		if (err < 0) @@ -368,18 +368,18 @@ int ui_browser__run(struct ui_browser *self, int delay_secs)  		if (key == K_RESIZE) {  			ui__refresh_dimensions(false); -			ui_browser__refresh_dimensions(self); -			__ui_browser__show_title(self, self->title); -			ui_helpline__puts(self->helpline); +			ui_browser__refresh_dimensions(browser); +			__ui_browser__show_title(browser, browser->title); +			ui_helpline__puts(browser->helpline);  			continue;  		} -		if (self->use_navkeypressed && !self->navkeypressed) { +		if (browser->use_navkeypressed && !browser->navkeypressed) {  			if (key == K_DOWN || key == K_UP ||  			    key == K_PGDN || key == K_PGUP ||  			    key == K_HOME || key == K_END ||  			    key == ' ') { -				self->navkeypressed = true; +				browser->navkeypressed = true;  				continue;  			} else  				return key; @@ -387,59 +387,59 @@ int ui_browser__run(struct ui_browser *self, int delay_secs)  		switch (key) {  		case K_DOWN: -			if (self->index == self->nr_entries - 1) +			if (browser->index == browser->nr_entries - 1)  				break; -			++self->index; -			if (self->index == self->top_idx + self->height) { -				++self->top_idx; -				self->seek(self, +1, SEEK_CUR); +			++browser->index; +			if (browser->index == browser->top_idx + browser->height) { +				++browser->top_idx; +				browser->seek(browser, +1, SEEK_CUR);  			}  			break;  		case K_UP: -			if (self->index == 0) +			if (browser->index == 0)  				break; -			--self->index; -			if (self->index < self->top_idx) { -				--self->top_idx; -				self->seek(self, -1, SEEK_CUR); +			--browser->index; +			if (browser->index < browser->top_idx) { +				--browser->top_idx; +				browser->seek(browser, -1, SEEK_CUR);  			}  			break;  		case K_PGDN:  		case ' ': -			if (self->top_idx + self->height > self->nr_entries - 1) +			if (browser->top_idx + browser->height > browser->nr_entries - 1)  				break; -			offset = self->height; -			if (self->index + offset > self->nr_entries - 1) -				offset = self->nr_entries - 1 - self->index; -			self->index += offset; -			self->top_idx += offset; -			self->seek(self, +offset, SEEK_CUR); +			offset = browser->height; +			if (browser->index + offset > browser->nr_entries - 1) +				offset = browser->nr_entries - 1 - browser->index; +			browser->index += offset; +			browser->top_idx += offset; +			browser->seek(browser, +offset, SEEK_CUR);  			break;  		case K_PGUP: -			if (self->top_idx == 0) +			if (browser->top_idx == 0)  				break; -			if (self->top_idx < self->height) -				offset = self->top_idx; +			if (browser->top_idx < browser->height) +				offset = browser->top_idx;  			else -				offset = self->height; +				offset = browser->height; -			self->index -= offset; -			self->top_idx -= offset; -			self->seek(self, -offset, SEEK_CUR); +			browser->index -= offset; +			browser->top_idx -= offset; +			browser->seek(browser, -offset, SEEK_CUR);  			break;  		case K_HOME: -			ui_browser__reset_index(self); +			ui_browser__reset_index(browser);  			break;  		case K_END: -			offset = self->height - 1; -			if (offset >= self->nr_entries) -				offset = self->nr_entries - 1; +			offset = browser->height - 1; +			if (offset >= browser->nr_entries) +				offset = browser->nr_entries - 1; -			self->index = self->nr_entries - 1; -			self->top_idx = self->index - offset; -			self->seek(self, -offset, SEEK_END); +			browser->index = browser->nr_entries - 1; +			browser->top_idx = browser->index - offset; +			browser->seek(browser, -offset, SEEK_END);  			break;  		default:  			return key; @@ -448,22 +448,22 @@ int ui_browser__run(struct ui_browser *self, int delay_secs)  	return -1;  } -unsigned int ui_browser__list_head_refresh(struct ui_browser *self) +unsigned int ui_browser__list_head_refresh(struct ui_browser *browser)  {  	struct list_head *pos; -	struct list_head *head = self->entries; +	struct list_head *head = browser->entries;  	int row = 0; -	if (self->top == NULL || self->top == self->entries) -                self->top = ui_browser__list_head_filter_entries(self, head->next); +	if (browser->top == NULL || browser->top == browser->entries) +                browser->top = ui_browser__list_head_filter_entries(browser, head->next); -	pos = self->top; +	pos = browser->top;  	list_for_each_from(pos, head) { -		if (!self->filter || !self->filter(self, pos)) { -			ui_browser__gotorc(self, row, 0); -			self->write(self, pos, row); -			if (++row == self->height) +		if (!browser->filter || !browser->filter(browser, pos)) { +			ui_browser__gotorc(browser, row, 0); +			browser->write(browser, pos, row); +			if (++row == browser->height)  				break;  		}  	} @@ -708,4 +708,6 @@ void ui_browser__init(void)  		struct ui_browser__colorset *c = &ui_browser__colorsets[i++];  		sltt_set_color(c->colorset, c->name, c->fg, c->bg);  	} + +	annotate_browser__init();  } diff --git a/tools/perf/ui/browser.h b/tools/perf/ui/browser.h index dd96d822990..af70314605e 100644 --- a/tools/perf/ui/browser.h +++ b/tools/perf/ui/browser.h @@ -69,4 +69,5 @@ void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whenc  unsigned int ui_browser__list_head_refresh(struct ui_browser *self);  void ui_browser__init(void); +void annotate_browser__init(void);  #endif /* _PERF_UI_BROWSER_H_ */ diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c index 6e0ef79be16..67a2703e666 100644 --- a/tools/perf/ui/browsers/annotate.c +++ b/tools/perf/ui/browsers/annotate.c @@ -19,6 +19,16 @@ struct browser_disasm_line {  	int		jump_sources;  }; +static struct annotate_browser_opt { +	bool hide_src_code, +	     use_offset, +	     jump_arrows, +	     show_nr_jumps; +} annotate_browser__opts = { +	.use_offset	= true, +	.jump_arrows	= true, +}; +  struct annotate_browser {  	struct ui_browser b;  	struct rb_root	  entries; @@ -30,10 +40,6 @@ struct annotate_browser {  	int		    nr_entries;  	int		    max_jump_sources;  	int		    nr_jumps; -	bool		    hide_src_code; -	bool		    use_offset; -	bool		    jump_arrows; -	bool		    show_nr_jumps;  	bool		    searching_backwards;  	u8		    addr_width;  	u8		    jumps_width; @@ -48,11 +54,9 @@ static inline struct browser_disasm_line *disasm_line__browser(struct disasm_lin  	return (struct browser_disasm_line *)(dl + 1);  } -static bool disasm_line__filter(struct ui_browser *browser, void *entry) +static bool disasm_line__filter(struct ui_browser *browser __used, void *entry)  { -	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b); - -	if (ab->hide_src_code) { +	if (annotate_browser__opts.hide_src_code) {  		struct disasm_line *dl = list_entry(entry, struct disasm_line, node);  		return dl->offset == -1;  	} @@ -79,30 +83,30 @@ static int annotate_browser__set_jumps_percent_color(struct annotate_browser *br  	 return ui_browser__set_color(&browser->b, color);  } -static void annotate_browser__write(struct ui_browser *self, void *entry, int row) +static void annotate_browser__write(struct ui_browser *browser, void *entry, int row)  { -	struct annotate_browser *ab = container_of(self, struct annotate_browser, b); +	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);  	struct disasm_line *dl = list_entry(entry, struct disasm_line, node);  	struct browser_disasm_line *bdl = disasm_line__browser(dl); -	bool current_entry = ui_browser__is_current_entry(self, row); -	bool change_color = (!ab->hide_src_code && -			     (!current_entry || (self->use_navkeypressed && -					         !self->navkeypressed))); -	int width = self->width, printed; +	bool current_entry = ui_browser__is_current_entry(browser, row); +	bool change_color = (!annotate_browser__opts.hide_src_code && +			     (!current_entry || (browser->use_navkeypressed && +					         !browser->navkeypressed))); +	int width = browser->width, printed;  	char bf[256];  	if (dl->offset != -1 && bdl->percent != 0.0) { -		ui_browser__set_percent_color(self, bdl->percent, current_entry); +		ui_browser__set_percent_color(browser, bdl->percent, current_entry);  		slsmg_printf("%6.2f ", bdl->percent);  	} else { -		ui_browser__set_percent_color(self, 0, current_entry); +		ui_browser__set_percent_color(browser, 0, current_entry);  		slsmg_write_nstring(" ", 7);  	}  	SLsmg_write_char(' ');  	/* The scroll bar isn't being used */ -	if (!self->navkeypressed) +	if (!browser->navkeypressed)  		width += 1;  	if (!*dl->line) @@ -116,14 +120,14 @@ static void annotate_browser__write(struct ui_browser *self, void *entry, int ro  		u64 addr = dl->offset;  		int color = -1; -		if (!ab->use_offset) +		if (!annotate_browser__opts.use_offset)  			addr += ab->start; -		if (!ab->use_offset) { +		if (!annotate_browser__opts.use_offset) {  			printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr);  		} else {  			if (bdl->jump_sources) { -				if (ab->show_nr_jumps) { +				if (annotate_browser__opts.show_nr_jumps) {  					int prev;  					printed = scnprintf(bf, sizeof(bf), "%*d ",  							    ab->jumps_width, @@ -131,7 +135,7 @@ static void annotate_browser__write(struct ui_browser *self, void *entry, int ro  					prev = annotate_browser__set_jumps_percent_color(ab, bdl->jump_sources,  											 current_entry);  					slsmg_write_nstring(bf, printed); -					ui_browser__set_color(self, prev); +					ui_browser__set_color(browser, prev);  				}  				printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ": ", @@ -143,19 +147,19 @@ static void annotate_browser__write(struct ui_browser *self, void *entry, int ro  		}  		if (change_color) -			color = ui_browser__set_color(self, HE_COLORSET_ADDR); +			color = ui_browser__set_color(browser, HE_COLORSET_ADDR);  		slsmg_write_nstring(bf, printed);  		if (change_color) -			ui_browser__set_color(self, color); +			ui_browser__set_color(browser, color);  		if (dl->ins && dl->ins->ops->scnprintf) {  			if (ins__is_jump(dl->ins)) {  				bool fwd = dl->ops.target.offset > (u64)dl->offset; -				ui_browser__write_graph(self, fwd ? SLSMG_DARROW_CHAR : +				ui_browser__write_graph(browser, fwd ? SLSMG_DARROW_CHAR :  								    SLSMG_UARROW_CHAR);  				SLsmg_write_char(' ');  			} else if (ins__is_call(dl->ins)) { -				ui_browser__write_graph(self, SLSMG_RARROW_CHAR); +				ui_browser__write_graph(browser, SLSMG_RARROW_CHAR);  				SLsmg_write_char(' ');  			} else {  				slsmg_write_nstring(" ", 2); @@ -164,12 +168,12 @@ static void annotate_browser__write(struct ui_browser *self, void *entry, int ro  			if (strcmp(dl->name, "retq")) {  				slsmg_write_nstring(" ", 2);  			} else { -				ui_browser__write_graph(self, SLSMG_LARROW_CHAR); +				ui_browser__write_graph(browser, SLSMG_LARROW_CHAR);  				SLsmg_write_char(' ');  			}  		} -		disasm_line__scnprintf(dl, bf, sizeof(bf), !ab->use_offset); +		disasm_line__scnprintf(dl, bf, sizeof(bf), !annotate_browser__opts.use_offset);  		slsmg_write_nstring(bf, width - 10 - printed);  	} @@ -184,7 +188,7 @@ static void annotate_browser__draw_current_jump(struct ui_browser *browser)  	struct browser_disasm_line *btarget, *bcursor;  	unsigned int from, to; -	if (!cursor->ins || !ins__is_jump(cursor->ins) || +	if (!cursor || !cursor->ins || !ins__is_jump(cursor->ins) ||  	    !disasm_line__has_offset(cursor))  		return; @@ -195,7 +199,7 @@ static void annotate_browser__draw_current_jump(struct ui_browser *browser)  	bcursor = disasm_line__browser(cursor);  	btarget = disasm_line__browser(target); -	if (ab->hide_src_code) { +	if (annotate_browser__opts.hide_src_code) {  		from = bcursor->idx_asm;  		to = btarget->idx_asm;  	} else { @@ -209,10 +213,9 @@ static void annotate_browser__draw_current_jump(struct ui_browser *browser)  static unsigned int annotate_browser__refresh(struct ui_browser *browser)  { -	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);  	int ret = ui_browser__list_head_refresh(browser); -	if (ab->jump_arrows) +	if (annotate_browser__opts.jump_arrows)  		annotate_browser__draw_current_jump(browser);  	ui_browser__set_color(browser, HE_COLORSET_NORMAL); @@ -272,27 +275,27 @@ static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_l  	rb_insert_color(&bdl->rb_node, root);  } -static void annotate_browser__set_top(struct annotate_browser *self, +static void annotate_browser__set_top(struct annotate_browser *browser,  				      struct disasm_line *pos, u32 idx)  {  	unsigned back; -	ui_browser__refresh_dimensions(&self->b); -	back = self->b.height / 2; -	self->b.top_idx = self->b.index = idx; +	ui_browser__refresh_dimensions(&browser->b); +	back = browser->b.height / 2; +	browser->b.top_idx = browser->b.index = idx; -	while (self->b.top_idx != 0 && back != 0) { +	while (browser->b.top_idx != 0 && back != 0) {  		pos = list_entry(pos->node.prev, struct disasm_line, node); -		if (disasm_line__filter(&self->b, &pos->node)) +		if (disasm_line__filter(&browser->b, &pos->node))  			continue; -		--self->b.top_idx; +		--browser->b.top_idx;  		--back;  	} -	self->b.top = pos; -	self->b.navkeypressed = true; +	browser->b.top = pos; +	browser->b.navkeypressed = true;  }  static void annotate_browser__set_rb_top(struct annotate_browser *browser, @@ -300,10 +303,14 @@ static void annotate_browser__set_rb_top(struct annotate_browser *browser,  {  	struct browser_disasm_line *bpos;  	struct disasm_line *pos; +	u32 idx;  	bpos = rb_entry(nd, struct browser_disasm_line, rb_node);  	pos = ((struct disasm_line *)bpos) - 1; -	annotate_browser__set_top(browser, pos, bpos->idx); +	idx = bpos->idx; +	if (annotate_browser__opts.hide_src_code) +		idx = bpos->idx_asm; +	annotate_browser__set_top(browser, pos, idx);  	browser->curr_hot = nd;  } @@ -343,12 +350,12 @@ static bool annotate_browser__toggle_source(struct annotate_browser *browser)  	dl = list_entry(browser->b.top, struct disasm_line, node);  	bdl = disasm_line__browser(dl); -	if (browser->hide_src_code) { +	if (annotate_browser__opts.hide_src_code) {  		if (bdl->idx_asm < offset)  			offset = bdl->idx;  		browser->b.nr_entries = browser->nr_entries; -		browser->hide_src_code = false; +		annotate_browser__opts.hide_src_code = false;  		browser->b.seek(&browser->b, -offset, SEEK_CUR);  		browser->b.top_idx = bdl->idx - offset;  		browser->b.index = bdl->idx; @@ -363,7 +370,7 @@ static bool annotate_browser__toggle_source(struct annotate_browser *browser)  			offset = bdl->idx_asm;  		browser->b.nr_entries = browser->nr_asm_entries; -		browser->hide_src_code = true; +		annotate_browser__opts.hide_src_code = true;  		browser->b.seek(&browser->b, -offset, SEEK_CUR);  		browser->b.top_idx = bdl->idx_asm - offset;  		browser->b.index = bdl->idx_asm; @@ -372,6 +379,12 @@ static bool annotate_browser__toggle_source(struct annotate_browser *browser)  	return true;  } +static void annotate_browser__init_asm_mode(struct annotate_browser *browser) +{ +	ui_browser__reset_index(&browser->b); +	browser->b.nr_entries = browser->nr_asm_entries; +} +  static bool annotate_browser__callq(struct annotate_browser *browser,  				    int evidx, void (*timer)(void *arg),  				    void *arg, int delay_secs) @@ -574,33 +587,46 @@ bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,  	return __annotate_browser__search_reverse(browser);  } -static int annotate_browser__run(struct annotate_browser *self, int evidx, +static void annotate_browser__update_addr_width(struct annotate_browser *browser) +{ +	if (annotate_browser__opts.use_offset) +		browser->target_width = browser->min_addr_width; +	else +		browser->target_width = browser->max_addr_width; + +	browser->addr_width = browser->target_width; + +	if (annotate_browser__opts.show_nr_jumps) +		browser->addr_width += browser->jumps_width + 1; +} + +static int annotate_browser__run(struct annotate_browser *browser, int evidx,  				 void(*timer)(void *arg),  				 void *arg, int delay_secs)  {  	struct rb_node *nd = NULL; -	struct map_symbol *ms = self->b.priv; +	struct map_symbol *ms = browser->b.priv;  	struct symbol *sym = ms->sym;  	const char *help = "Press 'h' for help on key bindings";  	int key; -	if (ui_browser__show(&self->b, sym->name, help) < 0) +	if (ui_browser__show(&browser->b, sym->name, help) < 0)  		return -1; -	annotate_browser__calc_percent(self, evidx); +	annotate_browser__calc_percent(browser, evidx); -	if (self->curr_hot) { -		annotate_browser__set_rb_top(self, self->curr_hot); -		self->b.navkeypressed = false; +	if (browser->curr_hot) { +		annotate_browser__set_rb_top(browser, browser->curr_hot); +		browser->b.navkeypressed = false;  	} -	nd = self->curr_hot; +	nd = browser->curr_hot;  	while (1) { -		key = ui_browser__run(&self->b, delay_secs); +		key = ui_browser__run(&browser->b, delay_secs);  		if (delay_secs != 0) { -			annotate_browser__calc_percent(self, evidx); +			annotate_browser__calc_percent(browser, evidx);  			/*  			 * Current line focus got out of the list of most active  			 * lines, NULL it so that if TAB|UNTAB is pressed, we @@ -622,27 +648,27 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx,  			if (nd != NULL) {  				nd = rb_prev(nd);  				if (nd == NULL) -					nd = rb_last(&self->entries); +					nd = rb_last(&browser->entries);  			} else -				nd = self->curr_hot; +				nd = browser->curr_hot;  			break;  		case K_UNTAB:  			if (nd != NULL)  				nd = rb_next(nd);  				if (nd == NULL) -					nd = rb_first(&self->entries); +					nd = rb_first(&browser->entries);  			else -				nd = self->curr_hot; +				nd = browser->curr_hot;  			break;  		case K_F1:  		case 'h': -			ui_browser__help_window(&self->b, +			ui_browser__help_window(&browser->b,  		"UP/DOWN/PGUP\n"  		"PGDN/SPACE    Navigate\n"  		"q/ESC/CTRL+C  Exit\n\n"  		"->            Go to target\n"  		"<-            Exit\n" -		"h             Cycle thru hottest instructions\n" +		"H             Cycle thru hottest instructions\n"  		"j             Toggle showing jump to target arrows\n"  		"J             Toggle showing number of jump sources on targets\n"  		"n             Search next string\n" @@ -652,57 +678,62 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx,  		"?             Search previous string\n");  			continue;  		case 'H': -			nd = self->curr_hot; +			nd = browser->curr_hot;  			break;  		case 's': -			if (annotate_browser__toggle_source(self)) +			if (annotate_browser__toggle_source(browser))  				ui_helpline__puts(help);  			continue;  		case 'o': -			self->use_offset = !self->use_offset; -			if (self->use_offset) -				self->target_width = self->min_addr_width; -			else -				self->target_width = self->max_addr_width; -update_addr_width: -			self->addr_width = self->target_width; -			if (self->show_nr_jumps) -				self->addr_width += self->jumps_width + 1; +			annotate_browser__opts.use_offset = !annotate_browser__opts.use_offset; +			annotate_browser__update_addr_width(browser);  			continue;  		case 'j': -			self->jump_arrows = !self->jump_arrows; +			annotate_browser__opts.jump_arrows = !annotate_browser__opts.jump_arrows;  			continue;  		case 'J': -			self->show_nr_jumps = !self->show_nr_jumps; -			goto update_addr_width; +			annotate_browser__opts.show_nr_jumps = !annotate_browser__opts.show_nr_jumps; +			annotate_browser__update_addr_width(browser); +			continue;  		case '/': -			if (annotate_browser__search(self, delay_secs)) { +			if (annotate_browser__search(browser, delay_secs)) {  show_help:  				ui_helpline__puts(help);  			}  			continue;  		case 'n': -			if (self->searching_backwards ? -			    annotate_browser__continue_search_reverse(self, delay_secs) : -			    annotate_browser__continue_search(self, delay_secs)) +			if (browser->searching_backwards ? +			    annotate_browser__continue_search_reverse(browser, delay_secs) : +			    annotate_browser__continue_search(browser, delay_secs))  				goto show_help;  			continue;  		case '?': -			if (annotate_browser__search_reverse(self, delay_secs)) +			if (annotate_browser__search_reverse(browser, delay_secs))  				goto show_help;  			continue; +		case 'D': { +			static int seq; +			ui_helpline__pop(); +			ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d", +					   seq++, browser->b.nr_entries, +					   browser->b.height, +					   browser->b.index, +					   browser->b.top_idx, +					   browser->nr_asm_entries); +		} +			continue;  		case K_ENTER:  		case K_RIGHT: -			if (self->selection == NULL) +			if (browser->selection == NULL)  				ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org"); -			else if (self->selection->offset == -1) +			else if (browser->selection->offset == -1)  				ui_helpline__puts("Actions are only available for assembly lines."); -			else if (!self->selection->ins) { -				if (strcmp(self->selection->name, "retq")) +			else if (!browser->selection->ins) { +				if (strcmp(browser->selection->name, "retq"))  					goto show_sup_ins;  				goto out; -			} else if (!(annotate_browser__jump(self) || -				     annotate_browser__callq(self, evidx, timer, arg, delay_secs))) { +			} else if (!(annotate_browser__jump(browser) || +				     annotate_browser__callq(browser, evidx, timer, arg, delay_secs))) {  show_sup_ins:  				ui_helpline__puts("Actions are only available for 'callq', 'retq' & jump instructions.");  			} @@ -717,10 +748,10 @@ show_sup_ins:  		}  		if (nd != NULL) -			annotate_browser__set_rb_top(self, nd); +			annotate_browser__set_rb_top(browser, nd);  	}  out: -	ui_browser__hide(&self->b); +	ui_browser__hide(&browser->b);  	return key;  } @@ -783,7 +814,7 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,  {  	struct disasm_line *pos, *n;  	struct annotation *notes; -	const size_t size = symbol__size(sym); +	size_t size;  	struct map_symbol ms = {  		.map = map,  		.sym = sym, @@ -797,14 +828,14 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,  			.priv	 = &ms,  			.use_navkeypressed = true,  		}, -		.use_offset = true, -		.jump_arrows = true,  	};  	int ret = -1;  	if (sym == NULL)  		return -1; +	size = symbol__size(sym); +  	if (map->dso->annotate_warned)  		return -1; @@ -855,6 +886,12 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,  	browser.b.nr_entries = browser.nr_entries;  	browser.b.entries = ¬es->src->source,  	browser.b.width += 18; /* Percentage */ + +	if (annotate_browser__opts.hide_src_code) +		annotate_browser__init_asm_mode(&browser); + +	annotate_browser__update_addr_width(&browser); +  	ret = annotate_browser__run(&browser, evidx, timer, arg, delay_secs);  	list_for_each_entry_safe(pos, n, ¬es->src->source, node) {  		list_del(&pos->node); @@ -865,3 +902,52 @@ out_free_offsets:  	free(browser.offsets);  	return ret;  } + +#define ANNOTATE_CFG(n) \ +	{ .name = #n, .value = &annotate_browser__opts.n, } +	 +/* + * Keep the entries sorted, they are bsearch'ed + */ +static struct annotate__config { +	const char *name; +	bool *value; +} annotate__configs[] = { +	ANNOTATE_CFG(hide_src_code), +	ANNOTATE_CFG(jump_arrows), +	ANNOTATE_CFG(show_nr_jumps), +	ANNOTATE_CFG(use_offset), +}; + +#undef ANNOTATE_CFG + +static int annotate_config__cmp(const void *name, const void *cfgp) +{ +	const struct annotate__config *cfg = cfgp; + +	return strcmp(name, cfg->name); +} + +static int annotate__config(const char *var, const char *value, void *data __used) +{ +	struct annotate__config *cfg; +	const char *name; + +	if (prefixcmp(var, "annotate.") != 0) +		return 0; + +	name = var + 9; +	cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs), +		      sizeof(struct annotate__config), annotate_config__cmp); + +	if (cfg == NULL) +		return -1; + +	*cfg->value = perf_config_bool(name, value); +	return 0; +} + +void annotate_browser__init(void) +{ +	perf_config(annotate__config, NULL); +} diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index a372a4b0263..482f0517b61 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -23,24 +23,25 @@ struct hist_browser {  	struct hists	    *hists;  	struct hist_entry   *he_selection;  	struct map_symbol   *selection; +	int		     print_seq;  	bool		     has_symbols;  }; -static int hists__browser_title(struct hists *self, char *bf, size_t size, +static int hists__browser_title(struct hists *hists, char *bf, size_t size,  				const char *ev_name); -static void hist_browser__refresh_dimensions(struct hist_browser *self) +static void hist_browser__refresh_dimensions(struct hist_browser *browser)  {  	/* 3 == +/- toggle symbol before actual hist_entry rendering */ -	self->b.width = 3 + (hists__sort_list_width(self->hists) + +	browser->b.width = 3 + (hists__sort_list_width(browser->hists) +  			     sizeof("[k]"));  } -static void hist_browser__reset(struct hist_browser *self) +static void hist_browser__reset(struct hist_browser *browser)  { -	self->b.nr_entries = self->hists->nr_entries; -	hist_browser__refresh_dimensions(self); -	ui_browser__reset_index(&self->b); +	browser->b.nr_entries = browser->hists->nr_entries; +	hist_browser__refresh_dimensions(browser); +	ui_browser__reset_index(&browser->b);  }  static char tree__folded_sign(bool unfolded) @@ -48,32 +49,32 @@ static char tree__folded_sign(bool unfolded)  	return unfolded ? '-' : '+';  } -static char map_symbol__folded(const struct map_symbol *self) +static char map_symbol__folded(const struct map_symbol *ms)  { -	return self->has_children ? tree__folded_sign(self->unfolded) : ' '; +	return ms->has_children ? tree__folded_sign(ms->unfolded) : ' ';  } -static char hist_entry__folded(const struct hist_entry *self) +static char hist_entry__folded(const struct hist_entry *he)  { -	return map_symbol__folded(&self->ms); +	return map_symbol__folded(&he->ms);  } -static char callchain_list__folded(const struct callchain_list *self) +static char callchain_list__folded(const struct callchain_list *cl)  { -	return map_symbol__folded(&self->ms); +	return map_symbol__folded(&cl->ms);  } -static void map_symbol__set_folding(struct map_symbol *self, bool unfold) +static void map_symbol__set_folding(struct map_symbol *ms, bool unfold)  { -	self->unfolded = unfold ? self->has_children : false; +	ms->unfolded = unfold ? ms->has_children : false;  } -static int callchain_node__count_rows_rb_tree(struct callchain_node *self) +static int callchain_node__count_rows_rb_tree(struct callchain_node *node)  {  	int n = 0;  	struct rb_node *nd; -	for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) { +	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {  		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);  		struct callchain_list *chain;  		char folded_sign = ' '; /* No children */ @@ -123,23 +124,23 @@ static int callchain__count_rows(struct rb_root *chain)  	return n;  } -static bool map_symbol__toggle_fold(struct map_symbol *self) +static bool map_symbol__toggle_fold(struct map_symbol *ms)  { -	if (!self) +	if (!ms)  		return false; -	if (!self->has_children) +	if (!ms->has_children)  		return false; -	self->unfolded = !self->unfolded; +	ms->unfolded = !ms->unfolded;  	return true;  } -static void callchain_node__init_have_children_rb_tree(struct callchain_node *self) +static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)  { -	struct rb_node *nd = rb_first(&self->rb_root); +	struct rb_node *nd = rb_first(&node->rb_root); -	for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) { +	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {  		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);  		struct callchain_list *chain;  		bool first = true; @@ -158,49 +159,49 @@ static void callchain_node__init_have_children_rb_tree(struct callchain_node *se  	}  } -static void callchain_node__init_have_children(struct callchain_node *self) +static void callchain_node__init_have_children(struct callchain_node *node)  {  	struct callchain_list *chain; -	list_for_each_entry(chain, &self->val, list) -		chain->ms.has_children = !RB_EMPTY_ROOT(&self->rb_root); +	list_for_each_entry(chain, &node->val, list) +		chain->ms.has_children = !RB_EMPTY_ROOT(&node->rb_root); -	callchain_node__init_have_children_rb_tree(self); +	callchain_node__init_have_children_rb_tree(node);  } -static void callchain__init_have_children(struct rb_root *self) +static void callchain__init_have_children(struct rb_root *root)  {  	struct rb_node *nd; -	for (nd = rb_first(self); nd; nd = rb_next(nd)) { +	for (nd = rb_first(root); nd; nd = rb_next(nd)) {  		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);  		callchain_node__init_have_children(node);  	}  } -static void hist_entry__init_have_children(struct hist_entry *self) +static void hist_entry__init_have_children(struct hist_entry *he)  { -	if (!self->init_have_children) { -		self->ms.has_children = !RB_EMPTY_ROOT(&self->sorted_chain); -		callchain__init_have_children(&self->sorted_chain); -		self->init_have_children = true; +	if (!he->init_have_children) { +		he->ms.has_children = !RB_EMPTY_ROOT(&he->sorted_chain); +		callchain__init_have_children(&he->sorted_chain); +		he->init_have_children = true;  	}  } -static bool hist_browser__toggle_fold(struct hist_browser *self) +static bool hist_browser__toggle_fold(struct hist_browser *browser)  { -	if (map_symbol__toggle_fold(self->selection)) { -		struct hist_entry *he = self->he_selection; +	if (map_symbol__toggle_fold(browser->selection)) { +		struct hist_entry *he = browser->he_selection;  		hist_entry__init_have_children(he); -		self->hists->nr_entries -= he->nr_rows; +		browser->hists->nr_entries -= he->nr_rows;  		if (he->ms.unfolded)  			he->nr_rows = callchain__count_rows(&he->sorted_chain);  		else  			he->nr_rows = 0; -		self->hists->nr_entries += he->nr_rows; -		self->b.nr_entries = self->hists->nr_entries; +		browser->hists->nr_entries += he->nr_rows; +		browser->b.nr_entries = browser->hists->nr_entries;  		return true;  	} @@ -209,12 +210,12 @@ static bool hist_browser__toggle_fold(struct hist_browser *self)  	return false;  } -static int callchain_node__set_folding_rb_tree(struct callchain_node *self, bool unfold) +static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)  {  	int n = 0;  	struct rb_node *nd; -	for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) { +	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {  		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);  		struct callchain_list *chain;  		bool has_children = false; @@ -263,37 +264,37 @@ static int callchain__set_folding(struct rb_root *chain, bool unfold)  	return n;  } -static void hist_entry__set_folding(struct hist_entry *self, bool unfold) +static void hist_entry__set_folding(struct hist_entry *he, bool unfold)  { -	hist_entry__init_have_children(self); -	map_symbol__set_folding(&self->ms, unfold); +	hist_entry__init_have_children(he); +	map_symbol__set_folding(&he->ms, unfold); -	if (self->ms.has_children) { -		int n = callchain__set_folding(&self->sorted_chain, unfold); -		self->nr_rows = unfold ? n : 0; +	if (he->ms.has_children) { +		int n = callchain__set_folding(&he->sorted_chain, unfold); +		he->nr_rows = unfold ? n : 0;  	} else -		self->nr_rows = 0; +		he->nr_rows = 0;  } -static void hists__set_folding(struct hists *self, bool unfold) +static void hists__set_folding(struct hists *hists, bool unfold)  {  	struct rb_node *nd; -	self->nr_entries = 0; +	hists->nr_entries = 0; -	for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { +	for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {  		struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);  		hist_entry__set_folding(he, unfold); -		self->nr_entries += 1 + he->nr_rows; +		hists->nr_entries += 1 + he->nr_rows;  	}  } -static void hist_browser__set_folding(struct hist_browser *self, bool unfold) +static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)  { -	hists__set_folding(self->hists, unfold); -	self->b.nr_entries = self->hists->nr_entries; +	hists__set_folding(browser->hists, unfold); +	browser->b.nr_entries = browser->hists->nr_entries;  	/* Go to the start, we may be way after valid entries after a collapse */ -	ui_browser__reset_index(&self->b); +	ui_browser__reset_index(&browser->b);  }  static void ui_browser__warn_lost_events(struct ui_browser *browser) @@ -305,64 +306,64 @@ static void ui_browser__warn_lost_events(struct ui_browser *browser)  		"Or reduce the sampling frequency.");  } -static int hist_browser__run(struct hist_browser *self, const char *ev_name, +static int hist_browser__run(struct hist_browser *browser, const char *ev_name,  			     void(*timer)(void *arg), void *arg, int delay_secs)  {  	int key;  	char title[160]; -	self->b.entries = &self->hists->entries; -	self->b.nr_entries = self->hists->nr_entries; +	browser->b.entries = &browser->hists->entries; +	browser->b.nr_entries = browser->hists->nr_entries; -	hist_browser__refresh_dimensions(self); -	hists__browser_title(self->hists, title, sizeof(title), ev_name); +	hist_browser__refresh_dimensions(browser); +	hists__browser_title(browser->hists, title, sizeof(title), ev_name); -	if (ui_browser__show(&self->b, title, +	if (ui_browser__show(&browser->b, title,  			     "Press '?' for help on key bindings") < 0)  		return -1;  	while (1) { -		key = ui_browser__run(&self->b, delay_secs); +		key = ui_browser__run(&browser->b, delay_secs);  		switch (key) {  		case K_TIMER:  			timer(arg); -			ui_browser__update_nr_entries(&self->b, self->hists->nr_entries); +			ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries); -			if (self->hists->stats.nr_lost_warned != -			    self->hists->stats.nr_events[PERF_RECORD_LOST]) { -				self->hists->stats.nr_lost_warned = -					self->hists->stats.nr_events[PERF_RECORD_LOST]; -				ui_browser__warn_lost_events(&self->b); +			if (browser->hists->stats.nr_lost_warned != +			    browser->hists->stats.nr_events[PERF_RECORD_LOST]) { +				browser->hists->stats.nr_lost_warned = +					browser->hists->stats.nr_events[PERF_RECORD_LOST]; +				ui_browser__warn_lost_events(&browser->b);  			} -			hists__browser_title(self->hists, title, sizeof(title), ev_name); -			ui_browser__show_title(&self->b, title); +			hists__browser_title(browser->hists, title, sizeof(title), ev_name); +			ui_browser__show_title(&browser->b, title);  			continue;  		case 'D': { /* Debug */  			static int seq; -			struct hist_entry *h = rb_entry(self->b.top, +			struct hist_entry *h = rb_entry(browser->b.top,  							struct hist_entry, rb_node);  			ui_helpline__pop();  			ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d", -					   seq++, self->b.nr_entries, -					   self->hists->nr_entries, -					   self->b.height, -					   self->b.index, -					   self->b.top_idx, +					   seq++, browser->b.nr_entries, +					   browser->hists->nr_entries, +					   browser->b.height, +					   browser->b.index, +					   browser->b.top_idx,  					   h->row_offset, h->nr_rows);  		}  			break;  		case 'C':  			/* Collapse the whole world. */ -			hist_browser__set_folding(self, false); +			hist_browser__set_folding(browser, false);  			break;  		case 'E':  			/* Expand the whole world. */ -			hist_browser__set_folding(self, true); +			hist_browser__set_folding(browser, true);  			break;  		case K_ENTER: -			if (hist_browser__toggle_fold(self)) +			if (hist_browser__toggle_fold(browser))  				break;  			/* fall thru */  		default: @@ -370,23 +371,23 @@ static int hist_browser__run(struct hist_browser *self, const char *ev_name,  		}  	}  out: -	ui_browser__hide(&self->b); +	ui_browser__hide(&browser->b);  	return key;  } -static char *callchain_list__sym_name(struct callchain_list *self, +static char *callchain_list__sym_name(struct callchain_list *cl,  				      char *bf, size_t bfsize)  { -	if (self->ms.sym) -		return self->ms.sym->name; +	if (cl->ms.sym) +		return cl->ms.sym->name; -	snprintf(bf, bfsize, "%#" PRIx64, self->ip); +	snprintf(bf, bfsize, "%#" PRIx64, cl->ip);  	return bf;  }  #define LEVEL_OFFSET_STEP 3 -static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self, +static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browser,  						     struct callchain_node *chain_node,  						     u64 total, int level,  						     unsigned short row, @@ -444,21 +445,21 @@ static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self,  			}  			color = HE_COLORSET_NORMAL; -			width = self->b.width - (offset + extra_offset + 2); -			if (ui_browser__is_current_entry(&self->b, row)) { -				self->selection = &chain->ms; +			width = browser->b.width - (offset + extra_offset + 2); +			if (ui_browser__is_current_entry(&browser->b, row)) { +				browser->selection = &chain->ms;  				color = HE_COLORSET_SELECTED;  				*is_current_entry = true;  			} -			ui_browser__set_color(&self->b, color); -			ui_browser__gotorc(&self->b, row, 0); +			ui_browser__set_color(&browser->b, color); +			ui_browser__gotorc(&browser->b, row, 0);  			slsmg_write_nstring(" ", offset + extra_offset);  			slsmg_printf("%c ", folded_sign);  			slsmg_write_nstring(str, width);  			free(alloc_str); -			if (++row == self->b.height) +			if (++row == browser->b.height)  				goto out;  do_next:  			if (folded_sign == '+') @@ -467,11 +468,11 @@ do_next:  		if (folded_sign == '-') {  			const int new_level = level + (extra_offset ? 2 : 1); -			row += hist_browser__show_callchain_node_rb_tree(self, child, new_total, +			row += hist_browser__show_callchain_node_rb_tree(browser, child, new_total,  									 new_level, row, row_offset,  									 is_current_entry);  		} -		if (row == self->b.height) +		if (row == browser->b.height)  			goto out;  		node = next;  	} @@ -479,7 +480,7 @@ out:  	return row - first_row;  } -static int hist_browser__show_callchain_node(struct hist_browser *self, +static int hist_browser__show_callchain_node(struct hist_browser *browser,  					     struct callchain_node *node,  					     int level, unsigned short row,  					     off_t *row_offset, @@ -488,7 +489,7 @@ static int hist_browser__show_callchain_node(struct hist_browser *self,  	struct callchain_list *chain;  	int first_row = row,  	     offset = level * LEVEL_OFFSET_STEP, -	     width = self->b.width - offset; +	     width = browser->b.width - offset;  	char folded_sign = ' ';  	list_for_each_entry(chain, &node->val, list) { @@ -503,26 +504,26 @@ static int hist_browser__show_callchain_node(struct hist_browser *self,  		}  		color = HE_COLORSET_NORMAL; -		if (ui_browser__is_current_entry(&self->b, row)) { -			self->selection = &chain->ms; +		if (ui_browser__is_current_entry(&browser->b, row)) { +			browser->selection = &chain->ms;  			color = HE_COLORSET_SELECTED;  			*is_current_entry = true;  		}  		s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); -		ui_browser__gotorc(&self->b, row, 0); -		ui_browser__set_color(&self->b, color); +		ui_browser__gotorc(&browser->b, row, 0); +		ui_browser__set_color(&browser->b, color);  		slsmg_write_nstring(" ", offset);  		slsmg_printf("%c ", folded_sign);  		slsmg_write_nstring(s, width - 2); -		if (++row == self->b.height) +		if (++row == browser->b.height)  			goto out;  	}  	if (folded_sign == '-') -		row += hist_browser__show_callchain_node_rb_tree(self, node, -								 self->hists->stats.total_period, +		row += hist_browser__show_callchain_node_rb_tree(browser, node, +								 browser->hists->stats.total_period,  								 level + 1, row,  								 row_offset,  								 is_current_entry); @@ -530,7 +531,7 @@ out:  	return row - first_row;  } -static int hist_browser__show_callchain(struct hist_browser *self, +static int hist_browser__show_callchain(struct hist_browser *browser,  					struct rb_root *chain,  					int level, unsigned short row,  					off_t *row_offset, @@ -542,31 +543,31 @@ static int hist_browser__show_callchain(struct hist_browser *self,  	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {  		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); -		row += hist_browser__show_callchain_node(self, node, level, +		row += hist_browser__show_callchain_node(browser, node, level,  							 row, row_offset,  							 is_current_entry); -		if (row == self->b.height) +		if (row == browser->b.height)  			break;  	}  	return row - first_row;  } -static int hist_browser__show_entry(struct hist_browser *self, +static int hist_browser__show_entry(struct hist_browser *browser,  				    struct hist_entry *entry,  				    unsigned short row)  {  	char s[256];  	double percent;  	int printed = 0; -	int width = self->b.width - 6; /* The percentage */ +	int width = browser->b.width - 6; /* The percentage */  	char folded_sign = ' '; -	bool current_entry = ui_browser__is_current_entry(&self->b, row); +	bool current_entry = ui_browser__is_current_entry(&browser->b, row);  	off_t row_offset = entry->row_offset;  	if (current_entry) { -		self->he_selection = entry; -		self->selection = &entry->ms; +		browser->he_selection = entry; +		browser->selection = &entry->ms;  	}  	if (symbol_conf.use_callchain) { @@ -575,11 +576,11 @@ static int hist_browser__show_entry(struct hist_browser *self,  	}  	if (row_offset == 0) { -		hist_entry__snprintf(entry, s, sizeof(s), self->hists); -		percent = (entry->period * 100.0) / self->hists->stats.total_period; +		hist_entry__snprintf(entry, s, sizeof(s), browser->hists); +		percent = (entry->period * 100.0) / browser->hists->stats.total_period; -		ui_browser__set_percent_color(&self->b, percent, current_entry); -		ui_browser__gotorc(&self->b, row, 0); +		ui_browser__set_percent_color(&browser->b, percent, current_entry); +		ui_browser__gotorc(&browser->b, row, 0);  		if (symbol_conf.use_callchain) {  			slsmg_printf("%c ", folded_sign);  			width -= 2; @@ -588,11 +589,11 @@ static int hist_browser__show_entry(struct hist_browser *self,  		slsmg_printf(" %5.2f%%", percent);  		/* The scroll bar isn't being used */ -		if (!self->b.navkeypressed) +		if (!browser->b.navkeypressed)  			width += 1; -		if (!current_entry || !self->b.navkeypressed) -			ui_browser__set_color(&self->b, HE_COLORSET_NORMAL); +		if (!current_entry || !browser->b.navkeypressed) +			ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);  		if (symbol_conf.show_nr_samples) {  			slsmg_printf(" %11u", entry->nr_events); @@ -610,12 +611,12 @@ static int hist_browser__show_entry(struct hist_browser *self,  	} else  		--row_offset; -	if (folded_sign == '-' && row != self->b.height) { -		printed += hist_browser__show_callchain(self, &entry->sorted_chain, +	if (folded_sign == '-' && row != browser->b.height) { +		printed += hist_browser__show_callchain(browser, &entry->sorted_chain,  							1, row, &row_offset,  							¤t_entry);  		if (current_entry) -			self->he_selection = entry; +			browser->he_selection = entry;  	}  	return printed; @@ -631,22 +632,22 @@ static void ui_browser__hists_init_top(struct ui_browser *browser)  	}  } -static unsigned int hist_browser__refresh(struct ui_browser *self) +static unsigned int hist_browser__refresh(struct ui_browser *browser)  {  	unsigned row = 0;  	struct rb_node *nd; -	struct hist_browser *hb = container_of(self, struct hist_browser, b); +	struct hist_browser *hb = container_of(browser, struct hist_browser, b); -	ui_browser__hists_init_top(self); +	ui_browser__hists_init_top(browser); -	for (nd = self->top; nd; nd = rb_next(nd)) { +	for (nd = browser->top; nd; nd = rb_next(nd)) {  		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);  		if (h->filtered)  			continue;  		row += hist_browser__show_entry(hb, h, row); -		if (row == self->height) +		if (row == browser->height)  			break;  	} @@ -679,27 +680,27 @@ static struct rb_node *hists__filter_prev_entries(struct rb_node *nd)  	return NULL;  } -static void ui_browser__hists_seek(struct ui_browser *self, +static void ui_browser__hists_seek(struct ui_browser *browser,  				   off_t offset, int whence)  {  	struct hist_entry *h;  	struct rb_node *nd;  	bool first = true; -	if (self->nr_entries == 0) +	if (browser->nr_entries == 0)  		return; -	ui_browser__hists_init_top(self); +	ui_browser__hists_init_top(browser);  	switch (whence) {  	case SEEK_SET: -		nd = hists__filter_entries(rb_first(self->entries)); +		nd = hists__filter_entries(rb_first(browser->entries));  		break;  	case SEEK_CUR: -		nd = self->top; +		nd = browser->top;  		goto do_offset;  	case SEEK_END: -		nd = hists__filter_prev_entries(rb_last(self->entries)); +		nd = hists__filter_prev_entries(rb_last(browser->entries));  		first = false;  		break;  	default: @@ -710,7 +711,7 @@ static void ui_browser__hists_seek(struct ui_browser *self,  	 * Moves not relative to the first visible entry invalidates its  	 * row_offset:  	 */ -	h = rb_entry(self->top, struct hist_entry, rb_node); +	h = rb_entry(browser->top, struct hist_entry, rb_node);  	h->row_offset = 0;  	/* @@ -738,7 +739,7 @@ do_offset:  				} else {  					h->row_offset += offset;  					offset = 0; -					self->top = nd; +					browser->top = nd;  					break;  				}  			} @@ -746,7 +747,7 @@ do_offset:  			if (nd == NULL)  				break;  			--offset; -			self->top = nd; +			browser->top = nd;  		} while (offset != 0);  	} else if (offset < 0) {  		while (1) { @@ -759,7 +760,7 @@ do_offset:  					} else {  						h->row_offset += offset;  						offset = 0; -						self->top = nd; +						browser->top = nd;  						break;  					}  				} else { @@ -769,7 +770,7 @@ do_offset:  					} else {  						h->row_offset = h->nr_rows + offset;  						offset = 0; -						self->top = nd; +						browser->top = nd;  						break;  					}  				} @@ -779,7 +780,7 @@ do_offset:  			if (nd == NULL)  				break;  			++offset; -			self->top = nd; +			browser->top = nd;  			if (offset == 0) {  				/*  				 * Last unfiltered hist_entry, check if it is @@ -794,54 +795,244 @@ do_offset:  			first = false;  		}  	} else { -		self->top = nd; +		browser->top = nd;  		h = rb_entry(nd, struct hist_entry, rb_node);  		h->row_offset = 0;  	}  } +static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser *browser, +							struct callchain_node *chain_node, +							u64 total, int level, +							FILE *fp) +{ +	struct rb_node *node; +	int offset = level * LEVEL_OFFSET_STEP; +	u64 new_total, remaining; +	int printed = 0; + +	if (callchain_param.mode == CHAIN_GRAPH_REL) +		new_total = chain_node->children_hit; +	else +		new_total = total; + +	remaining = new_total; +	node = rb_first(&chain_node->rb_root); +	while (node) { +		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); +		struct rb_node *next = rb_next(node); +		u64 cumul = callchain_cumul_hits(child); +		struct callchain_list *chain; +		char folded_sign = ' '; +		int first = true; +		int extra_offset = 0; + +		remaining -= cumul; + +		list_for_each_entry(chain, &child->val, list) { +			char ipstr[BITS_PER_LONG / 4 + 1], *alloc_str; +			const char *str; +			bool was_first = first; + +			if (first) +				first = false; +			else +				extra_offset = LEVEL_OFFSET_STEP; + +			folded_sign = callchain_list__folded(chain); + +			alloc_str = NULL; +			str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); +			if (was_first) { +				double percent = cumul * 100.0 / new_total; + +				if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0) +					str = "Not enough memory!"; +				else +					str = alloc_str; +			} + +			printed += fprintf(fp, "%*s%c %s\n", offset + extra_offset, " ", folded_sign, str); +			free(alloc_str); +			if (folded_sign == '+') +				break; +		} + +		if (folded_sign == '-') { +			const int new_level = level + (extra_offset ? 2 : 1); +			printed += hist_browser__fprintf_callchain_node_rb_tree(browser, child, new_total, +										new_level, fp); +		} + +		node = next; +	} + +	return printed; +} + +static int hist_browser__fprintf_callchain_node(struct hist_browser *browser, +						struct callchain_node *node, +						int level, FILE *fp) +{ +	struct callchain_list *chain; +	int offset = level * LEVEL_OFFSET_STEP; +	char folded_sign = ' '; +	int printed = 0; + +	list_for_each_entry(chain, &node->val, list) { +		char ipstr[BITS_PER_LONG / 4 + 1], *s; + +		folded_sign = callchain_list__folded(chain); +		s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); +		printed += fprintf(fp, "%*s%c %s\n", offset, " ", folded_sign, s); +	} + +	if (folded_sign == '-') +		printed += hist_browser__fprintf_callchain_node_rb_tree(browser, node, +									browser->hists->stats.total_period, +									level + 1,  fp); +	return printed; +} + +static int hist_browser__fprintf_callchain(struct hist_browser *browser, +					   struct rb_root *chain, int level, FILE *fp) +{ +	struct rb_node *nd; +	int printed = 0; + +	for (nd = rb_first(chain); nd; nd = rb_next(nd)) { +		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); + +		printed += hist_browser__fprintf_callchain_node(browser, node, level, fp); +	} + +	return printed; +} + +static int hist_browser__fprintf_entry(struct hist_browser *browser, +				       struct hist_entry *he, FILE *fp) +{ +	char s[8192]; +	double percent; +	int printed = 0; +	char folded_sign = ' '; + +	if (symbol_conf.use_callchain) +		folded_sign = hist_entry__folded(he); + +	hist_entry__snprintf(he, s, sizeof(s), browser->hists); +	percent = (he->period * 100.0) / browser->hists->stats.total_period; + +	if (symbol_conf.use_callchain) +		printed += fprintf(fp, "%c ", folded_sign); + +	printed += fprintf(fp, " %5.2f%%", percent); + +	if (symbol_conf.show_nr_samples) +		printed += fprintf(fp, " %11u", he->nr_events); + +	if (symbol_conf.show_total_period) +		printed += fprintf(fp, " %12" PRIu64, he->period); + +	printed += fprintf(fp, "%s\n", rtrim(s)); + +	if (folded_sign == '-') +		printed += hist_browser__fprintf_callchain(browser, &he->sorted_chain, 1, fp); + +	return printed; +} + +static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp) +{ +	struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries)); +	int printed = 0; + +	while (nd) { +		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); + +		printed += hist_browser__fprintf_entry(browser, h, fp); +		nd = hists__filter_entries(rb_next(nd)); +	} + +	return printed; +} + +static int hist_browser__dump(struct hist_browser *browser) +{ +	char filename[64]; +	FILE *fp; + +	while (1) { +		scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq); +		if (access(filename, F_OK)) +			break; +		/* + 		 * XXX: Just an arbitrary lazy upper limit + 		 */ +		if (++browser->print_seq == 8192) { +			ui_helpline__fpush("Too many perf.hist.N files, nothing written!"); +			return -1; +		} +	} + +	fp = fopen(filename, "w"); +	if (fp == NULL) { +		char bf[64]; +		strerror_r(errno, bf, sizeof(bf)); +		ui_helpline__fpush("Couldn't write to %s: %s", filename, bf); +		return -1; +	} + +	++browser->print_seq; +	hist_browser__fprintf(browser, fp); +	fclose(fp); +	ui_helpline__fpush("%s written!", filename); + +	return 0; +} +  static struct hist_browser *hist_browser__new(struct hists *hists)  { -	struct hist_browser *self = zalloc(sizeof(*self)); +	struct hist_browser *browser = zalloc(sizeof(*browser)); -	if (self) { -		self->hists = hists; -		self->b.refresh = hist_browser__refresh; -		self->b.seek = ui_browser__hists_seek; -		self->b.use_navkeypressed = true; +	if (browser) { +		browser->hists = hists; +		browser->b.refresh = hist_browser__refresh; +		browser->b.seek = ui_browser__hists_seek; +		browser->b.use_navkeypressed = true;  		if (sort__branch_mode == 1) -			self->has_symbols = sort_sym_from.list.next != NULL; +			browser->has_symbols = sort_sym_from.list.next != NULL;  		else -			self->has_symbols = sort_sym.list.next != NULL; +			browser->has_symbols = sort_sym.list.next != NULL;  	} -	return self; +	return browser;  } -static void hist_browser__delete(struct hist_browser *self) +static void hist_browser__delete(struct hist_browser *browser)  { -	free(self); +	free(browser);  } -static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self) +static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)  { -	return self->he_selection; +	return browser->he_selection;  } -static struct thread *hist_browser__selected_thread(struct hist_browser *self) +static struct thread *hist_browser__selected_thread(struct hist_browser *browser)  { -	return self->he_selection->thread; +	return browser->he_selection->thread;  } -static int hists__browser_title(struct hists *self, char *bf, size_t size, +static int hists__browser_title(struct hists *hists, char *bf, size_t size,  				const char *ev_name)  {  	char unit;  	int printed; -	const struct dso *dso = self->dso_filter; -	const struct thread *thread = self->thread_filter; -	unsigned long nr_samples = self->stats.nr_events[PERF_RECORD_SAMPLE]; -	u64 nr_events = self->stats.total_period; +	const struct dso *dso = hists->dso_filter; +	const struct thread *thread = hists->thread_filter; +	unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE]; +	u64 nr_events = hists->stats.total_period;  	nr_samples = convert_unit(nr_samples, &unit);  	printed = scnprintf(bf, size, @@ -849,9 +1040,9 @@ static int hists__browser_title(struct hists *self, char *bf, size_t size,  			   nr_samples, unit, ev_name, nr_events); -	if (self->uid_filter_str) +	if (hists->uid_filter_str)  		printed += snprintf(bf + printed, size - printed, -				    ", UID: %s", self->uid_filter_str); +				    ", UID: %s", hists->uid_filter_str);  	if (thread)  		printed += scnprintf(bf + printed, size - printed,  				    ", Thread: %s(%d)", @@ -879,8 +1070,8 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,  				    void(*timer)(void *arg), void *arg,  				    int delay_secs)  { -	struct hists *self = &evsel->hists; -	struct hist_browser *browser = hist_browser__new(self); +	struct hists *hists = &evsel->hists; +	struct hist_browser *browser = hist_browser__new(hists);  	struct branch_info *bi;  	struct pstack *fstack;  	char *options[16]; @@ -937,6 +1128,9 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,  			    browser->selection->map->dso->annotate_warned)  				continue;  			goto do_annotate; +		case 'P': +			hist_browser__dump(browser); +			continue;  		case 'd':  			goto zoom_dso;  		case 't': @@ -946,8 +1140,8 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,  					"Please enter the name of symbol you want to see",  					buf, "ENTER: OK, ESC: Cancel",  					delay_secs * 2) == K_ENTER) { -				self->symbol_filter_str = *buf ? buf : NULL; -				hists__filter_by_symbol(self); +				hists->symbol_filter_str = *buf ? buf : NULL; +				hists__filter_by_symbol(hists);  				hist_browser__reset(browser);  			}  			continue; @@ -969,6 +1163,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,  					"E             Expand all callchains\n"  					"d             Zoom into current DSO\n"  					"t             Zoom into current Thread\n" +					"P             Print histograms to perf.hist.N\n"  					"/             Filter symbol by name");  			continue;  		case K_ENTER: @@ -1128,7 +1323,7 @@ zoom_out_dso:  				sort_dso.elide = true;  				pstack__push(fstack, &browser->hists->dso_filter);  			} -			hists__filter_by_dso(self); +			hists__filter_by_dso(hists);  			hist_browser__reset(browser);  		} else if (choice == zoom_thread) {  zoom_thread: @@ -1146,7 +1341,7 @@ zoom_out_thread:  				sort_thread.elide = true;  				pstack__push(fstack, &browser->hists->thread_filter);  			} -			hists__filter_by_thread(self); +			hists__filter_by_thread(hists);  			hist_browser__reset(browser);  		}  	} @@ -1172,7 +1367,7 @@ static void perf_evsel_menu__write(struct ui_browser *browser,  	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);  	bool current_entry = ui_browser__is_current_entry(browser, row);  	unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE]; -	const char *ev_name = event_name(evsel); +	const char *ev_name = perf_evsel__name(evsel);  	char bf[256], unit;  	const char *warn = " ";  	size_t printed; @@ -1240,7 +1435,7 @@ browse_hists:  			 */  			if (timer)  				timer(arg); -			ev_name = event_name(pos); +			ev_name = perf_evsel__name(pos);  			key = perf_evsel__hists_browse(pos, nr_events, help,  						       ev_name, true, timer,  						       arg, delay_secs); @@ -1309,17 +1504,11 @@ static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,  	ui_helpline__push("Press ESC to exit");  	list_for_each_entry(pos, &evlist->entries, node) { -		const char *ev_name = event_name(pos); +		const char *ev_name = perf_evsel__name(pos);  		size_t line_len = strlen(ev_name) + 7;  		if (menu.b.width < line_len)  			menu.b.width = line_len; -		/* -		 * Cache the evsel name, tracepoints have a _high_ cost per -		 * event_name() call. -		 */ -		if (pos->name == NULL) -			pos->name = strdup(ev_name);  	}  	return perf_evsel_menu__run(&menu, evlist->nr_entries, help, timer, @@ -1330,11 +1519,10 @@ int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,  				  void(*timer)(void *arg), void *arg,  				  int delay_secs)  { -  	if (evlist->nr_entries == 1) {  		struct perf_evsel *first = list_entry(evlist->entries.next,  						      struct perf_evsel, node); -		const char *ev_name = event_name(first); +		const char *ev_name = perf_evsel__name(first);  		return perf_evsel__hists_browse(first, evlist->nr_entries, help,  						ev_name, false, timer, arg,  						delay_secs); diff --git a/tools/perf/ui/gtk/browser.c b/tools/perf/ui/gtk/browser.c index 0656c381a89..ec12e0b4ded 100644 --- a/tools/perf/ui/gtk/browser.c +++ b/tools/perf/ui/gtk/browser.c @@ -11,8 +11,8 @@  static void perf_gtk__signal(int sig)  { +	perf_gtk__exit(false);  	psignal(sig, "perf"); -	gtk_main_quit();  }  static void perf_gtk__resize_window(GtkWidget *window) @@ -122,13 +122,59 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists)  	gtk_container_add(GTK_CONTAINER(window), view);  } +#ifdef HAVE_GTK_INFO_BAR +static GtkWidget *perf_gtk__setup_info_bar(void) +{ +	GtkWidget *info_bar; +	GtkWidget *label; +	GtkWidget *content_area; + +	info_bar = gtk_info_bar_new(); +	gtk_widget_set_no_show_all(info_bar, TRUE); + +	label = gtk_label_new(""); +	gtk_widget_show(label); + +	content_area = gtk_info_bar_get_content_area(GTK_INFO_BAR(info_bar)); +	gtk_container_add(GTK_CONTAINER(content_area), label); + +	gtk_info_bar_add_button(GTK_INFO_BAR(info_bar), GTK_STOCK_OK, +				GTK_RESPONSE_OK); +	g_signal_connect(info_bar, "response", +			 G_CALLBACK(gtk_widget_hide), NULL); + +	pgctx->info_bar = info_bar; +	pgctx->message_label = label; + +	return info_bar; +} +#endif + +static GtkWidget *perf_gtk__setup_statusbar(void) +{ +	GtkWidget *stbar; +	unsigned ctxid; + +	stbar = gtk_statusbar_new(); + +	ctxid = gtk_statusbar_get_context_id(GTK_STATUSBAR(stbar), +					     "perf report"); +	pgctx->statbar = stbar; +	pgctx->statbar_ctx_id = ctxid; + +	return stbar; +} +  int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist,  				  const char *help __used,  				  void (*timer) (void *arg)__used,  				  void *arg __used, int delay_secs __used)  {  	struct perf_evsel *pos; +	GtkWidget *vbox;  	GtkWidget *notebook; +	GtkWidget *info_bar; +	GtkWidget *statbar;  	GtkWidget *window;  	signal(SIGSEGV, perf_gtk__signal); @@ -143,11 +189,17 @@ int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist,  	g_signal_connect(window, "delete_event", gtk_main_quit, NULL); +	pgctx = perf_gtk__activate_context(window); +	if (!pgctx) +		return -1; + +	vbox = gtk_vbox_new(FALSE, 0); +  	notebook = gtk_notebook_new();  	list_for_each_entry(pos, &evlist->entries, node) {  		struct hists *hists = &pos->hists; -		const char *evname = event_name(pos); +		const char *evname = perf_evsel__name(pos);  		GtkWidget *scrolled_window;  		GtkWidget *tab_label; @@ -164,7 +216,16 @@ int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist,  		gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled_window, tab_label);  	} -	gtk_container_add(GTK_CONTAINER(window), notebook); +	gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0); + +	info_bar = perf_gtk__setup_info_bar(); +	if (info_bar) +		gtk_box_pack_start(GTK_BOX(vbox), info_bar, FALSE, FALSE, 0); + +	statbar = perf_gtk__setup_statusbar(); +	gtk_box_pack_start(GTK_BOX(vbox), statbar, FALSE, FALSE, 0); + +	gtk_container_add(GTK_CONTAINER(window), vbox);  	gtk_widget_show_all(window); @@ -174,5 +235,7 @@ int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist,  	gtk_main(); +	perf_gtk__deactivate_context(&pgctx); +  	return 0;  } diff --git a/tools/perf/ui/gtk/gtk.h b/tools/perf/ui/gtk/gtk.h index 75177ee0403..a4d0f2b4a2d 100644 --- a/tools/perf/ui/gtk/gtk.h +++ b/tools/perf/ui/gtk/gtk.h @@ -1,8 +1,39 @@  #ifndef _PERF_GTK_H_  #define _PERF_GTK_H_ 1 +#include <stdbool.h> +  #pragma GCC diagnostic ignored "-Wstrict-prototypes"  #include <gtk/gtk.h>  #pragma GCC diagnostic error "-Wstrict-prototypes" + +struct perf_gtk_context { +	GtkWidget *main_window; + +#ifdef HAVE_GTK_INFO_BAR +	GtkWidget *info_bar; +	GtkWidget *message_label; +#endif +	GtkWidget *statbar; +	guint statbar_ctx_id; +}; + +extern struct perf_gtk_context *pgctx; + +static inline bool perf_gtk__is_active_context(struct perf_gtk_context *ctx) +{ +	return ctx && ctx->main_window; +} + +struct perf_gtk_context *perf_gtk__activate_context(GtkWidget *window); +int perf_gtk__deactivate_context(struct perf_gtk_context **ctx); + +#ifndef HAVE_GTK_INFO_BAR +static inline GtkWidget *perf_gtk__setup_info_bar(void) +{ +	return NULL; +} +#endif +  #endif /* _PERF_GTK_H_ */ diff --git a/tools/perf/ui/gtk/setup.c b/tools/perf/ui/gtk/setup.c index 82952995776..92879ce61e2 100644 --- a/tools/perf/ui/gtk/setup.c +++ b/tools/perf/ui/gtk/setup.c @@ -1,12 +1,17 @@  #include "gtk.h"  #include "../../util/cache.h" +#include "../../util/debug.h" + +extern struct perf_error_ops perf_gtk_eops;  int perf_gtk__init(void)  { +	perf_error__register(&perf_gtk_eops);  	return gtk_init_check(NULL, NULL) ? 0 : -1;  }  void perf_gtk__exit(bool wait_for_ok __used)  { +	perf_error__unregister(&perf_gtk_eops);  	gtk_main_quit();  } diff --git a/tools/perf/ui/gtk/util.c b/tools/perf/ui/gtk/util.c new file mode 100644 index 00000000000..0ead373c0df --- /dev/null +++ b/tools/perf/ui/gtk/util.c @@ -0,0 +1,129 @@ +#include "../util.h" +#include "../../util/debug.h" +#include "gtk.h" + +#include <string.h> + + +struct perf_gtk_context *pgctx; + +struct perf_gtk_context *perf_gtk__activate_context(GtkWidget *window) +{ +	struct perf_gtk_context *ctx; + +	ctx = malloc(sizeof(*pgctx)); +	if (ctx) +		ctx->main_window = window; + +	return ctx; +} + +int perf_gtk__deactivate_context(struct perf_gtk_context **ctx) +{ +	if (!perf_gtk__is_active_context(*ctx)) +		return -1; + +	free(*ctx); +	*ctx = NULL; +	return 0; +} + +static int perf_gtk__error(const char *format, va_list args) +{ +	char *msg; +	GtkWidget *dialog; + +	if (!perf_gtk__is_active_context(pgctx) || +	    vasprintf(&msg, format, args) < 0) { +		fprintf(stderr, "Error:\n"); +		vfprintf(stderr, format, args); +		fprintf(stderr, "\n"); +		return -1; +	} + +	dialog = gtk_message_dialog_new_with_markup(GTK_WINDOW(pgctx->main_window), +					GTK_DIALOG_DESTROY_WITH_PARENT, +					GTK_MESSAGE_ERROR, +					GTK_BUTTONS_CLOSE, +					"<b>Error</b>\n\n%s", msg); +	gtk_dialog_run(GTK_DIALOG(dialog)); + +	gtk_widget_destroy(dialog); +	free(msg); +	return 0; +} + +#ifdef HAVE_GTK_INFO_BAR +static int perf_gtk__warning_info_bar(const char *format, va_list args) +{ +	char *msg; + +	if (!perf_gtk__is_active_context(pgctx) || +	    vasprintf(&msg, format, args) < 0) { +		fprintf(stderr, "Warning:\n"); +		vfprintf(stderr, format, args); +		fprintf(stderr, "\n"); +		return -1; +	} + +	gtk_label_set_text(GTK_LABEL(pgctx->message_label), msg); +	gtk_info_bar_set_message_type(GTK_INFO_BAR(pgctx->info_bar), +				      GTK_MESSAGE_WARNING); +	gtk_widget_show(pgctx->info_bar); + +	free(msg); +	return 0; +} +#else +static int perf_gtk__warning_statusbar(const char *format, va_list args) +{ +	char *msg, *p; + +	if (!perf_gtk__is_active_context(pgctx) || +	    vasprintf(&msg, format, args) < 0) { +		fprintf(stderr, "Warning:\n"); +		vfprintf(stderr, format, args); +		fprintf(stderr, "\n"); +		return -1; +	} + +	gtk_statusbar_pop(GTK_STATUSBAR(pgctx->statbar), +			  pgctx->statbar_ctx_id); + +	/* Only first line can be displayed */ +	p = strchr(msg, '\n'); +	if (p) +		*p = '\0'; + +	gtk_statusbar_push(GTK_STATUSBAR(pgctx->statbar), +			   pgctx->statbar_ctx_id, msg); + +	free(msg); +	return 0; +} +#endif + +struct perf_error_ops perf_gtk_eops = { +	.error		= perf_gtk__error, +#ifdef HAVE_GTK_INFO_BAR +	.warning	= perf_gtk__warning_info_bar, +#else +	.warning	= perf_gtk__warning_statusbar, +#endif +}; + +/* + * FIXME: Functions below should be implemented properly. + *        For now, just add stubs for NO_NEWT=1 build. + */ +#ifdef NO_NEWT_SUPPORT +int ui_helpline__show_help(const char *format __used, va_list ap __used) +{ +	return 0; +} + +void ui_progress__update(u64 curr __used, u64 total __used, +			 const char *title __used) +{ +} +#endif diff --git a/tools/perf/ui/setup.c b/tools/perf/ui/setup.c index 9f5f888f73e..791fb15ce35 100644 --- a/tools/perf/ui/setup.c +++ b/tools/perf/ui/setup.c @@ -22,6 +22,7 @@ void setup_browser(bool fallback_to_pager)  			break;  		/* fall through */  	default: +		use_browser = 0;  		if (fallback_to_pager)  			setup_pager();  		break; diff --git a/tools/perf/ui/tui/setup.c b/tools/perf/ui/tui/setup.c index d33e943ac43..e813c1d1734 100644 --- a/tools/perf/ui/tui/setup.c +++ b/tools/perf/ui/tui/setup.c @@ -15,6 +15,8 @@ pthread_mutex_t ui__lock = PTHREAD_MUTEX_INITIALIZER;  static volatile int ui__need_resize; +extern struct perf_error_ops perf_tui_eops; +  void ui__refresh_dimensions(bool force)  {  	if (force || ui__need_resize) { @@ -122,6 +124,8 @@ int ui__init(void)  	signal(SIGINT, ui__signal);  	signal(SIGQUIT, ui__signal);  	signal(SIGTERM, ui__signal); + +	perf_error__register(&perf_tui_eops);  out:  	return err;  } @@ -137,4 +141,6 @@ void ui__exit(bool wait_for_ok)  	SLsmg_refresh();  	SLsmg_reset_smg();  	SLang_reset_tty(); + +	perf_error__unregister(&perf_tui_eops);  } diff --git a/tools/perf/ui/tui/util.c b/tools/perf/ui/tui/util.c new file mode 100644 index 00000000000..092902e30ce --- /dev/null +++ b/tools/perf/ui/tui/util.c @@ -0,0 +1,243 @@ +#include "../../util/util.h" +#include <signal.h> +#include <stdbool.h> +#include <string.h> +#include <sys/ttydefaults.h> + +#include "../../util/cache.h" +#include "../../util/debug.h" +#include "../browser.h" +#include "../keysyms.h" +#include "../helpline.h" +#include "../ui.h" +#include "../util.h" +#include "../libslang.h" + +static void ui_browser__argv_write(struct ui_browser *browser, +				   void *entry, int row) +{ +	char **arg = entry; +	bool current_entry = ui_browser__is_current_entry(browser, row); + +	ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED : +						       HE_COLORSET_NORMAL); +	slsmg_write_nstring(*arg, browser->width); +} + +static int popup_menu__run(struct ui_browser *menu) +{ +	int key; + +	if (ui_browser__show(menu, " ", "ESC: exit, ENTER|->: Select option") < 0) +		return -1; + +	while (1) { +		key = ui_browser__run(menu, 0); + +		switch (key) { +		case K_RIGHT: +		case K_ENTER: +			key = menu->index; +			break; +		case K_LEFT: +		case K_ESC: +		case 'q': +		case CTRL('c'): +			key = -1; +			break; +		default: +			continue; +		} + +		break; +	} + +	ui_browser__hide(menu); +	return key; +} + +int ui__popup_menu(int argc, char * const argv[]) +{ +	struct ui_browser menu = { +		.entries    = (void *)argv, +		.refresh    = ui_browser__argv_refresh, +		.seek	    = ui_browser__argv_seek, +		.write	    = ui_browser__argv_write, +		.nr_entries = argc, +	}; + +	return popup_menu__run(&menu); +} + +int ui_browser__input_window(const char *title, const char *text, char *input, +			     const char *exit_msg, int delay_secs) +{ +	int x, y, len, key; +	int max_len = 60, nr_lines = 0; +	static char buf[50]; +	const char *t; + +	t = text; +	while (1) { +		const char *sep = strchr(t, '\n'); + +		if (sep == NULL) +			sep = strchr(t, '\0'); +		len = sep - t; +		if (max_len < len) +			max_len = len; +		++nr_lines; +		if (*sep == '\0') +			break; +		t = sep + 1; +	} + +	max_len += 2; +	nr_lines += 8; +	y = SLtt_Screen_Rows / 2 - nr_lines / 2; +	x = SLtt_Screen_Cols / 2 - max_len / 2; + +	SLsmg_set_color(0); +	SLsmg_draw_box(y, x++, nr_lines, max_len); +	if (title) { +		SLsmg_gotorc(y, x + 1); +		SLsmg_write_string((char *)title); +	} +	SLsmg_gotorc(++y, x); +	nr_lines -= 7; +	max_len -= 2; +	SLsmg_write_wrapped_string((unsigned char *)text, y, x, +				   nr_lines, max_len, 1); +	y += nr_lines; +	len = 5; +	while (len--) { +		SLsmg_gotorc(y + len - 1, x); +		SLsmg_write_nstring((char *)" ", max_len); +	} +	SLsmg_draw_box(y++, x + 1, 3, max_len - 2); + +	SLsmg_gotorc(y + 3, x); +	SLsmg_write_nstring((char *)exit_msg, max_len); +	SLsmg_refresh(); + +	x += 2; +	len = 0; +	key = ui__getch(delay_secs); +	while (key != K_TIMER && key != K_ENTER && key != K_ESC) { +		if (key == K_BKSPC) { +			if (len == 0) +				goto next_key; +			SLsmg_gotorc(y, x + --len); +			SLsmg_write_char(' '); +		} else { +			buf[len] = key; +			SLsmg_gotorc(y, x + len++); +			SLsmg_write_char(key); +		} +		SLsmg_refresh(); + +		/* XXX more graceful overflow handling needed */ +		if (len == sizeof(buf) - 1) { +			ui_helpline__push("maximum size of symbol name reached!"); +			key = K_ENTER; +			break; +		} +next_key: +		key = ui__getch(delay_secs); +	} + +	buf[len] = '\0'; +	strncpy(input, buf, len+1); +	return key; +} + +int ui__question_window(const char *title, const char *text, +			const char *exit_msg, int delay_secs) +{ +	int x, y; +	int max_len = 0, nr_lines = 0; +	const char *t; + +	t = text; +	while (1) { +		const char *sep = strchr(t, '\n'); +		int len; + +		if (sep == NULL) +			sep = strchr(t, '\0'); +		len = sep - t; +		if (max_len < len) +			max_len = len; +		++nr_lines; +		if (*sep == '\0') +			break; +		t = sep + 1; +	} + +	max_len += 2; +	nr_lines += 4; +	y = SLtt_Screen_Rows / 2 - nr_lines / 2, +	x = SLtt_Screen_Cols / 2 - max_len / 2; + +	SLsmg_set_color(0); +	SLsmg_draw_box(y, x++, nr_lines, max_len); +	if (title) { +		SLsmg_gotorc(y, x + 1); +		SLsmg_write_string((char *)title); +	} +	SLsmg_gotorc(++y, x); +	nr_lines -= 2; +	max_len -= 2; +	SLsmg_write_wrapped_string((unsigned char *)text, y, x, +				   nr_lines, max_len, 1); +	SLsmg_gotorc(y + nr_lines - 2, x); +	SLsmg_write_nstring((char *)" ", max_len); +	SLsmg_gotorc(y + nr_lines - 1, x); +	SLsmg_write_nstring((char *)exit_msg, max_len); +	SLsmg_refresh(); +	return ui__getch(delay_secs); +} + +int ui__help_window(const char *text) +{ +	return ui__question_window("Help", text, "Press any key...", 0); +} + +int ui__dialog_yesno(const char *msg) +{ +	return ui__question_window(NULL, msg, "Enter: Yes, ESC: No", 0); +} + +static int __ui__warning(const char *title, const char *format, va_list args) +{ +	char *s; + +	if (vasprintf(&s, format, args) > 0) { +		int key; + +		pthread_mutex_lock(&ui__lock); +		key = ui__question_window(title, s, "Press any key...", 0); +		pthread_mutex_unlock(&ui__lock); +		free(s); +		return key; +	} + +	fprintf(stderr, "%s\n", title); +	vfprintf(stderr, format, args); +	return K_ESC; +} + +static int perf_tui__error(const char *format, va_list args) +{ +	return __ui__warning("Error:", format, args); +} + +static int perf_tui__warning(const char *format, va_list args) +{ +	return __ui__warning("Warning:", format, args); +} + +struct perf_error_ops perf_tui_eops = { +	.error		= perf_tui__error, +	.warning	= perf_tui__warning, +}; diff --git a/tools/perf/ui/util.c b/tools/perf/ui/util.c index ad4374a16bb..4f989774c8c 100644 --- a/tools/perf/ui/util.c +++ b/tools/perf/ui/util.c @@ -1,250 +1,85 @@ -#include "../util.h" -#include <signal.h> -#include <stdbool.h> -#include <string.h> -#include <sys/ttydefaults.h> - -#include "../cache.h" -#include "../debug.h" -#include "browser.h" -#include "keysyms.h" -#include "helpline.h" -#include "ui.h"  #include "util.h" -#include "libslang.h" - -static void ui_browser__argv_write(struct ui_browser *browser, -				   void *entry, int row) -{ -	char **arg = entry; -	bool current_entry = ui_browser__is_current_entry(browser, row); +#include "../debug.h" -	ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED : -						       HE_COLORSET_NORMAL); -	slsmg_write_nstring(*arg, browser->width); -} -static int popup_menu__run(struct ui_browser *menu) +/* + * Default error logging functions + */ +static int perf_stdio__error(const char *format, va_list args)  { -	int key; - -	if (ui_browser__show(menu, " ", "ESC: exit, ENTER|->: Select option") < 0) -		return -1; - -	while (1) { -		key = ui_browser__run(menu, 0); - -		switch (key) { -		case K_RIGHT: -		case K_ENTER: -			key = menu->index; -			break; -		case K_LEFT: -		case K_ESC: -		case 'q': -		case CTRL('c'): -			key = -1; -			break; -		default: -			continue; -		} - -		break; -	} - -	ui_browser__hide(menu); -	return key; +	fprintf(stderr, "Error:\n"); +	vfprintf(stderr, format, args); +	return 0;  } -int ui__popup_menu(int argc, char * const argv[]) +static int perf_stdio__warning(const char *format, va_list args)  { -	struct ui_browser menu = { -		.entries    = (void *)argv, -		.refresh    = ui_browser__argv_refresh, -		.seek	    = ui_browser__argv_seek, -		.write	    = ui_browser__argv_write, -		.nr_entries = argc, -	}; - -	return popup_menu__run(&menu); +	fprintf(stderr, "Warning:\n"); +	vfprintf(stderr, format, args); +	return 0;  } -int ui_browser__input_window(const char *title, const char *text, char *input, -			     const char *exit_msg, int delay_secs) +static struct perf_error_ops default_eops =  { -	int x, y, len, key; -	int max_len = 60, nr_lines = 0; -	static char buf[50]; -	const char *t; - -	t = text; -	while (1) { -		const char *sep = strchr(t, '\n'); - -		if (sep == NULL) -			sep = strchr(t, '\0'); -		len = sep - t; -		if (max_len < len) -			max_len = len; -		++nr_lines; -		if (*sep == '\0') -			break; -		t = sep + 1; -	} - -	max_len += 2; -	nr_lines += 8; -	y = SLtt_Screen_Rows / 2 - nr_lines / 2; -	x = SLtt_Screen_Cols / 2 - max_len / 2; - -	SLsmg_set_color(0); -	SLsmg_draw_box(y, x++, nr_lines, max_len); -	if (title) { -		SLsmg_gotorc(y, x + 1); -		SLsmg_write_string((char *)title); -	} -	SLsmg_gotorc(++y, x); -	nr_lines -= 7; -	max_len -= 2; -	SLsmg_write_wrapped_string((unsigned char *)text, y, x, -				   nr_lines, max_len, 1); -	y += nr_lines; -	len = 5; -	while (len--) { -		SLsmg_gotorc(y + len - 1, x); -		SLsmg_write_nstring((char *)" ", max_len); -	} -	SLsmg_draw_box(y++, x + 1, 3, max_len - 2); - -	SLsmg_gotorc(y + 3, x); -	SLsmg_write_nstring((char *)exit_msg, max_len); -	SLsmg_refresh(); +	.error		= perf_stdio__error, +	.warning	= perf_stdio__warning, +}; -	x += 2; -	len = 0; -	key = ui__getch(delay_secs); -	while (key != K_TIMER && key != K_ENTER && key != K_ESC) { -		if (key == K_BKSPC) { -			if (len == 0) -				goto next_key; -			SLsmg_gotorc(y, x + --len); -			SLsmg_write_char(' '); -		} else { -			buf[len] = key; -			SLsmg_gotorc(y, x + len++); -			SLsmg_write_char(key); -		} -		SLsmg_refresh(); +static struct perf_error_ops *perf_eops = &default_eops; -		/* XXX more graceful overflow handling needed */ -		if (len == sizeof(buf) - 1) { -			ui_helpline__push("maximum size of symbol name reached!"); -			key = K_ENTER; -			break; -		} -next_key: -		key = ui__getch(delay_secs); -	} -	buf[len] = '\0'; -	strncpy(input, buf, len+1); -	return key; -} - -int ui__question_window(const char *title, const char *text, -			const char *exit_msg, int delay_secs) +int ui__error(const char *format, ...)  { -	int x, y; -	int max_len = 0, nr_lines = 0; -	const char *t; - -	t = text; -	while (1) { -		const char *sep = strchr(t, '\n'); -		int len; - -		if (sep == NULL) -			sep = strchr(t, '\0'); -		len = sep - t; -		if (max_len < len) -			max_len = len; -		++nr_lines; -		if (*sep == '\0') -			break; -		t = sep + 1; -	} - -	max_len += 2; -	nr_lines += 4; -	y = SLtt_Screen_Rows / 2 - nr_lines / 2, -	x = SLtt_Screen_Cols / 2 - max_len / 2; +	int ret; +	va_list args; -	SLsmg_set_color(0); -	SLsmg_draw_box(y, x++, nr_lines, max_len); -	if (title) { -		SLsmg_gotorc(y, x + 1); -		SLsmg_write_string((char *)title); -	} -	SLsmg_gotorc(++y, x); -	nr_lines -= 2; -	max_len -= 2; -	SLsmg_write_wrapped_string((unsigned char *)text, y, x, -				   nr_lines, max_len, 1); -	SLsmg_gotorc(y + nr_lines - 2, x); -	SLsmg_write_nstring((char *)" ", max_len); -	SLsmg_gotorc(y + nr_lines - 1, x); -	SLsmg_write_nstring((char *)exit_msg, max_len); -	SLsmg_refresh(); -	return ui__getch(delay_secs); -} +	va_start(args, format); +	ret = perf_eops->error(format, args); +	va_end(args); -int ui__help_window(const char *text) -{ -	return ui__question_window("Help", text, "Press any key...", 0); +	return ret;  } -int ui__dialog_yesno(const char *msg) -{ -	return ui__question_window(NULL, msg, "Enter: Yes, ESC: No", 0); -} - -int __ui__warning(const char *title, const char *format, va_list args) +int ui__warning(const char *format, ...)  { -	char *s; - -	if (use_browser > 0 && vasprintf(&s, format, args) > 0) { -		int key; +	int ret; +	va_list args; -		pthread_mutex_lock(&ui__lock); -		key = ui__question_window(title, s, "Press any key...", 0); -		pthread_mutex_unlock(&ui__lock); -		free(s); -		return key; -	} +	va_start(args, format); +	ret = perf_eops->warning(format, args); +	va_end(args); -	fprintf(stderr, "%s:\n", title); -	vfprintf(stderr, format, args); -	return K_ESC; +	return ret;  } -int ui__warning(const char *format, ...) + +/** + * perf_error__register - Register error logging functions + * @eops: The pointer to error logging function struct + * + * Register UI-specific error logging functions. Before calling this, + * other logging functions should be unregistered, if any. + */ +int perf_error__register(struct perf_error_ops *eops)  { -	int key; -	va_list args; +	if (perf_eops != &default_eops) +		return -1; -	va_start(args, format); -	key = __ui__warning("Warning", format, args); -	va_end(args); -	return key; +	perf_eops = eops; +	return 0;  } -int ui__error(const char *format, ...) +/** + * perf_error__unregister - Unregister error logging functions + * @eops: The pointer to error logging function struct + * + * Unregister already registered error logging functions. + */ +int perf_error__unregister(struct perf_error_ops *eops)  { -	int key; -	va_list args; +	if (perf_eops != eops) +		return -1; -	va_start(args, format); -	key = __ui__warning("Error", format, args); -	va_end(args); -	return key; +	perf_eops = &default_eops; +	return 0;  } diff --git a/tools/perf/ui/util.h b/tools/perf/ui/util.h index 2d1738bd71c..361f08c52d3 100644 --- a/tools/perf/ui/util.h +++ b/tools/perf/ui/util.h @@ -9,6 +9,13 @@ int ui__help_window(const char *text);  int ui__dialog_yesno(const char *msg);  int ui__question_window(const char *title, const char *text,  			const char *exit_msg, int delay_secs); -int __ui__warning(const char *title, const char *format, va_list args); + +struct perf_error_ops { +	int (*error)(const char *format, va_list args); +	int (*warning)(const char *format, va_list args); +}; + +int perf_error__register(struct perf_error_ops *eops); +int perf_error__unregister(struct perf_error_ops *eops);  #endif /* _PERF_UI_UTIL_H_ */ diff --git a/tools/perf/util/PERF-VERSION-GEN b/tools/perf/util/PERF-VERSION-GEN index ad73300f7ba..95264f30417 100755 --- a/tools/perf/util/PERF-VERSION-GEN +++ b/tools/perf/util/PERF-VERSION-GEN @@ -12,7 +12,7 @@ LF='  # First check if there is a .git to get the version from git describe  # otherwise try to get the version from the kernel makefile  if test -d ../../.git -o -f ../../.git && -	VN=$(git describe --abbrev=4 HEAD 2>/dev/null) && +	VN=$(git describe --match 'v[0-9].[0-9]*' --abbrev=4 HEAD 2>/dev/null) &&  	case "$VN" in  	*$LF*) (exit 1) ;;  	v[0-9]*) diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 9f7106a8d9a..3a6bff47614 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c @@ -18,6 +18,8 @@  #include "util.h"  #include "callchain.h" +__thread struct callchain_cursor callchain_cursor; +  bool ip_callchain__valid(struct ip_callchain *chain,  			 const union perf_event *event)  { diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index 7f9c0f1ae3a..3bdb407f9cd 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h @@ -76,6 +76,8 @@ struct callchain_cursor {  	struct callchain_cursor_node	*curr;  }; +extern __thread struct callchain_cursor callchain_cursor; +  static inline void callchain_init(struct callchain_root *root)  {  	INIT_LIST_HEAD(&root->node.siblings); diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c index 0deac6a14b6..6faa3a18bfb 100644 --- a/tools/perf/util/config.c +++ b/tools/perf/util/config.c @@ -120,7 +120,7 @@ static char *parse_value(void)  static inline int iskeychar(int c)  { -	return isalnum(c) || c == '-'; +	return isalnum(c) || c == '-' || c == '_';  }  static int get_value(config_fn_t fn, void *data, char *name, unsigned int len) diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c index efb1fce259a..4dfe0bb3c32 100644 --- a/tools/perf/util/debug.c +++ b/tools/perf/util/debug.c @@ -47,7 +47,7 @@ int dump_printf(const char *fmt, ...)  	return ret;  } -#ifdef NO_NEWT_SUPPORT +#if defined(NO_NEWT_SUPPORT) && defined(NO_GTK2_SUPPORT)  int ui__warning(const char *format, ...)  {  	va_list args; diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h index 6bebe7f0a20..015c91dbc09 100644 --- a/tools/perf/util/debug.h +++ b/tools/perf/util/debug.h @@ -12,8 +12,9 @@ int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2)));  void trace_event(union perf_event *event);  struct ui_progress; +struct perf_error_ops; -#ifdef NO_NEWT_SUPPORT +#if defined(NO_NEWT_SUPPORT) && defined(NO_GTK2_SUPPORT)  static inline int ui_helpline__show_help(const char *format __used, va_list ap __used)  {  	return 0; @@ -23,12 +24,28 @@ static inline void ui_progress__update(u64 curr __used, u64 total __used,  				       const char *title __used) {}  #define ui__error(format, arg...) ui__warning(format, ##arg) -#else + +static inline int +perf_error__register(struct perf_error_ops *eops __used) +{ +	return 0; +} + +static inline int +perf_error__unregister(struct perf_error_ops *eops __used) +{ +	return 0; +} + +#else /* NO_NEWT_SUPPORT && NO_GTK2_SUPPORT */ +  extern char ui_helpline__last_msg[];  int ui_helpline__show_help(const char *format, va_list ap);  #include "../ui/progress.h"  int ui__error(const char *format, ...) __attribute__((format(printf, 1, 2))); -#endif +#include "../ui/util.h" + +#endif /* NO_NEWT_SUPPORT && NO_GTK2_SUPPORT */  int ui__warning(const char *format, ...) __attribute__((format(printf, 1, 2)));  int ui__error_paranoid(void); diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 4ac5f5ae4ce..f74e9560350 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -159,6 +159,17 @@ out_delete_partial_list:  	return -1;  } +int __perf_evlist__add_default_attrs(struct perf_evlist *evlist, +				     struct perf_event_attr *attrs, size_t nr_attrs) +{ +	size_t i; + +	for (i = 0; i < nr_attrs; i++) +		event_attr_init(attrs + i); + +	return perf_evlist__add_attrs(evlist, attrs, nr_attrs); +} +  static int trace_event__id(const char *evname)  {  	char *filename, *colon; @@ -213,8 +224,8 @@ out_free_attrs:  	return err;  } -static struct perf_evsel * -	perf_evlist__find_tracepoint_by_id(struct perf_evlist *evlist, int id) +struct perf_evsel * +perf_evlist__find_tracepoint_by_id(struct perf_evlist *evlist, int id)  {  	struct perf_evsel *evsel; @@ -263,7 +274,8 @@ void perf_evlist__disable(struct perf_evlist *evlist)  	for (cpu = 0; cpu < evlist->cpus->nr; cpu++) {  		list_for_each_entry(pos, &evlist->entries, node) {  			for (thread = 0; thread < evlist->threads->nr; thread++) -				ioctl(FD(pos, cpu, thread), PERF_EVENT_IOC_DISABLE); +				ioctl(FD(pos, cpu, thread), +				      PERF_EVENT_IOC_DISABLE, 0);  		}  	}  } @@ -276,7 +288,8 @@ void perf_evlist__enable(struct perf_evlist *evlist)  	for (cpu = 0; cpu < evlist->cpus->nr; cpu++) {  		list_for_each_entry(pos, &evlist->entries, node) {  			for (thread = 0; thread < evlist->threads->nr; thread++) -				ioctl(FD(pos, cpu, thread), PERF_EVENT_IOC_ENABLE); +				ioctl(FD(pos, cpu, thread), +				      PERF_EVENT_IOC_ENABLE, 0);  		}  	}  } diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 58abb63ac13..40d4d3cdced 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -54,6 +54,8 @@ void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry);  int perf_evlist__add_default(struct perf_evlist *evlist);  int perf_evlist__add_attrs(struct perf_evlist *evlist,  			   struct perf_event_attr *attrs, size_t nr_attrs); +int __perf_evlist__add_default_attrs(struct perf_evlist *evlist, +				     struct perf_event_attr *attrs, size_t nr_attrs);  int perf_evlist__add_tracepoints(struct perf_evlist *evlist,  				 const char *tracepoints[], size_t nr_tracepoints);  int perf_evlist__set_tracepoints_handlers(struct perf_evlist *evlist, @@ -62,6 +64,8 @@ int perf_evlist__set_tracepoints_handlers(struct perf_evlist *evlist,  #define perf_evlist__add_attrs_array(evlist, array) \  	perf_evlist__add_attrs(evlist, array, ARRAY_SIZE(array)) +#define perf_evlist__add_default_attrs(evlist, array) \ +	__perf_evlist__add_default_attrs(evlist, array, ARRAY_SIZE(array))  #define perf_evlist__add_tracepoints_array(evlist, array) \  	perf_evlist__add_tracepoints(evlist, array, ARRAY_SIZE(array)) @@ -69,6 +73,9 @@ int perf_evlist__set_tracepoints_handlers(struct perf_evlist *evlist,  #define perf_evlist__set_tracepoints_handlers_array(evlist, array) \  	perf_evlist__set_tracepoints_handlers(evlist, array, ARRAY_SIZE(array)) +struct perf_evsel * +perf_evlist__find_tracepoint_by_id(struct perf_evlist *evlist, int id); +  void perf_evlist__id_add(struct perf_evlist *evlist, struct perf_evsel *evsel,  			 int cpu, int thread, u64 id); diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 57e4ce57bbc..e8177136486 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -15,6 +15,7 @@  #include "cpumap.h"  #include "thread_map.h"  #include "target.h" +#include "../../../include/linux/hw_breakpoint.h"  #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y))  #define GROUP_FD(group_fd, cpu) (*(int *)xyarray__entry(group_fd, cpu, 0)) @@ -64,6 +65,266 @@ struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx)  	return evsel;  } +static const char *perf_evsel__hw_names[PERF_COUNT_HW_MAX] = { +	"cycles", +	"instructions", +	"cache-references", +	"cache-misses", +	"branches", +	"branch-misses", +	"bus-cycles", +	"stalled-cycles-frontend", +	"stalled-cycles-backend", +	"ref-cycles", +}; + +static const char *__perf_evsel__hw_name(u64 config) +{ +	if (config < PERF_COUNT_HW_MAX && perf_evsel__hw_names[config]) +		return perf_evsel__hw_names[config]; + +	return "unknown-hardware"; +} + +static int perf_evsel__add_modifiers(struct perf_evsel *evsel, char *bf, size_t size) +{ +	int colon = 0, r = 0; +	struct perf_event_attr *attr = &evsel->attr; +	bool exclude_guest_default = false; + +#define MOD_PRINT(context, mod)	do {					\ +		if (!attr->exclude_##context) {				\ +			if (!colon) colon = ++r;			\ +			r += scnprintf(bf + r, size - r, "%c", mod);	\ +		} } while(0) + +	if (attr->exclude_kernel || attr->exclude_user || attr->exclude_hv) { +		MOD_PRINT(kernel, 'k'); +		MOD_PRINT(user, 'u'); +		MOD_PRINT(hv, 'h'); +		exclude_guest_default = true; +	} + +	if (attr->precise_ip) { +		if (!colon) +			colon = ++r; +		r += scnprintf(bf + r, size - r, "%.*s", attr->precise_ip, "ppp"); +		exclude_guest_default = true; +	} + +	if (attr->exclude_host || attr->exclude_guest == exclude_guest_default) { +		MOD_PRINT(host, 'H'); +		MOD_PRINT(guest, 'G'); +	} +#undef MOD_PRINT +	if (colon) +		bf[colon - 1] = ':'; +	return r; +} + +static int perf_evsel__hw_name(struct perf_evsel *evsel, char *bf, size_t size) +{ +	int r = scnprintf(bf, size, "%s", __perf_evsel__hw_name(evsel->attr.config)); +	return r + perf_evsel__add_modifiers(evsel, bf + r, size - r); +} + +static const char *perf_evsel__sw_names[PERF_COUNT_SW_MAX] = { +	"cpu-clock", +	"task-clock", +	"page-faults", +	"context-switches", +	"CPU-migrations", +	"minor-faults", +	"major-faults", +	"alignment-faults", +	"emulation-faults", +}; + +static const char *__perf_evsel__sw_name(u64 config) +{ +	if (config < PERF_COUNT_SW_MAX && perf_evsel__sw_names[config]) +		return perf_evsel__sw_names[config]; +	return "unknown-software"; +} + +static int perf_evsel__sw_name(struct perf_evsel *evsel, char *bf, size_t size) +{ +	int r = scnprintf(bf, size, "%s", __perf_evsel__sw_name(evsel->attr.config)); +	return r + perf_evsel__add_modifiers(evsel, bf + r, size - r); +} + +static int __perf_evsel__bp_name(char *bf, size_t size, u64 addr, u64 type) +{ +	int r; + +	r = scnprintf(bf, size, "mem:0x%" PRIx64 ":", addr); + +	if (type & HW_BREAKPOINT_R) +		r += scnprintf(bf + r, size - r, "r"); + +	if (type & HW_BREAKPOINT_W) +		r += scnprintf(bf + r, size - r, "w"); + +	if (type & HW_BREAKPOINT_X) +		r += scnprintf(bf + r, size - r, "x"); + +	return r; +} + +static int perf_evsel__bp_name(struct perf_evsel *evsel, char *bf, size_t size) +{ +	struct perf_event_attr *attr = &evsel->attr; +	int r = __perf_evsel__bp_name(bf, size, attr->bp_addr, attr->bp_type); +	return r + perf_evsel__add_modifiers(evsel, bf + r, size - r); +} + +const char *perf_evsel__hw_cache[PERF_COUNT_HW_CACHE_MAX] +				[PERF_EVSEL__MAX_ALIASES] = { + { "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",			}, + { "branch",	"branches",	"bpu",		"btb",		"bpc",	}, + { "node",								}, +}; + +const char *perf_evsel__hw_cache_op[PERF_COUNT_HW_CACHE_OP_MAX] +				   [PERF_EVSEL__MAX_ALIASES] = { + { "load",	"loads",	"read",					}, + { "store",	"stores",	"write",				}, + { "prefetch",	"prefetches",	"speculative-read", "speculative-load",	}, +}; + +const char *perf_evsel__hw_cache_result[PERF_COUNT_HW_CACHE_RESULT_MAX] +				       [PERF_EVSEL__MAX_ALIASES] = { + { "refs",	"Reference",	"ops",		"access",		}, + { "misses",	"miss",							}, +}; + +#define C(x)		PERF_COUNT_HW_CACHE_##x +#define CACHE_READ	(1 << C(OP_READ)) +#define CACHE_WRITE	(1 << C(OP_WRITE)) +#define CACHE_PREFETCH	(1 << C(OP_PREFETCH)) +#define COP(x)		(1 << x) + +/* + * cache operartion stat + * L1I : Read and prefetch only + * ITLB and BPU : Read-only + */ +static unsigned long perf_evsel__hw_cache_stat[C(MAX)] = { + [C(L1D)]	= (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH), + [C(L1I)]	= (CACHE_READ | CACHE_PREFETCH), + [C(LL)]	= (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH), + [C(DTLB)]	= (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH), + [C(ITLB)]	= (CACHE_READ), + [C(BPU)]	= (CACHE_READ), + [C(NODE)]	= (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH), +}; + +bool perf_evsel__is_cache_op_valid(u8 type, u8 op) +{ +	if (perf_evsel__hw_cache_stat[type] & COP(op)) +		return true;	/* valid */ +	else +		return false;	/* invalid */ +} + +int __perf_evsel__hw_cache_type_op_res_name(u8 type, u8 op, u8 result, +					    char *bf, size_t size) +{ +	if (result) { +		return scnprintf(bf, size, "%s-%s-%s", perf_evsel__hw_cache[type][0], +				 perf_evsel__hw_cache_op[op][0], +				 perf_evsel__hw_cache_result[result][0]); +	} + +	return scnprintf(bf, size, "%s-%s", perf_evsel__hw_cache[type][0], +			 perf_evsel__hw_cache_op[op][1]); +} + +static int __perf_evsel__hw_cache_name(u64 config, char *bf, size_t size) +{ +	u8 op, result, type = (config >>  0) & 0xff; +	const char *err = "unknown-ext-hardware-cache-type"; + +	if (type > PERF_COUNT_HW_CACHE_MAX) +		goto out_err; + +	op = (config >>  8) & 0xff; +	err = "unknown-ext-hardware-cache-op"; +	if (op > PERF_COUNT_HW_CACHE_OP_MAX) +		goto out_err; + +	result = (config >> 16) & 0xff; +	err = "unknown-ext-hardware-cache-result"; +	if (result > PERF_COUNT_HW_CACHE_RESULT_MAX) +		goto out_err; + +	err = "invalid-cache"; +	if (!perf_evsel__is_cache_op_valid(type, op)) +		goto out_err; + +	return __perf_evsel__hw_cache_type_op_res_name(type, op, result, bf, size); +out_err: +	return scnprintf(bf, size, "%s", err); +} + +static int perf_evsel__hw_cache_name(struct perf_evsel *evsel, char *bf, size_t size) +{ +	int ret = __perf_evsel__hw_cache_name(evsel->attr.config, bf, size); +	return ret + perf_evsel__add_modifiers(evsel, bf + ret, size - ret); +} + +static int perf_evsel__raw_name(struct perf_evsel *evsel, char *bf, size_t size) +{ +	int ret = scnprintf(bf, size, "raw 0x%" PRIx64, evsel->attr.config); +	return ret + perf_evsel__add_modifiers(evsel, bf + ret, size - ret); +} + +const char *perf_evsel__name(struct perf_evsel *evsel) +{ +	char bf[128]; + +	if (evsel->name) +		return evsel->name; + +	switch (evsel->attr.type) { +	case PERF_TYPE_RAW: +		perf_evsel__raw_name(evsel, bf, sizeof(bf)); +		break; + +	case PERF_TYPE_HARDWARE: +		perf_evsel__hw_name(evsel, bf, sizeof(bf)); +		break; + +	case PERF_TYPE_HW_CACHE: +		perf_evsel__hw_cache_name(evsel, bf, sizeof(bf)); +		break; + +	case PERF_TYPE_SOFTWARE: +		perf_evsel__sw_name(evsel, bf, sizeof(bf)); +		break; + +	case PERF_TYPE_TRACEPOINT: +		scnprintf(bf, sizeof(bf), "%s", "unknown tracepoint"); +		break; + +	case PERF_TYPE_BREAKPOINT: +		perf_evsel__bp_name(evsel, bf, sizeof(bf)); +		break; + +	default: +		scnprintf(bf, sizeof(bf), "%s", "unknown attr type"); +		break; +	} + +	evsel->name = strdup(bf); + +	return evsel->name ?: "unknown"; +} +  void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts,  			struct perf_evsel *first)  { @@ -404,16 +665,24 @@ int perf_evsel__open_per_thread(struct perf_evsel *evsel,  }  static int perf_event__parse_id_sample(const union perf_event *event, u64 type, -				       struct perf_sample *sample) +				       struct perf_sample *sample, +				       bool swapped)  {  	const u64 *array = event->sample.array; +	union u64_swap u;  	array += ((event->header.size -  		   sizeof(event->header)) / sizeof(u64)) - 1;  	if (type & PERF_SAMPLE_CPU) { -		u32 *p = (u32 *)array; -		sample->cpu = *p; +		u.val64 = *array; +		if (swapped) { +			/* undo swap of u64, then swap on individual u32s */ +			u.val64 = bswap_64(u.val64); +			u.val32[0] = bswap_32(u.val32[0]); +		} + +		sample->cpu = u.val32[0];  		array--;  	} @@ -433,9 +702,16 @@ static int perf_event__parse_id_sample(const union perf_event *event, u64 type,  	}  	if (type & PERF_SAMPLE_TID) { -		u32 *p = (u32 *)array; -		sample->pid = p[0]; -		sample->tid = p[1]; +		u.val64 = *array; +		if (swapped) { +			/* undo swap of u64, then swap on individual u32s */ +			u.val64 = bswap_64(u.val64); +			u.val32[0] = bswap_32(u.val32[0]); +			u.val32[1] = bswap_32(u.val32[1]); +		} + +		sample->pid = u.val32[0]; +		sample->tid = u.val32[1];  	}  	return 0; @@ -472,7 +748,7 @@ int perf_event__parse_sample(const union perf_event *event, u64 type,  	if (event->header.type != PERF_RECORD_SAMPLE) {  		if (!sample_id_all)  			return 0; -		return perf_event__parse_id_sample(event, type, data); +		return perf_event__parse_id_sample(event, type, data, swapped);  	}  	array = event->sample.array; diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 3d6b3e4cb66..67cc5033d19 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -83,6 +83,20 @@ void perf_evsel__config(struct perf_evsel *evsel,  			struct perf_record_opts *opts,  			struct perf_evsel *first); +bool perf_evsel__is_cache_op_valid(u8 type, u8 op); + +#define PERF_EVSEL__MAX_ALIASES 8 + +extern const char *perf_evsel__hw_cache[PERF_COUNT_HW_CACHE_MAX] +				       [PERF_EVSEL__MAX_ALIASES]; +extern const char *perf_evsel__hw_cache_op[PERF_COUNT_HW_CACHE_OP_MAX] +					  [PERF_EVSEL__MAX_ALIASES]; +const char *perf_evsel__hw_cache_result[PERF_COUNT_HW_CACHE_RESULT_MAX] +				       [PERF_EVSEL__MAX_ALIASES]; +int __perf_evsel__hw_cache_type_op_res_name(u8 type, u8 op, u8 result, +					    char *bf, size_t size); +const char *perf_evsel__name(struct perf_evsel *evsel); +  int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads);  int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads);  int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus); diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 2dd5edf161b..5a47aba4675 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -641,7 +641,7 @@ static int write_event_desc(int fd, struct perf_header *h __used,  		/*  		 * write event string as passed on cmdline  		 */ -		ret = do_write_string(fd, event_name(attr)); +		ret = do_write_string(fd, perf_evsel__name(attr));  		if (ret < 0)  			return ret;  		/* @@ -1474,15 +1474,15 @@ out:  static int process_tracing_data(struct perf_file_section *section __unused,  			      struct perf_header *ph __unused, -			      int feat __unused, int fd) +			      int feat __unused, int fd, void *data)  { -	trace_report(fd, false); +	trace_report(fd, data, false);  	return 0;  }  static int process_build_id(struct perf_file_section *section,  			    struct perf_header *ph, -			    int feat __unused, int fd) +			    int feat __unused, int fd, void *data __used)  {  	if (perf_header__read_build_ids(ph, fd, section->offset, section->size))  		pr_debug("Failed to read buildids, continuing...\n"); @@ -1493,7 +1493,7 @@ struct feature_ops {  	int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist);  	void (*print)(struct perf_header *h, int fd, FILE *fp);  	int (*process)(struct perf_file_section *section, -		       struct perf_header *h, int feat, int fd); +		       struct perf_header *h, int feat, int fd, void *data);  	const char *name;  	bool full_only;  }; @@ -1942,7 +1942,6 @@ int perf_file_header__read(struct perf_file_header *header,  		else  			return -1;  	} else if (ph->needs_swap) { -		unsigned int i;  		/*  		 * feature bitmap is declared as an array of unsigned longs --  		 * not good since its size can differ between the host that @@ -1958,14 +1957,17 @@ int perf_file_header__read(struct perf_file_header *header,  		 * file), punt and fallback to the original behavior --  		 * clearing all feature bits and setting buildid.  		 */ -		for (i = 0; i < BITS_TO_LONGS(HEADER_FEAT_BITS); ++i) -			header->adds_features[i] = bswap_64(header->adds_features[i]); +		mem_bswap_64(&header->adds_features, +			    BITS_TO_U64(HEADER_FEAT_BITS));  		if (!test_bit(HEADER_HOSTNAME, header->adds_features)) { -			for (i = 0; i < BITS_TO_LONGS(HEADER_FEAT_BITS); ++i) { -				header->adds_features[i] = bswap_64(header->adds_features[i]); -				header->adds_features[i] = bswap_32(header->adds_features[i]); -			} +			/* unswap as u64 */ +			mem_bswap_64(&header->adds_features, +				    BITS_TO_U64(HEADER_FEAT_BITS)); + +			/* unswap as u32 */ +			mem_bswap_32(&header->adds_features, +				    BITS_TO_U32(HEADER_FEAT_BITS));  		}  		if (!test_bit(HEADER_HOSTNAME, header->adds_features)) { @@ -1986,7 +1988,7 @@ int perf_file_header__read(struct perf_file_header *header,  static int perf_file_section__process(struct perf_file_section *section,  				      struct perf_header *ph, -				      int feat, int fd, void *data __used) +				      int feat, int fd, void *data)  {  	if (lseek(fd, section->offset, SEEK_SET) == (off_t)-1) {  		pr_debug("Failed to lseek to %" PRIu64 " offset for feature " @@ -2002,7 +2004,7 @@ static int perf_file_section__process(struct perf_file_section *section,  	if (!feat_ops[feat].process)  		return 0; -	return feat_ops[feat].process(section, ph, feat, fd); +	return feat_ops[feat].process(section, ph, feat, fd, data);  }  static int perf_file_header__read_pipe(struct perf_pipe_file_header *header, @@ -2091,6 +2093,38 @@ static int read_attr(int fd, struct perf_header *ph,  	return ret <= 0 ? -1 : 0;  } +static int perf_evsel__set_tracepoint_name(struct perf_evsel *evsel, +					   struct pevent *pevent) +{ +	struct event_format *event = pevent_find_event(pevent, +						       evsel->attr.config); +	char bf[128]; + +	if (event == NULL) +		return -1; + +	snprintf(bf, sizeof(bf), "%s:%s", event->system, event->name); +	evsel->name = strdup(bf); +	if (event->name == NULL) +		return -1; + +	return 0; +} + +static int perf_evlist__set_tracepoint_names(struct perf_evlist *evlist, +					     struct pevent *pevent) +{ +	struct perf_evsel *pos; + +	list_for_each_entry(pos, &evlist->entries, node) { +		if (pos->attr.type == PERF_TYPE_TRACEPOINT && +		    perf_evsel__set_tracepoint_name(pos, pevent)) +			return -1; +	} + +	return 0; +} +  int perf_session__read_header(struct perf_session *session, int fd)  {  	struct perf_header *header = &session->header; @@ -2167,11 +2201,14 @@ int perf_session__read_header(struct perf_session *session, int fd)  		event_count =  f_header.event_types.size / sizeof(struct perf_trace_event_type);  	} -	perf_header__process_sections(header, fd, NULL, +	perf_header__process_sections(header, fd, &session->pevent,  				      perf_file_section__process);  	lseek(fd, header->data_offset, SEEK_SET); +	if (perf_evlist__set_tracepoint_names(session->evlist, session->pevent)) +		goto out_delete_evlist; +  	header->frozen = 1;  	return 0;  out_errno: @@ -2385,8 +2422,8 @@ int perf_event__process_tracing_data(union perf_event *event,  	lseek(session->fd, offset + sizeof(struct tracing_data_event),  	      SEEK_SET); -	size_read = trace_report(session->fd, session->repipe); - +	size_read = trace_report(session->fd, &session->pevent, +				 session->repipe);  	padding = ALIGN(size_read, sizeof(u64)) - size_read;  	if (read(session->fd, buf, padding) < 0) diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 1293b5ebea4..514e2a4b367 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -378,7 +378,7 @@ void hist_entry__free(struct hist_entry *he)   * collapse the histogram   */ -static bool hists__collapse_insert_entry(struct hists *hists, +static bool hists__collapse_insert_entry(struct hists *hists __used,  					 struct rb_root *root,  					 struct hist_entry *he)  { @@ -397,8 +397,9 @@ static bool hists__collapse_insert_entry(struct hists *hists,  			iter->period += he->period;  			iter->nr_events += he->nr_events;  			if (symbol_conf.use_callchain) { -				callchain_cursor_reset(&hists->callchain_cursor); -				callchain_merge(&hists->callchain_cursor, iter->callchain, +				callchain_cursor_reset(&callchain_cursor); +				callchain_merge(&callchain_cursor, +						iter->callchain,  						he->callchain);  			}  			hist_entry__free(he); diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index cfc64e293f9..0b096c27a41 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -47,6 +47,7 @@ enum hist_column {  	HISTC_SYMBOL_TO,  	HISTC_DSO_FROM,  	HISTC_DSO_TO, +	HISTC_SRCLINE,  	HISTC_NR_COLS, /* Last entry */  }; @@ -67,8 +68,6 @@ struct hists {  	struct events_stats	stats;  	u64			event_stream;  	u16			col_len[HISTC_NR_COLS]; -	/* Best would be to reuse the session callchain cursor */ -	struct callchain_cursor	callchain_cursor;  };  struct hist_entry *__hists__add_entry(struct hists *self, diff --git a/tools/perf/util/include/linux/bitops.h b/tools/perf/util/include/linux/bitops.h index f1584833bd2..587a230d207 100644 --- a/tools/perf/util/include/linux/bitops.h +++ b/tools/perf/util/include/linux/bitops.h @@ -8,6 +8,8 @@  #define BITS_PER_LONG __WORDSIZE  #define BITS_PER_BYTE           8  #define BITS_TO_LONGS(nr)       DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long)) +#define BITS_TO_U64(nr)         DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u64)) +#define BITS_TO_U32(nr)         DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u32))  #define for_each_set_bit(bit, addr, size) \  	for ((bit) = find_first_bit((addr), (size));		\ diff --git a/tools/perf/util/include/linux/kernel.h b/tools/perf/util/include/linux/kernel.h index 1eb804fd3fb..b6842c1d02a 100644 --- a/tools/perf/util/include/linux/kernel.h +++ b/tools/perf/util/include/linux/kernel.h @@ -108,4 +108,14 @@ int eprintf(int level,  #define pr_debug3(fmt, ...) pr_debugN(3, pr_fmt(fmt), ##__VA_ARGS__)  #define pr_debug4(fmt, ...) pr_debugN(4, pr_fmt(fmt), ##__VA_ARGS__) +/* + * This looks more complex than it should be. But we need to + * get the type for the ~ right in round_down (it needs to be + * as wide as the result!), and we want to evaluate the macro + * arguments just once each. + */ +#define __round_mask(x, y) ((__typeof__(x))((y)-1)) +#define round_up(x, y) ((((x)-1) | __round_mask(x, y))+1) +#define round_down(x, y) ((x) & ~__round_mask(x, y)) +  #endif diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 35ae56864e4..a1f4e366914 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -669,25 +669,26 @@ struct machine *machines__find(struct rb_root *self, pid_t pid)  struct machine *machines__findnew(struct rb_root *self, pid_t pid)  {  	char path[PATH_MAX]; -	const char *root_dir; +	const char *root_dir = "";  	struct machine *machine = machines__find(self, pid); -	if (!machine || machine->pid != pid) { -		if (pid == HOST_KERNEL_ID || pid == DEFAULT_GUEST_KERNEL_ID) -			root_dir = ""; -		else { -			if (!symbol_conf.guestmount) -				goto out; -			sprintf(path, "%s/%d", symbol_conf.guestmount, pid); -			if (access(path, R_OK)) { -				pr_err("Can't access file %s\n", path); -				goto out; -			} -			root_dir = path; +	if (machine && (machine->pid == pid)) +		goto out; + +	if ((pid != HOST_KERNEL_ID) && +	    (pid != DEFAULT_GUEST_KERNEL_ID) && +	    (symbol_conf.guestmount)) { +		sprintf(path, "%s/%d", symbol_conf.guestmount, pid); +		if (access(path, R_OK)) { +			pr_err("Can't access file %s\n", path); +			machine = NULL; +			goto out;  		} -		machine = machines__add(self, pid, root_dir); +		root_dir = path;  	} +	machine = machines__add(self, pid, root_dir); +  out:  	return machine;  } diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index 81371bad4ef..c14c665d9a2 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h @@ -157,7 +157,7 @@ void machine__exit(struct machine *self);  void machine__delete(struct machine *self);  int machine__resolve_callchain(struct machine *machine, -			       struct perf_evsel *evsel, struct thread *thread, +			       struct thread *thread,  			       struct ip_callchain *chain,  			       struct symbol **parent);  int maps__set_kallsyms_ref_reloc_sym(struct map **maps, const char *symbol_name, diff --git a/tools/perf/util/pager.c b/tools/perf/util/pager.c index 1915de20dca..3322b8446e8 100644 --- a/tools/perf/util/pager.c +++ b/tools/perf/util/pager.c @@ -57,6 +57,10 @@ void setup_pager(void)  	}  	if (!pager)  		pager = getenv("PAGER"); +	if (!pager) { +		if (!access("/usr/bin/pager", X_OK)) +			pager = "/usr/bin/pager"; +	}  	if (!pager)  		pager = "less";  	else if (!*pager || !strcmp(pager, "cat")) diff --git a/tools/perf/util/parse-events-test.c b/tools/perf/util/parse-events-test.c index 76b98e2a587..1b997d2b89c 100644 --- a/tools/perf/util/parse-events-test.c +++ b/tools/perf/util/parse-events-test.c @@ -181,6 +181,22 @@ static int test__checkevent_breakpoint_w(struct perf_evlist *evlist)  	return 0;  } +static int test__checkevent_breakpoint_rw(struct perf_evlist *evlist) +{ +	struct perf_evsel *evsel = list_entry(evlist->entries.next, +					      struct perf_evsel, node); + +	TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); +	TEST_ASSERT_VAL("wrong type", +			PERF_TYPE_BREAKPOINT == evsel->attr.type); +	TEST_ASSERT_VAL("wrong config", 0 == evsel->attr.config); +	TEST_ASSERT_VAL("wrong bp_type", +		(HW_BREAKPOINT_R|HW_BREAKPOINT_W) == evsel->attr.bp_type); +	TEST_ASSERT_VAL("wrong bp_len", +			HW_BREAKPOINT_LEN_4 == evsel->attr.bp_len); +	return 0; +} +  static int test__checkevent_tracepoint_modifier(struct perf_evlist *evlist)  {  	struct perf_evsel *evsel = list_entry(evlist->entries.next, @@ -309,6 +325,8 @@ static int test__checkevent_breakpoint_modifier(struct perf_evlist *evlist)  	TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);  	TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);  	TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); +	TEST_ASSERT_VAL("wrong name", +			!strcmp(perf_evsel__name(evsel), "mem:0x0:rw:u"));  	return test__checkevent_breakpoint(evlist);  } @@ -322,6 +340,8 @@ static int test__checkevent_breakpoint_x_modifier(struct perf_evlist *evlist)  	TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);  	TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);  	TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); +	TEST_ASSERT_VAL("wrong name", +			!strcmp(perf_evsel__name(evsel), "mem:0x0:x:k"));  	return test__checkevent_breakpoint_x(evlist);  } @@ -335,6 +355,8 @@ static int test__checkevent_breakpoint_r_modifier(struct perf_evlist *evlist)  	TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);  	TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);  	TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip); +	TEST_ASSERT_VAL("wrong name", +			!strcmp(perf_evsel__name(evsel), "mem:0x0:r:hp"));  	return test__checkevent_breakpoint_r(evlist);  } @@ -348,10 +370,27 @@ static int test__checkevent_breakpoint_w_modifier(struct perf_evlist *evlist)  	TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);  	TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);  	TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip); +	TEST_ASSERT_VAL("wrong name", +			!strcmp(perf_evsel__name(evsel), "mem:0x0:w:up"));  	return test__checkevent_breakpoint_w(evlist);  } +static int test__checkevent_breakpoint_rw_modifier(struct perf_evlist *evlist) +{ +	struct perf_evsel *evsel = list_entry(evlist->entries.next, +					      struct perf_evsel, node); + +	TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); +	TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); +	TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); +	TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip); +	TEST_ASSERT_VAL("wrong name", +			!strcmp(perf_evsel__name(evsel), "mem:0x0:rw:kp")); + +	return test__checkevent_breakpoint_rw(evlist); +} +  static int test__checkevent_pmu(struct perf_evlist *evlist)  { @@ -413,19 +452,63 @@ static int test__checkevent_pmu_name(struct perf_evlist *evlist)  {  	struct perf_evsel *evsel; -	/* cpu/config=1,name=krava1/u */ +	/* cpu/config=1,name=krava/u */  	evsel = list_entry(evlist->entries.next, struct perf_evsel, node);  	TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries);  	TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type);  	TEST_ASSERT_VAL("wrong config",  1 == evsel->attr.config); -	TEST_ASSERT_VAL("wrong name", !strcmp(evsel->name, "krava")); +	TEST_ASSERT_VAL("wrong name", !strcmp(perf_evsel__name(evsel), "krava")); -	/* cpu/config=2/" */ +	/* cpu/config=2/u" */  	evsel = list_entry(evsel->node.next, struct perf_evsel, node);  	TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries);  	TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type);  	TEST_ASSERT_VAL("wrong config",  2 == evsel->attr.config); -	TEST_ASSERT_VAL("wrong name", !strcmp(evsel->name, "raw 0x2")); +	TEST_ASSERT_VAL("wrong name", +			!strcmp(perf_evsel__name(evsel), "raw 0x2:u")); + +	return 0; +} + +static int test__checkterms_simple(struct list_head *terms) +{ +	struct parse_events__term *term; + +	/* config=10 */ +	term = list_entry(terms->next, struct parse_events__term, list); +	TEST_ASSERT_VAL("wrong type term", +			term->type_term == PARSE_EVENTS__TERM_TYPE_CONFIG); +	TEST_ASSERT_VAL("wrong type val", +			term->type_val == PARSE_EVENTS__TERM_TYPE_NUM); +	TEST_ASSERT_VAL("wrong val", term->val.num == 10); +	TEST_ASSERT_VAL("wrong config", !term->config); + +	/* config1 */ +	term = list_entry(term->list.next, struct parse_events__term, list); +	TEST_ASSERT_VAL("wrong type term", +			term->type_term == PARSE_EVENTS__TERM_TYPE_CONFIG1); +	TEST_ASSERT_VAL("wrong type val", +			term->type_val == PARSE_EVENTS__TERM_TYPE_NUM); +	TEST_ASSERT_VAL("wrong val", term->val.num == 1); +	TEST_ASSERT_VAL("wrong config", !term->config); + +	/* config2=3 */ +	term = list_entry(term->list.next, struct parse_events__term, list); +	TEST_ASSERT_VAL("wrong type term", +			term->type_term == PARSE_EVENTS__TERM_TYPE_CONFIG2); +	TEST_ASSERT_VAL("wrong type val", +			term->type_val == PARSE_EVENTS__TERM_TYPE_NUM); +	TEST_ASSERT_VAL("wrong val", term->val.num == 3); +	TEST_ASSERT_VAL("wrong config", !term->config); + +	/* umask=1*/ +	term = list_entry(term->list.next, struct parse_events__term, list); +	TEST_ASSERT_VAL("wrong type term", +			term->type_term == PARSE_EVENTS__TERM_TYPE_USER); +	TEST_ASSERT_VAL("wrong type val", +			term->type_val == PARSE_EVENTS__TERM_TYPE_NUM); +	TEST_ASSERT_VAL("wrong val", term->val.num == 1); +	TEST_ASSERT_VAL("wrong config", !strcmp(term->config, "umask"));  	return 0;  } @@ -541,10 +624,16 @@ static struct test__event_st test__events[] = {  		.name  = "instructions:H",  		.check = test__checkevent_exclude_guest_modifier,  	}, +	[26] = { +		.name  = "mem:0:rw", +		.check = test__checkevent_breakpoint_rw, +	}, +	[27] = { +		.name  = "mem:0:rw:kp", +		.check = test__checkevent_breakpoint_rw_modifier, +	},  }; -#define TEST__EVENTS_CNT (sizeof(test__events) / sizeof(struct test__event_st)) -  static struct test__event_st test__events_pmu[] = {  	[0] = {  		.name  = "cpu/config=10,config1,config2=3,period=1000/u", @@ -556,10 +645,23 @@ static struct test__event_st test__events_pmu[] = {  	},  }; -#define TEST__EVENTS_PMU_CNT (sizeof(test__events_pmu) / \ -			      sizeof(struct test__event_st)) +struct test__term { +	const char *str; +	__u32 type; +	int (*check)(struct list_head *terms); +}; + +static struct test__term test__terms[] = { +	[0] = { +		.str   = "config=10,config1,config2=3,umask=1", +		.check = test__checkterms_simple, +	}, +}; -static int test(struct test__event_st *e) +#define TEST__TERMS_CNT (sizeof(test__terms) / \ +			 sizeof(struct test__term)) + +static int test_event(struct test__event_st *e)  {  	struct perf_evlist *evlist;  	int ret; @@ -590,7 +692,48 @@ static int test_events(struct test__event_st *events, unsigned cnt)  		struct test__event_st *e = &events[i];  		pr_debug("running test %d '%s'\n", i, e->name); -		ret = test(e); +		ret = test_event(e); +		if (ret) +			break; +	} + +	return ret; +} + +static int test_term(struct test__term *t) +{ +	struct list_head *terms; +	int ret; + +	terms = malloc(sizeof(*terms)); +	if (!terms) +		return -ENOMEM; + +	INIT_LIST_HEAD(terms); + +	ret = parse_events_terms(terms, t->str); +	if (ret) { +		pr_debug("failed to parse terms '%s', err %d\n", +			 t->str , ret); +		return ret; +	} + +	ret = t->check(terms); +	parse_events__free_terms(terms); + +	return ret; +} + +static int test_terms(struct test__term *terms, unsigned cnt) +{ +	int ret = 0; +	unsigned i; + +	for (i = 0; i < cnt; i++) { +		struct test__term *t = &terms[i]; + +		pr_debug("running test %d '%s'\n", i, t->str); +		ret = test_term(t);  		if (ret)  			break;  	} @@ -617,9 +760,17 @@ int parse_events__test(void)  {  	int ret; -	ret = test_events(test__events, TEST__EVENTS_CNT); -	if (!ret && test_pmu()) -		ret = test_events(test__events_pmu, TEST__EVENTS_PMU_CNT); +#define TEST_EVENTS(tests)				\ +do {							\ +	ret = test_events(tests, ARRAY_SIZE(tests));	\ +	if (ret)					\ +		return ret;				\ +} while (0) -	return ret; +	TEST_EVENTS(test__events); + +	if (test_pmu()) +		TEST_EVENTS(test__events_pmu); + +	return test_terms(test__terms, ARRAY_SIZE(test__terms));  } diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index fac7d59309b..1aa721d7c10 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -11,14 +11,14 @@  #include "cache.h"  #include "header.h"  #include "debugfs.h" +#include "parse-events-bison.h" +#define YY_EXTRA_TYPE int  #include "parse-events-flex.h"  #include "pmu.h"  #define MAX_NAME_LEN 100  struct event_symbol { -	u8		type; -	u64		config;  	const char	*symbol;  	const char	*alias;  }; @@ -26,32 +26,88 @@ struct event_symbol {  #ifdef PARSER_DEBUG  extern int parse_events_debug;  #endif -int parse_events_parse(struct list_head *list, int *idx); +int parse_events_parse(void *data, void *scanner); -#define CHW(x) .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_##x -#define CSW(x) .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_##x - -static struct event_symbol event_symbols[] = { -  { CHW(CPU_CYCLES),			"cpu-cycles",			"cycles"		}, -  { CHW(STALLED_CYCLES_FRONTEND),	"stalled-cycles-frontend",	"idle-cycles-frontend"	}, -  { CHW(STALLED_CYCLES_BACKEND),	"stalled-cycles-backend",	"idle-cycles-backend"	}, -  { CHW(INSTRUCTIONS),			"instructions",			""			}, -  { CHW(CACHE_REFERENCES),		"cache-references",		""			}, -  { CHW(CACHE_MISSES),			"cache-misses",			""			}, -  { CHW(BRANCH_INSTRUCTIONS),		"branch-instructions",		"branches"		}, -  { CHW(BRANCH_MISSES),			"branch-misses",		""			}, -  { CHW(BUS_CYCLES),			"bus-cycles",			""			}, -  { CHW(REF_CPU_CYCLES),		"ref-cycles",			""			}, +static struct event_symbol event_symbols_hw[PERF_COUNT_HW_MAX] = { +	[PERF_COUNT_HW_CPU_CYCLES] = { +		.symbol = "cpu-cycles", +		.alias  = "cycles", +	}, +	[PERF_COUNT_HW_INSTRUCTIONS] = { +		.symbol = "instructions", +		.alias  = "", +	}, +	[PERF_COUNT_HW_CACHE_REFERENCES] = { +		.symbol = "cache-references", +		.alias  = "", +	}, +	[PERF_COUNT_HW_CACHE_MISSES] = { +		.symbol = "cache-misses", +		.alias  = "", +	}, +	[PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = { +		.symbol = "branch-instructions", +		.alias  = "branches", +	}, +	[PERF_COUNT_HW_BRANCH_MISSES] = { +		.symbol = "branch-misses", +		.alias  = "", +	}, +	[PERF_COUNT_HW_BUS_CYCLES] = { +		.symbol = "bus-cycles", +		.alias  = "", +	}, +	[PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] = { +		.symbol = "stalled-cycles-frontend", +		.alias  = "idle-cycles-frontend", +	}, +	[PERF_COUNT_HW_STALLED_CYCLES_BACKEND] = { +		.symbol = "stalled-cycles-backend", +		.alias  = "idle-cycles-backend", +	}, +	[PERF_COUNT_HW_REF_CPU_CYCLES] = { +		.symbol = "ref-cycles", +		.alias  = "", +	}, +}; -  { CSW(CPU_CLOCK),			"cpu-clock",			""			}, -  { CSW(TASK_CLOCK),			"task-clock",			""			}, -  { CSW(PAGE_FAULTS),			"page-faults",			"faults"		}, -  { CSW(PAGE_FAULTS_MIN),		"minor-faults",			""			}, -  { CSW(PAGE_FAULTS_MAJ),		"major-faults",			""			}, -  { CSW(CONTEXT_SWITCHES),		"context-switches",		"cs"			}, -  { CSW(CPU_MIGRATIONS),		"cpu-migrations",		"migrations"		}, -  { CSW(ALIGNMENT_FAULTS),		"alignment-faults",		""			}, -  { CSW(EMULATION_FAULTS),		"emulation-faults",		""			}, +static struct event_symbol event_symbols_sw[PERF_COUNT_SW_MAX] = { +	[PERF_COUNT_SW_CPU_CLOCK] = { +		.symbol = "cpu-clock", +		.alias  = "", +	}, +	[PERF_COUNT_SW_TASK_CLOCK] = { +		.symbol = "task-clock", +		.alias  = "", +	}, +	[PERF_COUNT_SW_PAGE_FAULTS] = { +		.symbol = "page-faults", +		.alias  = "faults", +	}, +	[PERF_COUNT_SW_CONTEXT_SWITCHES] = { +		.symbol = "context-switches", +		.alias  = "cs", +	}, +	[PERF_COUNT_SW_CPU_MIGRATIONS] = { +		.symbol = "cpu-migrations", +		.alias  = "migrations", +	}, +	[PERF_COUNT_SW_PAGE_FAULTS_MIN] = { +		.symbol = "minor-faults", +		.alias  = "", +	}, +	[PERF_COUNT_SW_PAGE_FAULTS_MAJ] = { +		.symbol = "major-faults", +		.alias  = "", +	}, +	[PERF_COUNT_SW_ALIGNMENT_FAULTS] = { +		.symbol = "alignment-faults", +		.alias  = "", +	}, +	[PERF_COUNT_SW_EMULATION_FAULTS] = { +		.symbol = "emulation-faults", +		.alias  = "", +	},  };  #define __PERF_EVENT_FIELD(config, name) \ @@ -62,76 +118,6 @@ static struct event_symbol event_symbols[] = {  #define PERF_EVENT_TYPE(config)		__PERF_EVENT_FIELD(config, TYPE)  #define PERF_EVENT_ID(config)		__PERF_EVENT_FIELD(config, EVENT) -static const char *hw_event_names[PERF_COUNT_HW_MAX] = { -	"cycles", -	"instructions", -	"cache-references", -	"cache-misses", -	"branches", -	"branch-misses", -	"bus-cycles", -	"stalled-cycles-frontend", -	"stalled-cycles-backend", -	"ref-cycles", -}; - -static const char *sw_event_names[PERF_COUNT_SW_MAX] = { -	"cpu-clock", -	"task-clock", -	"page-faults", -	"context-switches", -	"CPU-migrations", -	"minor-faults", -	"major-faults", -	"alignment-faults", -	"emulation-faults", -}; - -#define MAX_ALIASES 8 - -static const char *hw_cache[PERF_COUNT_HW_CACHE_MAX][MAX_ALIASES] = { - { "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",			}, - { "branch",	"branches",	"bpu",		"btb",		"bpc",	}, - { "node",								}, -}; - -static const char *hw_cache_op[PERF_COUNT_HW_CACHE_OP_MAX][MAX_ALIASES] = { - { "load",	"loads",	"read",					}, - { "store",	"stores",	"write",				}, - { "prefetch",	"prefetches",	"speculative-read", "speculative-load",	}, -}; - -static const char *hw_cache_result[PERF_COUNT_HW_CACHE_RESULT_MAX] -				  [MAX_ALIASES] = { - { "refs",	"Reference",	"ops",		"access",		}, - { "misses",	"miss",							}, -}; - -#define C(x)		PERF_COUNT_HW_CACHE_##x -#define CACHE_READ	(1 << C(OP_READ)) -#define CACHE_WRITE	(1 << C(OP_WRITE)) -#define CACHE_PREFETCH	(1 << C(OP_PREFETCH)) -#define COP(x)		(1 << x) - -/* - * cache operartion stat - * L1I : Read and prefetch only - * ITLB and BPU : Read-only - */ -static unsigned long hw_cache_stat[C(MAX)] = { - [C(L1D)]	= (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH), - [C(L1I)]	= (CACHE_READ | CACHE_PREFETCH), - [C(LL)]	= (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH), - [C(DTLB)]	= (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH), - [C(ITLB)]	= (CACHE_READ), - [C(BPU)]	= (CACHE_READ), - [C(NODE)]	= (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH), -}; -  #define for_each_subsystem(sys_dir, sys_dirent, sys_next)	       \  	while (!readdir_r(sys_dir, &sys_dirent, &sys_next) && sys_next)	       \  	if (sys_dirent.d_type == DT_DIR &&				       \ @@ -231,48 +217,6 @@ struct tracepoint_path *tracepoint_id_to_path(u64 config)  	return NULL;  } -#define TP_PATH_LEN (MAX_EVENT_LENGTH * 2 + 1) -static const char *tracepoint_id_to_name(u64 config) -{ -	static char buf[TP_PATH_LEN]; -	struct tracepoint_path *path; - -	path = tracepoint_id_to_path(config); -	if (path) { -		snprintf(buf, TP_PATH_LEN, "%s:%s", path->system, path->name); -		free(path->name); -		free(path->system); -		free(path); -	} else -		snprintf(buf, TP_PATH_LEN, "%s:%s", "unknown", "unknown"); - -	return buf; -} - -static int is_cache_op_valid(u8 cache_type, u8 cache_op) -{ -	if (hw_cache_stat[cache_type] & COP(cache_op)) -		return 1;	/* valid */ -	else -		return 0;	/* invalid */ -} - -static char *event_cache_name(u8 cache_type, u8 cache_op, u8 cache_result) -{ -	static char name[50]; - -	if (cache_result) { -		sprintf(name, "%s-%s-%s", hw_cache[cache_type][0], -			hw_cache_op[cache_op][0], -			hw_cache_result[cache_result][0]); -	} else { -		sprintf(name, "%s-%s", hw_cache[cache_type][0], -			hw_cache_op[cache_op][1]); -	} - -	return name; -} -  const char *event_type(int type)  {  	switch (type) { @@ -295,68 +239,6 @@ const char *event_type(int type)  	return "unknown";  } -const char *event_name(struct perf_evsel *evsel) -{ -	u64 config = evsel->attr.config; -	int type = evsel->attr.type; - -	if (evsel->name) -		return evsel->name; - -	return __event_name(type, config); -} - -const char *__event_name(int type, u64 config) -{ -	static char buf[32]; - -	if (type == PERF_TYPE_RAW) { -		sprintf(buf, "raw 0x%" PRIx64, config); -		return buf; -	} - -	switch (type) { -	case PERF_TYPE_HARDWARE: -		if (config < PERF_COUNT_HW_MAX && hw_event_names[config]) -			return hw_event_names[config]; -		return "unknown-hardware"; - -	case PERF_TYPE_HW_CACHE: { -		u8 cache_type, cache_op, cache_result; - -		cache_type   = (config >>  0) & 0xff; -		if (cache_type > PERF_COUNT_HW_CACHE_MAX) -			return "unknown-ext-hardware-cache-type"; - -		cache_op     = (config >>  8) & 0xff; -		if (cache_op > PERF_COUNT_HW_CACHE_OP_MAX) -			return "unknown-ext-hardware-cache-op"; - -		cache_result = (config >> 16) & 0xff; -		if (cache_result > PERF_COUNT_HW_CACHE_RESULT_MAX) -			return "unknown-ext-hardware-cache-result"; - -		if (!is_cache_op_valid(cache_type, cache_op)) -			return "invalid-cache"; - -		return event_cache_name(cache_type, cache_op, cache_result); -	} - -	case PERF_TYPE_SOFTWARE: -		if (config < PERF_COUNT_SW_MAX && sw_event_names[config]) -			return sw_event_names[config]; -		return "unknown-software"; - -	case PERF_TYPE_TRACEPOINT: -		return tracepoint_id_to_name(config); - -	default: -		break; -	} - -	return "unknown"; -} -  static int add_event(struct list_head **_list, int *idx,  		     struct perf_event_attr *attr, char *name)  { @@ -378,19 +260,20 @@ static int add_event(struct list_head **_list, int *idx,  		return -ENOMEM;  	} -	evsel->name = strdup(name); +	if (name) +		evsel->name = strdup(name);  	list_add_tail(&evsel->node, list);  	*_list = list;  	return 0;  } -static int parse_aliases(char *str, const char *names[][MAX_ALIASES], int size) +static int parse_aliases(char *str, const char *names[][PERF_EVSEL__MAX_ALIASES], int size)  {  	int i, j;  	int n, longest = -1;  	for (i = 0; i < size; i++) { -		for (j = 0; j < MAX_ALIASES && names[i][j]; j++) { +		for (j = 0; j < PERF_EVSEL__MAX_ALIASES && names[i][j]; j++) {  			n = strlen(names[i][j]);  			if (n > longest && !strncasecmp(str, names[i][j], n))  				longest = n; @@ -415,7 +298,7 @@ int parse_events_add_cache(struct list_head **list, int *idx,  	 * No fallback - if we cannot get a clear cache type  	 * then bail out:  	 */ -	cache_type = parse_aliases(type, hw_cache, +	cache_type = parse_aliases(type, perf_evsel__hw_cache,  				   PERF_COUNT_HW_CACHE_MAX);  	if (cache_type == -1)  		return -EINVAL; @@ -428,18 +311,18 @@ int parse_events_add_cache(struct list_head **list, int *idx,  		snprintf(name + n, MAX_NAME_LEN - n, "-%s\n", str);  		if (cache_op == -1) { -			cache_op = parse_aliases(str, hw_cache_op, +			cache_op = parse_aliases(str, perf_evsel__hw_cache_op,  						 PERF_COUNT_HW_CACHE_OP_MAX);  			if (cache_op >= 0) { -				if (!is_cache_op_valid(cache_type, cache_op)) +				if (!perf_evsel__is_cache_op_valid(cache_type, cache_op))  					return -EINVAL;  				continue;  			}  		}  		if (cache_result == -1) { -			cache_result = parse_aliases(str, hw_cache_result, -						PERF_COUNT_HW_CACHE_RESULT_MAX); +			cache_result = parse_aliases(str, perf_evsel__hw_cache_result, +						     PERF_COUNT_HW_CACHE_RESULT_MAX);  			if (cache_result >= 0)  				continue;  		} @@ -554,21 +437,31 @@ parse_breakpoint_type(const char *type, struct perf_event_attr *attr)  		if (!type || !type[i])  			break; +#define CHECK_SET_TYPE(bit)		\ +do {					\ +	if (attr->bp_type & bit)	\ +		return -EINVAL;		\ +	else				\ +		attr->bp_type |= bit;	\ +} while (0) +  		switch (type[i]) {  		case 'r': -			attr->bp_type |= HW_BREAKPOINT_R; +			CHECK_SET_TYPE(HW_BREAKPOINT_R);  			break;  		case 'w': -			attr->bp_type |= HW_BREAKPOINT_W; +			CHECK_SET_TYPE(HW_BREAKPOINT_W);  			break;  		case 'x': -			attr->bp_type |= HW_BREAKPOINT_X; +			CHECK_SET_TYPE(HW_BREAKPOINT_X);  			break;  		default:  			return -EINVAL;  		}  	} +#undef CHECK_SET_TYPE +  	if (!attr->bp_type) /* Default */  		attr->bp_type = HW_BREAKPOINT_R | HW_BREAKPOINT_W; @@ -579,7 +472,6 @@ int parse_events_add_breakpoint(struct list_head **list, int *idx,  				void *ptr, char *type)  {  	struct perf_event_attr attr; -	char name[MAX_NAME_LEN];  	memset(&attr, 0, sizeof(attr));  	attr.bp_addr = (unsigned long) ptr; @@ -598,8 +490,7 @@ int parse_events_add_breakpoint(struct list_head **list, int *idx,  	attr.type = PERF_TYPE_BREAKPOINT; -	snprintf(name, MAX_NAME_LEN, "mem:%p:%s", ptr, type ? type : "rw"); -	return add_event(list, idx, &attr, name); +	return add_event(list, idx, &attr, NULL);  }  static int config_term(struct perf_event_attr *attr, @@ -671,8 +562,7 @@ int parse_events_add_numeric(struct list_head **list, int *idx,  	    config_attr(&attr, head_config, 1))  		return -EINVAL; -	return add_event(list, idx, &attr, -			 (char *) __event_name(type, config)); +	return add_event(list, idx, &attr, NULL);  }  static int parse_events__is_name_term(struct parse_events__term *term) @@ -680,8 +570,7 @@ static int parse_events__is_name_term(struct parse_events__term *term)  	return term->type_term == PARSE_EVENTS__TERM_TYPE_NAME;  } -static char *pmu_event_name(struct perf_event_attr *attr, -			    struct list_head *head_terms) +static char *pmu_event_name(struct list_head *head_terms)  {  	struct parse_events__term *term; @@ -689,7 +578,7 @@ static char *pmu_event_name(struct perf_event_attr *attr,  		if (parse_events__is_name_term(term))  			return term->val.str; -	return (char *) __event_name(PERF_TYPE_RAW, attr->config); +	return NULL;  }  int parse_events_add_pmu(struct list_head **list, int *idx, @@ -704,6 +593,9 @@ int parse_events_add_pmu(struct list_head **list, int *idx,  	memset(&attr, 0, sizeof(attr)); +	if (perf_pmu__check_alias(pmu, head_config)) +		return -EINVAL; +  	/*  	 * Configure hardcoded terms first, no need to check  	 * return value when called with fail == 0 ;) @@ -714,7 +606,7 @@ int parse_events_add_pmu(struct list_head **list, int *idx,  		return -EINVAL;  	return add_event(list, idx, &attr, -			 pmu_event_name(&attr, head_config)); +			 pmu_event_name(head_config));  }  void parse_events_update_lists(struct list_head *list_event, @@ -792,27 +684,62 @@ int parse_events_modifier(struct list_head *list, char *str)  	return 0;  } -int parse_events(struct perf_evlist *evlist, const char *str, int unset __used) +static int parse_events__scanner(const char *str, void *data, int start_token)  { -	LIST_HEAD(list); -	LIST_HEAD(list_tmp);  	YY_BUFFER_STATE buffer; -	int ret, idx = evlist->nr_entries; +	void *scanner; +	int ret; -	buffer = parse_events__scan_string(str); +	ret = parse_events_lex_init_extra(start_token, &scanner); +	if (ret) +		return ret; + +	buffer = parse_events__scan_string(str, scanner);  #ifdef PARSER_DEBUG  	parse_events_debug = 1;  #endif -	ret = parse_events_parse(&list, &idx); +	ret = parse_events_parse(data, scanner); + +	parse_events__flush_buffer(buffer, scanner); +	parse_events__delete_buffer(buffer, scanner); +	parse_events_lex_destroy(scanner); +	return ret; +} + +/* + * parse event config string, return a list of event terms. + */ +int parse_events_terms(struct list_head *terms, const char *str) +{ +	struct parse_events_data__terms data = { +		.terms = NULL, +	}; +	int ret; -	parse_events__flush_buffer(buffer); -	parse_events__delete_buffer(buffer); -	parse_events_lex_destroy(); +	ret = parse_events__scanner(str, &data, PE_START_TERMS); +	if (!ret) { +		list_splice(data.terms, terms); +		free(data.terms); +		return 0; +	} +	parse_events__free_terms(data.terms); +	return ret; +} + +int parse_events(struct perf_evlist *evlist, const char *str, int unset __used) +{ +	struct parse_events_data__events data = { +		.list = LIST_HEAD_INIT(data.list), +		.idx  = evlist->nr_entries, +	}; +	int ret; + +	ret = parse_events__scanner(str, &data, PE_START_EVENTS);  	if (!ret) { -		int entries = idx - evlist->nr_entries; -		perf_evlist__splice_list_tail(evlist, &list, entries); +		int entries = data.idx - evlist->nr_entries; +		perf_evlist__splice_list_tail(evlist, &data.list, entries);  		return 0;  	} @@ -951,16 +878,13 @@ int is_valid_tracepoint(const char *event_string)  	return 0;  } -void print_events_type(u8 type) +static void __print_events_type(u8 type, struct event_symbol *syms, +				unsigned max)  { -	struct event_symbol *syms = event_symbols; -	unsigned int i;  	char name[64]; +	unsigned i; -	for (i = 0; i < ARRAY_SIZE(event_symbols); i++, syms++) { -		if (type != syms->type) -			continue; - +	for (i = 0; i < max ; i++, syms++) {  		if (strlen(syms->alias))  			snprintf(name, sizeof(name),  "%s OR %s",  				 syms->symbol, syms->alias); @@ -972,19 +896,28 @@ void print_events_type(u8 type)  	}  } +void print_events_type(u8 type) +{ +	if (type == PERF_TYPE_SOFTWARE) +		__print_events_type(type, event_symbols_sw, PERF_COUNT_SW_MAX); +	else +		__print_events_type(type, event_symbols_hw, PERF_COUNT_HW_MAX); +} +  int print_hwcache_events(const char *event_glob)  {  	unsigned int type, op, i, printed = 0; +	char name[64];  	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)) +			if (!perf_evsel__is_cache_op_valid(type, op))  				continue;  			for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) { -				char *name = event_cache_name(type, op, i); - +				__perf_evsel__hw_cache_type_op_res_name(type, op, i, +									name, sizeof(name));  				if (event_glob != NULL && !strglobmatch(name, event_glob))  					continue; @@ -998,26 +931,13 @@ int print_hwcache_events(const char *event_glob)  	return printed;  } -/* - * Print the help text for the event symbols: - */ -void print_events(const char *event_glob) +static void print_symbol_events(const char *event_glob, unsigned type, +				struct event_symbol *syms, unsigned max)  { -	unsigned int i, type, prev_type = -1, printed = 0, ntypes_printed = 0; -	struct event_symbol *syms = event_symbols; +	unsigned i, printed = 0;  	char name[MAX_NAME_LEN]; -	printf("\n"); -	printf("List of pre-defined events (to be used in -e):\n"); - -	for (i = 0; i < ARRAY_SIZE(event_symbols); i++, syms++) { -		type = syms->type; - -		if (type != prev_type && printed) { -			printf("\n"); -			printed = 0; -			ntypes_printed++; -		} +	for (i = 0; i < max; i++, syms++) {  		if (event_glob != NULL &&   		    !(strglobmatch(syms->symbol, event_glob) || @@ -1028,17 +948,31 @@ void print_events(const char *event_glob)  			snprintf(name, MAX_NAME_LEN, "%s OR %s", syms->symbol, syms->alias);  		else  			strncpy(name, syms->symbol, MAX_NAME_LEN); -		printf("  %-50s [%s]\n", name, -			event_type_descriptors[type]); -		prev_type = type; -		++printed; +		printf("  %-50s [%s]\n", name, event_type_descriptors[type]); + +		printed++;  	} -	if (ntypes_printed) { -		printed = 0; +	if (printed)  		printf("\n"); -	} +} + +/* + * Print the help text for the event symbols: + */ +void print_events(const char *event_glob) +{ + +	printf("\n"); +	printf("List of pre-defined events (to be used in -e):\n"); + +	print_symbol_events(event_glob, PERF_TYPE_HARDWARE, +			    event_symbols_hw, PERF_COUNT_HW_MAX); + +	print_symbol_events(event_glob, PERF_TYPE_SOFTWARE, +			    event_symbols_sw, PERF_COUNT_SW_MAX); +  	print_hwcache_events(event_glob);  	if (event_glob != NULL) @@ -1111,6 +1045,13 @@ int parse_events__term_str(struct parse_events__term **term,  			config, str, 0);  } +int parse_events__term_clone(struct parse_events__term **new, +			     struct parse_events__term *term) +{ +	return new_term(new, term->type_val, term->type_term, term->config, +			term->val.str, term->val.num); +} +  void parse_events__free_terms(struct list_head *terms)  {  	struct parse_events__term *term, *h; diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index 8cac57ab4ee..ee9c218a193 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h @@ -26,13 +26,12 @@ extern struct tracepoint_path *tracepoint_id_to_path(u64 config);  extern bool have_tracepoints(struct list_head *evlist);  const char *event_type(int type); -const char *event_name(struct perf_evsel *event); -extern const char *__event_name(int type, u64 config);  extern int parse_events_option(const struct option *opt, const char *str,  			       int unset);  extern int parse_events(struct perf_evlist *evlist, const char *str,  			int unset); +extern int parse_events_terms(struct list_head *terms, const char *str);  extern int parse_filter(const struct option *opt, const char *str, int unset);  #define EVENTS_HELP_MAX (128*1024) @@ -63,11 +62,22 @@ struct parse_events__term {  	struct list_head list;  }; +struct parse_events_data__events { +	struct list_head list; +	int idx; +}; + +struct parse_events_data__terms { +	struct list_head *terms; +}; +  int parse_events__is_hardcoded_term(struct parse_events__term *term);  int parse_events__term_num(struct parse_events__term **_term,  			   int type_term, char *config, long num);  int parse_events__term_str(struct parse_events__term **_term,  			   int type_term, char *config, char *str); +int parse_events__term_clone(struct parse_events__term **new, +			     struct parse_events__term *term);  void parse_events__free_terms(struct list_head *terms);  int parse_events_modifier(struct list_head *list, char *str);  int parse_events_add_tracepoint(struct list_head **list, int *idx, @@ -83,8 +93,7 @@ int parse_events_add_pmu(struct list_head **list, int *idx,  			 char *pmu , struct list_head *head_config);  void parse_events_update_lists(struct list_head *list_event,  			       struct list_head *list_all); -void parse_events_error(struct list_head *list_all, -			int *idx, char const *msg); +void parse_events_error(void *data, void *scanner, char const *msg);  int parse_events__test(void);  void print_events(const char *event_glob); diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l index 618a8e78839..384ca74c6b2 100644 --- a/tools/perf/util/parse-events.l +++ b/tools/perf/util/parse-events.l @@ -1,4 +1,6 @@ +%option reentrant +%option bison-bridge  %option prefix="parse_events_"  %option stack @@ -8,7 +10,10 @@  #include "parse-events-bison.h"  #include "parse-events.h" -static int __value(char *str, int base, int token) +char *parse_events_get_text(yyscan_t yyscanner); +YYSTYPE *parse_events_get_lval(yyscan_t yyscanner); + +static int __value(YYSTYPE *yylval, char *str, int base, int token)  {  	long num; @@ -17,35 +22,48 @@ static int __value(char *str, int base, int token)  	if (errno)  		return PE_ERROR; -	parse_events_lval.num = num; +	yylval->num = num;  	return token;  } -static int value(int base) +static int value(yyscan_t scanner, int base)  { -	return __value(parse_events_text, base, PE_VALUE); +	YYSTYPE *yylval = parse_events_get_lval(scanner); +	char *text = parse_events_get_text(scanner); + +	return __value(yylval, text, base, PE_VALUE);  } -static int raw(void) +static int raw(yyscan_t scanner)  { -	return __value(parse_events_text + 1, 16, PE_RAW); +	YYSTYPE *yylval = parse_events_get_lval(scanner); +	char *text = parse_events_get_text(scanner); + +	return __value(yylval, text + 1, 16, PE_RAW);  } -static int str(int token) +static int str(yyscan_t scanner, int token)  { -	parse_events_lval.str = strdup(parse_events_text); +	YYSTYPE *yylval = parse_events_get_lval(scanner); +	char *text = parse_events_get_text(scanner); + +	yylval->str = strdup(text);  	return token;  } -static int sym(int type, int config) +static int sym(yyscan_t scanner, int type, int config)  { -	parse_events_lval.num = (type << 16) + config; -	return PE_VALUE_SYM; +	YYSTYPE *yylval = parse_events_get_lval(scanner); + +	yylval->num = (type << 16) + config; +	return type == PERF_TYPE_HARDWARE ? PE_VALUE_SYM_HW : PE_VALUE_SYM_SW;  } -static int term(int type) +static int term(yyscan_t scanner, int type)  { -	parse_events_lval.num = type; +	YYSTYPE *yylval = parse_events_get_lval(scanner); + +	yylval->num = type;  	return PE_TERM;  } @@ -58,28 +76,41 @@ num_hex		0x[a-fA-F0-9]+  num_raw_hex	[a-fA-F0-9]+  name		[a-zA-Z_*?][a-zA-Z0-9_*?]*  modifier_event	[ukhpGH]{1,8} -modifier_bp	[rwx] +modifier_bp	[rwx]{1,3}  %% -cpu-cycles|cycles				{ return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES); } -stalled-cycles-frontend|idle-cycles-frontend	{ return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_FRONTEND); } -stalled-cycles-backend|idle-cycles-backend	{ return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_BACKEND); } -instructions					{ return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_INSTRUCTIONS); } -cache-references				{ return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_REFERENCES); } -cache-misses					{ return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_MISSES); } -branch-instructions|branches			{ return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_BRANCH_INSTRUCTIONS); } -branch-misses					{ return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_BRANCH_MISSES); } -bus-cycles					{ return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_BUS_CYCLES); } -ref-cycles					{ return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_REF_CPU_CYCLES); } -cpu-clock					{ return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_CLOCK); } -task-clock					{ return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_TASK_CLOCK); } -page-faults|faults				{ return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_PAGE_FAULTS); } -minor-faults					{ return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_PAGE_FAULTS_MIN); } -major-faults					{ return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_PAGE_FAULTS_MAJ); } -context-switches|cs				{ return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CONTEXT_SWITCHES); } -cpu-migrations|migrations			{ return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_MIGRATIONS); } -alignment-faults				{ return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_ALIGNMENT_FAULTS); } -emulation-faults				{ return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_EMULATION_FAULTS); } + +%{ +	{ +		int start_token; + +		start_token = (int) parse_events_get_extra(yyscanner); +		if (start_token) { +			parse_events_set_extra(NULL, yyscanner); +			return start_token; +		} +         } +%} + +cpu-cycles|cycles				{ return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES); } +stalled-cycles-frontend|idle-cycles-frontend	{ return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_FRONTEND); } +stalled-cycles-backend|idle-cycles-backend	{ return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_BACKEND); } +instructions					{ return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_INSTRUCTIONS); } +cache-references				{ return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_REFERENCES); } +cache-misses					{ return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_MISSES); } +branch-instructions|branches			{ return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_BRANCH_INSTRUCTIONS); } +branch-misses					{ return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_BRANCH_MISSES); } +bus-cycles					{ return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_BUS_CYCLES); } +ref-cycles					{ return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_REF_CPU_CYCLES); } +cpu-clock					{ return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_CLOCK); } +task-clock					{ return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_TASK_CLOCK); } +page-faults|faults				{ return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_PAGE_FAULTS); } +minor-faults					{ return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_PAGE_FAULTS_MIN); } +major-faults					{ return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_PAGE_FAULTS_MAJ); } +context-switches|cs				{ return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CONTEXT_SWITCHES); } +cpu-migrations|migrations			{ return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_MIGRATIONS); } +alignment-faults				{ return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_ALIGNMENT_FAULTS); } +emulation-faults				{ return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_EMULATION_FAULTS); }  L1-dcache|l1-d|l1d|L1-data		|  L1-icache|l1-i|l1i|L1-instruction	| @@ -87,14 +118,14 @@ LLC|L2					|  dTLB|d-tlb|Data-TLB			|  iTLB|i-tlb|Instruction-TLB		|  branch|branches|bpu|btb|bpc		| -node					{ return str(PE_NAME_CACHE_TYPE); } +node					{ return str(yyscanner, PE_NAME_CACHE_TYPE); }  load|loads|read				|  store|stores|write			|  prefetch|prefetches			|  speculative-read|speculative-load	|  refs|Reference|ops|access		| -misses|miss				{ return str(PE_NAME_CACHE_OP_RESULT); } +misses|miss				{ return str(yyscanner, PE_NAME_CACHE_OP_RESULT); }  	/*  	 * These are event config hardcoded term names to be specified @@ -102,38 +133,39 @@ misses|miss				{ return str(PE_NAME_CACHE_OP_RESULT); }  	 * so we can put them here directly. In case the we have a conflict  	 * in future, this needs to go into '//' condition block.  	 */ -config			{ return term(PARSE_EVENTS__TERM_TYPE_CONFIG); } -config1			{ return term(PARSE_EVENTS__TERM_TYPE_CONFIG1); } -config2			{ return term(PARSE_EVENTS__TERM_TYPE_CONFIG2); } -name			{ return term(PARSE_EVENTS__TERM_TYPE_NAME); } -period			{ return term(PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD); } -branch_type		{ return term(PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE); } +config			{ return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG); } +config1			{ return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG1); } +config2			{ return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG2); } +name			{ return term(yyscanner, PARSE_EVENTS__TERM_TYPE_NAME); } +period			{ return term(yyscanner, PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD); } +branch_type		{ return term(yyscanner, PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE); }  mem:			{ BEGIN(mem); return PE_PREFIX_MEM; } -r{num_raw_hex}		{ return raw(); } -{num_dec}		{ return value(10); } -{num_hex}		{ return value(16); } +r{num_raw_hex}		{ return raw(yyscanner); } +{num_dec}		{ return value(yyscanner, 10); } +{num_hex}		{ return value(yyscanner, 16); } -{modifier_event}	{ return str(PE_MODIFIER_EVENT); } -{name}			{ return str(PE_NAME); } +{modifier_event}	{ return str(yyscanner, PE_MODIFIER_EVENT); } +{name}			{ return str(yyscanner, PE_NAME); }  "/"			{ return '/'; }  -			{ return '-'; }  ,			{ return ','; }  :			{ return ':'; }  =			{ return '='; } +\n			{ }  <mem>{ -{modifier_bp}		{ return str(PE_MODIFIER_BP); } +{modifier_bp}		{ return str(yyscanner, PE_MODIFIER_BP); }  :			{ return ':'; } -{num_dec}		{ return value(10); } -{num_hex}		{ return value(16); } +{num_dec}		{ return value(yyscanner, 10); } +{num_hex}		{ return value(yyscanner, 16); }  	/*  	 * We need to separate 'mem:' scanner part, in order to get specific  	 * modifier bits parsed out. Otherwise we would need to handle PE_NAME  	 * and we'd need to parse it manually. During the escape from <mem>  	 * state we need to put the escaping char back, so we dont miss it.  	 */ -.			{ unput(*parse_events_text); BEGIN(INITIAL); } +.			{ unput(*yytext); BEGIN(INITIAL); }  	/*  	 * We destroy the scanner after reaching EOF,  	 * but anyway just to be sure get back to INIT state. @@ -143,7 +175,7 @@ r{num_raw_hex}		{ return raw(); }  %% -int parse_events_wrap(void) +int parse_events_wrap(void *scanner __used)  {  	return 1;  } diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y index 362cc59332a..2bc5fbff2b5 100644 --- a/tools/perf/util/parse-events.y +++ b/tools/perf/util/parse-events.y @@ -1,7 +1,8 @@ - +%pure-parser  %name-prefix "parse_events_" -%parse-param {struct list_head *list_all} -%parse-param {int *idx} +%parse-param {void *_data} +%parse-param {void *scanner} +%lex-param {void* scanner}  %{ @@ -12,8 +13,9 @@  #include "types.h"  #include "util.h"  #include "parse-events.h" +#include "parse-events-bison.h" -extern int parse_events_lex (void); +extern int parse_events_lex (YYSTYPE* lvalp, void* scanner);  #define ABORT_ON(val) \  do { \ @@ -23,14 +25,16 @@ do { \  %} -%token PE_VALUE PE_VALUE_SYM PE_RAW PE_TERM +%token PE_START_EVENTS PE_START_TERMS +%token PE_VALUE PE_VALUE_SYM_HW PE_VALUE_SYM_SW PE_RAW PE_TERM  %token PE_NAME  %token PE_MODIFIER_EVENT PE_MODIFIER_BP  %token PE_NAME_CACHE_TYPE PE_NAME_CACHE_OP_RESULT  %token PE_PREFIX_MEM PE_PREFIX_RAW  %token PE_ERROR  %type <num> PE_VALUE -%type <num> PE_VALUE_SYM +%type <num> PE_VALUE_SYM_HW +%type <num> PE_VALUE_SYM_SW  %type <num> PE_RAW  %type <num> PE_TERM  %type <str> PE_NAME @@ -38,6 +42,7 @@ do { \  %type <str> PE_NAME_CACHE_OP_RESULT  %type <str> PE_MODIFIER_EVENT  %type <str> PE_MODIFIER_BP +%type <num> value_sym  %type <head> event_config  %type <term> event_term  %type <head> event_pmu @@ -58,24 +63,33 @@ do { \  }  %% +start: +PE_START_EVENTS events +| +PE_START_TERMS  terms +  events:  events ',' event | event  event:  event_def PE_MODIFIER_EVENT  { +	struct parse_events_data__events *data = _data; +  	/*  	 * Apply modifier on all events added by single event definition  	 * (there could be more events added for multiple tracepoint  	 * definitions via '*?'.  	 */  	ABORT_ON(parse_events_modifier($1, $2)); -	parse_events_update_lists($1, list_all); +	parse_events_update_lists($1, &data->list);  }  |  event_def  { -	parse_events_update_lists($1, list_all); +	struct parse_events_data__events *data = _data; + +	parse_events_update_lists($1, &data->list);  }  event_def: event_pmu | @@ -89,104 +103,131 @@ event_def: event_pmu |  event_pmu:  PE_NAME '/' event_config '/'  { +	struct parse_events_data__events *data = _data;  	struct list_head *list = NULL; -	ABORT_ON(parse_events_add_pmu(&list, idx, $1, $3)); +	ABORT_ON(parse_events_add_pmu(&list, &data->idx, $1, $3));  	parse_events__free_terms($3);  	$$ = list;  } +value_sym: +PE_VALUE_SYM_HW +| +PE_VALUE_SYM_SW +  event_legacy_symbol: -PE_VALUE_SYM '/' event_config '/' +value_sym '/' event_config '/'  { +	struct parse_events_data__events *data = _data;  	struct list_head *list = NULL;  	int type = $1 >> 16;  	int config = $1 & 255; -	ABORT_ON(parse_events_add_numeric(&list, idx, type, config, $3)); +	ABORT_ON(parse_events_add_numeric(&list, &data->idx, +					  type, config, $3));  	parse_events__free_terms($3);  	$$ = list;  }  | -PE_VALUE_SYM sep_slash_dc +value_sym sep_slash_dc  { +	struct parse_events_data__events *data = _data;  	struct list_head *list = NULL;  	int type = $1 >> 16;  	int config = $1 & 255; -	ABORT_ON(parse_events_add_numeric(&list, idx, type, config, NULL)); +	ABORT_ON(parse_events_add_numeric(&list, &data->idx, +					  type, config, NULL));  	$$ = list;  }  event_legacy_cache:  PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT '-' PE_NAME_CACHE_OP_RESULT  { +	struct parse_events_data__events *data = _data;  	struct list_head *list = NULL; -	ABORT_ON(parse_events_add_cache(&list, idx, $1, $3, $5)); +	ABORT_ON(parse_events_add_cache(&list, &data->idx, $1, $3, $5));  	$$ = list;  }  |  PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT  { +	struct parse_events_data__events *data = _data;  	struct list_head *list = NULL; -	ABORT_ON(parse_events_add_cache(&list, idx, $1, $3, NULL)); +	ABORT_ON(parse_events_add_cache(&list, &data->idx, $1, $3, NULL));  	$$ = list;  }  |  PE_NAME_CACHE_TYPE  { +	struct parse_events_data__events *data = _data;  	struct list_head *list = NULL; -	ABORT_ON(parse_events_add_cache(&list, idx, $1, NULL, NULL)); +	ABORT_ON(parse_events_add_cache(&list, &data->idx, $1, NULL, NULL));  	$$ = list;  }  event_legacy_mem:  PE_PREFIX_MEM PE_VALUE ':' PE_MODIFIER_BP sep_dc  { +	struct parse_events_data__events *data = _data;  	struct list_head *list = NULL; -	ABORT_ON(parse_events_add_breakpoint(&list, idx, (void *) $2, $4)); +	ABORT_ON(parse_events_add_breakpoint(&list, &data->idx, +					     (void *) $2, $4));  	$$ = list;  }  |  PE_PREFIX_MEM PE_VALUE sep_dc  { +	struct parse_events_data__events *data = _data;  	struct list_head *list = NULL; -	ABORT_ON(parse_events_add_breakpoint(&list, idx, (void *) $2, NULL)); +	ABORT_ON(parse_events_add_breakpoint(&list, &data->idx, +					     (void *) $2, NULL));  	$$ = list;  }  event_legacy_tracepoint:  PE_NAME ':' PE_NAME  { +	struct parse_events_data__events *data = _data;  	struct list_head *list = NULL; -	ABORT_ON(parse_events_add_tracepoint(&list, idx, $1, $3)); +	ABORT_ON(parse_events_add_tracepoint(&list, &data->idx, $1, $3));  	$$ = list;  }  event_legacy_numeric:  PE_VALUE ':' PE_VALUE  { +	struct parse_events_data__events *data = _data;  	struct list_head *list = NULL; -	ABORT_ON(parse_events_add_numeric(&list, idx, $1, $3, NULL)); +	ABORT_ON(parse_events_add_numeric(&list, &data->idx, $1, $3, NULL));  	$$ = list;  }  event_legacy_raw:  PE_RAW  { +	struct parse_events_data__events *data = _data;  	struct list_head *list = NULL; -	ABORT_ON(parse_events_add_numeric(&list, idx, PERF_TYPE_RAW, $1, NULL)); +	ABORT_ON(parse_events_add_numeric(&list, &data->idx, +					  PERF_TYPE_RAW, $1, NULL));  	$$ = list;  } +terms: event_config +{ +	struct parse_events_data__terms *data = _data; +	data->terms = $1; +} +  event_config:  event_config ',' event_term  { @@ -267,8 +308,7 @@ sep_slash_dc: '/' | ':' |  %% -void parse_events_error(struct list_head *list_all __used, -			int *idx __used, +void parse_events_error(void *data __used, void *scanner __used,  			char const *msg __used)  {  } diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index a119a537169..67715a42cd6 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -72,7 +72,7 @@ static int pmu_format(char *name, struct list_head *format)  		 "%s/bus/event_source/devices/%s/format", sysfs, name);  	if (stat(path, &st) < 0) -		return -1; +		return 0;	/* no error if format does not exist */  	if (pmu_format_parse(path, format))  		return -1; @@ -80,6 +80,114 @@ static int pmu_format(char *name, struct list_head *format)  	return 0;  } +static int perf_pmu__new_alias(struct list_head *list, char *name, FILE *file) +{ +	struct perf_pmu__alias *alias; +	char buf[256]; +	int ret; + +	ret = fread(buf, 1, sizeof(buf), file); +	if (ret == 0) +		return -EINVAL; +	buf[ret] = 0; + +	alias = malloc(sizeof(*alias)); +	if (!alias) +		return -ENOMEM; + +	INIT_LIST_HEAD(&alias->terms); +	ret = parse_events_terms(&alias->terms, buf); +	if (ret) { +		free(alias); +		return ret; +	} + +	alias->name = strdup(name); +	list_add_tail(&alias->list, list); +	return 0; +} + +/* + * Process all the sysfs attributes located under the directory + * specified in 'dir' parameter. + */ +static int pmu_aliases_parse(char *dir, struct list_head *head) +{ +	struct dirent *evt_ent; +	DIR *event_dir; +	int ret = 0; + +	event_dir = opendir(dir); +	if (!event_dir) +		return -EINVAL; + +	while (!ret && (evt_ent = readdir(event_dir))) { +		char path[PATH_MAX]; +		char *name = evt_ent->d_name; +		FILE *file; + +		if (!strcmp(name, ".") || !strcmp(name, "..")) +			continue; + +		snprintf(path, PATH_MAX, "%s/%s", dir, name); + +		ret = -EINVAL; +		file = fopen(path, "r"); +		if (!file) +			break; +		ret = perf_pmu__new_alias(head, name, file); +		fclose(file); +	} + +	closedir(event_dir); +	return ret; +} + +/* + * Reading the pmu event aliases definition, which should be located at: + * /sys/bus/event_source/devices/<dev>/events as sysfs group attributes. + */ +static int pmu_aliases(char *name, struct list_head *head) +{ +	struct stat st; +	char path[PATH_MAX]; +	const char *sysfs; + +	sysfs = sysfs_find_mountpoint(); +	if (!sysfs) +		return -1; + +	snprintf(path, PATH_MAX, +		 "%s/bus/event_source/devices/%s/events", sysfs, name); + +	if (stat(path, &st) < 0) +		return -1; + +	if (pmu_aliases_parse(path, head)) +		return -1; + +	return 0; +} + +static int pmu_alias_terms(struct perf_pmu__alias *alias, +			   struct list_head *terms) +{ +	struct parse_events__term *term, *clone; +	LIST_HEAD(list); +	int ret; + +	list_for_each_entry(term, &alias->terms, list) { +		ret = parse_events__term_clone(&clone, term); +		if (ret) { +			parse_events__free_terms(&list); +			return ret; +		} +		list_add_tail(&clone->list, &list); +	} +	list_splice(&list, terms); +	return 0; +} +  /*   * Reading/parsing the default pmu type value, which should be   * located at: @@ -118,6 +226,7 @@ static struct perf_pmu *pmu_lookup(char *name)  {  	struct perf_pmu *pmu;  	LIST_HEAD(format); +	LIST_HEAD(aliases);  	__u32 type;  	/* @@ -135,10 +244,15 @@ static struct perf_pmu *pmu_lookup(char *name)  	if (!pmu)  		return NULL; +	pmu_aliases(name, &aliases); +  	INIT_LIST_HEAD(&pmu->format); +	INIT_LIST_HEAD(&pmu->aliases);  	list_splice(&format, &pmu->format); +	list_splice(&aliases, &pmu->aliases);  	pmu->name = strdup(name);  	pmu->type = type; +	list_add_tail(&pmu->list, &pmus);  	return pmu;  } @@ -279,6 +393,59 @@ int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,  	return pmu_config(&pmu->format, attr, head_terms);  } +static struct perf_pmu__alias *pmu_find_alias(struct perf_pmu *pmu, +					      struct parse_events__term *term) +{ +	struct perf_pmu__alias *alias; +	char *name; + +	if (parse_events__is_hardcoded_term(term)) +		return NULL; + +	if (term->type_val == PARSE_EVENTS__TERM_TYPE_NUM) { +		if (term->val.num != 1) +			return NULL; +		if (pmu_find_format(&pmu->format, term->config)) +			return NULL; +		name = term->config; +	} else if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR) { +		if (strcasecmp(term->config, "event")) +			return NULL; +		name = term->val.str; +	} else { +		return NULL; +	} + +	list_for_each_entry(alias, &pmu->aliases, list) { +		if (!strcasecmp(alias->name, name)) +			return alias; +	} +	return NULL; +} + +/* + * Find alias in the terms list and replace it with the terms + * defined for the alias + */ +int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms) +{ +	struct parse_events__term *term, *h; +	struct perf_pmu__alias *alias; +	int ret; + +	list_for_each_entry_safe(term, h, head_terms, list) { +		alias = pmu_find_alias(pmu, term); +		if (!alias) +			continue; +		ret = pmu_alias_terms(alias, &term->list); +		if (ret) +			return ret; +		list_del(&term->list); +		free(term); +	} +	return 0; +} +  int perf_pmu__new_format(struct list_head *list, char *name,  			 int config, unsigned long *bits)  { diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h index 68c0db965e1..535f2c5258a 100644 --- a/tools/perf/util/pmu.h +++ b/tools/perf/util/pmu.h @@ -19,17 +19,26 @@ struct perf_pmu__format {  	struct list_head list;  }; +struct perf_pmu__alias { +	char *name; +	struct list_head terms; +	struct list_head list; +}; +  struct perf_pmu {  	char *name;  	__u32 type;  	struct list_head format; +	struct list_head aliases;  	struct list_head list;  };  struct perf_pmu *perf_pmu__find(char *name);  int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,  		     struct list_head *head_terms); - +int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms); +struct list_head *perf_pmu__alias(struct perf_pmu *pmu, +				struct list_head *head_terms);  int perf_pmu_wrap(void);  void perf_pmu_error(struct list_head *list, char *name, char const *msg); diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 59dccc98b55..0dda25d82d0 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -2164,16 +2164,12 @@ int del_perf_probe_events(struct strlist *dellist)  error:  	if (kfd >= 0) { -		if (namelist) -			strlist__delete(namelist); - +		strlist__delete(namelist);  		close(kfd);  	}  	if (ufd >= 0) { -		if (unamelist) -			strlist__delete(unamelist); - +		strlist__delete(unamelist);  		close(ufd);  	} diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c index 4c1b3d72a1d..02dfa19a467 100644 --- a/tools/perf/util/scripting-engines/trace-event-perl.c +++ b/tools/perf/util/scripting-engines/trace-event-perl.c @@ -209,6 +209,10 @@ static void define_event_symbols(struct event_format *event,  		define_symbolic_values(args->symbol.symbols, ev_name,  				       cur_field_name);  		break; +	case PRINT_HEX: +		define_event_symbols(event, ev_name, args->hex.field); +		define_event_symbols(event, ev_name, args->hex.size); +		break;  	case PRINT_BSTRING:  	case PRINT_DYNAMIC_ARRAY:  	case PRINT_STRING: @@ -233,7 +237,8 @@ static void define_event_symbols(struct event_format *event,  		define_event_symbols(event, ev_name, args->next);  } -static inline struct event_format *find_cache_event(int type) +static inline +struct event_format *find_cache_event(struct pevent *pevent, int type)  {  	static char ev_name[256];  	struct event_format *event; @@ -241,7 +246,7 @@ static inline struct event_format *find_cache_event(int type)  	if (events[type])  		return events[type]; -	events[type] = event = trace_find_event(type); +	events[type] = event = pevent_find_event(pevent, type);  	if (!event)  		return NULL; @@ -252,7 +257,8 @@ static inline struct event_format *find_cache_event(int type)  	return event;  } -static void perl_process_tracepoint(union perf_event *pevent __unused, +static void perl_process_tracepoint(union perf_event *perf_event __unused, +				    struct pevent *pevent,  				    struct perf_sample *sample,  				    struct perf_evsel *evsel,  				    struct machine *machine __unused, @@ -275,13 +281,13 @@ static void perl_process_tracepoint(union perf_event *pevent __unused,  	if (evsel->attr.type != PERF_TYPE_TRACEPOINT)  		return; -	type = trace_parse_common_type(data); +	type = trace_parse_common_type(pevent, data); -	event = find_cache_event(type); +	event = find_cache_event(pevent, type);  	if (!event)  		die("ug! no event found for type %d", type); -	pid = trace_parse_common_pid(data); +	pid = trace_parse_common_pid(pevent, data);  	sprintf(handler, "%s::%s", event->system, event->name); @@ -314,7 +320,8 @@ static void perl_process_tracepoint(union perf_event *pevent __unused,  				offset = field->offset;  			XPUSHs(sv_2mortal(newSVpv((char *)data + offset, 0)));  		} else { /* FIELD_IS_NUMERIC */ -			val = read_size(data + field->offset, field->size); +			val = read_size(pevent, data + field->offset, +					field->size);  			if (field->flags & FIELD_IS_SIGNED) {  				XPUSHs(sv_2mortal(newSViv(val)));  			} else { @@ -368,14 +375,15 @@ static void perl_process_event_generic(union perf_event *pevent __unused,  	LEAVE;  } -static void perl_process_event(union perf_event *pevent, +static void perl_process_event(union perf_event *event, +			       struct pevent *pevent,  			       struct perf_sample *sample,  			       struct perf_evsel *evsel,  			       struct machine *machine,  			       struct thread *thread)  { -	perl_process_tracepoint(pevent, sample, evsel, machine, thread); -	perl_process_event_generic(pevent, sample, evsel, machine, thread); +	perl_process_tracepoint(event, pevent, sample, evsel, machine, thread); +	perl_process_event_generic(event, sample, evsel, machine, thread);  }  static void run_start_sub(void) @@ -448,7 +456,7 @@ static int perl_stop_script(void)  	return 0;  } -static int perl_generate_script(const char *outfile) +static int perl_generate_script(struct pevent *pevent, const char *outfile)  {  	struct event_format *event = NULL;  	struct format_field *f; @@ -495,7 +503,7 @@ static int perl_generate_script(const char *outfile)  	fprintf(ofp, "sub trace_begin\n{\n\t# optional\n}\n\n");  	fprintf(ofp, "sub trace_end\n{\n\t# optional\n}\n\n"); -	while ((event = trace_find_next_event(event))) { +	while ((event = trace_find_next_event(pevent, event))) {  		fprintf(ofp, "sub %s::%s\n{\n", event->system, event->name);  		fprintf(ofp, "\tmy ("); diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index acb9795286c..ce4d1b0c386 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -166,6 +166,10 @@ static void define_event_symbols(struct event_format *event,  		define_values(PRINT_SYMBOL, args->symbol.symbols, ev_name,  			      cur_field_name);  		break; +	case PRINT_HEX: +		define_event_symbols(event, ev_name, args->hex.field); +		define_event_symbols(event, ev_name, args->hex.size); +		break;  	case PRINT_STRING:  		break;  	case PRINT_TYPE: @@ -190,7 +194,8 @@ static void define_event_symbols(struct event_format *event,  		define_event_symbols(event, ev_name, args->next);  } -static inline struct event_format *find_cache_event(int type) +static inline +struct event_format *find_cache_event(struct pevent *pevent, int type)  {  	static char ev_name[256];  	struct event_format *event; @@ -198,7 +203,7 @@ static inline struct event_format *find_cache_event(int type)  	if (events[type])  		return events[type]; -	events[type] = event = trace_find_event(type); +	events[type] = event = pevent_find_event(pevent, type);  	if (!event)  		return NULL; @@ -209,7 +214,8 @@ static inline struct event_format *find_cache_event(int type)  	return event;  } -static void python_process_event(union perf_event *pevent __unused, +static void python_process_event(union perf_event *perf_event __unused, +				 struct pevent *pevent,  				 struct perf_sample *sample,  				 struct perf_evsel *evsel __unused,  				 struct machine *machine __unused, @@ -233,13 +239,13 @@ static void python_process_event(union perf_event *pevent __unused,  	if (!t)  		Py_FatalError("couldn't create Python tuple"); -	type = trace_parse_common_type(data); +	type = trace_parse_common_type(pevent, data); -	event = find_cache_event(type); +	event = find_cache_event(pevent, type);  	if (!event)  		die("ug! no event found for type %d", type); -	pid = trace_parse_common_pid(data); +	pid = trace_parse_common_pid(pevent, data);  	sprintf(handler_name, "%s__%s", event->system, event->name); @@ -284,7 +290,8 @@ static void python_process_event(union perf_event *pevent __unused,  				offset = field->offset;  			obj = PyString_FromString((char *)data + offset);  		} else { /* FIELD_IS_NUMERIC */ -			val = read_size(data + field->offset, field->size); +			val = read_size(pevent, data + field->offset, +					field->size);  			if (field->flags & FIELD_IS_SIGNED) {  				if ((long long)val >= LONG_MIN &&  				    (long long)val <= LONG_MAX) @@ -438,7 +445,7 @@ out:  	return err;  } -static int python_generate_script(const char *outfile) +static int python_generate_script(struct pevent *pevent, const char *outfile)  {  	struct event_format *event = NULL;  	struct format_field *f; @@ -487,7 +494,7 @@ static int python_generate_script(const char *outfile)  	fprintf(ofp, "def trace_end():\n");  	fprintf(ofp, "\tprint \"in trace_end\"\n\n"); -	while ((event = trace_find_next_event(event))) { +	while ((event = trace_find_next_event(pevent, event))) {  		fprintf(ofp, "def %s__%s(", event->system, event->name);  		fprintf(ofp, "event_name, ");  		fprintf(ofp, "context, "); diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 93d355d2710..8e485592ca2 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -14,6 +14,7 @@  #include "sort.h"  #include "util.h"  #include "cpumap.h" +#include "event-parse.h"  static int perf_session__open(struct perf_session *self, bool force)  { @@ -288,7 +289,7 @@ struct branch_info *machine__resolve_bstack(struct machine *self,  	return bi;  } -int machine__resolve_callchain(struct machine *self, struct perf_evsel *evsel, +int machine__resolve_callchain(struct machine *self,  			       struct thread *thread,  			       struct ip_callchain *chain,  			       struct symbol **parent) @@ -297,7 +298,12 @@ int machine__resolve_callchain(struct machine *self, struct perf_evsel *evsel,  	unsigned int i;  	int err; -	callchain_cursor_reset(&evsel->hists.callchain_cursor); +	callchain_cursor_reset(&callchain_cursor); + +	if (chain->nr > PERF_MAX_STACK_DEPTH) { +		pr_warning("corrupted callchain. skipping...\n"); +		return 0; +	}  	for (i = 0; i < chain->nr; i++) {  		u64 ip; @@ -317,7 +323,14 @@ int machine__resolve_callchain(struct machine *self, struct perf_evsel *evsel,  			case PERF_CONTEXT_USER:  				cpumode = PERF_RECORD_MISC_USER;	break;  			default: -				break; +				pr_debug("invalid callchain context: " +					 "%"PRId64"\n", (s64) ip); +				/* +				 * It seems the callchain is corrupted. +				 * Discard all. +				 */ +				callchain_cursor_reset(&callchain_cursor); +				return 0;  			}  			continue;  		} @@ -333,7 +346,7 @@ int machine__resolve_callchain(struct machine *self, struct perf_evsel *evsel,  				break;  		} -		err = callchain_cursor_append(&evsel->hists.callchain_cursor, +		err = callchain_cursor_append(&callchain_cursor,  					      ip, al.map, al.sym);  		if (err)  			return err; @@ -429,6 +442,16 @@ static void perf_tool__fill_defaults(struct perf_tool *tool)  			tool->finished_round = process_finished_round_stub;  	}  } +  +void mem_bswap_32(void *src, int byte_size) +{ +	u32 *m = src; +	while (byte_size > 0) { +		*m = bswap_32(*m); +		byte_size -= sizeof(u32); +		++m; +	} +}  void mem_bswap_64(void *src, int byte_size)  { @@ -441,37 +464,65 @@ void mem_bswap_64(void *src, int byte_size)  	}  } -static void perf_event__all64_swap(union perf_event *event) +static void swap_sample_id_all(union perf_event *event, void *data) +{ +	void *end = (void *) event + event->header.size; +	int size = end - data; + +	BUG_ON(size % sizeof(u64)); +	mem_bswap_64(data, size); +} + +static void perf_event__all64_swap(union perf_event *event, +				   bool sample_id_all __used)  {  	struct perf_event_header *hdr = &event->header;  	mem_bswap_64(hdr + 1, event->header.size - sizeof(*hdr));  } -static void perf_event__comm_swap(union perf_event *event) +static void perf_event__comm_swap(union perf_event *event, bool sample_id_all)  {  	event->comm.pid = bswap_32(event->comm.pid);  	event->comm.tid = bswap_32(event->comm.tid); + +	if (sample_id_all) { +		void *data = &event->comm.comm; + +		data += ALIGN(strlen(data) + 1, sizeof(u64)); +		swap_sample_id_all(event, data); +	}  } -static void perf_event__mmap_swap(union perf_event *event) +static void perf_event__mmap_swap(union perf_event *event, +				  bool sample_id_all)  {  	event->mmap.pid	  = bswap_32(event->mmap.pid);  	event->mmap.tid	  = bswap_32(event->mmap.tid);  	event->mmap.start = bswap_64(event->mmap.start);  	event->mmap.len	  = bswap_64(event->mmap.len);  	event->mmap.pgoff = bswap_64(event->mmap.pgoff); + +	if (sample_id_all) { +		void *data = &event->mmap.filename; + +		data += ALIGN(strlen(data) + 1, sizeof(u64)); +		swap_sample_id_all(event, data); +	}  } -static void perf_event__task_swap(union perf_event *event) +static void perf_event__task_swap(union perf_event *event, bool sample_id_all)  {  	event->fork.pid	 = bswap_32(event->fork.pid);  	event->fork.tid	 = bswap_32(event->fork.tid);  	event->fork.ppid = bswap_32(event->fork.ppid);  	event->fork.ptid = bswap_32(event->fork.ptid);  	event->fork.time = bswap_64(event->fork.time); + +	if (sample_id_all) +		swap_sample_id_all(event, &event->fork + 1);  } -static void perf_event__read_swap(union perf_event *event) +static void perf_event__read_swap(union perf_event *event, bool sample_id_all)  {  	event->read.pid		 = bswap_32(event->read.pid);  	event->read.tid		 = bswap_32(event->read.tid); @@ -479,6 +530,9 @@ static void perf_event__read_swap(union perf_event *event)  	event->read.time_enabled = bswap_64(event->read.time_enabled);  	event->read.time_running = bswap_64(event->read.time_running);  	event->read.id		 = bswap_64(event->read.id); + +	if (sample_id_all) +		swap_sample_id_all(event, &event->read + 1);  }  static u8 revbyte(u8 b) @@ -530,7 +584,8 @@ void perf_event__attr_swap(struct perf_event_attr *attr)  	swap_bitfield((u8 *) (&attr->read_format + 1), sizeof(u64));  } -static void perf_event__hdr_attr_swap(union perf_event *event) +static void perf_event__hdr_attr_swap(union perf_event *event, +				      bool sample_id_all __used)  {  	size_t size; @@ -541,18 +596,21 @@ static void perf_event__hdr_attr_swap(union perf_event *event)  	mem_bswap_64(event->attr.id, size);  } -static void perf_event__event_type_swap(union perf_event *event) +static void perf_event__event_type_swap(union perf_event *event, +					bool sample_id_all __used)  {  	event->event_type.event_type.event_id =  		bswap_64(event->event_type.event_type.event_id);  } -static void perf_event__tracing_data_swap(union perf_event *event) +static void perf_event__tracing_data_swap(union perf_event *event, +					  bool sample_id_all __used)  {  	event->tracing_data.size = bswap_32(event->tracing_data.size);  } -typedef void (*perf_event__swap_op)(union perf_event *event); +typedef void (*perf_event__swap_op)(union perf_event *event, +				    bool sample_id_all);  static perf_event__swap_op perf_event__swap_ops[] = {  	[PERF_RECORD_MMAP]		  = perf_event__mmap_swap, @@ -868,7 +926,7 @@ static struct machine *  		else  			pid = event->ip.pid; -		return perf_session__find_machine(session, pid); +		return perf_session__findnew_machine(session, pid);  	}  	return perf_session__find_host_machine(session); @@ -986,6 +1044,15 @@ static int perf_session__process_user_event(struct perf_session *session, union  	}  } +static void event_swap(union perf_event *event, bool sample_id_all) +{ +	perf_event__swap_op swap; + +	swap = perf_event__swap_ops[event->header.type]; +	if (swap) +		swap(event, sample_id_all); +} +  static int perf_session__process_event(struct perf_session *session,  				       union perf_event *event,  				       struct perf_tool *tool, @@ -994,9 +1061,8 @@ static int perf_session__process_event(struct perf_session *session,  	struct perf_sample sample;  	int ret; -	if (session->header.needs_swap && -	    perf_event__swap_ops[event->header.type]) -		perf_event__swap_ops[event->header.type](event); +	if (session->header.needs_swap) +		event_swap(event, session->sample_id_all);  	if (event->header.type >= PERF_RECORD_HEADER_MAX)  		return -EINVAL; @@ -1383,7 +1449,7 @@ size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp)  	ret += hists__fprintf_nr_events(&session->hists, fp);  	list_for_each_entry(pos, &session->evlist->entries, node) { -		ret += fprintf(fp, "%s stats:\n", event_name(pos)); +		ret += fprintf(fp, "%s stats:\n", perf_evsel__name(pos));  		ret += hists__fprintf_nr_events(&pos->hists, fp);  	} @@ -1424,11 +1490,10 @@ struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session,  }  void perf_event__print_ip(union perf_event *event, struct perf_sample *sample, -			  struct machine *machine, struct perf_evsel *evsel, -			  int print_sym, int print_dso, int print_symoffset) +			  struct machine *machine, int print_sym, +			  int print_dso, int print_symoffset)  {  	struct addr_location al; -	struct callchain_cursor *cursor = &evsel->hists.callchain_cursor;  	struct callchain_cursor_node *node;  	if (perf_event__preprocess_sample(event, machine, &al, sample, @@ -1440,16 +1505,16 @@ void perf_event__print_ip(union perf_event *event, struct perf_sample *sample,  	if (symbol_conf.use_callchain && sample->callchain) { -		if (machine__resolve_callchain(machine, evsel, al.thread, +		if (machine__resolve_callchain(machine, al.thread,  						sample->callchain, NULL) != 0) {  			if (verbose)  				error("Failed to resolve callchain. Skipping\n");  			return;  		} -		callchain_cursor_commit(cursor); +		callchain_cursor_commit(&callchain_cursor);  		while (1) { -			node = callchain_cursor_current(cursor); +			node = callchain_cursor_current(&callchain_cursor);  			if (!node)  				break; @@ -1460,12 +1525,12 @@ void perf_event__print_ip(union perf_event *event, struct perf_sample *sample,  			}  			if (print_dso) {  				printf(" ("); -				map__fprintf_dsoname(al.map, stdout); +				map__fprintf_dsoname(node->map, stdout);  				printf(")");  			}  			printf("\n"); -			callchain_cursor_advance(cursor); +			callchain_cursor_advance(&callchain_cursor);  		}  	} else { @@ -1546,3 +1611,58 @@ void perf_session__fprintf_info(struct perf_session *session, FILE *fp,  	perf_header__fprintf_info(session, fp, full);  	fprintf(fp, "# ========\n#\n");  } + + +int __perf_session__set_tracepoints_handlers(struct perf_session *session, +					     const struct perf_evsel_str_handler *assocs, +					     size_t nr_assocs) +{ +	struct perf_evlist *evlist = session->evlist; +	struct event_format *format; +	struct perf_evsel *evsel; +	char *tracepoint, *name; +	size_t i; +	int err; + +	for (i = 0; i < nr_assocs; i++) { +		err = -ENOMEM; +		tracepoint = strdup(assocs[i].name); +		if (tracepoint == NULL) +			goto out; + +		err = -ENOENT; +		name = strchr(tracepoint, ':'); +		if (name == NULL) +			goto out_free; + +		*name++ = '\0'; +		format = pevent_find_event_by_name(session->pevent, +						   tracepoint, name); +		if (format == NULL) { +			/* +			 * Adding a handler for an event not in the session, +			 * just ignore it. +			 */ +			goto next; +		} + +		evsel = perf_evlist__find_tracepoint_by_id(evlist, format->id); +		if (evsel == NULL) +			goto next; + +		err = -EEXIST; +		if (evsel->handler.func != NULL) +			goto out_free; +		evsel->handler.func = assocs[i].handler; +next: +		free(tracepoint); +	} + +	err = 0; +out: +	return err; + +out_free: +	free(tracepoint); +	goto out; +} diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 7a5434c0056..7c435bde6eb 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -33,6 +33,7 @@ struct perf_session {  	struct machine		host_machine;  	struct rb_root		machines;  	struct perf_evlist	*evlist; +	struct pevent		*pevent;  	/*  	 * FIXME: Need to split this up further, we need global  	 *	  stats + per event stats. 'perf diff' also needs @@ -80,6 +81,7 @@ struct branch_info *machine__resolve_bstack(struct machine *self,  bool perf_session__has_traces(struct perf_session *self, const char *msg);  void mem_bswap_64(void *src, int byte_size); +void mem_bswap_32(void *src, int byte_size);  void perf_event__attr_swap(struct perf_event_attr *attr);  int perf_session__create_kernel_maps(struct perf_session *self); @@ -150,11 +152,20 @@ struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session,  					    unsigned int type);  void perf_event__print_ip(union perf_event *event, struct perf_sample *sample, -			  struct machine *machine, struct perf_evsel *evsel, -			  int print_sym, int print_dso, int print_symoffset); +			  struct machine *machine, int print_sym, +			  int print_dso, int print_symoffset);  int perf_session__cpu_bitmap(struct perf_session *session,  			     const char *cpu_list, unsigned long *cpu_bitmap);  void perf_session__fprintf_info(struct perf_session *s, FILE *fp, bool full); + +struct perf_evsel_str_handler; + +int __perf_session__set_tracepoints_handlers(struct perf_session *session, +					     const struct perf_evsel_str_handler *assocs, +					     size_t nr_assocs); + +#define perf_session__set_tracepoints_handlers(session, array) \ +	__perf_session__set_tracepoints_handlers(session, array, ARRAY_SIZE(array))  #endif /* __PERF_SESSION_H */ diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index a27237430c5..0f5a0a496bc 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -241,6 +241,54 @@ struct sort_entry sort_sym = {  	.se_width_idx	= HISTC_SYMBOL,  }; +/* --sort srcline */ + +static int64_t +sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right) +{ +	return (int64_t)(right->ip - left->ip); +} + +static int hist_entry__srcline_snprintf(struct hist_entry *self, char *bf, +				   size_t size, unsigned int width __used) +{ +	FILE *fp; +	char cmd[PATH_MAX + 2], *path = self->srcline, *nl; +	size_t line_len; + +	if (path != NULL) +		goto out_path; + +	snprintf(cmd, sizeof(cmd), "addr2line -e %s %016" PRIx64, +		 self->ms.map->dso->long_name, self->ip); +	fp = popen(cmd, "r"); +	if (!fp) +		goto out_ip; + +	if (getline(&path, &line_len, fp) < 0 || !line_len) +		goto out_ip; +	fclose(fp); +	self->srcline = strdup(path); +	if (self->srcline == NULL) +		goto out_ip; + +	nl = strchr(self->srcline, '\n'); +	if (nl != NULL) +		*nl = '\0'; +	path = self->srcline; +out_path: +	return repsep_snprintf(bf, size, "%s", path); +out_ip: +	return repsep_snprintf(bf, size, "%-#*llx", BITS_PER_LONG / 4, self->ip); +} + +struct sort_entry sort_srcline = { +	.se_header	= "Source:Line", +	.se_cmp		= sort__srcline_cmp, +	.se_snprintf	= hist_entry__srcline_snprintf, +	.se_width_idx	= HISTC_SRCLINE, +}; +  /* --sort parent */  static int64_t @@ -439,6 +487,7 @@ static struct sort_dimension sort_dimensions[] = {  	DIM(SORT_PARENT, "parent", sort_parent),  	DIM(SORT_CPU, "cpu", sort_cpu),  	DIM(SORT_MISPREDICT, "mispredict", sort_mispredict), +	DIM(SORT_SRCLINE, "srcline", sort_srcline),  };  int sort_dimension__add(const char *tok) diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 472aa5a63a5..e724b26acd5 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -71,6 +71,7 @@ struct hist_entry {  	char			level;  	bool			used;  	u8			filtered; +	char			*srcline;  	struct symbol		*parent;  	union {  		unsigned long	  position; @@ -93,6 +94,7 @@ enum sort_type {  	SORT_SYM_FROM,  	SORT_SYM_TO,  	SORT_MISPREDICT, +	SORT_SRCLINE,  };  /* diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c index d5836382ff2..199bc4d8905 100644 --- a/tools/perf/util/string.c +++ b/tools/perf/util/string.c @@ -313,3 +313,25 @@ int strtailcmp(const char *s1, const char *s2)  	return 0;  } +/** + * rtrim - Removes trailing whitespace from @s. + * @s: The string to be stripped. + * + * Note that the first trailing whitespace is replaced with a %NUL-terminator + * in the given string @s. Returns @s. + */ +char *rtrim(char *s) +{ +	size_t size = strlen(s); +	char *end; + +	if (!size) +		return s; + +	end = s + size - 1; +	while (end >= s && isspace(*end)) +		end--; +	*(end + 1) = '\0'; + +	return s; +} diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index e2ba8858f3e..50958bbeb26 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -323,6 +323,7 @@ struct dso *dso__new(const char *name)  		dso->sorted_by_name = 0;  		dso->has_build_id = 0;  		dso->kernel = DSO_TYPE_USER; +		dso->needs_swap = DSO_SWAP__UNSET;  		INIT_LIST_HEAD(&dso->node);  	} @@ -1156,6 +1157,33 @@ static size_t elf_addr_to_index(Elf *elf, GElf_Addr addr)  	return -1;  } +static int dso__swap_init(struct dso *dso, unsigned char eidata) +{ +	static unsigned int const endian = 1; + +	dso->needs_swap = DSO_SWAP__NO; + +	switch (eidata) { +	case ELFDATA2LSB: +		/* We are big endian, DSO is little endian. */ +		if (*(unsigned char const *)&endian != 1) +			dso->needs_swap = DSO_SWAP__YES; +		break; + +	case ELFDATA2MSB: +		/* We are little endian, DSO is big endian. */ +		if (*(unsigned char const *)&endian != 0) +			dso->needs_swap = DSO_SWAP__YES; +		break; + +	default: +		pr_err("unrecognized DSO data encoding %d\n", eidata); +		return -EINVAL; +	} + +	return 0; +} +  static int dso__load_sym(struct dso *dso, struct map *map, const char *name,  			 int fd, symbol_filter_t filter, int kmodule,  			 int want_symtab) @@ -1187,6 +1215,9 @@ static int dso__load_sym(struct dso *dso, struct map *map, const char *name,  		goto out_elf_end;  	} +	if (dso__swap_init(dso, ehdr.e_ident[EI_DATA])) +		goto out_elf_end; +  	/* Always reject images with a mismatched build-id: */  	if (dso->has_build_id) {  		u8 build_id[BUILD_ID_SIZE]; @@ -1272,7 +1303,7 @@ static int dso__load_sym(struct dso *dso, struct map *map, const char *name,  		if (opdsec && sym.st_shndx == opdidx) {  			u32 offset = sym.st_value - opdshdr.sh_addr;  			u64 *opd = opddata->d_buf + offset; -			sym.st_value = *opd; +			sym.st_value = DSO__SWAP(dso, u64, *opd);  			sym.st_shndx = elf_addr_to_index(elf, sym.st_value);  		} @@ -1447,14 +1478,31 @@ static int elf_read_build_id(Elf *elf, void *bf, size_t size)  		goto out;  	} -	sec = elf_section_by_name(elf, &ehdr, &shdr, -				  ".note.gnu.build-id", NULL); -	if (sec == NULL) { +	/* +	 * Check following sections for notes: +	 *   '.note.gnu.build-id' +	 *   '.notes' +	 *   '.note' (VDSO specific) +	 */ +	do { +		sec = elf_section_by_name(elf, &ehdr, &shdr, +					  ".note.gnu.build-id", NULL); +		if (sec) +			break; +  		sec = elf_section_by_name(elf, &ehdr, &shdr,  					  ".notes", NULL); -		if (sec == NULL) -			goto out; -	} +		if (sec) +			break; + +		sec = elf_section_by_name(elf, &ehdr, &shdr, +					  ".note", NULL); +		if (sec) +			break; + +		return err; + +	} while (0);  	data = elf_getdata(sec, NULL);  	if (data == NULL) @@ -1559,11 +1607,62 @@ out:  	return err;  } +static int filename__read_debuglink(const char *filename, +				    char *debuglink, size_t size) +{ +	int fd, err = -1; +	Elf *elf; +	GElf_Ehdr ehdr; +	GElf_Shdr shdr; +	Elf_Data *data; +	Elf_Scn *sec; +	Elf_Kind ek; + +	fd = open(filename, O_RDONLY); +	if (fd < 0) +		goto out; + +	elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); +	if (elf == NULL) { +		pr_debug2("%s: cannot read %s ELF file.\n", __func__, filename); +		goto out_close; +	} + +	ek = elf_kind(elf); +	if (ek != ELF_K_ELF) +		goto out_close; + +	if (gelf_getehdr(elf, &ehdr) == NULL) { +		pr_err("%s: cannot get elf header.\n", __func__); +		goto out_close; +	} + +	sec = elf_section_by_name(elf, &ehdr, &shdr, +				  ".gnu_debuglink", NULL); +	if (sec == NULL) +		goto out_close; + +	data = elf_getdata(sec, NULL); +	if (data == NULL) +		goto out_close; + +	/* the start of this section is a zero-terminated string */ +	strncpy(debuglink, data->d_buf, size); + +	elf_end(elf); + +out_close: +	close(fd); +out: +	return err; +} +  char dso__symtab_origin(const struct dso *dso)  {  	static const char origin[] = {  		[SYMTAB__KALLSYMS]	      = 'k',  		[SYMTAB__JAVA_JIT]	      = 'j', +		[SYMTAB__DEBUGLINK]           = 'l',  		[SYMTAB__BUILD_ID_CACHE]      = 'B',  		[SYMTAB__FEDORA_DEBUGINFO]    = 'f',  		[SYMTAB__UBUNTU_DEBUGINFO]    = 'u', @@ -1631,10 +1730,22 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter)  	 */  	want_symtab = 1;  restart: -	for (dso->symtab_type = SYMTAB__BUILD_ID_CACHE; +	for (dso->symtab_type = SYMTAB__DEBUGLINK;  	     dso->symtab_type != SYMTAB__NOT_FOUND;  	     dso->symtab_type++) {  		switch (dso->symtab_type) { +		case SYMTAB__DEBUGLINK: { +			char *debuglink; +			strncpy(name, dso->long_name, size); +			debuglink = name + dso->long_name_len; +			while (debuglink != name && *debuglink != '/') +				debuglink--; +			if (*debuglink == '/') +				debuglink++; +			filename__read_debuglink(dso->long_name, debuglink, +						 size - (debuglink - name)); +			} +			break;  		case SYMTAB__BUILD_ID_CACHE:  			/* skip the locally configured cache if a symfs is given */  			if (symbol_conf.symfs[0] || @@ -2786,8 +2897,11 @@ int machine__load_vmlinux_path(struct machine *machine, enum map_type type,  struct map *dso__new_map(const char *name)  { +	struct map *map = NULL;  	struct dso *dso = dso__new(name); -	struct map *map = map__new2(0, dso, MAP__FUNCTION); + +	if (dso) +		map = map__new2(0, dso, MAP__FUNCTION);  	return map;  } diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 5649d63798c..a884b99017f 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -9,6 +9,7 @@  #include <linux/list.h>  #include <linux/rbtree.h>  #include <stdio.h> +#include <byteswap.h>  #ifdef HAVE_CPLUS_DEMANGLE  extern char *cplus_demangle(const char *, int); @@ -160,11 +161,18 @@ enum dso_kernel_type {  	DSO_TYPE_GUEST_KERNEL  }; +enum dso_swap_type { +	DSO_SWAP__UNSET, +	DSO_SWAP__NO, +	DSO_SWAP__YES, +}; +  struct dso {  	struct list_head node;  	struct rb_root	 symbols[MAP__NR_TYPES];  	struct rb_root	 symbol_names[MAP__NR_TYPES];  	enum dso_kernel_type	kernel; +	enum dso_swap_type	needs_swap;  	u8		 adjust_symbols:1;  	u8		 has_build_id:1;  	u8		 hit:1; @@ -182,6 +190,28 @@ struct dso {  	char		 name[0];  }; +#define DSO__SWAP(dso, type, val)			\ +({							\ +	type ____r = val;				\ +	BUG_ON(dso->needs_swap == DSO_SWAP__UNSET);	\ +	if (dso->needs_swap == DSO_SWAP__YES) {		\ +		switch (sizeof(____r)) {		\ +		case 2:					\ +			____r = bswap_16(val);		\ +			break;				\ +		case 4:					\ +			____r = bswap_32(val);		\ +			break;				\ +		case 8:					\ +			____r = bswap_64(val);		\ +			break;				\ +		default:				\ +			BUG_ON(1);			\ +		}					\ +	}						\ +	____r;						\ +}) +  struct dso *dso__new(const char *name);  void dso__delete(struct dso *dso); @@ -227,6 +257,7 @@ enum symtab_type {  	SYMTAB__KALLSYMS = 0,  	SYMTAB__GUEST_KALLSYMS,  	SYMTAB__JAVA_JIT, +	SYMTAB__DEBUGLINK,  	SYMTAB__BUILD_ID_CACHE,  	SYMTAB__FEDORA_DEBUGINFO,  	SYMTAB__UBUNTU_DEBUGINFO, diff --git a/tools/perf/util/thread_map.c b/tools/perf/util/thread_map.c index 84d9bd78200..9b5f856cc28 100644 --- a/tools/perf/util/thread_map.c +++ b/tools/perf/util/thread_map.c @@ -188,28 +188,27 @@ static struct thread_map *thread_map__new_by_pid_str(const char *pid_str)  		nt = realloc(threads, (sizeof(*threads) +  				       sizeof(pid_t) * total_tasks));  		if (nt == NULL) -			goto out_free_threads; +			goto out_free_namelist;  		threads = nt; -		if (threads) { -			for (i = 0; i < items; i++) -				threads->map[j++] = atoi(namelist[i]->d_name); -			threads->nr = total_tasks; -		} - -		for (i = 0; i < items; i++) +		for (i = 0; i < items; i++) { +			threads->map[j++] = atoi(namelist[i]->d_name);  			free(namelist[i]); +		} +		threads->nr = total_tasks;  		free(namelist); - -		if (!threads) -			break;  	}  out:  	strlist__delete(slist);  	return threads; +out_free_namelist: +	for (i = 0; i < items; i++) +		free(namelist[i]); +	free(namelist); +  out_free_threads:  	free(threads);  	threads = NULL; diff --git a/tools/perf/util/top.c b/tools/perf/util/top.c index abe0e8e9506..7eeebcee291 100644 --- a/tools/perf/util/top.c +++ b/tools/perf/util/top.c @@ -65,7 +65,7 @@ size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size)  				top->freq ? "Hz" : "");  	} -	ret += SNPRINTF(bf + ret, size - ret, "%s", event_name(top->sym_evsel)); +	ret += SNPRINTF(bf + ret, size - ret, "%s", perf_evsel__name(top->sym_evsel));  	ret += SNPRINTF(bf + ret, size - ret, "], "); diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c index df2fddbf0cd..0715c843c2e 100644 --- a/tools/perf/util/trace-event-parse.c +++ b/tools/perf/util/trace-event-parse.c @@ -32,29 +32,25 @@ int header_page_size_size;  int header_page_ts_size;  int header_page_data_offset; -struct pevent *perf_pevent; -static struct pevent *pevent; -  bool latency_format; -int read_trace_init(int file_bigendian, int host_bigendian) +struct pevent *read_trace_init(int file_bigendian, int host_bigendian)  { -	if (pevent) -		return 0; - -	perf_pevent = pevent_alloc(); -	pevent = perf_pevent; +	struct pevent *pevent = pevent_alloc(); -	pevent_set_flag(pevent, PEVENT_NSEC_OUTPUT); -	pevent_set_file_bigendian(pevent, file_bigendian); -	pevent_set_host_bigendian(pevent, host_bigendian); +	if (pevent != NULL) { +		pevent_set_flag(pevent, PEVENT_NSEC_OUTPUT); +		pevent_set_file_bigendian(pevent, file_bigendian); +		pevent_set_host_bigendian(pevent, host_bigendian); +	} -	return 0; +	return pevent;  }  static int get_common_field(struct scripting_context *context,  			    int *offset, int *size, const char *type)  { +	struct pevent *pevent = context->pevent;  	struct event_format *event;  	struct format_field *field; @@ -150,7 +146,7 @@ void *raw_field_ptr(struct event_format *event, const char *name, void *data)  	return data + field->offset;  } -int trace_parse_common_type(void *data) +int trace_parse_common_type(struct pevent *pevent, void *data)  {  	struct pevent_record record; @@ -158,7 +154,7 @@ int trace_parse_common_type(void *data)  	return pevent_data_type(pevent, &record);  } -int trace_parse_common_pid(void *data) +int trace_parse_common_pid(struct pevent *pevent, void *data)  {  	struct pevent_record record; @@ -166,27 +162,21 @@ int trace_parse_common_pid(void *data)  	return pevent_data_pid(pevent, &record);  } -unsigned long long read_size(void *ptr, int size) +unsigned long long read_size(struct pevent *pevent, void *ptr, int size)  {  	return pevent_read_number(pevent, ptr, size);  } -struct event_format *trace_find_event(int type) -{ -	return pevent_find_event(pevent, type); -} - - -void print_trace_event(int cpu, void *data, int size) +void print_trace_event(struct pevent *pevent, int cpu, void *data, int size)  {  	struct event_format *event;  	struct pevent_record record;  	struct trace_seq s;  	int type; -	type = trace_parse_common_type(data); +	type = trace_parse_common_type(pevent, data); -	event = trace_find_event(type); +	event = pevent_find_event(pevent, type);  	if (!event) {  		warning("ug! no event found for type %d", type);  		return; @@ -198,13 +188,12 @@ void print_trace_event(int cpu, void *data, int size)  	record.data = data;  	trace_seq_init(&s); -	pevent_print_event(pevent, &s, &record); +	pevent_event_info(&s, event, &record);  	trace_seq_do_printf(&s); -	printf("\n");  } -void print_event(int cpu, void *data, int size, unsigned long long nsecs, -		  char *comm) +void print_event(struct pevent *pevent, int cpu, void *data, int size, +		 unsigned long long nsecs, char *comm)  {  	struct pevent_record record;  	struct trace_seq s; @@ -227,7 +216,8 @@ void print_event(int cpu, void *data, int size, unsigned long long nsecs,  	printf("\n");  } -void parse_proc_kallsyms(char *file, unsigned int size __unused) +void parse_proc_kallsyms(struct pevent *pevent, +			 char *file, unsigned int size __unused)  {  	unsigned long long addr;  	char *func; @@ -258,7 +248,8 @@ void parse_proc_kallsyms(char *file, unsigned int size __unused)  	}  } -void parse_ftrace_printk(char *file, unsigned int size __unused) +void parse_ftrace_printk(struct pevent *pevent, +			 char *file, unsigned int size __unused)  {  	unsigned long long addr;  	char *printk; @@ -282,17 +273,19 @@ void parse_ftrace_printk(char *file, unsigned int size __unused)  	}  } -int parse_ftrace_file(char *buf, unsigned long size) +int parse_ftrace_file(struct pevent *pevent, char *buf, unsigned long size)  {  	return pevent_parse_event(pevent, buf, size, "ftrace");  } -int parse_event_file(char *buf, unsigned long size, char *sys) +int parse_event_file(struct pevent *pevent, +		     char *buf, unsigned long size, char *sys)  {  	return pevent_parse_event(pevent, buf, size, sys);  } -struct event_format *trace_find_next_event(struct event_format *event) +struct event_format *trace_find_next_event(struct pevent *pevent, +					   struct event_format *event)  {  	static int idx; diff --git a/tools/perf/util/trace-event-read.c b/tools/perf/util/trace-event-read.c index f097e0dd6c5..719ed74a856 100644 --- a/tools/perf/util/trace-event-read.c +++ b/tools/perf/util/trace-event-read.c @@ -114,20 +114,20 @@ static void skip(int size)  	};  } -static unsigned int read4(void) +static unsigned int read4(struct pevent *pevent)  {  	unsigned int data;  	read_or_die(&data, 4); -	return __data2host4(perf_pevent, data); +	return __data2host4(pevent, data);  } -static unsigned long long read8(void) +static unsigned long long read8(struct pevent *pevent)  {  	unsigned long long data;  	read_or_die(&data, 8); -	return __data2host8(perf_pevent, data); +	return __data2host8(pevent, data);  }  static char *read_string(void) @@ -168,12 +168,12 @@ static char *read_string(void)  	return str;  } -static void read_proc_kallsyms(void) +static void read_proc_kallsyms(struct pevent *pevent)  {  	unsigned int size;  	char *buf; -	size = read4(); +	size = read4(pevent);  	if (!size)  		return; @@ -181,29 +181,29 @@ static void read_proc_kallsyms(void)  	read_or_die(buf, size);  	buf[size] = '\0'; -	parse_proc_kallsyms(buf, size); +	parse_proc_kallsyms(pevent, buf, size);  	free(buf);  } -static void read_ftrace_printk(void) +static void read_ftrace_printk(struct pevent *pevent)  {  	unsigned int size;  	char *buf; -	size = read4(); +	size = read4(pevent);  	if (!size)  		return;  	buf = malloc_or_die(size);  	read_or_die(buf, size); -	parse_ftrace_printk(buf, size); +	parse_ftrace_printk(pevent, buf, size);  	free(buf);  } -static void read_header_files(void) +static void read_header_files(struct pevent *pevent)  {  	unsigned long long size;  	char *header_event; @@ -214,7 +214,7 @@ static void read_header_files(void)  	if (memcmp(buf, "header_page", 12) != 0)  		die("did not read header page"); -	size = read8(); +	size = read8(pevent);  	skip(size);  	/* @@ -227,47 +227,48 @@ static void read_header_files(void)  	if (memcmp(buf, "header_event", 13) != 0)  		die("did not read header event"); -	size = read8(); +	size = read8(pevent);  	header_event = malloc_or_die(size);  	read_or_die(header_event, size);  	free(header_event);  } -static void read_ftrace_file(unsigned long long size) +static void read_ftrace_file(struct pevent *pevent, unsigned long long size)  {  	char *buf;  	buf = malloc_or_die(size);  	read_or_die(buf, size); -	parse_ftrace_file(buf, size); +	parse_ftrace_file(pevent, buf, size);  	free(buf);  } -static void read_event_file(char *sys, unsigned long long size) +static void read_event_file(struct pevent *pevent, char *sys, +			    unsigned long long size)  {  	char *buf;  	buf = malloc_or_die(size);  	read_or_die(buf, size); -	parse_event_file(buf, size, sys); +	parse_event_file(pevent, buf, size, sys);  	free(buf);  } -static void read_ftrace_files(void) +static void read_ftrace_files(struct pevent *pevent)  {  	unsigned long long size;  	int count;  	int i; -	count = read4(); +	count = read4(pevent);  	for (i = 0; i < count; i++) { -		size = read8(); -		read_ftrace_file(size); +		size = read8(pevent); +		read_ftrace_file(pevent, size);  	}  } -static void read_event_files(void) +static void read_event_files(struct pevent *pevent)  {  	unsigned long long size;  	char *sys; @@ -275,15 +276,15 @@ static void read_event_files(void)  	int count;  	int i,x; -	systems = read4(); +	systems = read4(pevent);  	for (i = 0; i < systems; i++) {  		sys = read_string(); -		count = read4(); +		count = read4(pevent);  		for (x=0; x < count; x++) { -			size = read8(); -			read_event_file(sys, size); +			size = read8(pevent); +			read_event_file(pevent, sys, size);  		}  	}  } @@ -377,7 +378,7 @@ static int calc_index(void *ptr, int cpu)  	return (unsigned long)ptr - (unsigned long)cpu_data[cpu].page;  } -struct pevent_record *trace_peek_data(int cpu) +struct pevent_record *trace_peek_data(struct pevent *pevent, int cpu)  {  	struct pevent_record *data;  	void *page = cpu_data[cpu].page; @@ -399,15 +400,15 @@ struct pevent_record *trace_peek_data(int cpu)  		/* FIXME: handle header page */  		if (header_page_ts_size != 8)  			die("expected a long long type for timestamp"); -		cpu_data[cpu].timestamp = data2host8(perf_pevent, ptr); +		cpu_data[cpu].timestamp = data2host8(pevent, ptr);  		ptr += 8;  		switch (header_page_size_size) {  		case 4: -			cpu_data[cpu].page_size = data2host4(perf_pevent, ptr); +			cpu_data[cpu].page_size = data2host4(pevent, ptr);  			ptr += 4;  			break;  		case 8: -			cpu_data[cpu].page_size = data2host8(perf_pevent, ptr); +			cpu_data[cpu].page_size = data2host8(pevent, ptr);  			ptr += 8;  			break;  		default: @@ -421,10 +422,10 @@ read_again:  	if (idx >= cpu_data[cpu].page_size) {  		get_next_page(cpu); -		return trace_peek_data(cpu); +		return trace_peek_data(pevent, cpu);  	} -	type_len_ts = data2host4(perf_pevent, ptr); +	type_len_ts = data2host4(pevent, ptr);  	ptr += 4;  	type_len = type_len4host(type_len_ts); @@ -434,14 +435,14 @@ read_again:  	case RINGBUF_TYPE_PADDING:  		if (!delta)  			die("error, hit unexpected end of page"); -		length = data2host4(perf_pevent, ptr); +		length = data2host4(pevent, ptr);  		ptr += 4;  		length *= 4;  		ptr += length;  		goto read_again;  	case RINGBUF_TYPE_TIME_EXTEND: -		extend = data2host4(perf_pevent, ptr); +		extend = data2host4(pevent, ptr);  		ptr += 4;  		extend <<= TS_SHIFT;  		extend += delta; @@ -452,7 +453,7 @@ read_again:  		ptr += 12;  		break;  	case 0: -		length = data2host4(perf_pevent, ptr); +		length = data2host4(pevent, ptr);  		ptr += 4;  		die("here! length=%d", length);  		break; @@ -477,17 +478,17 @@ read_again:  	return data;  } -struct pevent_record *trace_read_data(int cpu) +struct pevent_record *trace_read_data(struct pevent *pevent, int cpu)  {  	struct pevent_record *data; -	data = trace_peek_data(cpu); +	data = trace_peek_data(pevent, cpu);  	cpu_data[cpu].next = NULL;  	return data;  } -ssize_t trace_report(int fd, bool __repipe) +ssize_t trace_report(int fd, struct pevent **ppevent, bool __repipe)  {  	char buf[BUFSIZ];  	char test[] = { 23, 8, 68 }; @@ -519,30 +520,32 @@ ssize_t trace_report(int fd, bool __repipe)  	file_bigendian = buf[0];  	host_bigendian = bigendian(); -	read_trace_init(file_bigendian, host_bigendian); +	*ppevent = read_trace_init(file_bigendian, host_bigendian); +	if (*ppevent == NULL) +		die("read_trace_init failed");  	read_or_die(buf, 1);  	long_size = buf[0]; -	page_size = read4(); +	page_size = read4(*ppevent); -	read_header_files(); +	read_header_files(*ppevent); -	read_ftrace_files(); -	read_event_files(); -	read_proc_kallsyms(); -	read_ftrace_printk(); +	read_ftrace_files(*ppevent); +	read_event_files(*ppevent); +	read_proc_kallsyms(*ppevent); +	read_ftrace_printk(*ppevent);  	size = calc_data_size - 1;  	calc_data_size = 0;  	repipe = false;  	if (show_funcs) { -		pevent_print_funcs(perf_pevent); +		pevent_print_funcs(*ppevent);  		return size;  	}  	if (show_printk) { -		pevent_print_printk(perf_pevent); +		pevent_print_printk(*ppevent);  		return size;  	} diff --git a/tools/perf/util/trace-event-scripting.c b/tools/perf/util/trace-event-scripting.c index 18ae6c1831d..474aa7a7df4 100644 --- a/tools/perf/util/trace-event-scripting.c +++ b/tools/perf/util/trace-event-scripting.c @@ -36,6 +36,7 @@ static int stop_script_unsupported(void)  }  static void process_event_unsupported(union perf_event *event __unused, +				      struct pevent *pevent __unused,  				      struct perf_sample *sample __unused,  				      struct perf_evsel *evsel __unused,  				      struct machine *machine __unused, @@ -61,7 +62,8 @@ static int python_start_script_unsupported(const char *script __unused,  	return -1;  } -static int python_generate_script_unsupported(const char *outfile __unused) +static int python_generate_script_unsupported(struct pevent *pevent __unused, +					      const char *outfile __unused)  {  	print_python_unsupported_msg(); @@ -122,7 +124,8 @@ static int perl_start_script_unsupported(const char *script __unused,  	return -1;  } -static int perl_generate_script_unsupported(const char *outfile __unused) +static int perl_generate_script_unsupported(struct pevent *pevent __unused, +					    const char *outfile __unused)  {  	print_perl_unsupported_msg(); diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h index 639852ac111..8fef1d6687b 100644 --- a/tools/perf/util/trace-event.h +++ b/tools/perf/util/trace-event.h @@ -8,6 +8,7 @@  struct machine;  struct perf_sample;  union perf_event; +struct perf_tool;  struct thread;  extern int header_page_size_size; @@ -29,35 +30,36 @@ enum {  int bigendian(void); -int read_trace_init(int file_bigendian, int host_bigendian); -void print_trace_event(int cpu, void *data, int size); +struct pevent *read_trace_init(int file_bigendian, int host_bigendian); +void print_trace_event(struct pevent *pevent, int cpu, void *data, int size); -void print_event(int cpu, void *data, int size, unsigned long long nsecs, -		  char *comm); +void print_event(struct pevent *pevent, int cpu, void *data, int size, +		 unsigned long long nsecs, char *comm); -int parse_ftrace_file(char *buf, unsigned long size); -int parse_event_file(char *buf, unsigned long size, char *sys); +int parse_ftrace_file(struct pevent *pevent, char *buf, unsigned long size); +int parse_event_file(struct pevent *pevent, +		     char *buf, unsigned long size, char *sys); -struct pevent_record *trace_peek_data(int cpu); -struct event_format *trace_find_event(int type); +struct pevent_record *trace_peek_data(struct pevent *pevent, int cpu);  unsigned long long  raw_field_value(struct event_format *event, const char *name, void *data);  void *raw_field_ptr(struct event_format *event, const char *name, void *data); -void parse_proc_kallsyms(char *file, unsigned int size __unused); -void parse_ftrace_printk(char *file, unsigned int size __unused); +void parse_proc_kallsyms(struct pevent *pevent, char *file, unsigned int size); +void parse_ftrace_printk(struct pevent *pevent, char *file, unsigned int size); -ssize_t trace_report(int fd, bool repipe); +ssize_t trace_report(int fd, struct pevent **pevent, bool repipe); -int trace_parse_common_type(void *data); -int trace_parse_common_pid(void *data); +int trace_parse_common_type(struct pevent *pevent, void *data); +int trace_parse_common_pid(struct pevent *pevent, void *data); -struct event_format *trace_find_next_event(struct event_format *event); -unsigned long long read_size(void *ptr, int size); +struct event_format *trace_find_next_event(struct pevent *pevent, +					   struct event_format *event); +unsigned long long read_size(struct pevent *pevent, void *ptr, int size);  unsigned long long eval_flag(const char *flag); -struct pevent_record *trace_read_data(int cpu); +struct pevent_record *trace_read_data(struct pevent *pevent, int cpu);  int read_tracing_data(int fd, struct list_head *pattrs);  struct tracing_data { @@ -77,11 +79,12 @@ struct scripting_ops {  	int (*start_script) (const char *script, int argc, const char **argv);  	int (*stop_script) (void);  	void (*process_event) (union perf_event *event, +			       struct pevent *pevent,  			       struct perf_sample *sample,  			       struct perf_evsel *evsel,  			       struct machine *machine,  			       struct thread *thread); -	int (*generate_script) (const char *outfile); +	int (*generate_script) (struct pevent *pevent, const char *outfile);  };  int script_spec_register(const char *spec, struct scripting_ops *ops); @@ -90,6 +93,7 @@ void setup_perl_scripting(void);  void setup_python_scripting(void);  struct scripting_context { +	struct pevent *pevent;  	void *event_data;  }; diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 2daaedb83d8..b13c7331eaf 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -264,4 +264,6 @@ bool is_power_of_2(unsigned long n)  size_t hex_width(u64 v); +char *rtrim(char *s); +  #endif  |