diff options
Diffstat (limited to 'tools/perf/ui/browsers/hists.c')
| -rw-r--r-- | tools/perf/ui/browsers/hists.c | 646 | 
1 files changed, 410 insertions, 236 deletions
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index 995b7a8596b1..c42adb600091 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -25,6 +25,9 @@ struct hist_browser {  	struct hists	    *hists;  	struct hist_entry   *he_selection;  	struct map_symbol   *selection; +	struct hist_browser_timer *hbt; +	struct pstack	    *pstack; +	struct perf_session_env *env;  	int		     print_seq;  	bool		     show_dso;  	bool		     show_headers; @@ -60,7 +63,7 @@ static int hist_browser__get_folding(struct hist_browser *browser)  		struct hist_entry *he =  			rb_entry(nd, struct hist_entry, rb_node); -		if (he->ms.unfolded) +		if (he->unfolded)  			unfolded_rows += he->nr_rows;  	}  	return unfolded_rows; @@ -136,24 +139,19 @@ static char tree__folded_sign(bool unfolded)  	return unfolded ? '-' : '+';  } -static char map_symbol__folded(const struct map_symbol *ms) -{ -	return ms->has_children ? tree__folded_sign(ms->unfolded) : ' '; -} -  static char hist_entry__folded(const struct hist_entry *he)  { -	return map_symbol__folded(&he->ms); +	return he->has_children ? tree__folded_sign(he->unfolded) : ' ';  }  static char callchain_list__folded(const struct callchain_list *cl)  { -	return map_symbol__folded(&cl->ms); +	return cl->has_children ? tree__folded_sign(cl->unfolded) : ' ';  } -static void map_symbol__set_folding(struct map_symbol *ms, bool unfold) +static void callchain_list__set_folding(struct callchain_list *cl, bool unfold)  { -	ms->unfolded = unfold ? ms->has_children : false; +	cl->unfolded = unfold ? cl->has_children : false;  }  static int callchain_node__count_rows_rb_tree(struct callchain_node *node) @@ -189,7 +187,7 @@ static int callchain_node__count_rows(struct callchain_node *node)  	list_for_each_entry(chain, &node->val, list) {  		++n; -		unfolded = chain->ms.unfolded; +		unfolded = chain->unfolded;  	}  	if (unfolded) @@ -211,15 +209,27 @@ static int callchain__count_rows(struct rb_root *chain)  	return n;  } -static bool map_symbol__toggle_fold(struct map_symbol *ms) +static bool hist_entry__toggle_fold(struct hist_entry *he)  { -	if (!ms) +	if (!he)  		return false; -	if (!ms->has_children) +	if (!he->has_children)  		return false; -	ms->unfolded = !ms->unfolded; +	he->unfolded = !he->unfolded; +	return true; +} + +static bool callchain_list__toggle_fold(struct callchain_list *cl) +{ +	if (!cl) +		return false; + +	if (!cl->has_children) +		return false; + +	cl->unfolded = !cl->unfolded;  	return true;  } @@ -235,10 +245,10 @@ static void callchain_node__init_have_children_rb_tree(struct callchain_node *no  		list_for_each_entry(chain, &child->val, list) {  			if (first) {  				first = false; -				chain->ms.has_children = chain->list.next != &child->val || +				chain->has_children = chain->list.next != &child->val ||  							 !RB_EMPTY_ROOT(&child->rb_root);  			} else -				chain->ms.has_children = chain->list.next == &child->val && +				chain->has_children = chain->list.next == &child->val &&  							 !RB_EMPTY_ROOT(&child->rb_root);  		} @@ -252,11 +262,11 @@ static void callchain_node__init_have_children(struct callchain_node *node,  	struct callchain_list *chain;  	chain = list_entry(node->val.next, struct callchain_list, list); -	chain->ms.has_children = has_sibling; +	chain->has_children = has_sibling;  	if (!list_empty(&node->val)) {  		chain = list_entry(node->val.prev, struct callchain_list, list); -		chain->ms.has_children = !RB_EMPTY_ROOT(&node->rb_root); +		chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);  	}  	callchain_node__init_have_children_rb_tree(node); @@ -276,7 +286,7 @@ static void callchain__init_have_children(struct rb_root *root)  static void hist_entry__init_have_children(struct hist_entry *he)  {  	if (!he->init_have_children) { -		he->ms.has_children = !RB_EMPTY_ROOT(&he->sorted_chain); +		he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain);  		callchain__init_have_children(&he->sorted_chain);  		he->init_have_children = true;  	} @@ -284,14 +294,22 @@ static void hist_entry__init_have_children(struct hist_entry *he)  static bool hist_browser__toggle_fold(struct hist_browser *browser)  { -	if (map_symbol__toggle_fold(browser->selection)) { -		struct hist_entry *he = browser->he_selection; +	struct hist_entry *he = browser->he_selection; +	struct map_symbol *ms = browser->selection; +	struct callchain_list *cl = container_of(ms, struct callchain_list, ms); +	bool has_children; +	if (ms == &he->ms) +		has_children = hist_entry__toggle_fold(he); +	else +		has_children = callchain_list__toggle_fold(cl); + +	if (has_children) {  		hist_entry__init_have_children(he);  		browser->b.nr_entries -= he->nr_rows;  		browser->nr_callchain_rows -= he->nr_rows; -		if (he->ms.unfolded) +		if (he->unfolded)  			he->nr_rows = callchain__count_rows(&he->sorted_chain);  		else  			he->nr_rows = 0; @@ -318,8 +336,8 @@ static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool  		list_for_each_entry(chain, &child->val, list) {  			++n; -			map_symbol__set_folding(&chain->ms, unfold); -			has_children = chain->ms.has_children; +			callchain_list__set_folding(chain, unfold); +			has_children = chain->has_children;  		}  		if (has_children) @@ -337,8 +355,8 @@ static int callchain_node__set_folding(struct callchain_node *node, bool unfold)  	list_for_each_entry(chain, &node->val, list) {  		++n; -		map_symbol__set_folding(&chain->ms, unfold); -		has_children = chain->ms.has_children; +		callchain_list__set_folding(chain, unfold); +		has_children = chain->has_children;  	}  	if (has_children) @@ -363,9 +381,9 @@ static int callchain__set_folding(struct rb_root *chain, bool unfold)  static void hist_entry__set_folding(struct hist_entry *he, bool unfold)  {  	hist_entry__init_have_children(he); -	map_symbol__set_folding(&he->ms, unfold); +	he->unfolded = unfold ? he->has_children : false; -	if (he->ms.has_children) { +	if (he->has_children) {  		int n = callchain__set_folding(&he->sorted_chain, unfold);  		he->nr_rows = unfold ? n : 0;  	} else @@ -406,11 +424,11 @@ static void ui_browser__warn_lost_events(struct ui_browser *browser)  		"Or reduce the sampling frequency.");  } -static int hist_browser__run(struct hist_browser *browser, -			     struct hist_browser_timer *hbt) +static int hist_browser__run(struct hist_browser *browser, const char *help)  {  	int key;  	char title[160]; +	struct hist_browser_timer *hbt = browser->hbt;  	int delay_secs = hbt ? hbt->refresh : 0;  	browser->b.entries = &browser->hists->entries; @@ -418,8 +436,7 @@ static int hist_browser__run(struct hist_browser *browser,  	hists__browser_title(browser->hists, hbt, title, sizeof(title)); -	if (ui_browser__show(&browser->b, title, -			     "Press '?' for help on key bindings") < 0) +	if (ui_browser__show(&browser->b, title, help) < 0)  		return -1;  	while (1) { @@ -1016,7 +1033,7 @@ do_offset:  	if (offset > 0) {  		do {  			h = rb_entry(nd, struct hist_entry, rb_node); -			if (h->ms.unfolded) { +			if (h->unfolded) {  				u16 remaining = h->nr_rows - h->row_offset;  				if (offset > remaining) {  					offset -= remaining; @@ -1037,7 +1054,7 @@ do_offset:  	} else if (offset < 0) {  		while (1) {  			h = rb_entry(nd, struct hist_entry, rb_node); -			if (h->ms.unfolded) { +			if (h->unfolded) {  				if (first) {  					if (-offset > h->row_offset) {  						offset += h->row_offset; @@ -1074,7 +1091,7 @@ do_offset:  				 * row_offset at its last entry.  				 */  				h = rb_entry(nd, struct hist_entry, rb_node); -				if (h->ms.unfolded) +				if (h->unfolded)  					h->row_offset = h->nr_rows;  				break;  			} @@ -1195,7 +1212,9 @@ static int hist_browser__dump(struct hist_browser *browser)  	return 0;  } -static struct hist_browser *hist_browser__new(struct hists *hists) +static struct hist_browser *hist_browser__new(struct hists *hists, +					      struct hist_browser_timer *hbt, +					      struct perf_session_env *env)  {  	struct hist_browser *browser = zalloc(sizeof(*browser)); @@ -1206,6 +1225,8 @@ static struct hist_browser *hist_browser__new(struct hists *hists)  		browser->b.seek = ui_browser__hists_seek;  		browser->b.use_navkeypressed = true;  		browser->show_headers = symbol_conf.show_hist_headers; +		browser->hbt = hbt; +		browser->env = env;  	}  	return browser; @@ -1395,6 +1416,257 @@ close_file_and_continue:  	return ret;  } +struct popup_action { +	struct thread 		*thread; +	struct dso		*dso; +	struct map_symbol 	ms; + +	int (*fn)(struct hist_browser *browser, struct popup_action *act); +}; + +static int +do_annotate(struct hist_browser *browser, struct popup_action *act) +{ +	struct perf_evsel *evsel; +	struct annotation *notes; +	struct hist_entry *he; +	int err; + +	if (!objdump_path && perf_session_env__lookup_objdump(browser->env)) +		return 0; + +	notes = symbol__annotation(act->ms.sym); +	if (!notes->src) +		return 0; + +	evsel = hists_to_evsel(browser->hists); +	err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt); +	he = hist_browser__selected_entry(browser); +	/* +	 * offer option to annotate the other branch source or target +	 * (if they exists) when returning from annotate +	 */ +	if ((err == 'q' || err == CTRL('c')) && he->branch_info) +		return 1; + +	ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries); +	if (err) +		ui_browser__handle_resize(&browser->b); +	return 0; +} + +static int +add_annotate_opt(struct hist_browser *browser __maybe_unused, +		 struct popup_action *act, char **optstr, +		 struct map *map, struct symbol *sym) +{ +	if (sym == NULL || map->dso->annotate_warned) +		return 0; + +	if (asprintf(optstr, "Annotate %s", sym->name) < 0) +		return 0; + +	act->ms.map = map; +	act->ms.sym = sym; +	act->fn = do_annotate; +	return 1; +} + +static int +do_zoom_thread(struct hist_browser *browser, struct popup_action *act) +{ +	struct thread *thread = act->thread; + +	if (browser->hists->thread_filter) { +		pstack__remove(browser->pstack, &browser->hists->thread_filter); +		perf_hpp__set_elide(HISTC_THREAD, false); +		thread__zput(browser->hists->thread_filter); +		ui_helpline__pop(); +	} else { +		ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"", +				   thread->comm_set ? thread__comm_str(thread) : "", +				   thread->tid); +		browser->hists->thread_filter = thread__get(thread); +		perf_hpp__set_elide(HISTC_THREAD, false); +		pstack__push(browser->pstack, &browser->hists->thread_filter); +	} + +	hists__filter_by_thread(browser->hists); +	hist_browser__reset(browser); +	return 0; +} + +static int +add_thread_opt(struct hist_browser *browser, struct popup_action *act, +	       char **optstr, struct thread *thread) +{ +	if (thread == NULL) +		return 0; + +	if (asprintf(optstr, "Zoom %s %s(%d) thread", +		     browser->hists->thread_filter ? "out of" : "into", +		     thread->comm_set ? thread__comm_str(thread) : "", +		     thread->tid) < 0) +		return 0; + +	act->thread = thread; +	act->fn = do_zoom_thread; +	return 1; +} + +static int +do_zoom_dso(struct hist_browser *browser, struct popup_action *act) +{ +	struct dso *dso = act->dso; + +	if (browser->hists->dso_filter) { +		pstack__remove(browser->pstack, &browser->hists->dso_filter); +		perf_hpp__set_elide(HISTC_DSO, false); +		browser->hists->dso_filter = NULL; +		ui_helpline__pop(); +	} else { +		if (dso == NULL) +			return 0; +		ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"", +				   dso->kernel ? "the Kernel" : dso->short_name); +		browser->hists->dso_filter = dso; +		perf_hpp__set_elide(HISTC_DSO, true); +		pstack__push(browser->pstack, &browser->hists->dso_filter); +	} + +	hists__filter_by_dso(browser->hists); +	hist_browser__reset(browser); +	return 0; +} + +static int +add_dso_opt(struct hist_browser *browser, struct popup_action *act, +	    char **optstr, struct dso *dso) +{ +	if (dso == NULL) +		return 0; + +	if (asprintf(optstr, "Zoom %s %s DSO", +		     browser->hists->dso_filter ? "out of" : "into", +		     dso->kernel ? "the Kernel" : dso->short_name) < 0) +		return 0; + +	act->dso = dso; +	act->fn = do_zoom_dso; +	return 1; +} + +static int +do_browse_map(struct hist_browser *browser __maybe_unused, +	      struct popup_action *act) +{ +	map__browse(act->ms.map); +	return 0; +} + +static int +add_map_opt(struct hist_browser *browser __maybe_unused, +	    struct popup_action *act, char **optstr, struct map *map) +{ +	if (map == NULL) +		return 0; + +	if (asprintf(optstr, "Browse map details") < 0) +		return 0; + +	act->ms.map = map; +	act->fn = do_browse_map; +	return 1; +} + +static int +do_run_script(struct hist_browser *browser __maybe_unused, +	      struct popup_action *act) +{ +	char script_opt[64]; +	memset(script_opt, 0, sizeof(script_opt)); + +	if (act->thread) { +		scnprintf(script_opt, sizeof(script_opt), " -c %s ", +			  thread__comm_str(act->thread)); +	} else if (act->ms.sym) { +		scnprintf(script_opt, sizeof(script_opt), " -S %s ", +			  act->ms.sym->name); +	} + +	script_browse(script_opt); +	return 0; +} + +static int +add_script_opt(struct hist_browser *browser __maybe_unused, +	       struct popup_action *act, char **optstr, +	       struct thread *thread, struct symbol *sym) +{ +	if (thread) { +		if (asprintf(optstr, "Run scripts for samples of thread [%s]", +			     thread__comm_str(thread)) < 0) +			return 0; +	} else if (sym) { +		if (asprintf(optstr, "Run scripts for samples of symbol [%s]", +			     sym->name) < 0) +			return 0; +	} else { +		if (asprintf(optstr, "Run scripts for all samples") < 0) +			return 0; +	} + +	act->thread = thread; +	act->ms.sym = sym; +	act->fn = do_run_script; +	return 1; +} + +static int +do_switch_data(struct hist_browser *browser __maybe_unused, +	       struct popup_action *act __maybe_unused) +{ +	if (switch_data_file()) { +		ui__warning("Won't switch the data files due to\n" +			    "no valid data file get selected!\n"); +		return 0; +	} + +	return K_SWITCH_INPUT_DATA; +} + +static int +add_switch_opt(struct hist_browser *browser, +	       struct popup_action *act, char **optstr) +{ +	if (!is_report_browser(browser->hbt)) +		return 0; + +	if (asprintf(optstr, "Switch to another data file in PWD") < 0) +		return 0; + +	act->fn = do_switch_data; +	return 1; +} + +static int +do_exit_browser(struct hist_browser *browser __maybe_unused, +		struct popup_action *act __maybe_unused) +{ +	return 0; +} + +static int +add_exit_opt(struct hist_browser *browser __maybe_unused, +	     struct popup_action *act, char **optstr) +{ +	if (asprintf(optstr, "Exit") < 0) +		return 0; + +	act->fn = do_exit_browser; +	return 1; +} +  static void hist_browser__update_nr_entries(struct hist_browser *hb)  {  	u64 nr_entries = 0; @@ -1421,14 +1693,14 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,  				    struct perf_session_env *env)  {  	struct hists *hists = evsel__hists(evsel); -	struct hist_browser *browser = hist_browser__new(hists); +	struct hist_browser *browser = hist_browser__new(hists, hbt, env);  	struct branch_info *bi; -	struct pstack *fstack; -	char *options[16]; +#define MAX_OPTIONS  16 +	char *options[MAX_OPTIONS]; +	struct popup_action actions[MAX_OPTIONS];  	int nr_options = 0;  	int key = -1;  	char buf[64]; -	char script_opt[64];  	int delay_secs = hbt ? hbt->refresh : 0;  	struct perf_hpp_fmt *fmt; @@ -1463,23 +1735,29 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,  	"t             Zoom into current Thread\n"  	"V             Verbose (DSO names in callchains, etc)\n"  	"z             Toggle zeroing of samples\n" +	"f             Enable/Disable events\n"  	"/             Filter symbol by name";  	if (browser == NULL)  		return -1; +	/* reset abort key so that it can get Ctrl-C as a key */ +	SLang_reset_tty(); +	SLang_init_tty(0, 0, 0); +  	if (min_pcnt) {  		browser->min_pcnt = min_pcnt;  		hist_browser__update_nr_entries(browser);  	} -	fstack = pstack__new(2); -	if (fstack == NULL) +	browser->pstack = pstack__new(2); +	if (browser->pstack == NULL)  		goto out;  	ui_helpline__push(helpline);  	memset(options, 0, sizeof(options)); +	memset(actions, 0, sizeof(actions));  	perf_hpp__for_each_format(fmt)  		perf_hpp__reset_width(fmt, hists); @@ -1489,16 +1767,12 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,  	while (1) {  		struct thread *thread = NULL; -		const struct dso *dso = NULL; -		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; +		struct dso *dso = NULL; +		int choice = 0;  		nr_options = 0; -		key = hist_browser__run(browser, hbt); +		key = hist_browser__run(browser, helpline);  		if (browser->he_selection != NULL) {  			thread = hist_browser__selected_thread(browser); @@ -1526,17 +1800,25 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,  			    browser->selection->sym == NULL ||  			    browser->selection->map->dso->annotate_warned)  				continue; -			goto do_annotate; + +			actions->ms.map = browser->selection->map; +			actions->ms.sym = browser->selection->sym; +			do_annotate(browser, actions); +			continue;  		case 'P':  			hist_browser__dump(browser);  			continue;  		case 'd': -			goto zoom_dso; +			actions->dso = dso; +			do_zoom_dso(browser, actions); +			continue;  		case 'V':  			browser->show_dso = !browser->show_dso;  			continue;  		case 't': -			goto zoom_thread; +			actions->thread = thread; +			do_zoom_thread(browser, actions); +			continue;  		case '/':  			if (ui_browser__input_window("Symbol to show",  					"Please enter the name of symbol you want to see", @@ -1548,12 +1830,18 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,  			}  			continue;  		case 'r': -			if (is_report_browser(hbt)) -				goto do_scripts; +			if (is_report_browser(hbt)) { +				actions->thread = NULL; +				actions->ms.sym = NULL; +				do_run_script(browser, actions); +			}  			continue;  		case 's': -			if (is_report_browser(hbt)) -				goto do_data_switch; +			if (is_report_browser(hbt)) { +				key = do_switch_data(browser, actions); +				if (key == K_SWITCH_INPUT_DATA) +					goto out_free_stack; +			}  			continue;  		case 'i':  			/* env->arch is NULL for live-mode (i.e. perf top) */ @@ -1583,7 +1871,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,  		case K_LEFT: {  			const void *top; -			if (pstack__empty(fstack)) { +			if (pstack__empty(browser->pstack)) {  				/*  				 * Go back to the perf_evsel_menu__run or other user  				 */ @@ -1591,11 +1879,17 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,  					goto out_free_stack;  				continue;  			} -			top = pstack__pop(fstack); -			if (top == &browser->hists->dso_filter) -				goto zoom_out_dso; +			top = pstack__peek(browser->pstack); +			if (top == &browser->hists->dso_filter) { +				/* +				 * No need to set actions->dso here since +				 * it's just to remove the current filter. +				 * Ditto for thread below. +				 */ +				do_zoom_dso(browser, actions); +			}  			if (top == &browser->hists->thread_filter) -				goto zoom_out_thread; +				do_zoom_thread(browser, actions);  			continue;  		}  		case K_ESC: @@ -1607,7 +1901,12 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,  		case 'q':  		case CTRL('c'):  			goto out_free_stack; +		case 'f': +			if (!is_report_browser(hbt)) +				goto out_free_stack; +			/* Fall thru */  		default: +			helpline = "Press '?' for help on key bindings";  			continue;  		} @@ -1623,196 +1922,71 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,  			if (bi == NULL)  				goto skip_annotation; -			if (bi->from.sym != NULL && -			    !bi->from.map->dso->annotate_warned && -			    asprintf(&options[nr_options], "Annotate %s", bi->from.sym->name) > 0) { -				annotate_f = nr_options++; -			} - -			if (bi->to.sym != NULL && -			    !bi->to.map->dso->annotate_warned && -			    (bi->to.sym != bi->from.sym || -			     bi->to.map->dso != bi->from.map->dso) && -			    asprintf(&options[nr_options], "Annotate %s", bi->to.sym->name) > 0) { -				annotate_t = nr_options++; -			} +			nr_options += add_annotate_opt(browser, +						       &actions[nr_options], +						       &options[nr_options], +						       bi->from.map, +						       bi->from.sym); +			if (bi->to.sym != bi->from.sym) +				nr_options += add_annotate_opt(browser, +							&actions[nr_options], +							&options[nr_options], +							bi->to.map, +							bi->to.sym);  		} else { -			if (browser->selection->sym != NULL && -			    !browser->selection->map->dso->annotate_warned) { -				struct annotation *notes; - -				notes = symbol__annotation(browser->selection->sym); - -				if (notes->src && -				    asprintf(&options[nr_options], "Annotate %s", -						 browser->selection->sym->name) > 0) { -					annotate = nr_options++; -				} -			} +			nr_options += add_annotate_opt(browser, +						       &actions[nr_options], +						       &options[nr_options], +						       browser->selection->map, +						       browser->selection->sym);  		}  skip_annotation: -		if (thread != NULL && -		    asprintf(&options[nr_options], "Zoom %s %s(%d) thread", -			     (browser->hists->thread_filter ? "out of" : "into"), -			     (thread->comm_set ? thread__comm_str(thread) : ""), -			     thread->tid) > 0) -			zoom_thread = nr_options++; - -		if (dso != NULL && -		    asprintf(&options[nr_options], "Zoom %s %s DSO", -			     (browser->hists->dso_filter ? "out of" : "into"), -			     (dso->kernel ? "the Kernel" : dso->short_name)) > 0) -			zoom_dso = nr_options++; - -		if (browser->selection != NULL && -		    browser->selection->map != NULL && -		    asprintf(&options[nr_options], "Browse map details") > 0) -			browse_map = nr_options++; +		nr_options += add_thread_opt(browser, &actions[nr_options], +					     &options[nr_options], thread); +		nr_options += add_dso_opt(browser, &actions[nr_options], +					  &options[nr_options], dso); +		nr_options += add_map_opt(browser, &actions[nr_options], +					  &options[nr_options], +					  browser->selection->map);  		/* perf script support */  		if (browser->he_selection) { -			struct symbol *sym; - -			if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]", -				     thread__comm_str(browser->he_selection->thread)) > 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++; +			nr_options += add_script_opt(browser, +						     &actions[nr_options], +						     &options[nr_options], +						     thread, NULL); +			nr_options += add_script_opt(browser, +						     &actions[nr_options], +						     &options[nr_options], +						     NULL, browser->selection->sym);  		} - -		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++; +		nr_options += add_script_opt(browser, &actions[nr_options], +					     &options[nr_options], NULL, NULL); +		nr_options += add_switch_opt(browser, &actions[nr_options], +					     &options[nr_options]);  add_exit_option: -		options[nr_options++] = (char *)"Exit"; -retry_popup_menu: -		choice = ui__popup_menu(nr_options, options); - -		if (choice == nr_options - 1) -			break; - -		if (choice == -1) { -			free_popup_options(options, nr_options - 1); -			continue; -		} - -		if (choice == annotate || choice == annotate_t || choice == annotate_f) { -			struct hist_entry *he; -			struct annotation *notes; -			struct map_symbol ms; -			int err; -do_annotate: -			if (!objdump_path && perf_session_env__lookup_objdump(env)) -				continue; - -			he = hist_browser__selected_entry(browser); -			if (he == NULL) -				continue; - -			if (choice == annotate_f) { -				ms.map = he->branch_info->from.map; -				ms.sym = he->branch_info->from.sym; -			} else if (choice == annotate_t) { -				ms.map = he->branch_info->to.map; -				ms.sym = he->branch_info->to.sym; -			} else { -				ms = *browser->selection; -			} +		nr_options += add_exit_opt(browser, &actions[nr_options], +					   &options[nr_options]); -			notes = symbol__annotation(ms.sym); -			if (!notes->src) -				continue; - -			err = map_symbol__tui_annotate(&ms, evsel, hbt); -			/* -			 * offer option to annotate the other branch source or target -			 * (if they exists) when returning from annotate -			 */ -			if ((err == 'q' || err == CTRL('c')) -			    && annotate_t != -2 && annotate_f != -2) -				goto retry_popup_menu; - -			ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries); -			if (err) -				ui_browser__handle_resize(&browser->b); - -		} else if (choice == browse_map) -			map__browse(browser->selection->map); -		else if (choice == zoom_dso) { -zoom_dso: -			if (browser->hists->dso_filter) { -				pstack__remove(fstack, &browser->hists->dso_filter); -zoom_out_dso: -				ui_helpline__pop(); -				browser->hists->dso_filter = NULL; -				perf_hpp__set_elide(HISTC_DSO, false); -			} else { -				if (dso == NULL) -					continue; -				ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"", -						   dso->kernel ? "the Kernel" : dso->short_name); -				browser->hists->dso_filter = dso; -				perf_hpp__set_elide(HISTC_DSO, true); -				pstack__push(fstack, &browser->hists->dso_filter); -			} -			hists__filter_by_dso(hists); -			hist_browser__reset(browser); -		} else if (choice == zoom_thread) { -zoom_thread: -			if (browser->hists->thread_filter) { -				pstack__remove(fstack, &browser->hists->thread_filter); -zoom_out_thread: -				ui_helpline__pop(); -				thread__zput(browser->hists->thread_filter); -				perf_hpp__set_elide(HISTC_THREAD, false); -			} else { -				ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"", -						   thread->comm_set ? thread__comm_str(thread) : "", -						   thread->tid); -				browser->hists->thread_filter = thread__get(thread); -				perf_hpp__set_elide(HISTC_THREAD, false); -				pstack__push(fstack, &browser->hists->thread_filter); -			} -			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); +		do { +			struct popup_action *act; -			if (choice == scripts_comm) -				sprintf(script_opt, " -c %s ", thread__comm_str(browser->he_selection->thread)); +			choice = ui__popup_menu(nr_options, options); +			if (choice == -1 || choice >= nr_options) +				break; -			if (choice == scripts_symbol) -				sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name); +			act = &actions[choice]; +			key = act->fn(browser, act); +		} while (key == 1); -			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"); -		} +		if (key == K_SWITCH_INPUT_DATA) +			break;  	}  out_free_stack: -	pstack__delete(fstack); +	pstack__delete(browser->pstack);  out:  	hist_browser__delete(browser); -	free_popup_options(options, nr_options - 1); +	free_popup_options(options, MAX_OPTIONS);  	return key;  }  |