diff options
| author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2013-05-01 08:47:44 -0700 | 
|---|---|---|
| committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2013-05-01 08:47:44 -0700 | 
| commit | bf61c8840efe60fd8f91446860b63338fb424158 (patch) | |
| tree | 7a71832407a4f0d6346db773343f4c3ae2257b19 /tools/perf/ui | |
| parent | 5846115b30f3a881e542c8bfde59a699c1c13740 (diff) | |
| parent | 0c6a61657da78098472fd0eb71cc01f2387fa1bb (diff) | |
| download | olio-linux-3.10-bf61c8840efe60fd8f91446860b63338fb424158.tar.xz olio-linux-3.10-bf61c8840efe60fd8f91446860b63338fb424158.zip  | |
Merge branch 'next' into for-linus
Prepare first set of updates for 3.10 merge window.
Diffstat (limited to 'tools/perf/ui')
| -rw-r--r-- | tools/perf/ui/browser.c | 6 | ||||
| -rw-r--r-- | tools/perf/ui/browsers/annotate.c | 78 | ||||
| -rw-r--r-- | tools/perf/ui/browsers/hists.c | 432 | ||||
| -rw-r--r-- | tools/perf/ui/browsers/scripts.c | 189 | ||||
| -rw-r--r-- | tools/perf/ui/gtk/annotate.c | 229 | ||||
| -rw-r--r-- | tools/perf/ui/gtk/browser.c | 237 | ||||
| -rw-r--r-- | tools/perf/ui/gtk/gtk.h | 11 | ||||
| -rw-r--r-- | tools/perf/ui/gtk/helpline.c | 23 | ||||
| -rw-r--r-- | tools/perf/ui/gtk/hists.c | 312 | ||||
| -rw-r--r-- | tools/perf/ui/gtk/progress.c | 59 | ||||
| -rw-r--r-- | tools/perf/ui/gtk/setup.c | 2 | ||||
| -rw-r--r-- | tools/perf/ui/gtk/util.c | 11 | ||||
| -rw-r--r-- | tools/perf/ui/helpline.c | 12 | ||||
| -rw-r--r-- | tools/perf/ui/helpline.h | 22 | ||||
| -rw-r--r-- | tools/perf/ui/hist.c | 491 | ||||
| -rw-r--r-- | tools/perf/ui/keysyms.h | 1 | ||||
| -rw-r--r-- | tools/perf/ui/progress.c | 44 | ||||
| -rw-r--r-- | tools/perf/ui/progress.h | 10 | ||||
| -rw-r--r-- | tools/perf/ui/setup.c | 3 | ||||
| -rw-r--r-- | tools/perf/ui/stdio/hist.c | 27 | ||||
| -rw-r--r-- | tools/perf/ui/tui/helpline.c | 29 | ||||
| -rw-r--r-- | tools/perf/ui/tui/progress.c | 42 | ||||
| -rw-r--r-- | tools/perf/ui/tui/setup.c | 1 | ||||
| -rw-r--r-- | tools/perf/ui/ui.h | 28 | ||||
| -rw-r--r-- | tools/perf/ui/util.c | 1 | 
25 files changed, 1673 insertions, 627 deletions
diff --git a/tools/perf/ui/browser.c b/tools/perf/ui/browser.c index 4aeb7d5df93..809ea4632a3 100644 --- a/tools/perf/ui/browser.c +++ b/tools/perf/ui/browser.c @@ -273,6 +273,8 @@ void ui_browser__hide(struct ui_browser *browser __maybe_unused)  {  	pthread_mutex_lock(&ui__lock);  	ui_helpline__pop(); +	free(browser->helpline); +	browser->helpline = NULL;  	pthread_mutex_unlock(&ui__lock);  } @@ -471,7 +473,7 @@ unsigned int ui_browser__list_head_refresh(struct ui_browser *browser)  	return row;  } -static struct ui_browser__colorset { +static struct ui_browser_colorset {  	const char *name, *fg, *bg;  	int colorset;  } ui_browser__colorsets[] = { @@ -706,7 +708,7 @@ void ui_browser__init(void)  	perf_config(ui_browser__color_config, NULL);  	while (ui_browser__colorsets[i].name) { -		struct ui_browser__colorset *c = &ui_browser__colorsets[i++]; +		struct ui_browser_colorset *c = &ui_browser__colorsets[i++];  		sltt_set_color(c->colorset, c->name, c->fg, c->bg);  	} diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c index 8f8cd2d73b3..7dca1555c61 100644 --- a/tools/perf/ui/browsers/annotate.c +++ b/tools/perf/ui/browsers/annotate.c @@ -182,15 +182,30 @@ static void annotate_browser__write(struct ui_browser *browser, void *entry, int  		ab->selection = dl;  } +static bool disasm_line__is_valid_jump(struct disasm_line *dl, struct symbol *sym) +{ +	if (!dl || !dl->ins || !ins__is_jump(dl->ins) +	    || !disasm_line__has_offset(dl) +	    || dl->ops.target.offset >= symbol__size(sym)) +		return false; + +	return true; +} +  static void annotate_browser__draw_current_jump(struct ui_browser *browser)  {  	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);  	struct disasm_line *cursor = ab->selection, *target;  	struct browser_disasm_line *btarget, *bcursor;  	unsigned int from, to; +	struct map_symbol *ms = ab->b.priv; +	struct symbol *sym = ms->sym; -	if (!cursor || !cursor->ins || !ins__is_jump(cursor->ins) || -	    !disasm_line__has_offset(cursor)) +	/* PLT symbols contain external offsets */ +	if (strstr(sym->name, "@plt")) +		return; + +	if (!disasm_line__is_valid_jump(cursor, sym))  		return;  	target = ab->offsets[cursor->ops.target.offset]; @@ -386,9 +401,8 @@ static void annotate_browser__init_asm_mode(struct annotate_browser *browser)  	browser->b.nr_entries = browser->nr_asm_entries;  } -static bool annotate_browser__callq(struct annotate_browser *browser, -				    int evidx, void (*timer)(void *arg), -				    void *arg, int delay_secs) +static bool annotate_browser__callq(struct annotate_browser *browser, int evidx, +				    struct hist_browser_timer *hbt)  {  	struct map_symbol *ms = browser->b.priv;  	struct disasm_line *dl = browser->selection; @@ -418,7 +432,7 @@ static bool annotate_browser__callq(struct annotate_browser *browser,  	}  	pthread_mutex_unlock(¬es->lock); -	symbol__tui_annotate(target, ms->map, evidx, timer, arg, delay_secs); +	symbol__tui_annotate(target, ms->map, evidx, hbt);  	ui_browser__show_title(&browser->b, sym->name);  	return true;  } @@ -602,13 +616,13 @@ static void annotate_browser__update_addr_width(struct annotate_browser *browser  }  static int annotate_browser__run(struct annotate_browser *browser, int evidx, -				 void(*timer)(void *arg), -				 void *arg, int delay_secs) +				 struct hist_browser_timer *hbt)  {  	struct rb_node *nd = NULL;  	struct map_symbol *ms = browser->b.priv;  	struct symbol *sym = ms->sym;  	const char *help = "Press 'h' for help on key bindings"; +	int delay_secs = hbt ? hbt->refresh : 0;  	int key;  	if (ui_browser__show(&browser->b, sym->name, help) < 0) @@ -639,8 +653,8 @@ static int annotate_browser__run(struct annotate_browser *browser, int evidx,  		switch (key) {  		case K_TIMER: -			if (timer != NULL) -				timer(arg); +			if (hbt) +				hbt->timer(hbt->arg);  			if (delay_secs != 0)  				symbol__annotate_decay_histogram(sym, evidx); @@ -676,8 +690,14 @@ static int annotate_browser__run(struct annotate_browser *browser, int evidx,  		"o             Toggle disassembler output/simplified view\n"  		"s             Toggle source code view\n"  		"/             Search string\n" +		"r             Run available scripts\n"  		"?             Search previous string\n");  			continue; +		case 'r': +			{ +				script_browse(NULL); +				continue; +			}  		case 'H':  			nd = browser->curr_hot;  			break; @@ -734,7 +754,7 @@ show_help:  					goto show_sup_ins;  				goto out;  			} else if (!(annotate_browser__jump(browser) || -				     annotate_browser__callq(browser, evidx, timer, arg, delay_secs))) { +				     annotate_browser__callq(browser, evidx, hbt))) {  show_sup_ins:  				ui_helpline__puts("Actions are only available for 'callq', 'retq' & jump instructions.");  			} @@ -757,32 +777,29 @@ out:  }  int hist_entry__tui_annotate(struct hist_entry *he, int evidx, -			     void(*timer)(void *arg), void *arg, int delay_secs) +			     struct hist_browser_timer *hbt)  { -	return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx, -				    timer, arg, delay_secs); +	return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx, hbt);  }  static void annotate_browser__mark_jump_targets(struct annotate_browser *browser,  						size_t size)  {  	u64 offset; +	struct map_symbol *ms = browser->b.priv; +	struct symbol *sym = ms->sym; + +	/* PLT symbols contain external offsets */ +	if (strstr(sym->name, "@plt")) +		return;  	for (offset = 0; offset < size; ++offset) {  		struct disasm_line *dl = browser->offsets[offset], *dlt;  		struct browser_disasm_line *bdlt; -		if (!dl || !dl->ins || !ins__is_jump(dl->ins) || -		    !disasm_line__has_offset(dl)) +		if (!disasm_line__is_valid_jump(dl, sym))  			continue; -		if (dl->ops.target.offset >= size) { -			ui__error("jump to after symbol!\n" -				  "size: %zx, jump target: %" PRIx64, -				  size, dl->ops.target.offset); -			continue; -		} -  		dlt = browser->offsets[dl->ops.target.offset];  		/*   		 * FIXME: Oops, no jump target? Buggy disassembler? Or do we @@ -810,8 +827,7 @@ static inline int width_jumps(int n)  }  int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, -			 void(*timer)(void *arg), void *arg, -			 int delay_secs) +			 struct hist_browser_timer *hbt)  {  	struct disasm_line *pos, *n;  	struct annotation *notes; @@ -893,7 +909,7 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,  	annotate_browser__update_addr_width(&browser); -	ret = annotate_browser__run(&browser, evidx, timer, arg, delay_secs); +	ret = annotate_browser__run(&browser, evidx, hbt);  	list_for_each_entry_safe(pos, n, ¬es->src->source, node) {  		list_del(&pos->node);  		disasm_line__free(pos); @@ -906,11 +922,11 @@ out_free_offsets:  #define ANNOTATE_CFG(n) \  	{ .name = #n, .value = &annotate_browser__opts.n, } -	 +  /*   * Keep the entries sorted, they are bsearch'ed   */ -static struct annotate__config { +static struct annotate_config {  	const char *name;  	bool *value;  } annotate__configs[] = { @@ -924,7 +940,7 @@ static struct annotate__config {  static int annotate_config__cmp(const void *name, const void *cfgp)  { -	const struct annotate__config *cfg = cfgp; +	const struct annotate_config *cfg = cfgp;  	return strcmp(name, cfg->name);  } @@ -932,7 +948,7 @@ static int annotate_config__cmp(const void *name, const void *cfgp)  static int annotate__config(const char *var, const char *value,  			    void *data __maybe_unused)  { -	struct annotate__config *cfg; +	struct annotate_config *cfg;  	const char *name;  	if (prefixcmp(var, "annotate.") != 0) @@ -940,7 +956,7 @@ static int annotate__config(const char *var, const char *value,  	name = var + 9;  	cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs), -		      sizeof(struct annotate__config), annotate_config__cmp); +		      sizeof(struct annotate_config), annotate_config__cmp);  	if (cfg == NULL)  		return -1; diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index ef2f93ca749..aa22704047d 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -11,6 +11,7 @@  #include "../../util/pstack.h"  #include "../../util/sort.h"  #include "../../util/util.h" +#include "../../arch/common.h"  #include "../browser.h"  #include "../helpline.h" @@ -310,10 +311,11 @@ static void ui_browser__warn_lost_events(struct ui_browser *browser)  }  static int hist_browser__run(struct hist_browser *browser, const char *ev_name, -			     void(*timer)(void *arg), void *arg, int delay_secs) +			     struct hist_browser_timer *hbt)  {  	int key;  	char title[160]; +	int delay_secs = hbt ? hbt->refresh : 0;  	browser->b.entries = &browser->hists->entries;  	browser->b.nr_entries = browser->hists->nr_entries; @@ -330,7 +332,7 @@ static int hist_browser__run(struct hist_browser *browser, const char *ev_name,  		switch (key) {  		case K_TIMER: -			timer(arg); +			hbt->timer(hbt->arg);  			ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);  			if (browser->hists->stats.nr_lost_warned != @@ -565,26 +567,128 @@ static int hist_browser__show_callchain(struct hist_browser *browser,  	return row - first_row;  } -#define HPP__COLOR_FN(_name, _field)					\ -static int hist_browser__hpp_color_ ## _name(struct perf_hpp *hpp,	\ -					     struct hist_entry *he)	\ +struct hpp_arg { +	struct ui_browser *b; +	char folded_sign; +	bool current_entry; +}; + +static int __hpp__color_callchain(struct hpp_arg *arg) +{ +	if (!symbol_conf.use_callchain) +		return 0; + +	slsmg_printf("%c ", arg->folded_sign); +	return 2; +} + +static int __hpp__color_fmt(struct perf_hpp *hpp, struct hist_entry *he, +			    u64 (*get_field)(struct hist_entry *), +			    int (*callchain_cb)(struct hpp_arg *)) +{ +	int ret = 0; +	double percent = 0.0; +	struct hists *hists = he->hists; +	struct hpp_arg *arg = hpp->ptr; + +	if (hists->stats.total_period) +		percent = 100.0 * get_field(he) / hists->stats.total_period; + +	ui_browser__set_percent_color(arg->b, percent, arg->current_entry); + +	if (callchain_cb) +		ret += callchain_cb(arg); + +	ret += scnprintf(hpp->buf, hpp->size, "%6.2f%%", percent); +	slsmg_printf("%s", hpp->buf); + +	if (symbol_conf.event_group) { +		int prev_idx, idx_delta; +		struct perf_evsel *evsel = hists_to_evsel(hists); +		struct hist_entry *pair; +		int nr_members = evsel->nr_members; + +		if (nr_members <= 1) +			goto out; + +		prev_idx = perf_evsel__group_idx(evsel); + +		list_for_each_entry(pair, &he->pairs.head, pairs.node) { +			u64 period = get_field(pair); +			u64 total = pair->hists->stats.total_period; + +			if (!total) +				continue; + +			evsel = hists_to_evsel(pair->hists); +			idx_delta = perf_evsel__group_idx(evsel) - prev_idx - 1; + +			while (idx_delta--) { +				/* +				 * zero-fill group members in the middle which +				 * have no sample +				 */ +				ui_browser__set_percent_color(arg->b, 0.0, +							arg->current_entry); +				ret += scnprintf(hpp->buf, hpp->size, +						 " %6.2f%%", 0.0); +				slsmg_printf("%s", hpp->buf); +			} + +			percent = 100.0 * period / total; +			ui_browser__set_percent_color(arg->b, percent, +						      arg->current_entry); +			ret += scnprintf(hpp->buf, hpp->size, +					 " %6.2f%%", percent); +			slsmg_printf("%s", hpp->buf); + +			prev_idx = perf_evsel__group_idx(evsel); +		} + +		idx_delta = nr_members - prev_idx - 1; + +		while (idx_delta--) { +			/* +			 * zero-fill group members at last which have no sample +			 */ +			ui_browser__set_percent_color(arg->b, 0.0, +						      arg->current_entry); +			ret += scnprintf(hpp->buf, hpp->size, +					 " %6.2f%%", 0.0); +			slsmg_printf("%s", hpp->buf); +		} +	} +out: +	if (!arg->current_entry || !arg->b->navkeypressed) +		ui_browser__set_color(arg->b, HE_COLORSET_NORMAL); + +	return ret; +} + +#define __HPP_COLOR_PERCENT_FN(_type, _field, _cb)			\ +static u64 __hpp_get_##_field(struct hist_entry *he)			\  {									\ -	struct hists *hists = he->hists;				\ -	double percent = 100.0 * he->stat._field / hists->stats.total_period; \ -	*(double *)hpp->ptr = percent;					\ -	return scnprintf(hpp->buf, hpp->size, "%6.2f%%", percent);	\ +	return he->stat._field;						\ +}									\ +									\ +static int hist_browser__hpp_color_##_type(struct perf_hpp *hpp,	\ +					   struct hist_entry *he)	\ +{									\ +	return __hpp__color_fmt(hpp, he, __hpp_get_##_field, _cb);	\  } -HPP__COLOR_FN(overhead, period) -HPP__COLOR_FN(overhead_sys, period_sys) -HPP__COLOR_FN(overhead_us, period_us) -HPP__COLOR_FN(overhead_guest_sys, period_guest_sys) -HPP__COLOR_FN(overhead_guest_us, period_guest_us) +__HPP_COLOR_PERCENT_FN(overhead, period, __hpp__color_callchain) +__HPP_COLOR_PERCENT_FN(overhead_sys, period_sys, NULL) +__HPP_COLOR_PERCENT_FN(overhead_us, period_us, NULL) +__HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys, NULL) +__HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us, NULL) -#undef HPP__COLOR_FN +#undef __HPP_COLOR_PERCENT_FN  void hist_browser__init_hpp(void)  { +	perf_hpp__column_enable(PERF_HPP__OVERHEAD); +  	perf_hpp__init();  	perf_hpp__format[PERF_HPP__OVERHEAD].color = @@ -604,13 +708,13 @@ static int hist_browser__show_entry(struct hist_browser *browser,  				    unsigned short row)  {  	char s[256]; -	double percent; -	int i, printed = 0; +	int printed = 0;  	int width = browser->b.width;  	char folded_sign = ' ';  	bool current_entry = ui_browser__is_current_entry(&browser->b, row);  	off_t row_offset = entry->row_offset;  	bool first = true; +	struct perf_hpp_fmt *fmt;  	if (current_entry) {  		browser->he_selection = entry; @@ -623,41 +727,30 @@ static int hist_browser__show_entry(struct hist_browser *browser,  	}  	if (row_offset == 0) { +		struct hpp_arg arg = { +			.b 		= &browser->b, +			.folded_sign	= folded_sign, +			.current_entry	= current_entry, +		};  		struct perf_hpp hpp = {  			.buf		= s,  			.size		= sizeof(s), +			.ptr		= &arg,  		};  		ui_browser__gotorc(&browser->b, row, 0); -		for (i = 0; i < PERF_HPP__MAX_INDEX; i++) { -			if (!perf_hpp__format[i].cond) -				continue; - +		perf_hpp__for_each_format(fmt) {  			if (!first) {  				slsmg_printf("  ");  				width -= 2;  			}  			first = false; -			if (perf_hpp__format[i].color) { -				hpp.ptr = &percent; -				/* It will set percent for us. See HPP__COLOR_FN above. */ -				width -= perf_hpp__format[i].color(&hpp, entry); - -				ui_browser__set_percent_color(&browser->b, percent, current_entry); - -				if (i == PERF_HPP__OVERHEAD && symbol_conf.use_callchain) { -					slsmg_printf("%c ", folded_sign); -					width -= 2; -				} - -				slsmg_printf("%s", s); - -				if (!current_entry || !browser->b.navkeypressed) -					ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL); +			if (fmt->color) { +				width -= fmt->color(&hpp, entry);  			} else { -				width -= perf_hpp__format[i].entry(&hpp, entry); +				width -= fmt->entry(&hpp, entry);  				slsmg_printf("%s", s);  			}  		} @@ -1096,6 +1189,21 @@ static int hists__browser_title(struct hists *hists, char *bf, size_t size,  	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; +	struct perf_evsel *evsel = hists_to_evsel(hists); +	char buf[512]; +	size_t buflen = sizeof(buf); + +	if (symbol_conf.event_group && evsel->nr_members > 1) { +		struct perf_evsel *pos; + +		perf_evsel__group_desc(evsel, buf, buflen); +		ev_name = buf; + +		for_each_group_member(pos, evsel) { +			nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE]; +			nr_events += pos->hists.stats.total_period; +		} +	}  	nr_samples = convert_unit(nr_samples, &unit);  	printed = scnprintf(bf, size, @@ -1127,11 +1235,107 @@ static inline void free_popup_options(char **options, int n)  	}  } +/* Check whether the browser is for 'top' or 'report' */ +static inline bool is_report_browser(void *timer) +{ +	return timer == NULL; +} + +/* + * Only runtime switching of perf data file will make "input_name" point + * to a malloced buffer. So add "is_input_name_malloced" flag to decide + * whether we need to call free() for current "input_name" during the switch. + */ +static bool is_input_name_malloced = false; + +static int switch_data_file(void) +{ +	char *pwd, *options[32], *abs_path[32], *tmp; +	DIR *pwd_dir; +	int nr_options = 0, choice = -1, ret = -1; +	struct dirent *dent; + +	pwd = getenv("PWD"); +	if (!pwd) +		return ret; + +	pwd_dir = opendir(pwd); +	if (!pwd_dir) +		return ret; + +	memset(options, 0, sizeof(options)); +	memset(options, 0, sizeof(abs_path)); + +	while ((dent = readdir(pwd_dir))) { +		char path[PATH_MAX]; +		u64 magic; +		char *name = dent->d_name; +		FILE *file; + +		if (!(dent->d_type == DT_REG)) +			continue; + +		snprintf(path, sizeof(path), "%s/%s", pwd, name); + +		file = fopen(path, "r"); +		if (!file) +			continue; + +		if (fread(&magic, 1, 8, file) < 8) +			goto close_file_and_continue; + +		if (is_perf_magic(magic)) { +			options[nr_options] = strdup(name); +			if (!options[nr_options]) +				goto close_file_and_continue; + +			abs_path[nr_options] = strdup(path); +			if (!abs_path[nr_options]) { +				free(options[nr_options]); +				ui__warning("Can't search all data files due to memory shortage.\n"); +				fclose(file); +				break; +			} + +			nr_options++; +		} + +close_file_and_continue: +		fclose(file); +		if (nr_options >= 32) { +			ui__warning("Too many perf data files in PWD!\n" +				    "Only the first 32 files will be listed.\n"); +			break; +		} +	} +	closedir(pwd_dir); + +	if (nr_options) { +		choice = ui__popup_menu(nr_options, options); +		if (choice < nr_options && choice >= 0) { +			tmp = strdup(abs_path[choice]); +			if (tmp) { +				if (is_input_name_malloced) +					free((void *)input_name); +				input_name = tmp; +				is_input_name_malloced = true; +				ret = 0; +			} else +				ui__warning("Data switch failed due to memory shortage!\n"); +		} +	} + +	free_popup_options(options, nr_options); +	free_popup_options(abs_path, nr_options); +	return ret; +} + +  static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,  				    const char *helpline, const char *ev_name,  				    bool left_exits, -				    void(*timer)(void *arg), void *arg, -				    int delay_secs) +				    struct hist_browser_timer *hbt, +				    struct perf_session_env *env)  {  	struct hists *hists = &evsel->hists;  	struct hist_browser *browser = hist_browser__new(hists); @@ -1141,6 +1345,8 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,  	int nr_options = 0;  	int key = -1;  	char buf[64]; +	char script_opt[64]; +	int delay_secs = hbt ? hbt->refresh : 0;  	if (browser == NULL)  		return -1; @@ -1159,10 +1365,12 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,  		int choice = 0,  		    annotate = -2, zoom_dso = -2, zoom_thread = -2,  		    annotate_f = -2, annotate_t = -2, browse_map = -2; +		int scripts_comm = -2, scripts_symbol = -2, +		    scripts_all = -2, switch_data = -2;  		nr_options = 0; -		key = hist_browser__run(browser, ev_name, timer, arg, delay_secs); +		key = hist_browser__run(browser, ev_name, hbt);  		if (browser->he_selection != NULL) {  			thread = hist_browser__selected_thread(browser); @@ -1211,6 +1419,14 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,  				hist_browser__reset(browser);  			}  			continue; +		case 'r': +			if (is_report_browser(hbt)) +				goto do_scripts; +			continue; +		case 's': +			if (is_report_browser(hbt)) +				goto do_data_switch; +			continue;  		case K_F1:  		case 'h':  		case '?': @@ -1229,6 +1445,8 @@ 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" +					"r             Run available scripts('perf report' only)\n" +					"s             Switch to another data file in PWD ('perf report' only)\n"  					"P             Print histograms to perf.hist.N\n"  					"V             Verbose (DSO names in callchains, etc)\n"  					"/             Filter symbol by name"); @@ -1317,6 +1535,28 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,  		    browser->selection->map != NULL &&  		    asprintf(&options[nr_options], "Browse map details") > 0)  			browse_map = nr_options++; + +		/* perf script support */ +		if (browser->he_selection) { +			struct symbol *sym; + +			if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]", +				browser->he_selection->thread->comm) > 0) +				scripts_comm = nr_options++; + +			sym = browser->he_selection->ms.sym; +			if (sym && sym->namelen && +				asprintf(&options[nr_options], "Run scripts for samples of symbol [%s]", +						sym->name) > 0) +				scripts_symbol = nr_options++; +		} + +		if (asprintf(&options[nr_options], "Run scripts for all samples") > 0) +			scripts_all = nr_options++; + +		if (is_report_browser(hbt) && asprintf(&options[nr_options], +				"Switch to another data file in PWD") > 0) +			switch_data = nr_options++;  add_exit_option:  		options[nr_options++] = (char *)"Exit";  retry_popup_menu: @@ -1334,6 +1574,9 @@ retry_popup_menu:  			struct hist_entry *he;  			int err;  do_annotate: +			if (!objdump_path && perf_session_env__lookup_objdump(env)) +				continue; +  			he = hist_browser__selected_entry(browser);  			if (he == NULL)  				continue; @@ -1356,8 +1599,7 @@ do_annotate:  			 * Don't let this be freed, say, by hists__decay_entry.  			 */  			he->used = true; -			err = hist_entry__tui_annotate(he, evsel->idx, -						       timer, arg, delay_secs); +			err = hist_entry__tui_annotate(he, evsel->idx, hbt);  			he->used = false;  			/*  			 * offer option to annotate the other branch source or target @@ -1411,6 +1653,30 @@ zoom_out_thread:  			hists__filter_by_thread(hists);  			hist_browser__reset(browser);  		} +		/* perf scripts support */ +		else if (choice == scripts_all || choice == scripts_comm || +				choice == scripts_symbol) { +do_scripts: +			memset(script_opt, 0, 64); + +			if (choice == scripts_comm) +				sprintf(script_opt, " -c %s ", browser->he_selection->thread->comm); + +			if (choice == scripts_symbol) +				sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name); + +			script_browse(script_opt); +		} +		/* Switch to another data file */ +		else if (choice == switch_data) { +do_data_switch: +			if (!switch_data_file()) { +				key = K_SWITCH_INPUT_DATA; +				break; +			} else +				ui__warning("Won't switch the data files due to\n" +					"no valid data file get selected!\n"); +		}  	}  out_free_stack:  	pstack__delete(fstack); @@ -1424,6 +1690,7 @@ struct perf_evsel_menu {  	struct ui_browser b;  	struct perf_evsel *selection;  	bool lost_events, lost_events_warned; +	struct perf_session_env *env;  };  static void perf_evsel_menu__write(struct ui_browser *browser, @@ -1442,6 +1709,16 @@ static void perf_evsel_menu__write(struct ui_browser *browser,  	ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :  						       HE_COLORSET_NORMAL); +	if (symbol_conf.event_group && evsel->nr_members > 1) { +		struct perf_evsel *pos; + +		ev_name = perf_evsel__group_name(evsel); + +		for_each_group_member(pos, evsel) { +			nr_events += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE]; +		} +	} +  	nr_events = convert_unit(nr_events, &unit);  	printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,  			   unit, unit == ' ' ? "" : " ", ev_name); @@ -1466,11 +1743,12 @@ static void perf_evsel_menu__write(struct ui_browser *browser,  static int perf_evsel_menu__run(struct perf_evsel_menu *menu,  				int nr_events, const char *help, -				void(*timer)(void *arg), void *arg, int delay_secs) +				struct hist_browser_timer *hbt)  {  	struct perf_evlist *evlist = menu->b.priv;  	struct perf_evsel *pos;  	const char *ev_name, *title = "Available samples"; +	int delay_secs = hbt ? hbt->refresh : 0;  	int key;  	if (ui_browser__show(&menu->b, title, @@ -1482,7 +1760,7 @@ static int perf_evsel_menu__run(struct perf_evsel_menu *menu,  		switch (key) {  		case K_TIMER: -			timer(arg); +			hbt->timer(hbt->arg);  			if (!menu->lost_events_warned && menu->lost_events) {  				ui_browser__warn_lost_events(&menu->b); @@ -1500,12 +1778,12 @@ browse_hists:  			 * Give the calling tool a chance to populate the non  			 * default evsel resorted hists tree.  			 */ -			if (timer) -				timer(arg); +			if (hbt) +				hbt->timer(hbt->arg);  			ev_name = perf_evsel__name(pos);  			key = perf_evsel__hists_browse(pos, nr_events, help, -						       ev_name, true, timer, -						       arg, delay_secs); +						       ev_name, true, hbt, +						       menu->env);  			ui_browser__show_title(&menu->b, title);  			switch (key) {  			case K_TAB: @@ -1525,6 +1803,7 @@ browse_hists:  						"Do you really want to exit?"))  					continue;  				/* Fall thru */ +			case K_SWITCH_INPUT_DATA:  			case 'q':  			case CTRL('c'):  				goto out; @@ -1551,10 +1830,21 @@ out:  	return key;  } +static bool filter_group_entries(struct ui_browser *self __maybe_unused, +				 void *entry) +{ +	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node); + +	if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel)) +		return true; + +	return false; +} +  static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, -					   const char *help, -					   void(*timer)(void *arg), void *arg, -					   int delay_secs) +					   int nr_entries, const char *help, +					   struct hist_browser_timer *hbt, +					   struct perf_session_env *env)  {  	struct perf_evsel *pos;  	struct perf_evsel_menu menu = { @@ -1563,9 +1853,11 @@ static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,  			.refresh    = ui_browser__list_head_refresh,  			.seek	    = ui_browser__list_head_seek,  			.write	    = perf_evsel_menu__write, -			.nr_entries = evlist->nr_entries, +			.filter	    = filter_group_entries, +			.nr_entries = nr_entries,  			.priv	    = evlist,  		}, +		.env = env,  	};  	ui_helpline__push("Press ESC to exit"); @@ -1578,23 +1870,37 @@ static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,  			menu.b.width = line_len;  	} -	return perf_evsel_menu__run(&menu, evlist->nr_entries, help, timer, -				    arg, delay_secs); +	return perf_evsel_menu__run(&menu, nr_entries, help, hbt);  }  int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, -				  void(*timer)(void *arg), void *arg, -				  int delay_secs) +				  struct hist_browser_timer *hbt, +				  struct perf_session_env *env)  { -	if (evlist->nr_entries == 1) { +	int nr_entries = evlist->nr_entries; + +single_entry: +	if (nr_entries == 1) {  		struct perf_evsel *first = list_entry(evlist->entries.next,  						      struct perf_evsel, node);  		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); + +		return perf_evsel__hists_browse(first, nr_entries, help, +						ev_name, false, hbt, env); +	} + +	if (symbol_conf.event_group) { +		struct perf_evsel *pos; + +		nr_entries = 0; +		list_for_each_entry(pos, &evlist->entries, node) +			if (perf_evsel__is_group_leader(pos)) +				nr_entries++; + +		if (nr_entries == 1) +			goto single_entry;  	} -	return __perf_evlist__tui_browse_hists(evlist, help, -					       timer, arg, delay_secs); +	return __perf_evlist__tui_browse_hists(evlist, nr_entries, help, +					       hbt, env);  } diff --git a/tools/perf/ui/browsers/scripts.c b/tools/perf/ui/browsers/scripts.c new file mode 100644 index 00000000000..cbbd44b0d93 --- /dev/null +++ b/tools/perf/ui/browsers/scripts.c @@ -0,0 +1,189 @@ +#include <elf.h> +#include <newt.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; +}; + +#define SCRIPT_NAMELEN	128 +#define SCRIPT_MAX_NO	64 +/* + * Usually the full path for a script is: + *	/home/username/libexec/perf-core/scripts/python/xxx.py + *	/home/username/libexec/perf-core/scripts/perl/xxx.pl + * So 256 should be long enough to contain the full path. + */ +#define SCRIPT_FULLPATH_LEN	256 + +/* + * 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) +{ +	char *buf, *names[SCRIPT_MAX_NO], *paths[SCRIPT_MAX_NO]; +	int i, num, choice, ret = -1; + +	/* Preset the script name to SCRIPT_NAMELEN */ +	buf = malloc(SCRIPT_MAX_NO * (SCRIPT_NAMELEN + SCRIPT_FULLPATH_LEN)); +	if (!buf) +		return ret; + +	for (i = 0; i < SCRIPT_MAX_NO; i++) { +		names[i] = buf + i * (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; +		} +	} + +	free(buf); +	return ret; +} + +static void script_browser__write(struct ui_browser *browser, +				   void *entry, int row) +{ +	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); + +	slsmg_write_nstring(sline->line, browser->width); +} + +static int script_browser__run(struct perf_script_browser *self) +{ +	int key; + +	if (ui_browser__show(&self->b, self->script_name, +			     "Press <- or ESC to exit") < 0) +		return -1; + +	while (1) { +		key = ui_browser__run(&self->b, 0); + +		/* We can add some special key handling here if needed */ +		break; +	} + +	ui_browser__hide(&self->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) +		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); + +	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; + +	if (line) +		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; +} diff --git a/tools/perf/ui/gtk/annotate.c b/tools/perf/ui/gtk/annotate.c new file mode 100644 index 00000000000..7d8dc581a54 --- /dev/null +++ b/tools/perf/ui/gtk/annotate.c @@ -0,0 +1,229 @@ +#include "gtk.h" +#include "util/debug.h" +#include "util/annotate.h" +#include "ui/helpline.h" + + +enum { +	ANN_COL__PERCENT, +	ANN_COL__OFFSET, +	ANN_COL__LINE, + +	MAX_ANN_COLS +}; + +static const char *const col_names[] = { +	"Overhead", +	"Offset", +	"Line" +}; + +static int perf_gtk__get_percent(char *buf, size_t size, struct symbol *sym, +				 struct disasm_line *dl, int evidx) +{ +	struct sym_hist *symhist; +	double percent = 0.0; +	const char *markup; +	int ret = 0; + +	strcpy(buf, ""); + +	if (dl->offset == (s64) -1) +		return 0; + +	symhist = annotation__histogram(symbol__annotation(sym), evidx); +	if (!symhist->addr[dl->offset]) +		return 0; + +	percent = 100.0 * symhist->addr[dl->offset] / symhist->sum; + +	markup = perf_gtk__get_percent_color(percent); +	if (markup) +		ret += scnprintf(buf, size, "%s", markup); +	ret += scnprintf(buf + ret, size - ret, "%6.2f%%", percent); +	if (markup) +		ret += scnprintf(buf + ret, size - ret, "</span>"); + +	return ret; +} + +static int perf_gtk__get_offset(char *buf, size_t size, struct symbol *sym, +				struct map *map, struct disasm_line *dl) +{ +	u64 start = map__rip_2objdump(map, sym->start); + +	strcpy(buf, ""); + +	if (dl->offset == (s64) -1) +		return 0; + +	return scnprintf(buf, size, "%"PRIx64, start + dl->offset); +} + +static int perf_gtk__get_line(char *buf, size_t size, struct disasm_line *dl) +{ +	int ret = 0; +	char *line = g_markup_escape_text(dl->line, -1); +	const char *markup = "<span fgcolor='gray'>"; + +	strcpy(buf, ""); + +	if (!line) +		return 0; + +	if (dl->offset != (s64) -1) +		markup = NULL; + +	if (markup) +		ret += scnprintf(buf, size, "%s", markup); +	ret += scnprintf(buf + ret, size - ret, "%s", line); +	if (markup) +		ret += scnprintf(buf + ret, size - ret, "</span>"); + +	g_free(line); +	return ret; +} + +static int perf_gtk__annotate_symbol(GtkWidget *window, struct symbol *sym, +				struct map *map, int evidx, +				struct hist_browser_timer *hbt __maybe_unused) +{ +	struct disasm_line *pos, *n; +	struct annotation *notes; +	GType col_types[MAX_ANN_COLS]; +	GtkCellRenderer *renderer; +	GtkListStore *store; +	GtkWidget *view; +	int i; +	char s[512]; + +	notes = symbol__annotation(sym); + +	for (i = 0; i < MAX_ANN_COLS; i++) { +		col_types[i] = G_TYPE_STRING; +	} +	store = gtk_list_store_newv(MAX_ANN_COLS, col_types); + +	view = gtk_tree_view_new(); +	renderer = gtk_cell_renderer_text_new(); + +	for (i = 0; i < MAX_ANN_COLS; i++) { +		gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), +					-1, col_names[i], renderer, "markup", +					i, NULL); +	} + +	gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store)); +	g_object_unref(GTK_TREE_MODEL(store)); + +	list_for_each_entry(pos, ¬es->src->source, node) { +		GtkTreeIter iter; + +		gtk_list_store_append(store, &iter); + +		if (perf_gtk__get_percent(s, sizeof(s), sym, pos, evidx)) +			gtk_list_store_set(store, &iter, ANN_COL__PERCENT, s, -1); +		if (perf_gtk__get_offset(s, sizeof(s), sym, map, pos)) +			gtk_list_store_set(store, &iter, ANN_COL__OFFSET, s, -1); +		if (perf_gtk__get_line(s, sizeof(s), pos)) +			gtk_list_store_set(store, &iter, ANN_COL__LINE, s, -1); +	} + +	gtk_container_add(GTK_CONTAINER(window), view); + +	list_for_each_entry_safe(pos, n, ¬es->src->source, node) { +		list_del(&pos->node); +		disasm_line__free(pos); +	} + +	return 0; +} + +int symbol__gtk_annotate(struct symbol *sym, struct map *map, int evidx, +			 struct hist_browser_timer *hbt) +{ +	GtkWidget *window; +	GtkWidget *notebook; +	GtkWidget *scrolled_window; +	GtkWidget *tab_label; + +	if (map->dso->annotate_warned) +		return -1; + +	if (symbol__annotate(sym, map, 0) < 0) { +		ui__error("%s", ui_helpline__current); +		return -1; +	} + +	if (perf_gtk__is_active_context(pgctx)) { +		window = pgctx->main_window; +		notebook = pgctx->notebook; +	} else { +		GtkWidget *vbox; +		GtkWidget *infobar; +		GtkWidget *statbar; + +		signal(SIGSEGV, perf_gtk__signal); +		signal(SIGFPE,  perf_gtk__signal); +		signal(SIGINT,  perf_gtk__signal); +		signal(SIGQUIT, perf_gtk__signal); +		signal(SIGTERM, perf_gtk__signal); + +		window = gtk_window_new(GTK_WINDOW_TOPLEVEL); +		gtk_window_set_title(GTK_WINDOW(window), "perf annotate"); + +		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(); +		pgctx->notebook = notebook; + +		gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0); + +		infobar = perf_gtk__setup_info_bar(); +		if (infobar) { +			gtk_box_pack_start(GTK_BOX(vbox), infobar, +					   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); +	} + +	scrolled_window = gtk_scrolled_window_new(NULL, NULL); +	tab_label = gtk_label_new(sym->name); + +	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), +				       GTK_POLICY_AUTOMATIC, +				       GTK_POLICY_AUTOMATIC); + +	gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled_window, +				 tab_label); + +	perf_gtk__annotate_symbol(scrolled_window, sym, map, evidx, hbt); +	return 0; +} + +void perf_gtk__show_annotations(void) +{ +	GtkWidget *window; + +	if (!perf_gtk__is_active_context(pgctx)) +		return; + +	window = pgctx->main_window; +	gtk_widget_show_all(window); + +	perf_gtk__resize_window(window); +	gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); + +	gtk_main(); + +	perf_gtk__deactivate_context(&pgctx); +} diff --git a/tools/perf/ui/gtk/browser.c b/tools/perf/ui/gtk/browser.c index 4125c628411..c95012cdb43 100644 --- a/tools/perf/ui/gtk/browser.c +++ b/tools/perf/ui/gtk/browser.c @@ -8,15 +8,13 @@  #include <signal.h> -#define MAX_COLUMNS			32 - -static void perf_gtk__signal(int sig) +void perf_gtk__signal(int sig)  {  	perf_gtk__exit(false);  	psignal(sig, "perf");  } -static void perf_gtk__resize_window(GtkWidget *window) +void perf_gtk__resize_window(GtkWidget *window)  {  	GdkRectangle rect;  	GdkScreen *screen; @@ -36,7 +34,7 @@ static void perf_gtk__resize_window(GtkWidget *window)  	gtk_window_resize(GTK_WINDOW(window), width, height);  } -static const char *perf_gtk__get_percent_color(double percent) +const char *perf_gtk__get_percent_color(double percent)  {  	if (percent >= MIN_RED)  		return "<span fgcolor='red'>"; @@ -45,155 +43,8 @@ static const char *perf_gtk__get_percent_color(double percent)  	return NULL;  } -#define HPP__COLOR_FN(_name, _field)						\ -static int perf_gtk__hpp_color_ ## _name(struct perf_hpp *hpp,			\ -					 struct hist_entry *he)			\ -{										\ -	struct hists *hists = he->hists;					\ -	double percent = 100.0 * he->stat._field / hists->stats.total_period;	\ -	const char *markup;							\ -	int ret = 0;								\ -										\ -	markup = perf_gtk__get_percent_color(percent);				\ -	if (markup)								\ -		ret += scnprintf(hpp->buf, hpp->size, "%s", markup);		\ -	ret += scnprintf(hpp->buf + ret, hpp->size - ret, "%6.2f%%", percent); 	\ -	if (markup)								\ -		ret += scnprintf(hpp->buf + ret, hpp->size - ret, "</span>"); 	\ -										\ -	return ret;								\ -} - -HPP__COLOR_FN(overhead, period) -HPP__COLOR_FN(overhead_sys, period_sys) -HPP__COLOR_FN(overhead_us, period_us) -HPP__COLOR_FN(overhead_guest_sys, period_guest_sys) -HPP__COLOR_FN(overhead_guest_us, period_guest_us) - -#undef HPP__COLOR_FN - -void perf_gtk__init_hpp(void) -{ -	perf_hpp__init(); - -	perf_hpp__format[PERF_HPP__OVERHEAD].color = -				perf_gtk__hpp_color_overhead; -	perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color = -				perf_gtk__hpp_color_overhead_sys; -	perf_hpp__format[PERF_HPP__OVERHEAD_US].color = -				perf_gtk__hpp_color_overhead_us; -	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color = -				perf_gtk__hpp_color_overhead_guest_sys; -	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color = -				perf_gtk__hpp_color_overhead_guest_us; -} - -static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists) -{ -	GType col_types[MAX_COLUMNS]; -	GtkCellRenderer *renderer; -	struct sort_entry *se; -	GtkListStore *store; -	struct rb_node *nd; -	GtkWidget *view; -	int i, col_idx; -	int nr_cols; -	char s[512]; - -	struct perf_hpp hpp = { -		.buf		= s, -		.size		= sizeof(s), -	}; - -	nr_cols = 0; - -	for (i = 0; i < PERF_HPP__MAX_INDEX; i++) { -		if (!perf_hpp__format[i].cond) -			continue; - -		col_types[nr_cols++] = G_TYPE_STRING; -	} - -	list_for_each_entry(se, &hist_entry__sort_list, list) { -		if (se->elide) -			continue; - -		col_types[nr_cols++] = G_TYPE_STRING; -	} - -	store = gtk_list_store_newv(nr_cols, col_types); - -	view = gtk_tree_view_new(); - -	renderer = gtk_cell_renderer_text_new(); - -	col_idx = 0; - -	for (i = 0; i < PERF_HPP__MAX_INDEX; i++) { -		if (!perf_hpp__format[i].cond) -			continue; - -		perf_hpp__format[i].header(&hpp); - -		gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -							    -1, s, -							    renderer, "markup", -							    col_idx++, NULL); -	} - -	list_for_each_entry(se, &hist_entry__sort_list, list) { -		if (se->elide) -			continue; - -		gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -							    -1, se->se_header, -							    renderer, "text", -							    col_idx++, NULL); -	} - -	gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store)); - -	g_object_unref(GTK_TREE_MODEL(store)); - -	for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { -		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); -		GtkTreeIter iter; - -		if (h->filtered) -			continue; - -		gtk_list_store_append(store, &iter); - -		col_idx = 0; - -		for (i = 0; i < PERF_HPP__MAX_INDEX; i++) { -			if (!perf_hpp__format[i].cond) -				continue; - -			if (perf_hpp__format[i].color) -				perf_hpp__format[i].color(&hpp, h); -			else -				perf_hpp__format[i].entry(&hpp, h); - -			gtk_list_store_set(store, &iter, col_idx++, s, -1); -		} - -		list_for_each_entry(se, &hist_entry__sort_list, list) { -			if (se->elide) -				continue; - -			se->se_snprintf(h, s, ARRAY_SIZE(s), -					hists__col_len(hists, se->se_width_idx)); - -			gtk_list_store_set(store, &iter, col_idx++, s, -1); -		} -	} - -	gtk_container_add(GTK_CONTAINER(window), view); -} -  #ifdef HAVE_GTK_INFO_BAR -static GtkWidget *perf_gtk__setup_info_bar(void) +GtkWidget *perf_gtk__setup_info_bar(void)  {  	GtkWidget *info_bar;  	GtkWidget *label; @@ -220,7 +71,7 @@ static GtkWidget *perf_gtk__setup_info_bar(void)  }  #endif -static GtkWidget *perf_gtk__setup_statusbar(void) +GtkWidget *perf_gtk__setup_statusbar(void)  {  	GtkWidget *stbar;  	unsigned ctxid; @@ -234,81 +85,3 @@ static GtkWidget *perf_gtk__setup_statusbar(void)  	return stbar;  } - -int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, -				  const char *help, -				  void (*timer) (void *arg)__maybe_unused, -				  void *arg __maybe_unused, -				  int delay_secs __maybe_unused) -{ -	struct perf_evsel *pos; -	GtkWidget *vbox; -	GtkWidget *notebook; -	GtkWidget *info_bar; -	GtkWidget *statbar; -	GtkWidget *window; - -	signal(SIGSEGV, perf_gtk__signal); -	signal(SIGFPE,  perf_gtk__signal); -	signal(SIGINT,  perf_gtk__signal); -	signal(SIGQUIT, perf_gtk__signal); -	signal(SIGTERM, perf_gtk__signal); - -	window = gtk_window_new(GTK_WINDOW_TOPLEVEL); - -	gtk_window_set_title(GTK_WINDOW(window), "perf report"); - -	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 = perf_evsel__name(pos); -		GtkWidget *scrolled_window; -		GtkWidget *tab_label; - -		scrolled_window = gtk_scrolled_window_new(NULL, NULL); - -		gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), -							GTK_POLICY_AUTOMATIC, -							GTK_POLICY_AUTOMATIC); - -		perf_gtk__show_hists(scrolled_window, hists); - -		tab_label = gtk_label_new(evname); - -		gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled_window, tab_label); -	} - -	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); - -	perf_gtk__resize_window(window); - -	gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); - -	ui_helpline__push(help); - -	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 687af0bba18..3d96785ef15 100644 --- a/tools/perf/ui/gtk/gtk.h +++ b/tools/perf/ui/gtk/gtk.h @@ -10,6 +10,7 @@  struct perf_gtk_context {  	GtkWidget *main_window; +	GtkWidget *notebook;  #ifdef HAVE_GTK_INFO_BAR  	GtkWidget *info_bar; @@ -30,9 +31,17 @@ struct perf_gtk_context *perf_gtk__activate_context(GtkWidget *window);  int perf_gtk__deactivate_context(struct perf_gtk_context **ctx);  void perf_gtk__init_helpline(void); +void perf_gtk__init_progress(void);  void perf_gtk__init_hpp(void); -#ifndef HAVE_GTK_INFO_BAR +void perf_gtk__signal(int sig); +void perf_gtk__resize_window(GtkWidget *window); +const char *perf_gtk__get_percent_color(double percent); +GtkWidget *perf_gtk__setup_statusbar(void); + +#ifdef HAVE_GTK_INFO_BAR +GtkWidget *perf_gtk__setup_info_bar(void); +#else  static inline GtkWidget *perf_gtk__setup_info_bar(void)  {  	return NULL; diff --git a/tools/perf/ui/gtk/helpline.c b/tools/perf/ui/gtk/helpline.c index 5db4432ff12..3388cbd1218 100644 --- a/tools/perf/ui/gtk/helpline.c +++ b/tools/perf/ui/gtk/helpline.c @@ -24,17 +24,7 @@ static void gtk_helpline_push(const char *msg)  			   pgctx->statbar_ctx_id, msg);  } -static struct ui_helpline gtk_helpline_fns = { -	.pop	= gtk_helpline_pop, -	.push	= gtk_helpline_push, -}; - -void perf_gtk__init_helpline(void) -{ -	helpline_fns = >k_helpline_fns; -} - -int perf_gtk__show_helpline(const char *fmt, va_list ap) +static int gtk_helpline_show(const char *fmt, va_list ap)  {  	int ret;  	char *ptr; @@ -54,3 +44,14 @@ int perf_gtk__show_helpline(const char *fmt, va_list ap)  	return ret;  } + +static struct ui_helpline gtk_helpline_fns = { +	.pop	= gtk_helpline_pop, +	.push	= gtk_helpline_push, +	.show	= gtk_helpline_show, +}; + +void perf_gtk__init_helpline(void) +{ +	helpline_fns = >k_helpline_fns; +} diff --git a/tools/perf/ui/gtk/hists.c b/tools/perf/ui/gtk/hists.c new file mode 100644 index 00000000000..1e764a8ad25 --- /dev/null +++ b/tools/perf/ui/gtk/hists.c @@ -0,0 +1,312 @@ +#include "../evlist.h" +#include "../cache.h" +#include "../evsel.h" +#include "../sort.h" +#include "../hist.h" +#include "../helpline.h" +#include "gtk.h" + +#define MAX_COLUMNS			32 + +static int __percent_color_snprintf(char *buf, size_t size, double percent) +{ +	int ret = 0; +	const char *markup; + +	markup = perf_gtk__get_percent_color(percent); +	if (markup) +		ret += scnprintf(buf, size, markup); + +	ret += scnprintf(buf + ret, size - ret, " %6.2f%%", percent); + +	if (markup) +		ret += scnprintf(buf + ret, size - ret, "</span>"); + +	return ret; +} + + +static int __hpp__color_fmt(struct perf_hpp *hpp, struct hist_entry *he, +			    u64 (*get_field)(struct hist_entry *)) +{ +	int ret; +	double percent = 0.0; +	struct hists *hists = he->hists; + +	if (hists->stats.total_period) +		percent = 100.0 * get_field(he) / hists->stats.total_period; + +	ret = __percent_color_snprintf(hpp->buf, hpp->size, percent); + +	if (symbol_conf.event_group) { +		int prev_idx, idx_delta; +		struct perf_evsel *evsel = hists_to_evsel(hists); +		struct hist_entry *pair; +		int nr_members = evsel->nr_members; + +		if (nr_members <= 1) +			return ret; + +		prev_idx = perf_evsel__group_idx(evsel); + +		list_for_each_entry(pair, &he->pairs.head, pairs.node) { +			u64 period = get_field(pair); +			u64 total = pair->hists->stats.total_period; + +			evsel = hists_to_evsel(pair->hists); +			idx_delta = perf_evsel__group_idx(evsel) - prev_idx - 1; + +			while (idx_delta--) { +				/* +				 * zero-fill group members in the middle which +				 * have no sample +				 */ +				ret += __percent_color_snprintf(hpp->buf + ret, +								hpp->size - ret, +								0.0); +			} + +			percent = 100.0 * period / total; +			ret += __percent_color_snprintf(hpp->buf + ret, +							hpp->size - ret, +							percent); + +			prev_idx = perf_evsel__group_idx(evsel); +		} + +		idx_delta = nr_members - prev_idx - 1; + +		while (idx_delta--) { +			/* +			 * zero-fill group members at last which have no sample +			 */ +			ret += __percent_color_snprintf(hpp->buf + ret, +							hpp->size - ret, +							0.0); +		} +	} +	return ret; +} + +#define __HPP_COLOR_PERCENT_FN(_type, _field)					\ +static u64 he_get_##_field(struct hist_entry *he)				\ +{										\ +	return he->stat._field;							\ +}										\ +										\ +static int perf_gtk__hpp_color_##_type(struct perf_hpp *hpp,			\ +				       struct hist_entry *he)			\ +{										\ +	return __hpp__color_fmt(hpp, he, he_get_##_field);			\ +} + +__HPP_COLOR_PERCENT_FN(overhead, period) +__HPP_COLOR_PERCENT_FN(overhead_sys, period_sys) +__HPP_COLOR_PERCENT_FN(overhead_us, period_us) +__HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys) +__HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us) + +#undef __HPP_COLOR_PERCENT_FN + + +void perf_gtk__init_hpp(void) +{ +	perf_hpp__column_enable(PERF_HPP__OVERHEAD); + +	perf_hpp__init(); + +	perf_hpp__format[PERF_HPP__OVERHEAD].color = +				perf_gtk__hpp_color_overhead; +	perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color = +				perf_gtk__hpp_color_overhead_sys; +	perf_hpp__format[PERF_HPP__OVERHEAD_US].color = +				perf_gtk__hpp_color_overhead_us; +	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color = +				perf_gtk__hpp_color_overhead_guest_sys; +	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color = +				perf_gtk__hpp_color_overhead_guest_us; +} + +static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists) +{ +	struct perf_hpp_fmt *fmt; +	GType col_types[MAX_COLUMNS]; +	GtkCellRenderer *renderer; +	struct sort_entry *se; +	GtkListStore *store; +	struct rb_node *nd; +	GtkWidget *view; +	int col_idx; +	int nr_cols; +	char s[512]; + +	struct perf_hpp hpp = { +		.buf		= s, +		.size		= sizeof(s), +		.ptr		= hists_to_evsel(hists), +	}; + +	nr_cols = 0; + +	perf_hpp__for_each_format(fmt) +		col_types[nr_cols++] = G_TYPE_STRING; + +	list_for_each_entry(se, &hist_entry__sort_list, list) { +		if (se->elide) +			continue; + +		col_types[nr_cols++] = G_TYPE_STRING; +	} + +	store = gtk_list_store_newv(nr_cols, col_types); + +	view = gtk_tree_view_new(); + +	renderer = gtk_cell_renderer_text_new(); + +	col_idx = 0; + +	perf_hpp__for_each_format(fmt) { +		fmt->header(&hpp); + +		gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), +							    -1, ltrim(s), +							    renderer, "markup", +							    col_idx++, NULL); +	} + +	list_for_each_entry(se, &hist_entry__sort_list, list) { +		if (se->elide) +			continue; + +		gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), +							    -1, se->se_header, +							    renderer, "text", +							    col_idx++, NULL); +	} + +	gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store)); + +	g_object_unref(GTK_TREE_MODEL(store)); + +	for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { +		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); +		GtkTreeIter iter; + +		if (h->filtered) +			continue; + +		gtk_list_store_append(store, &iter); + +		col_idx = 0; + +		perf_hpp__for_each_format(fmt) { +			if (fmt->color) +				fmt->color(&hpp, h); +			else +				fmt->entry(&hpp, h); + +			gtk_list_store_set(store, &iter, col_idx++, s, -1); +		} + +		list_for_each_entry(se, &hist_entry__sort_list, list) { +			if (se->elide) +				continue; + +			se->se_snprintf(h, s, ARRAY_SIZE(s), +					hists__col_len(hists, se->se_width_idx)); + +			gtk_list_store_set(store, &iter, col_idx++, s, -1); +		} +	} + +	gtk_container_add(GTK_CONTAINER(window), view); +} + +int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, +				  const char *help, +				  struct hist_browser_timer *hbt __maybe_unused) +{ +	struct perf_evsel *pos; +	GtkWidget *vbox; +	GtkWidget *notebook; +	GtkWidget *info_bar; +	GtkWidget *statbar; +	GtkWidget *window; + +	signal(SIGSEGV, perf_gtk__signal); +	signal(SIGFPE,  perf_gtk__signal); +	signal(SIGINT,  perf_gtk__signal); +	signal(SIGQUIT, perf_gtk__signal); +	signal(SIGTERM, perf_gtk__signal); + +	window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + +	gtk_window_set_title(GTK_WINDOW(window), "perf report"); + +	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(); + +	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); + +	list_for_each_entry(pos, &evlist->entries, node) { +		struct hists *hists = &pos->hists; +		const char *evname = perf_evsel__name(pos); +		GtkWidget *scrolled_window; +		GtkWidget *tab_label; +		char buf[512]; +		size_t size = sizeof(buf); + +		if (symbol_conf.event_group) { +			if (!perf_evsel__is_group_leader(pos)) +				continue; + +			if (pos->nr_members > 1) { +				perf_evsel__group_desc(pos, buf, size); +				evname = buf; +			} +		} + +		scrolled_window = gtk_scrolled_window_new(NULL, NULL); + +		gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), +							GTK_POLICY_AUTOMATIC, +							GTK_POLICY_AUTOMATIC); + +		perf_gtk__show_hists(scrolled_window, hists); + +		tab_label = gtk_label_new(evname); + +		gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled_window, tab_label); +	} + +	gtk_widget_show_all(window); + +	perf_gtk__resize_window(window); + +	gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); + +	ui_helpline__push(help); + +	gtk_main(); + +	perf_gtk__deactivate_context(&pgctx); + +	return 0; +} diff --git a/tools/perf/ui/gtk/progress.c b/tools/perf/ui/gtk/progress.c new file mode 100644 index 00000000000..482bcf3df9b --- /dev/null +++ b/tools/perf/ui/gtk/progress.c @@ -0,0 +1,59 @@ +#include <inttypes.h> + +#include "gtk.h" +#include "../progress.h" +#include "util.h" + +static GtkWidget *dialog; +static GtkWidget *progress; + +static void gtk_progress_update(u64 curr, u64 total, const char *title) +{ +	double fraction = total ? 1.0 * curr / total : 0.0; +	char buf[1024]; + +	if (dialog == NULL) { +		GtkWidget *vbox = gtk_vbox_new(TRUE, 5); +		GtkWidget *label = gtk_label_new(title); + +		dialog = gtk_window_new(GTK_WINDOW_TOPLEVEL); +		progress = gtk_progress_bar_new(); + +		gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, FALSE, 3); +		gtk_box_pack_start(GTK_BOX(vbox), progress, TRUE, TRUE, 3); + +		gtk_container_add(GTK_CONTAINER(dialog), vbox); + +		gtk_window_set_title(GTK_WINDOW(dialog), "perf"); +		gtk_window_resize(GTK_WINDOW(dialog), 300, 80); +		gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER); + +		gtk_widget_show_all(dialog); +	} + +	gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress), fraction); +	snprintf(buf, sizeof(buf), "%"PRIu64" / %"PRIu64, curr, total); +	gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress), buf); + +	/* we didn't call gtk_main yet, so do it manually */ +	while (gtk_events_pending()) +		gtk_main_iteration(); +} + +static void gtk_progress_finish(void) +{ +	/* this will also destroy all of its children */ +	gtk_widget_destroy(dialog); + +	dialog = NULL; +} + +static struct ui_progress gtk_progress_fns = { +	.update		= gtk_progress_update, +	.finish		= gtk_progress_finish, +}; + +void perf_gtk__init_progress(void) +{ +	progress_fns = >k_progress_fns; +} diff --git a/tools/perf/ui/gtk/setup.c b/tools/perf/ui/gtk/setup.c index 3c4c6ef7828..6c2dd2e423f 100644 --- a/tools/perf/ui/gtk/setup.c +++ b/tools/perf/ui/gtk/setup.c @@ -8,7 +8,9 @@ int perf_gtk__init(void)  {  	perf_error__register(&perf_gtk_eops);  	perf_gtk__init_helpline(); +	perf_gtk__init_progress();  	perf_gtk__init_hpp(); +  	return gtk_init_check(NULL, NULL) ? 0 : -1;  } diff --git a/tools/perf/ui/gtk/util.c b/tools/perf/ui/gtk/util.c index ccb046aac98..c06942a41c7 100644 --- a/tools/perf/ui/gtk/util.c +++ b/tools/perf/ui/gtk/util.c @@ -111,14 +111,3 @@ struct perf_error_ops perf_gtk_eops = {  	.warning	= perf_gtk__warning_statusbar,  #endif  }; - -/* - * FIXME: Functions below should be implemented properly. - *        For now, just add stubs for NO_NEWT=1 build. - */ -#ifndef NEWT_SUPPORT -void ui_progress__update(u64 curr __maybe_unused, u64 total __maybe_unused, -			 const char *title __maybe_unused) -{ -} -#endif diff --git a/tools/perf/ui/helpline.c b/tools/perf/ui/helpline.c index a49bcf3c190..700fb3cfa1c 100644 --- a/tools/perf/ui/helpline.c +++ b/tools/perf/ui/helpline.c @@ -16,9 +16,16 @@ static void nop_helpline__push(const char *msg __maybe_unused)  {  } +static int nop_helpline__show(const char *fmt __maybe_unused, +			       va_list ap __maybe_unused) +{ +	return 0; +} +  static struct ui_helpline default_helpline_fns = {  	.pop	= nop_helpline__pop,  	.push	= nop_helpline__push, +	.show	= nop_helpline__show,  };  struct ui_helpline *helpline_fns = &default_helpline_fns; @@ -59,3 +66,8 @@ void ui_helpline__puts(const char *msg)  	ui_helpline__pop();  	ui_helpline__push(msg);  } + +int ui_helpline__vshow(const char *fmt, va_list ap) +{ +	return helpline_fns->show(fmt, ap); +} diff --git a/tools/perf/ui/helpline.h b/tools/perf/ui/helpline.h index baa28a4d16b..46181f4fc07 100644 --- a/tools/perf/ui/helpline.h +++ b/tools/perf/ui/helpline.h @@ -9,6 +9,7 @@  struct ui_helpline {  	void (*pop)(void);  	void (*push)(const char *msg); +	int  (*show)(const char *fmt, va_list ap);  };  extern struct ui_helpline *helpline_fns; @@ -20,28 +21,9 @@ void ui_helpline__push(const char *msg);  void ui_helpline__vpush(const char *fmt, va_list ap);  void ui_helpline__fpush(const char *fmt, ...);  void ui_helpline__puts(const char *msg); +int  ui_helpline__vshow(const char *fmt, va_list ap);  extern char ui_helpline__current[512]; - -#ifdef NEWT_SUPPORT  extern char ui_helpline__last_msg[]; -int ui_helpline__show_help(const char *format, va_list ap); -#else -static inline int ui_helpline__show_help(const char *format __maybe_unused, -					 va_list ap __maybe_unused) -{ -	return 0; -} -#endif /* NEWT_SUPPORT */ - -#ifdef GTK2_SUPPORT -int perf_gtk__show_helpline(const char *format, va_list ap); -#else -static inline int perf_gtk__show_helpline(const char *format __maybe_unused, -					  va_list ap __maybe_unused) -{ -	return 0; -} -#endif /* GTK2_SUPPORT */  #endif /* _PERF_UI_HELPLINE_H_ */ diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c index f5a1e4f6526..d671e63aa35 100644 --- a/tools/perf/ui/hist.c +++ b/tools/perf/ui/hist.c @@ -3,151 +3,163 @@  #include "../util/hist.h"  #include "../util/util.h"  #include "../util/sort.h" - +#include "../util/evsel.h"  /* hist period print (hpp) functions */ -static int hpp__header_overhead(struct perf_hpp *hpp) -{ -	return scnprintf(hpp->buf, hpp->size, "Overhead"); -} -static int hpp__width_overhead(struct perf_hpp *hpp __maybe_unused) -{ -	return 8; -} +typedef int (*hpp_snprint_fn)(char *buf, size_t size, const char *fmt, ...); -static int hpp__color_overhead(struct perf_hpp *hpp, struct hist_entry *he) +static int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he, +		      u64 (*get_field)(struct hist_entry *), +		      const char *fmt, hpp_snprint_fn print_fn, +		      bool fmt_percent)  { +	int ret;  	struct hists *hists = he->hists; -	double percent = 100.0 * he->stat.period / hists->stats.total_period; -	return percent_color_snprintf(hpp->buf, hpp->size, " %6.2f%%", percent); -} +	if (fmt_percent) { +		double percent = 0.0; -static int hpp__entry_overhead(struct perf_hpp *hpp, struct hist_entry *he) -{ -	struct hists *hists = he->hists; -	double percent = 100.0 * he->stat.period / hists->stats.total_period; -	const char *fmt = symbol_conf.field_sep ? "%.2f" : " %6.2f%%"; +		if (hists->stats.total_period) +			percent = 100.0 * get_field(he) / +				  hists->stats.total_period; -	return scnprintf(hpp->buf, hpp->size, fmt, percent); -} +		ret = print_fn(hpp->buf, hpp->size, fmt, percent); +	} else +		ret = print_fn(hpp->buf, hpp->size, fmt, get_field(he)); -static int hpp__header_overhead_sys(struct perf_hpp *hpp) -{ -	const char *fmt = symbol_conf.field_sep ? "%s" : "%7s"; +	if (symbol_conf.event_group) { +		int prev_idx, idx_delta; +		struct perf_evsel *evsel = hists_to_evsel(hists); +		struct hist_entry *pair; +		int nr_members = evsel->nr_members; -	return scnprintf(hpp->buf, hpp->size, fmt, "sys"); -} +		if (nr_members <= 1) +			return ret; -static int hpp__width_overhead_sys(struct perf_hpp *hpp __maybe_unused) -{ -	return 7; -} +		prev_idx = perf_evsel__group_idx(evsel); -static int hpp__color_overhead_sys(struct perf_hpp *hpp, struct hist_entry *he) -{ -	struct hists *hists = he->hists; -	double percent = 100.0 * he->stat.period_sys / hists->stats.total_period; +		list_for_each_entry(pair, &he->pairs.head, pairs.node) { +			u64 period = get_field(pair); +			u64 total = pair->hists->stats.total_period; -	return percent_color_snprintf(hpp->buf, hpp->size, "%6.2f%%", percent); -} +			if (!total) +				continue; -static int hpp__entry_overhead_sys(struct perf_hpp *hpp, struct hist_entry *he) -{ -	struct hists *hists = he->hists; -	double percent = 100.0 * he->stat.period_sys / hists->stats.total_period; -	const char *fmt = symbol_conf.field_sep ? "%.2f" : "%6.2f%%"; +			evsel = hists_to_evsel(pair->hists); +			idx_delta = perf_evsel__group_idx(evsel) - prev_idx - 1; -	return scnprintf(hpp->buf, hpp->size, fmt, percent); -} +			while (idx_delta--) { +				/* +				 * zero-fill group members in the middle which +				 * have no sample +				 */ +				ret += print_fn(hpp->buf + ret, hpp->size - ret, +						fmt, 0); +			} -static int hpp__header_overhead_us(struct perf_hpp *hpp) -{ -	const char *fmt = symbol_conf.field_sep ? "%s" : "%7s"; - -	return scnprintf(hpp->buf, hpp->size, fmt, "user"); -} +			if (fmt_percent) +				ret += print_fn(hpp->buf + ret, hpp->size - ret, +						fmt, 100.0 * period / total); +			else +				ret += print_fn(hpp->buf + ret, hpp->size - ret, +						fmt, period); -static int hpp__width_overhead_us(struct perf_hpp *hpp __maybe_unused) -{ -	return 7; -} +			prev_idx = perf_evsel__group_idx(evsel); +		} -static int hpp__color_overhead_us(struct perf_hpp *hpp, struct hist_entry *he) -{ -	struct hists *hists = he->hists; -	double percent = 100.0 * he->stat.period_us / hists->stats.total_period; +		idx_delta = nr_members - prev_idx - 1; -	return percent_color_snprintf(hpp->buf, hpp->size, "%6.2f%%", percent); +		while (idx_delta--) { +			/* +			 * zero-fill group members at last which have no sample +			 */ +			ret += print_fn(hpp->buf + ret, hpp->size - ret, +					fmt, 0); +		} +	} +	return ret;  } -static int hpp__entry_overhead_us(struct perf_hpp *hpp, struct hist_entry *he) -{ -	struct hists *hists = he->hists; -	double percent = 100.0 * he->stat.period_us / hists->stats.total_period; -	const char *fmt = symbol_conf.field_sep ? "%.2f" : "%6.2f%%"; - -	return scnprintf(hpp->buf, hpp->size, fmt, percent); +#define __HPP_HEADER_FN(_type, _str, _min_width, _unit_width) 		\ +static int hpp__header_##_type(struct perf_hpp *hpp)			\ +{									\ +	int len = _min_width;						\ +									\ +	if (symbol_conf.event_group) {					\ +		struct perf_evsel *evsel = hpp->ptr;			\ +									\ +		len = max(len, evsel->nr_members * _unit_width);	\ +	}								\ +	return scnprintf(hpp->buf, hpp->size, "%*s", len, _str);	\  } -static int hpp__header_overhead_guest_sys(struct perf_hpp *hpp) -{ -	return scnprintf(hpp->buf, hpp->size, "guest sys"); +#define __HPP_WIDTH_FN(_type, _min_width, _unit_width) 			\ +static int hpp__width_##_type(struct perf_hpp *hpp __maybe_unused)	\ +{									\ +	int len = _min_width;						\ +									\ +	if (symbol_conf.event_group) {					\ +		struct perf_evsel *evsel = hpp->ptr;			\ +									\ +		len = max(len, evsel->nr_members * _unit_width);	\ +	}								\ +	return len;							\  } -static int hpp__width_overhead_guest_sys(struct perf_hpp *hpp __maybe_unused) -{ -	return 9; +#define __HPP_COLOR_PERCENT_FN(_type, _field)					\ +static u64 he_get_##_field(struct hist_entry *he)				\ +{										\ +	return he->stat._field;							\ +}										\ +										\ +static int hpp__color_##_type(struct perf_hpp *hpp, struct hist_entry *he) 	\ +{										\ +	return __hpp__fmt(hpp, he, he_get_##_field, " %6.2f%%",			\ +			  (hpp_snprint_fn)percent_color_snprintf, true);	\  } -static int hpp__color_overhead_guest_sys(struct perf_hpp *hpp, -					 struct hist_entry *he) -{ -	struct hists *hists = he->hists; -	double percent = 100.0 * he->stat.period_guest_sys / hists->stats.total_period; - -	return percent_color_snprintf(hpp->buf, hpp->size, " %6.2f%% ", percent); +#define __HPP_ENTRY_PERCENT_FN(_type, _field)					\ +static int hpp__entry_##_type(struct perf_hpp *hpp, struct hist_entry *he) 	\ +{										\ +	const char *fmt = symbol_conf.field_sep ? " %.2f" : " %6.2f%%";		\ +	return __hpp__fmt(hpp, he, he_get_##_field, fmt,			\ +			  scnprintf, true);					\  } -static int hpp__entry_overhead_guest_sys(struct perf_hpp *hpp, -					 struct hist_entry *he) -{ -	struct hists *hists = he->hists; -	double percent = 100.0 * he->stat.period_guest_sys / hists->stats.total_period; -	const char *fmt = symbol_conf.field_sep ? "%.2f" : " %6.2f%% "; - -	return scnprintf(hpp->buf, hpp->size, fmt, percent); +#define __HPP_ENTRY_RAW_FN(_type, _field)					\ +static u64 he_get_raw_##_field(struct hist_entry *he)				\ +{										\ +	return he->stat._field;							\ +}										\ +										\ +static int hpp__entry_##_type(struct perf_hpp *hpp, struct hist_entry *he) 	\ +{										\ +	const char *fmt = symbol_conf.field_sep ? " %"PRIu64 : " %11"PRIu64;	\ +	return __hpp__fmt(hpp, he, he_get_raw_##_field, fmt, scnprintf, false);	\  } -static int hpp__header_overhead_guest_us(struct perf_hpp *hpp) -{ -	return scnprintf(hpp->buf, hpp->size, "guest usr"); -} +#define HPP_PERCENT_FNS(_type, _str, _field, _min_width, _unit_width)	\ +__HPP_HEADER_FN(_type, _str, _min_width, _unit_width)			\ +__HPP_WIDTH_FN(_type, _min_width, _unit_width)				\ +__HPP_COLOR_PERCENT_FN(_type, _field)					\ +__HPP_ENTRY_PERCENT_FN(_type, _field) -static int hpp__width_overhead_guest_us(struct perf_hpp *hpp __maybe_unused) -{ -	return 9; -} +#define HPP_RAW_FNS(_type, _str, _field, _min_width, _unit_width)	\ +__HPP_HEADER_FN(_type, _str, _min_width, _unit_width)			\ +__HPP_WIDTH_FN(_type, _min_width, _unit_width)				\ +__HPP_ENTRY_RAW_FN(_type, _field) -static int hpp__color_overhead_guest_us(struct perf_hpp *hpp, -					struct hist_entry *he) -{ -	struct hists *hists = he->hists; -	double percent = 100.0 * he->stat.period_guest_us / hists->stats.total_period; -	return percent_color_snprintf(hpp->buf, hpp->size, " %6.2f%% ", percent); -} +HPP_PERCENT_FNS(overhead, "Overhead", period, 8, 8) +HPP_PERCENT_FNS(overhead_sys, "sys", period_sys, 8, 8) +HPP_PERCENT_FNS(overhead_us, "usr", period_us, 8, 8) +HPP_PERCENT_FNS(overhead_guest_sys, "guest sys", period_guest_sys, 9, 8) +HPP_PERCENT_FNS(overhead_guest_us, "guest usr", period_guest_us, 9, 8) -static int hpp__entry_overhead_guest_us(struct perf_hpp *hpp, -					struct hist_entry *he) -{ -	struct hists *hists = he->hists; -	double percent = 100.0 * he->stat.period_guest_us / hists->stats.total_period; -	const char *fmt = symbol_conf.field_sep ? "%.2f" : " %6.2f%% "; +HPP_RAW_FNS(samples, "Samples", nr_events, 12, 12) +HPP_RAW_FNS(period, "Period", period, 12, 12) -	return scnprintf(hpp->buf, hpp->size, fmt, percent); -}  static int hpp__header_baseline(struct perf_hpp *hpp)  { @@ -161,7 +173,7 @@ static int hpp__width_baseline(struct perf_hpp *hpp __maybe_unused)  static double baseline_percent(struct hist_entry *he)  { -	struct hist_entry *pair = he->pair; +	struct hist_entry *pair = hist_entry__next_pair(he);  	struct hists *pair_hists = pair ? pair->hists : NULL;  	double percent = 0.0; @@ -179,7 +191,10 @@ static int hpp__color_baseline(struct perf_hpp *hpp, struct hist_entry *he)  {  	double percent = baseline_percent(he); -	return percent_color_snprintf(hpp->buf, hpp->size, " %6.2f%%", percent); +	if (hist_entry__has_pairs(he) || symbol_conf.field_sep) +		return percent_color_snprintf(hpp->buf, hpp->size, " %6.2f%%", percent); +	else +		return scnprintf(hpp->buf, hpp->size, "        ");  }  static int hpp__entry_baseline(struct perf_hpp *hpp, struct hist_entry *he) @@ -187,45 +202,31 @@ static int hpp__entry_baseline(struct perf_hpp *hpp, struct hist_entry *he)  	double percent = baseline_percent(he);  	const char *fmt = symbol_conf.field_sep ? "%.2f" : " %6.2f%%"; -	return scnprintf(hpp->buf, hpp->size, fmt, percent); +	if (hist_entry__has_pairs(he) || symbol_conf.field_sep) +		return scnprintf(hpp->buf, hpp->size, fmt, percent); +	else +		return scnprintf(hpp->buf, hpp->size, "            ");  } -static int hpp__header_samples(struct perf_hpp *hpp) -{ -	const char *fmt = symbol_conf.field_sep ? "%s" : "%11s"; - -	return scnprintf(hpp->buf, hpp->size, fmt, "Samples"); -} - -static int hpp__width_samples(struct perf_hpp *hpp __maybe_unused) -{ -	return 11; -} - -static int hpp__entry_samples(struct perf_hpp *hpp, struct hist_entry *he) -{ -	const char *fmt = symbol_conf.field_sep ? "%" PRIu64 : "%11" PRIu64; - -	return scnprintf(hpp->buf, hpp->size, fmt, he->stat.nr_events); -} - -static int hpp__header_period(struct perf_hpp *hpp) +static int hpp__header_period_baseline(struct perf_hpp *hpp)  {  	const char *fmt = symbol_conf.field_sep ? "%s" : "%12s"; -	return scnprintf(hpp->buf, hpp->size, fmt, "Period"); +	return scnprintf(hpp->buf, hpp->size, fmt, "Period Base");  } -static int hpp__width_period(struct perf_hpp *hpp __maybe_unused) +static int hpp__width_period_baseline(struct perf_hpp *hpp __maybe_unused)  {  	return 12;  } -static int hpp__entry_period(struct perf_hpp *hpp, struct hist_entry *he) +static int hpp__entry_period_baseline(struct perf_hpp *hpp, struct hist_entry *he)  { +	struct hist_entry *pair = hist_entry__next_pair(he); +	u64 period = pair ? pair->stat.period : 0;  	const char *fmt = symbol_conf.field_sep ? "%" PRIu64 : "%12" PRIu64; -	return scnprintf(hpp->buf, hpp->size, fmt, he->stat.period); +	return scnprintf(hpp->buf, hpp->size, fmt, period);  }  static int hpp__header_delta(struct perf_hpp *hpp) @@ -242,104 +243,188 @@ static int hpp__width_delta(struct perf_hpp *hpp __maybe_unused)  static int hpp__entry_delta(struct perf_hpp *hpp, struct hist_entry *he)  { -	struct hist_entry *pair = he->pair; -	struct hists *pair_hists = pair ? pair->hists : NULL; -	struct hists *hists = he->hists; -	u64 old_total, new_total; -	double old_percent = 0, new_percent = 0; -	double diff; +	struct hist_entry *pair = hist_entry__next_pair(he);  	const char *fmt = symbol_conf.field_sep ? "%s" : "%7.7s";  	char buf[32] = " "; +	double diff = 0.0; -	old_total = pair_hists ? pair_hists->stats.total_period : 0; -	if (old_total > 0 && pair) -		old_percent = 100.0 * pair->stat.period / old_total; - -	new_total = hists->stats.total_period; -	if (new_total > 0) -		new_percent = 100.0 * he->stat.period / new_total; +	if (pair) { +		if (he->diff.computed) +			diff = he->diff.period_ratio_delta; +		else +			diff = perf_diff__compute_delta(he, pair); +	} else +		diff = perf_diff__period_percent(he, he->stat.period); -	diff = new_percent - old_percent;  	if (fabs(diff) >= 0.01)  		scnprintf(buf, sizeof(buf), "%+4.2F%%", diff);  	return scnprintf(hpp->buf, hpp->size, fmt, buf);  } -static int hpp__header_displ(struct perf_hpp *hpp) +static int hpp__header_ratio(struct perf_hpp *hpp)  { -	return scnprintf(hpp->buf, hpp->size, "Displ."); +	const char *fmt = symbol_conf.field_sep ? "%s" : "%14s"; + +	return scnprintf(hpp->buf, hpp->size, fmt, "Ratio");  } -static int hpp__width_displ(struct perf_hpp *hpp __maybe_unused) +static int hpp__width_ratio(struct perf_hpp *hpp __maybe_unused)  { -	return 6; +	return 14;  } -static int hpp__entry_displ(struct perf_hpp *hpp, -			    struct hist_entry *he) +static int hpp__entry_ratio(struct perf_hpp *hpp, struct hist_entry *he)  { -	struct hist_entry *pair = he->pair; -	long displacement = pair ? pair->position - he->position : 0; -	const char *fmt = symbol_conf.field_sep ? "%s" : "%6.6s"; +	struct hist_entry *pair = hist_entry__next_pair(he); +	const char *fmt = symbol_conf.field_sep ? "%s" : "%14s";  	char buf[32] = " "; +	double ratio = 0.0; + +	if (pair) { +		if (he->diff.computed) +			ratio = he->diff.period_ratio; +		else +			ratio = perf_diff__compute_ratio(he, pair); +	} -	if (displacement) -		scnprintf(buf, sizeof(buf), "%+4ld", displacement); +	if (ratio > 0.0) +		scnprintf(buf, sizeof(buf), "%+14.6F", ratio);  	return scnprintf(hpp->buf, hpp->size, fmt, buf);  } -#define HPP__COLOR_PRINT_FNS(_name)		\ -	.header	= hpp__header_ ## _name,		\ -	.width	= hpp__width_ ## _name,		\ -	.color	= hpp__color_ ## _name,		\ -	.entry	= hpp__entry_ ## _name +static int hpp__header_wdiff(struct perf_hpp *hpp) +{ +	const char *fmt = symbol_conf.field_sep ? "%s" : "%14s"; + +	return scnprintf(hpp->buf, hpp->size, fmt, "Weighted diff"); +} + +static int hpp__width_wdiff(struct perf_hpp *hpp __maybe_unused) +{ +	return 14; +} -#define HPP__PRINT_FNS(_name)			\ -	.header	= hpp__header_ ## _name,		\ -	.width	= hpp__width_ ## _name,		\ -	.entry	= hpp__entry_ ## _name +static int hpp__entry_wdiff(struct perf_hpp *hpp, struct hist_entry *he) +{ +	struct hist_entry *pair = hist_entry__next_pair(he); +	const char *fmt = symbol_conf.field_sep ? "%s" : "%14s"; +	char buf[32] = " "; +	s64 wdiff = 0; + +	if (pair) { +		if (he->diff.computed) +			wdiff = he->diff.wdiff; +		else +			wdiff = perf_diff__compute_wdiff(he, pair); +	} + +	if (wdiff != 0) +		scnprintf(buf, sizeof(buf), "%14ld", wdiff); + +	return scnprintf(hpp->buf, hpp->size, fmt, buf); +} + +static int hpp__header_formula(struct perf_hpp *hpp) +{ +	const char *fmt = symbol_conf.field_sep ? "%s" : "%70s"; + +	return scnprintf(hpp->buf, hpp->size, fmt, "Formula"); +} + +static int hpp__width_formula(struct perf_hpp *hpp __maybe_unused) +{ +	return 70; +} + +static int hpp__entry_formula(struct perf_hpp *hpp, struct hist_entry *he) +{ +	struct hist_entry *pair = hist_entry__next_pair(he); +	const char *fmt = symbol_conf.field_sep ? "%s" : "%-70s"; +	char buf[96] = " "; + +	if (pair) +		perf_diff__formula(he, pair, buf, sizeof(buf)); + +	return scnprintf(hpp->buf, hpp->size, fmt, buf); +} + +#define HPP__COLOR_PRINT_FNS(_name)			\ +	{						\ +		.header	= hpp__header_ ## _name,	\ +		.width	= hpp__width_ ## _name,		\ +		.color	= hpp__color_ ## _name,		\ +		.entry	= hpp__entry_ ## _name		\ +	} + +#define HPP__PRINT_FNS(_name)				\ +	{						\ +		.header	= hpp__header_ ## _name,	\ +		.width	= hpp__width_ ## _name,		\ +		.entry	= hpp__entry_ ## _name		\ +	}  struct perf_hpp_fmt perf_hpp__format[] = { -	{ .cond = false, HPP__COLOR_PRINT_FNS(baseline) }, -	{ .cond = true,  HPP__COLOR_PRINT_FNS(overhead) }, -	{ .cond = false, HPP__COLOR_PRINT_FNS(overhead_sys) }, -	{ .cond = false, HPP__COLOR_PRINT_FNS(overhead_us) }, -	{ .cond = false, HPP__COLOR_PRINT_FNS(overhead_guest_sys) }, -	{ .cond = false, HPP__COLOR_PRINT_FNS(overhead_guest_us) }, -	{ .cond = false, HPP__PRINT_FNS(samples) }, -	{ .cond = false, HPP__PRINT_FNS(period) }, -	{ .cond = false, HPP__PRINT_FNS(delta) }, -	{ .cond = false, HPP__PRINT_FNS(displ) } +	HPP__COLOR_PRINT_FNS(baseline), +	HPP__COLOR_PRINT_FNS(overhead), +	HPP__COLOR_PRINT_FNS(overhead_sys), +	HPP__COLOR_PRINT_FNS(overhead_us), +	HPP__COLOR_PRINT_FNS(overhead_guest_sys), +	HPP__COLOR_PRINT_FNS(overhead_guest_us), +	HPP__PRINT_FNS(samples), +	HPP__PRINT_FNS(period), +	HPP__PRINT_FNS(period_baseline), +	HPP__PRINT_FNS(delta), +	HPP__PRINT_FNS(ratio), +	HPP__PRINT_FNS(wdiff), +	HPP__PRINT_FNS(formula)  }; +LIST_HEAD(perf_hpp__list); + +  #undef HPP__COLOR_PRINT_FNS  #undef HPP__PRINT_FNS +#undef HPP_PERCENT_FNS +#undef HPP_RAW_FNS + +#undef __HPP_HEADER_FN +#undef __HPP_WIDTH_FN +#undef __HPP_COLOR_PERCENT_FN +#undef __HPP_ENTRY_PERCENT_FN +#undef __HPP_ENTRY_RAW_FN + +  void perf_hpp__init(void)  {  	if (symbol_conf.show_cpu_utilization) { -		perf_hpp__format[PERF_HPP__OVERHEAD_SYS].cond = true; -		perf_hpp__format[PERF_HPP__OVERHEAD_US].cond = true; +		perf_hpp__column_enable(PERF_HPP__OVERHEAD_SYS); +		perf_hpp__column_enable(PERF_HPP__OVERHEAD_US);  		if (perf_guest) { -			perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].cond = true; -			perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].cond = true; +			perf_hpp__column_enable(PERF_HPP__OVERHEAD_GUEST_SYS); +			perf_hpp__column_enable(PERF_HPP__OVERHEAD_GUEST_US);  		}  	}  	if (symbol_conf.show_nr_samples) -		perf_hpp__format[PERF_HPP__SAMPLES].cond = true; +		perf_hpp__column_enable(PERF_HPP__SAMPLES);  	if (symbol_conf.show_total_period) -		perf_hpp__format[PERF_HPP__PERIOD].cond = true; +		perf_hpp__column_enable(PERF_HPP__PERIOD);  } -void perf_hpp__column_enable(unsigned col, bool enable) +void perf_hpp__column_register(struct perf_hpp_fmt *format) +{ +	list_add_tail(&format->list, &perf_hpp__list); +} + +void perf_hpp__column_enable(unsigned col)  {  	BUG_ON(col >= PERF_HPP__MAX_INDEX); -	perf_hpp__format[col].cond = enable; +	perf_hpp__column_register(&perf_hpp__format[col]);  }  static inline void advance_hpp(struct perf_hpp *hpp, int inc) @@ -352,27 +437,29 @@ int hist_entry__period_snprintf(struct perf_hpp *hpp, struct hist_entry *he,  				bool color)  {  	const char *sep = symbol_conf.field_sep; +	struct perf_hpp_fmt *fmt;  	char *start = hpp->buf; -	int i, ret; +	int ret;  	bool first = true;  	if (symbol_conf.exclude_other && !he->parent)  		return 0; -	for (i = 0; i < PERF_HPP__MAX_INDEX; i++) { -		if (!perf_hpp__format[i].cond) -			continue; - +	perf_hpp__for_each_format(fmt) { +		/* +		 * If there's no field_sep, we still need +		 * to display initial '  '. +		 */  		if (!sep || !first) {  			ret = scnprintf(hpp->buf, hpp->size, "%s", sep ?: "  ");  			advance_hpp(hpp, ret); +		} else  			first = false; -		} -		if (color && perf_hpp__format[i].color) -			ret = perf_hpp__format[i].color(hpp, he); +		if (color && fmt->color) +			ret = fmt->color(hpp, he);  		else -			ret = perf_hpp__format[i].entry(hpp, he); +			ret = fmt->entry(hpp, he);  		advance_hpp(hpp, ret);  	} @@ -404,16 +491,18 @@ int hist_entry__sort_snprintf(struct hist_entry *he, char *s, size_t size,   */  unsigned int hists__sort_list_width(struct hists *hists)  { +	struct perf_hpp_fmt *fmt;  	struct sort_entry *se; -	int i, ret = 0; +	int i = 0, ret = 0; +	struct perf_hpp dummy_hpp = { +		.ptr	= hists_to_evsel(hists), +	}; -	for (i = 0; i < PERF_HPP__MAX_INDEX; i++) { -		if (!perf_hpp__format[i].cond) -			continue; +	perf_hpp__for_each_format(fmt) {  		if (i)  			ret += 2; -		ret += perf_hpp__format[i].width(NULL); +		ret += fmt->width(&dummy_hpp);  	}  	list_for_each_entry(se, &hist_entry__sort_list, list) diff --git a/tools/perf/ui/keysyms.h b/tools/perf/ui/keysyms.h index 809eca5707f..65092d576b4 100644 --- a/tools/perf/ui/keysyms.h +++ b/tools/perf/ui/keysyms.h @@ -23,5 +23,6 @@  #define K_TIMER	 -1  #define K_ERROR	 -2  #define K_RESIZE -3 +#define K_SWITCH_INPUT_DATA -4  #endif /* _PERF_KEYSYMS_H_ */ diff --git a/tools/perf/ui/progress.c b/tools/perf/ui/progress.c index 13aa64e50e1..3ec695607a4 100644 --- a/tools/perf/ui/progress.c +++ b/tools/perf/ui/progress.c @@ -1,32 +1,26 @@  #include "../cache.h"  #include "progress.h" -#include "libslang.h" -#include "ui.h" -#include "browser.h" -void ui_progress__update(u64 curr, u64 total, const char *title) +static void nop_progress_update(u64 curr __maybe_unused, +				u64 total __maybe_unused, +				const char *title __maybe_unused)  { -	int bar, y; -	/* -	 * FIXME: We should have a per UI backend way of showing progress, -	 * stdio will just show a percentage as NN%, etc. -	 */ -	if (use_browser <= 0) -		return; +} -	if (total == 0) -		return; +static struct ui_progress default_progress_fns = +{ +	.update		= nop_progress_update, +}; -	ui__refresh_dimensions(true); -	pthread_mutex_lock(&ui__lock); -	y = SLtt_Screen_Rows / 2 - 2; -	SLsmg_set_color(0); -	SLsmg_draw_box(y, 0, 3, SLtt_Screen_Cols); -	SLsmg_gotorc(y++, 1); -	SLsmg_write_string((char *)title); -	SLsmg_set_color(HE_COLORSET_SELECTED); -	bar = ((SLtt_Screen_Cols - 2) * curr) / total; -	SLsmg_fill_region(y, 1, 1, bar, ' '); -	SLsmg_refresh(); -	pthread_mutex_unlock(&ui__lock); +struct ui_progress *progress_fns = &default_progress_fns; + +void ui_progress__update(u64 curr, u64 total, const char *title) +{ +	return progress_fns->update(curr, total, title); +} + +void ui_progress__finish(void) +{ +	if (progress_fns->finish) +		progress_fns->finish();  } diff --git a/tools/perf/ui/progress.h b/tools/perf/ui/progress.h index d9c205b59aa..257cc224f9c 100644 --- a/tools/perf/ui/progress.h +++ b/tools/perf/ui/progress.h @@ -3,6 +3,16 @@  #include <../types.h> +struct ui_progress { +	void (*update)(u64, u64, const char *); +	void (*finish)(void); +}; + +extern struct ui_progress *progress_fns; + +void ui_progress__init(void); +  void ui_progress__update(u64 curr, u64 total, const char *title); +void ui_progress__finish(void);  #endif diff --git a/tools/perf/ui/setup.c b/tools/perf/ui/setup.c index ebb4cc10787..ae6a789cb0f 100644 --- a/tools/perf/ui/setup.c +++ b/tools/perf/ui/setup.c @@ -8,7 +8,7 @@ pthread_mutex_t ui__lock = PTHREAD_MUTEX_INITIALIZER;  void setup_browser(bool fallback_to_pager)  { -	if (!isatty(1) || dump_trace) +	if (use_browser < 2 && (!isatty(1) || dump_trace))  		use_browser = 0;  	/* default to TUI */ @@ -30,6 +30,7 @@ void setup_browser(bool fallback_to_pager)  		if (fallback_to_pager)  			setup_pager(); +		perf_hpp__column_enable(PERF_HPP__OVERHEAD);  		perf_hpp__init();  		break;  	} diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c index fbd4e32d074..ff1f60cf442 100644 --- a/tools/perf/ui/stdio/hist.c +++ b/tools/perf/ui/stdio/hist.c @@ -3,6 +3,7 @@  #include "../../util/util.h"  #include "../../util/hist.h"  #include "../../util/sort.h" +#include "../../util/evsel.h"  static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin) @@ -335,17 +336,19 @@ static int hist_entry__fprintf(struct hist_entry *he, size_t size,  size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,  		      int max_cols, FILE *fp)  { +	struct perf_hpp_fmt *fmt;  	struct sort_entry *se;  	struct rb_node *nd;  	size_t ret = 0;  	unsigned int width;  	const char *sep = symbol_conf.field_sep;  	const char *col_width = symbol_conf.col_width_list_str; -	int idx, nr_rows = 0; -	char bf[64]; +	int nr_rows = 0; +	char bf[96];  	struct perf_hpp dummy_hpp = {  		.buf	= bf,  		.size	= sizeof(bf), +		.ptr	= hists_to_evsel(hists),  	};  	bool first = true; @@ -355,16 +358,14 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,  		goto print_entries;  	fprintf(fp, "# "); -	for (idx = 0; idx < PERF_HPP__MAX_INDEX; idx++) { -		if (!perf_hpp__format[idx].cond) -			continue; +	perf_hpp__for_each_format(fmt) {  		if (!first)  			fprintf(fp, "%s", sep ?: "  ");  		else  			first = false; -		perf_hpp__format[idx].header(&dummy_hpp); +		fmt->header(&dummy_hpp);  		fprintf(fp, "%s", bf);  	} @@ -400,18 +401,16 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,  	first = true;  	fprintf(fp, "# "); -	for (idx = 0; idx < PERF_HPP__MAX_INDEX; idx++) { -		unsigned int i; -		if (!perf_hpp__format[idx].cond) -			continue; +	perf_hpp__for_each_format(fmt) { +		unsigned int i;  		if (!first)  			fprintf(fp, "%s", sep ?: "  ");  		else  			first = false; -		width = perf_hpp__format[idx].width(&dummy_hpp); +		width = fmt->width(&dummy_hpp);  		for (i = 0; i < width; i++)  			fprintf(fp, ".");  	} @@ -462,7 +461,7 @@ out:  	return ret;  } -size_t hists__fprintf_nr_events(struct hists *hists, FILE *fp) +size_t events_stats__fprintf(struct events_stats *stats, FILE *fp)  {  	int i;  	size_t ret = 0; @@ -470,7 +469,7 @@ size_t hists__fprintf_nr_events(struct hists *hists, FILE *fp)  	for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) {  		const char *name; -		if (hists->stats.nr_events[i] == 0) +		if (stats->nr_events[i] == 0)  			continue;  		name = perf_event__name(i); @@ -478,7 +477,7 @@ size_t hists__fprintf_nr_events(struct hists *hists, FILE *fp)  			continue;  		ret += fprintf(fp, "%16s events: %10d\n", name, -			       hists->stats.nr_events[i]); +			       stats->nr_events[i]);  	}  	return ret; diff --git a/tools/perf/ui/tui/helpline.c b/tools/perf/ui/tui/helpline.c index 2884d2f41e3..1c8b9afd5d6 100644 --- a/tools/perf/ui/tui/helpline.c +++ b/tools/perf/ui/tui/helpline.c @@ -8,6 +8,8 @@  #include "../ui.h"  #include "../libslang.h" +char ui_helpline__last_msg[1024]; +  static void tui_helpline__pop(void)  {  } @@ -23,20 +25,7 @@ static void tui_helpline__push(const char *msg)  	strncpy(ui_helpline__current, msg, sz)[sz - 1] = '\0';  } -struct ui_helpline tui_helpline_fns = { -	.pop	= tui_helpline__pop, -	.push	= tui_helpline__push, -}; - -void ui_helpline__init(void) -{ -	helpline_fns = &tui_helpline_fns; -	ui_helpline__puts(" "); -} - -char ui_helpline__last_msg[1024]; - -int ui_helpline__show_help(const char *format, va_list ap) +static int tui_helpline__show(const char *format, va_list ap)  {  	int ret;  	static int backlog; @@ -55,3 +44,15 @@ int ui_helpline__show_help(const char *format, va_list ap)  	return ret;  } + +struct ui_helpline tui_helpline_fns = { +	.pop	= tui_helpline__pop, +	.push	= tui_helpline__push, +	.show	= tui_helpline__show, +}; + +void ui_helpline__init(void) +{ +	helpline_fns = &tui_helpline_fns; +	ui_helpline__puts(" "); +} diff --git a/tools/perf/ui/tui/progress.c b/tools/perf/ui/tui/progress.c new file mode 100644 index 00000000000..6c2184d53cb --- /dev/null +++ b/tools/perf/ui/tui/progress.c @@ -0,0 +1,42 @@ +#include "../cache.h" +#include "../progress.h" +#include "../libslang.h" +#include "../ui.h" +#include "../browser.h" + +static void tui_progress__update(u64 curr, u64 total, const char *title) +{ +	int bar, y; +	/* +	 * FIXME: We should have a per UI backend way of showing progress, +	 * stdio will just show a percentage as NN%, etc. +	 */ +	if (use_browser <= 0) +		return; + +	if (total == 0) +		return; + +	ui__refresh_dimensions(true); +	pthread_mutex_lock(&ui__lock); +	y = SLtt_Screen_Rows / 2 - 2; +	SLsmg_set_color(0); +	SLsmg_draw_box(y, 0, 3, SLtt_Screen_Cols); +	SLsmg_gotorc(y++, 1); +	SLsmg_write_string((char *)title); +	SLsmg_set_color(HE_COLORSET_SELECTED); +	bar = ((SLtt_Screen_Cols - 2) * curr) / total; +	SLsmg_fill_region(y, 1, 1, bar, ' '); +	SLsmg_refresh(); +	pthread_mutex_unlock(&ui__lock); +} + +static struct ui_progress tui_progress_fns = +{ +	.update		= tui_progress__update, +}; + +void ui_progress__init(void) +{ +	progress_fns = &tui_progress_fns; +} diff --git a/tools/perf/ui/tui/setup.c b/tools/perf/ui/tui/setup.c index 60debb81537..81efa192e86 100644 --- a/tools/perf/ui/tui/setup.c +++ b/tools/perf/ui/tui/setup.c @@ -118,6 +118,7 @@ int ui__init(void)  	newtSetSuspendCallback(newt_suspend, NULL);  	ui_helpline__init();  	ui_browser__init(); +	ui_progress__init();  	signal(SIGSEGV, ui__signal);  	signal(SIGFPE, ui__signal); diff --git a/tools/perf/ui/ui.h b/tools/perf/ui/ui.h index 7b67045479f..d86359c9990 100644 --- a/tools/perf/ui/ui.h +++ b/tools/perf/ui/ui.h @@ -3,9 +3,37 @@  #include <pthread.h>  #include <stdbool.h> +#include <linux/compiler.h>  extern pthread_mutex_t ui__lock; +extern int use_browser; + +void setup_browser(bool fallback_to_pager); +void exit_browser(bool wait_for_ok); + +#ifdef NEWT_SUPPORT +int ui__init(void); +void ui__exit(bool wait_for_ok); +#else +static inline int ui__init(void) +{ +	return -1; +} +static inline void ui__exit(bool wait_for_ok __maybe_unused) {} +#endif + +#ifdef GTK2_SUPPORT +int perf_gtk__init(void); +void perf_gtk__exit(bool wait_for_ok); +#else +static inline int perf_gtk__init(void) +{ +	return -1; +} +static inline void perf_gtk__exit(bool wait_for_ok __maybe_unused) {} +#endif +  void ui__refresh_dimensions(bool force);  #endif /* _PERF_UI_H_ */ diff --git a/tools/perf/ui/util.c b/tools/perf/ui/util.c index 4f989774c8c..e3e0a963d03 100644 --- a/tools/perf/ui/util.c +++ b/tools/perf/ui/util.c @@ -52,7 +52,6 @@ int ui__warning(const char *format, ...)  	return ret;  } -  /**   * perf_error__register - Register error logging functions   * @eops: The pointer to error logging function struct  |