diff options
Diffstat (limited to 'tools/perf/ui/browsers/hists.c')
| -rw-r--r-- | tools/perf/ui/browsers/hists.c | 341 | 
1 files changed, 298 insertions, 43 deletions
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index ccc4bd16142..aa22704047d 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -567,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)			\ +{									\ +	return he->stat._field;						\ +}									\ +									\ +static int hist_browser__hpp_color_##_type(struct perf_hpp *hpp,	\ +					   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 __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 = @@ -606,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; @@ -625,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);  			}  		} @@ -1098,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, @@ -1135,6 +1241,96 @@ 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, @@ -1169,7 +1365,8 @@ 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; +		int scripts_comm = -2, scripts_symbol = -2, +		    scripts_all = -2, switch_data = -2;  		nr_options = 0; @@ -1226,6 +1423,10 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,  			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 '?': @@ -1245,6 +1446,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,  					"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"); @@ -1352,6 +1554,9 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,  		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: @@ -1462,6 +1667,16 @@ do_scripts:  			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); @@ -1494,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); @@ -1578,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; @@ -1604,8 +1830,19 @@ 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, +					   int nr_entries, const char *help,  					   struct hist_browser_timer *hbt,  					   struct perf_session_env *env)  { @@ -1616,7 +1853,8 @@ 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, @@ -1632,20 +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, hbt); +	return perf_evsel_menu__run(&menu, nr_entries, help, hbt);  }  int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,  				  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, + +		return perf_evsel__hists_browse(first, nr_entries, help,  						ev_name, false, hbt, env);  	} -	return __perf_evlist__tui_browse_hists(evlist, help, 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, nr_entries, help, +					       hbt, env);  }  |