diff options
Diffstat (limited to 'tools/perf/ui')
| -rw-r--r-- | tools/perf/ui/browser.c | 10 | ||||
| -rw-r--r-- | tools/perf/ui/browsers/Build | 1 | ||||
| -rw-r--r-- | tools/perf/ui/browsers/annotate.c | 2 | ||||
| -rw-r--r-- | tools/perf/ui/browsers/hists.c | 141 | ||||
| -rw-r--r-- | tools/perf/ui/browsers/res_sample.c | 91 | ||||
| -rw-r--r-- | tools/perf/ui/browsers/scripts.c | 274 | 
6 files changed, 362 insertions, 157 deletions
| diff --git a/tools/perf/ui/browser.c b/tools/perf/ui/browser.c index 4f75561424ed..4ad37d8c7d6a 100644 --- a/tools/perf/ui/browser.c +++ b/tools/perf/ui/browser.c @@ -611,14 +611,16 @@ void ui_browser__argv_seek(struct ui_browser *browser, off_t offset, int whence)  		browser->top = browser->entries;  		break;  	case SEEK_CUR: -		browser->top = browser->top + browser->top_idx + offset; +		browser->top = (char **)browser->top + offset;  		break;  	case SEEK_END: -		browser->top = browser->top + browser->nr_entries - 1 + offset; +		browser->top = (char **)browser->entries + browser->nr_entries - 1 + offset;  		break;  	default:  		return;  	} +	assert((char **)browser->top < (char **)browser->entries + browser->nr_entries); +	assert((char **)browser->top >= (char **)browser->entries);  }  unsigned int ui_browser__argv_refresh(struct ui_browser *browser) @@ -630,7 +632,9 @@ unsigned int ui_browser__argv_refresh(struct ui_browser *browser)  		browser->top = browser->entries;  	pos = (char **)browser->top; -	while (idx < browser->nr_entries) { +	while (idx < browser->nr_entries && +	       row < (unsigned)SLtt_Screen_Rows - 1) { +		assert(pos < (char **)browser->entries + browser->nr_entries);  		if (!browser->filter || !browser->filter(browser, *pos)) {  			ui_browser__gotorc(browser, row, 0);  			browser->write(browser, pos, row); diff --git a/tools/perf/ui/browsers/Build b/tools/perf/ui/browsers/Build index 8fee56b46502..fdf86f7981ca 100644 --- a/tools/perf/ui/browsers/Build +++ b/tools/perf/ui/browsers/Build @@ -3,6 +3,7 @@ perf-y += hists.o  perf-y += map.o  perf-y += scripts.o  perf-y += header.o +perf-y += res_sample.o  CFLAGS_annotate.o += -DENABLE_SLFUTURE_CONST  CFLAGS_hists.o    += -DENABLE_SLFUTURE_CONST diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c index 35bdfd8b1e71..98d934a36d86 100644 --- a/tools/perf/ui/browsers/annotate.c +++ b/tools/perf/ui/browsers/annotate.c @@ -750,7 +750,7 @@ static int annotate_browser__run(struct annotate_browser *browser,  			continue;  		case 'r':  			{ -				script_browse(NULL); +				script_browse(NULL, NULL);  				continue;  			}  		case 'k': diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index aef800d97ea1..3421ecbdd3f0 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -7,6 +7,7 @@  #include <string.h>  #include <linux/rbtree.h>  #include <sys/ttydefaults.h> +#include <linux/time64.h>  #include "../../util/callchain.h"  #include "../../util/evsel.h" @@ -30,6 +31,7 @@  #include "srcline.h"  #include "string2.h"  #include "units.h" +#include "time-utils.h"  #include "sane_ctype.h" @@ -1224,6 +1226,8 @@ void hist_browser__init_hpp(void)  				hist_browser__hpp_color_overhead_guest_us;  	perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =  				hist_browser__hpp_color_overhead_acc; + +	res_sample_init();  }  static int hist_browser__show_entry(struct hist_browser *browser, @@ -2338,9 +2342,12 @@ close_file_and_continue:  }  struct popup_action { +	unsigned long		time;  	struct thread 		*thread;  	struct map_symbol 	ms;  	int			socket; +	struct perf_evsel	*evsel; +	enum rstype		rstype;  	int (*fn)(struct hist_browser *browser, struct popup_action *act);  }; @@ -2527,46 +2534,137 @@ static int  do_run_script(struct hist_browser *browser __maybe_unused,  	      struct popup_action *act)  { -	char script_opt[64]; -	memset(script_opt, 0, sizeof(script_opt)); +	char *script_opt; +	int len; +	int n = 0; +	len = 100; +	if (act->thread) +		len += strlen(thread__comm_str(act->thread)); +	else if (act->ms.sym) +		len += strlen(act->ms.sym->name); +	script_opt = malloc(len); +	if (!script_opt) +		return -1; + +	script_opt[0] = 0;  	if (act->thread) { -		scnprintf(script_opt, sizeof(script_opt), " -c %s ", +		n = scnprintf(script_opt, len, " -c %s ",  			  thread__comm_str(act->thread));  	} else if (act->ms.sym) { -		scnprintf(script_opt, sizeof(script_opt), " -S %s ", +		n = scnprintf(script_opt, len, " -S %s ",  			  act->ms.sym->name);  	} -	script_browse(script_opt); +	if (act->time) { +		char start[32], end[32]; +		unsigned long starttime = act->time; +		unsigned long endtime = act->time + symbol_conf.time_quantum; + +		if (starttime == endtime) { /* Display 1ms as fallback */ +			starttime -= 1*NSEC_PER_MSEC; +			endtime += 1*NSEC_PER_MSEC; +		} +		timestamp__scnprintf_usec(starttime, start, sizeof start); +		timestamp__scnprintf_usec(endtime, end, sizeof end); +		n += snprintf(script_opt + n, len - n, " --time %s,%s", start, end); +	} + +	script_browse(script_opt, act->evsel); +	free(script_opt);  	return 0;  }  static int -add_script_opt(struct hist_browser *browser __maybe_unused, +do_res_sample_script(struct hist_browser *browser __maybe_unused, +		     struct popup_action *act) +{ +	struct hist_entry *he; + +	he = hist_browser__selected_entry(browser); +	res_sample_browse(he->res_samples, he->num_res, act->evsel, act->rstype); +	return 0; +} + +static int +add_script_opt_2(struct hist_browser *browser __maybe_unused,  	       struct popup_action *act, char **optstr, -	       struct thread *thread, struct symbol *sym) +	       struct thread *thread, struct symbol *sym, +	       struct perf_evsel *evsel, const char *tstr)  { +  	if (thread) { -		if (asprintf(optstr, "Run scripts for samples of thread [%s]", -			     thread__comm_str(thread)) < 0) +		if (asprintf(optstr, "Run scripts for samples of thread [%s]%s", +			     thread__comm_str(thread), tstr) < 0)  			return 0;  	} else if (sym) { -		if (asprintf(optstr, "Run scripts for samples of symbol [%s]", -			     sym->name) < 0) +		if (asprintf(optstr, "Run scripts for samples of symbol [%s]%s", +			     sym->name, tstr) < 0)  			return 0;  	} else { -		if (asprintf(optstr, "Run scripts for all samples") < 0) +		if (asprintf(optstr, "Run scripts for all samples%s", tstr) < 0)  			return 0;  	}  	act->thread = thread;  	act->ms.sym = sym; +	act->evsel = evsel;  	act->fn = do_run_script;  	return 1;  }  static int +add_script_opt(struct hist_browser *browser, +	       struct popup_action *act, char **optstr, +	       struct thread *thread, struct symbol *sym, +	       struct perf_evsel *evsel) +{ +	int n, j; +	struct hist_entry *he; + +	n = add_script_opt_2(browser, act, optstr, thread, sym, evsel, ""); + +	he = hist_browser__selected_entry(browser); +	if (sort_order && strstr(sort_order, "time")) { +		char tstr[128]; + +		optstr++; +		act++; +		j = sprintf(tstr, " in "); +		j += timestamp__scnprintf_usec(he->time, tstr + j, +					       sizeof tstr - j); +		j += sprintf(tstr + j, "-"); +		timestamp__scnprintf_usec(he->time + symbol_conf.time_quantum, +				          tstr + j, sizeof tstr - j); +		n += add_script_opt_2(browser, act, optstr, thread, sym, +					  evsel, tstr); +		act->time = he->time; +	} +	return n; +} + +static int +add_res_sample_opt(struct hist_browser *browser __maybe_unused, +		   struct popup_action *act, char **optstr, +		   struct res_sample *res_sample, +		   struct perf_evsel *evsel, +		   enum rstype type) +{ +	if (!res_sample) +		return 0; + +	if (asprintf(optstr, "Show context for individual samples %s", +		type == A_ASM ? "with assembler" : +		type == A_SOURCE ? "with source" : "") < 0) +		return 0; + +	act->fn = do_res_sample_script; +	act->evsel = evsel; +	act->rstype = type; +	return 1; +} + +static int  do_switch_data(struct hist_browser *browser __maybe_unused,  	       struct popup_action *act __maybe_unused)  { @@ -3031,7 +3129,7 @@ skip_annotation:  				nr_options += add_script_opt(browser,  							     &actions[nr_options],  							     &options[nr_options], -							     thread, NULL); +							     thread, NULL, evsel);  			}  			/*  			 * Note that browser->selection != NULL @@ -3046,11 +3144,24 @@ skip_annotation:  				nr_options += add_script_opt(browser,  							     &actions[nr_options],  							     &options[nr_options], -							     NULL, browser->selection->sym); +							     NULL, browser->selection->sym, +							     evsel);  			}  		}  		nr_options += add_script_opt(browser, &actions[nr_options], -					     &options[nr_options], NULL, NULL); +					     &options[nr_options], NULL, NULL, evsel); +		nr_options += add_res_sample_opt(browser, &actions[nr_options], +						 &options[nr_options], +				 hist_browser__selected_entry(browser)->res_samples, +				 evsel, A_NORMAL); +		nr_options += add_res_sample_opt(browser, &actions[nr_options], +						 &options[nr_options], +				 hist_browser__selected_entry(browser)->res_samples, +				 evsel, A_ASM); +		nr_options += add_res_sample_opt(browser, &actions[nr_options], +						 &options[nr_options], +				 hist_browser__selected_entry(browser)->res_samples, +				 evsel, A_SOURCE);  		nr_options += add_switch_opt(browser, &actions[nr_options],  					     &options[nr_options]);  skip_scripting: diff --git a/tools/perf/ui/browsers/res_sample.c b/tools/perf/ui/browsers/res_sample.c new file mode 100644 index 000000000000..c0dd73176d42 --- /dev/null +++ b/tools/perf/ui/browsers/res_sample.c @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Display a menu with individual samples to browse with perf script */ +#include "util.h" +#include "hist.h" +#include "evsel.h" +#include "hists.h" +#include "sort.h" +#include "config.h" +#include "time-utils.h" +#include <linux/time64.h> + +static u64 context_len = 10 * NSEC_PER_MSEC; + +static int res_sample_config(const char *var, const char *value, void *data __maybe_unused) +{ +	if (!strcmp(var, "samples.context")) +		return perf_config_u64(&context_len, var, value); +	return 0; +} + +void res_sample_init(void) +{ +	perf_config(res_sample_config, NULL); +} + +int res_sample_browse(struct res_sample *res_samples, int num_res, +		      struct perf_evsel *evsel, enum rstype rstype) +{ +	char **names; +	int i, n; +	int choice; +	char *cmd; +	char pbuf[256], tidbuf[32], cpubuf[32]; +	const char *perf = perf_exe(pbuf, sizeof pbuf); +	char trange[128], tsample[64]; +	struct res_sample *r; +	char extra_format[256]; + +	names = calloc(num_res, sizeof(char *)); +	if (!names) +		return -1; +	for (i = 0; i < num_res; i++) { +		char tbuf[64]; + +		timestamp__scnprintf_nsec(res_samples[i].time, tbuf, sizeof tbuf); +		if (asprintf(&names[i], "%s: CPU %d tid %d", tbuf, +			     res_samples[i].cpu, res_samples[i].tid) < 0) { +			while (--i >= 0) +				free(names[i]); +			free(names); +			return -1; +		} +	} +	choice = ui__popup_menu(num_res, names); +	for (i = 0; i < num_res; i++) +		free(names[i]); +	free(names); + +	if (choice < 0 || choice >= num_res) +		return -1; +	r = &res_samples[choice]; + +	n = timestamp__scnprintf_nsec(r->time - context_len, trange, sizeof trange); +	trange[n++] = ','; +	timestamp__scnprintf_nsec(r->time + context_len, trange + n, sizeof trange - n); + +	timestamp__scnprintf_nsec(r->time, tsample, sizeof tsample); + +	attr_to_script(extra_format, &evsel->attr); + +	if (asprintf(&cmd, "%s script %s%s --time %s %s%s %s%s --ns %s %s %s %s %s | less +/%s", +		     perf, +		     input_name ? "-i " : "", +		     input_name ? input_name : "", +		     trange, +		     r->cpu >= 0 ? "--cpu " : "", +		     r->cpu >= 0 ? (sprintf(cpubuf, "%d", r->cpu), cpubuf) : "", +		     r->tid ? "--tid " : "", +		     r->tid ? (sprintf(tidbuf, "%d", r->tid), tidbuf) : "", +		     extra_format, +		     rstype == A_ASM ? "-F +insn --xed" : +		     rstype == A_SOURCE ? "-F +srcline,+srccode" : "", +		     symbol_conf.inline_name ? "--inline" : "", +		     "--show-lost-events ", +		     r->tid ? "--show-switch-events --show-task-events " : "", +		     tsample) < 0) +		return -1; +	run_script(cmd); +	free(cmd); +	return 0; +} diff --git a/tools/perf/ui/browsers/scripts.c b/tools/perf/ui/browsers/scripts.c index 90a32ac69e76..27cf3ab88d13 100644 --- a/tools/perf/ui/browsers/scripts.c +++ b/tools/perf/ui/browsers/scripts.c @@ -1,34 +1,12 @@  // SPDX-License-Identifier: GPL-2.0 -#include <elf.h> -#include <inttypes.h> -#include <sys/ttydefaults.h> -#include <string.h>  #include "../../util/sort.h"  #include "../../util/util.h"  #include "../../util/hist.h"  #include "../../util/debug.h"  #include "../../util/symbol.h"  #include "../browser.h" -#include "../helpline.h"  #include "../libslang.h" - -/* 2048 lines should be enough for a script output */ -#define MAX_LINES		2048 - -/* 160 bytes for one output line */ -#define AVERAGE_LINE_LEN	160 - -struct script_line { -	struct list_head node; -	char line[AVERAGE_LINE_LEN]; -}; - -struct perf_script_browser { -	struct ui_browser b; -	struct list_head entries; -	const char *script_name; -	int nr_lines; -}; +#include "config.h"  #define SCRIPT_NAMELEN	128  #define SCRIPT_MAX_NO	64 @@ -40,149 +18,169 @@ struct perf_script_browser {   */  #define SCRIPT_FULLPATH_LEN	256 +struct script_config { +	const char **names; +	char **paths; +	int index; +	const char *perf; +	char extra_format[256]; +}; + +void attr_to_script(char *extra_format, struct perf_event_attr *attr) +{ +	extra_format[0] = 0; +	if (attr->read_format & PERF_FORMAT_GROUP) +		strcat(extra_format, " -F +metric"); +	if (attr->sample_type & PERF_SAMPLE_BRANCH_STACK) +		strcat(extra_format, " -F +brstackinsn --xed"); +	if (attr->sample_type & PERF_SAMPLE_REGS_INTR) +		strcat(extra_format, " -F +iregs"); +	if (attr->sample_type & PERF_SAMPLE_REGS_USER) +		strcat(extra_format, " -F +uregs"); +	if (attr->sample_type & PERF_SAMPLE_PHYS_ADDR) +		strcat(extra_format, " -F +phys_addr"); +} + +static int add_script_option(const char *name, const char *opt, +			     struct script_config *c) +{ +	c->names[c->index] = name; +	if (asprintf(&c->paths[c->index], +		     "%s script %s -F +metric %s %s", +		     c->perf, opt, symbol_conf.inline_name ? " --inline" : "", +		     c->extra_format) < 0) +		return -1; +	c->index++; +	return 0; +} + +static int scripts_config(const char *var, const char *value, void *data) +{ +	struct script_config *c = data; + +	if (!strstarts(var, "scripts.")) +		return -1; +	if (c->index >= SCRIPT_MAX_NO) +		return -1; +	c->names[c->index] = strdup(var + 7); +	if (!c->names[c->index]) +		return -1; +	if (asprintf(&c->paths[c->index], "%s %s", value, +		     c->extra_format) < 0) +		return -1; +	c->index++; +	return 0; +} +  /*   * When success, will copy the full path of the selected script   * into  the buffer pointed by script_name, and return 0.   * Return -1 on failure.   */ -static int list_scripts(char *script_name) +static int list_scripts(char *script_name, bool *custom, +			struct perf_evsel *evsel)  { -	char *buf, *names[SCRIPT_MAX_NO], *paths[SCRIPT_MAX_NO]; -	int i, num, choice, ret = -1; +	char *buf, *paths[SCRIPT_MAX_NO], *names[SCRIPT_MAX_NO]; +	int i, num, choice; +	int ret = 0; +	int max_std, custom_perf; +	char pbuf[256]; +	const char *perf = perf_exe(pbuf, sizeof pbuf); +	struct script_config scriptc = { +		.names = (const char **)names, +		.paths = paths, +		.perf = perf +	}; + +	script_name[0] = 0;  	/* Preset the script name to SCRIPT_NAMELEN */  	buf = malloc(SCRIPT_MAX_NO * (SCRIPT_NAMELEN + SCRIPT_FULLPATH_LEN));  	if (!buf) -		return ret; +		return -1; -	for (i = 0; i < SCRIPT_MAX_NO; i++) { -		names[i] = buf + i * (SCRIPT_NAMELEN + SCRIPT_FULLPATH_LEN); +	if (evsel) +		attr_to_script(scriptc.extra_format, &evsel->attr); +	add_script_option("Show individual samples", "", &scriptc); +	add_script_option("Show individual samples with assembler", "-F +insn --xed", +			  &scriptc); +	add_script_option("Show individual samples with source", "-F +srcline,+srccode", +			  &scriptc); +	perf_config(scripts_config, &scriptc); +	custom_perf = scriptc.index; +	add_script_option("Show samples with custom perf script arguments", "", &scriptc); +	i = scriptc.index; +	max_std = i; + +	for (; i < SCRIPT_MAX_NO; i++) { +		names[i] = buf + (i - max_std) * (SCRIPT_NAMELEN + SCRIPT_FULLPATH_LEN);  		paths[i] = names[i] + SCRIPT_NAMELEN;  	} -	num = find_scripts(names, paths); -	if (num > 0) { -		choice = ui__popup_menu(num, names); -		if (choice < num && choice >= 0) { -			strcpy(script_name, paths[choice]); -			ret = 0; -		} +	num = find_scripts(names + max_std, paths + max_std, SCRIPT_MAX_NO - max_std, +			SCRIPT_FULLPATH_LEN); +	if (num < 0) +		num = 0; +	choice = ui__popup_menu(num + max_std, (char * const *)names); +	if (choice < 0) { +		ret = -1; +		goto out;  	} +	if (choice == custom_perf) { +		char script_args[50]; +		int key = ui_browser__input_window("perf script command", +				"Enter perf script command line (without perf script prefix)", +				script_args, "", 0); +		if (key != K_ENTER) +			return -1; +		sprintf(script_name, "%s script %s", perf, script_args); +	} else if (choice < num + max_std) { +		strcpy(script_name, paths[choice]); +	} +	*custom = choice >= max_std; +out:  	free(buf); +	for (i = 0; i < max_std; i++) +		free(paths[i]);  	return ret;  } -static void script_browser__write(struct ui_browser *browser, -				   void *entry, int row) +void run_script(char *cmd)  { -	struct script_line *sline = list_entry(entry, struct script_line, node); -	bool current_entry = ui_browser__is_current_entry(browser, row); - -	ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED : -						       HE_COLORSET_NORMAL); - -	ui_browser__write_nstring(browser, sline->line, browser->width); +	pr_debug("Running %s\n", cmd); +	SLang_reset_tty(); +	if (system(cmd) < 0) +		pr_warning("Cannot run %s\n", cmd); +	/* +	 * SLang doesn't seem to reset the whole terminal, so be more +	 * forceful to get back to the original state. +	 */ +	printf("\033[c\033[H\033[J"); +	fflush(stdout); +	SLang_init_tty(0, 0, 0); +	SLsmg_refresh();  } -static int script_browser__run(struct perf_script_browser *browser) +int script_browse(const char *script_opt, struct perf_evsel *evsel)  { -	int key; +	char *cmd, script_name[SCRIPT_FULLPATH_LEN]; +	bool custom = false; -	if (ui_browser__show(&browser->b, browser->script_name, -			     "Press ESC to exit") < 0) +	memset(script_name, 0, SCRIPT_FULLPATH_LEN); +	if (list_scripts(script_name, &custom, evsel))  		return -1; -	while (1) { -		key = ui_browser__run(&browser->b, 0); - -		/* We can add some special key handling here if needed */ -		break; -	} - -	ui_browser__hide(&browser->b); -	return key; -} - - -int script_browse(const char *script_opt) -{ -	char cmd[SCRIPT_FULLPATH_LEN*2], script_name[SCRIPT_FULLPATH_LEN]; -	char *line = NULL; -	size_t len = 0; -	ssize_t retlen; -	int ret = -1, nr_entries = 0; -	FILE *fp; -	void *buf; -	struct script_line *sline; - -	struct perf_script_browser script = { -		.b = { -			.refresh    = ui_browser__list_head_refresh, -			.seek	    = ui_browser__list_head_seek, -			.write	    = script_browser__write, -		}, -		.script_name = script_name, -	}; - -	INIT_LIST_HEAD(&script.entries); - -	/* Save each line of the output in one struct script_line object. */ -	buf = zalloc((sizeof(*sline)) * MAX_LINES); -	if (!buf) +	if (asprintf(&cmd, "%s%s %s %s%s 2>&1 | less", +			custom ? "perf script -s " : "", +			script_name, +			script_opt ? script_opt : "", +			input_name ? "-i " : "", +			input_name ? input_name : "") < 0)  		return -1; -	sline = buf; - -	memset(script_name, 0, SCRIPT_FULLPATH_LEN); -	if (list_scripts(script_name)) -		goto exit; - -	sprintf(cmd, "perf script -s %s ", script_name); -	if (script_opt) -		strcat(cmd, script_opt); +	run_script(cmd); +	free(cmd); -	if (input_name) { -		strcat(cmd, " -i "); -		strcat(cmd, input_name); -	} - -	strcat(cmd, " 2>&1"); - -	fp = popen(cmd, "r"); -	if (!fp) -		goto exit; - -	while ((retlen = getline(&line, &len, fp)) != -1) { -		strncpy(sline->line, line, AVERAGE_LINE_LEN); - -		/* If one output line is very large, just cut it short */ -		if (retlen >= AVERAGE_LINE_LEN) { -			sline->line[AVERAGE_LINE_LEN - 1] = '\0'; -			sline->line[AVERAGE_LINE_LEN - 2] = '\n'; -		} -		list_add_tail(&sline->node, &script.entries); - -		if (script.b.width < retlen) -			script.b.width = retlen; - -		if (nr_entries++ >= MAX_LINES - 1) -			break; -		sline++; -	} - -	if (script.b.width > AVERAGE_LINE_LEN) -		script.b.width = AVERAGE_LINE_LEN; - -	free(line); -	pclose(fp); - -	script.nr_lines = nr_entries; -	script.b.nr_entries = nr_entries; -	script.b.entries = &script.entries; - -	ret = script_browser__run(&script); -exit: -	free(buf); -	return ret; +	return 0;  } |