Home | History | Annotate | Download | only in browsers
      1 #include <stdio.h>
      2 #include "../libslang.h"
      3 #include <stdlib.h>
      4 #include <string.h>
      5 #include <linux/rbtree.h>
      6 
      7 #include "../../util/evsel.h"
      8 #include "../../util/evlist.h"
      9 #include "../../util/hist.h"
     10 #include "../../util/pstack.h"
     11 #include "../../util/sort.h"
     12 #include "../../util/util.h"
     13 #include "../../arch/common.h"
     14 
     15 #include "../browser.h"
     16 #include "../helpline.h"
     17 #include "../util.h"
     18 #include "../ui.h"
     19 #include "map.h"
     20 
     21 struct hist_browser {
     22 	struct ui_browser   b;
     23 	struct hists	    *hists;
     24 	struct hist_entry   *he_selection;
     25 	struct map_symbol   *selection;
     26 	int		     print_seq;
     27 	bool		     show_dso;
     28 	float		     min_pcnt;
     29 	u64		     nr_pcnt_entries;
     30 };
     31 
     32 extern void hist_browser__init_hpp(void);
     33 
     34 static int hists__browser_title(struct hists *hists, char *bf, size_t size,
     35 				const char *ev_name);
     36 
     37 static void hist_browser__refresh_dimensions(struct hist_browser *browser)
     38 {
     39 	/* 3 == +/- toggle symbol before actual hist_entry rendering */
     40 	browser->b.width = 3 + (hists__sort_list_width(browser->hists) +
     41 			     sizeof("[k]"));
     42 }
     43 
     44 static void hist_browser__reset(struct hist_browser *browser)
     45 {
     46 	browser->b.nr_entries = browser->hists->nr_entries;
     47 	hist_browser__refresh_dimensions(browser);
     48 	ui_browser__reset_index(&browser->b);
     49 }
     50 
     51 static char tree__folded_sign(bool unfolded)
     52 {
     53 	return unfolded ? '-' : '+';
     54 }
     55 
     56 static char map_symbol__folded(const struct map_symbol *ms)
     57 {
     58 	return ms->has_children ? tree__folded_sign(ms->unfolded) : ' ';
     59 }
     60 
     61 static char hist_entry__folded(const struct hist_entry *he)
     62 {
     63 	return map_symbol__folded(&he->ms);
     64 }
     65 
     66 static char callchain_list__folded(const struct callchain_list *cl)
     67 {
     68 	return map_symbol__folded(&cl->ms);
     69 }
     70 
     71 static void map_symbol__set_folding(struct map_symbol *ms, bool unfold)
     72 {
     73 	ms->unfolded = unfold ? ms->has_children : false;
     74 }
     75 
     76 static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
     77 {
     78 	int n = 0;
     79 	struct rb_node *nd;
     80 
     81 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
     82 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
     83 		struct callchain_list *chain;
     84 		char folded_sign = ' '; /* No children */
     85 
     86 		list_for_each_entry(chain, &child->val, list) {
     87 			++n;
     88 			/* We need this because we may not have children */
     89 			folded_sign = callchain_list__folded(chain);
     90 			if (folded_sign == '+')
     91 				break;
     92 		}
     93 
     94 		if (folded_sign == '-') /* Have children and they're unfolded */
     95 			n += callchain_node__count_rows_rb_tree(child);
     96 	}
     97 
     98 	return n;
     99 }
    100 
    101 static int callchain_node__count_rows(struct callchain_node *node)
    102 {
    103 	struct callchain_list *chain;
    104 	bool unfolded = false;
    105 	int n = 0;
    106 
    107 	list_for_each_entry(chain, &node->val, list) {
    108 		++n;
    109 		unfolded = chain->ms.unfolded;
    110 	}
    111 
    112 	if (unfolded)
    113 		n += callchain_node__count_rows_rb_tree(node);
    114 
    115 	return n;
    116 }
    117 
    118 static int callchain__count_rows(struct rb_root *chain)
    119 {
    120 	struct rb_node *nd;
    121 	int n = 0;
    122 
    123 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
    124 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
    125 		n += callchain_node__count_rows(node);
    126 	}
    127 
    128 	return n;
    129 }
    130 
    131 static bool map_symbol__toggle_fold(struct map_symbol *ms)
    132 {
    133 	if (!ms)
    134 		return false;
    135 
    136 	if (!ms->has_children)
    137 		return false;
    138 
    139 	ms->unfolded = !ms->unfolded;
    140 	return true;
    141 }
    142 
    143 static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
    144 {
    145 	struct rb_node *nd = rb_first(&node->rb_root);
    146 
    147 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
    148 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
    149 		struct callchain_list *chain;
    150 		bool first = true;
    151 
    152 		list_for_each_entry(chain, &child->val, list) {
    153 			if (first) {
    154 				first = false;
    155 				chain->ms.has_children = chain->list.next != &child->val ||
    156 							 !RB_EMPTY_ROOT(&child->rb_root);
    157 			} else
    158 				chain->ms.has_children = chain->list.next == &child->val &&
    159 							 !RB_EMPTY_ROOT(&child->rb_root);
    160 		}
    161 
    162 		callchain_node__init_have_children_rb_tree(child);
    163 	}
    164 }
    165 
    166 static void callchain_node__init_have_children(struct callchain_node *node)
    167 {
    168 	struct callchain_list *chain;
    169 
    170 	list_for_each_entry(chain, &node->val, list)
    171 		chain->ms.has_children = !RB_EMPTY_ROOT(&node->rb_root);
    172 
    173 	callchain_node__init_have_children_rb_tree(node);
    174 }
    175 
    176 static void callchain__init_have_children(struct rb_root *root)
    177 {
    178 	struct rb_node *nd;
    179 
    180 	for (nd = rb_first(root); nd; nd = rb_next(nd)) {
    181 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
    182 		callchain_node__init_have_children(node);
    183 	}
    184 }
    185 
    186 static void hist_entry__init_have_children(struct hist_entry *he)
    187 {
    188 	if (!he->init_have_children) {
    189 		he->ms.has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
    190 		callchain__init_have_children(&he->sorted_chain);
    191 		he->init_have_children = true;
    192 	}
    193 }
    194 
    195 static bool hist_browser__toggle_fold(struct hist_browser *browser)
    196 {
    197 	if (map_symbol__toggle_fold(browser->selection)) {
    198 		struct hist_entry *he = browser->he_selection;
    199 
    200 		hist_entry__init_have_children(he);
    201 		browser->hists->nr_entries -= he->nr_rows;
    202 
    203 		if (he->ms.unfolded)
    204 			he->nr_rows = callchain__count_rows(&he->sorted_chain);
    205 		else
    206 			he->nr_rows = 0;
    207 		browser->hists->nr_entries += he->nr_rows;
    208 		browser->b.nr_entries = browser->hists->nr_entries;
    209 
    210 		return true;
    211 	}
    212 
    213 	/* If it doesn't have children, no toggling performed */
    214 	return false;
    215 }
    216 
    217 static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
    218 {
    219 	int n = 0;
    220 	struct rb_node *nd;
    221 
    222 	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
    223 		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
    224 		struct callchain_list *chain;
    225 		bool has_children = false;
    226 
    227 		list_for_each_entry(chain, &child->val, list) {
    228 			++n;
    229 			map_symbol__set_folding(&chain->ms, unfold);
    230 			has_children = chain->ms.has_children;
    231 		}
    232 
    233 		if (has_children)
    234 			n += callchain_node__set_folding_rb_tree(child, unfold);
    235 	}
    236 
    237 	return n;
    238 }
    239 
    240 static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
    241 {
    242 	struct callchain_list *chain;
    243 	bool has_children = false;
    244 	int n = 0;
    245 
    246 	list_for_each_entry(chain, &node->val, list) {
    247 		++n;
    248 		map_symbol__set_folding(&chain->ms, unfold);
    249 		has_children = chain->ms.has_children;
    250 	}
    251 
    252 	if (has_children)
    253 		n += callchain_node__set_folding_rb_tree(node, unfold);
    254 
    255 	return n;
    256 }
    257 
    258 static int callchain__set_folding(struct rb_root *chain, bool unfold)
    259 {
    260 	struct rb_node *nd;
    261 	int n = 0;
    262 
    263 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
    264 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
    265 		n += callchain_node__set_folding(node, unfold);
    266 	}
    267 
    268 	return n;
    269 }
    270 
    271 static void hist_entry__set_folding(struct hist_entry *he, bool unfold)
    272 {
    273 	hist_entry__init_have_children(he);
    274 	map_symbol__set_folding(&he->ms, unfold);
    275 
    276 	if (he->ms.has_children) {
    277 		int n = callchain__set_folding(&he->sorted_chain, unfold);
    278 		he->nr_rows = unfold ? n : 0;
    279 	} else
    280 		he->nr_rows = 0;
    281 }
    282 
    283 static void hists__set_folding(struct hists *hists, bool unfold)
    284 {
    285 	struct rb_node *nd;
    286 
    287 	hists->nr_entries = 0;
    288 
    289 	for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
    290 		struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
    291 		hist_entry__set_folding(he, unfold);
    292 		hists->nr_entries += 1 + he->nr_rows;
    293 	}
    294 }
    295 
    296 static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
    297 {
    298 	hists__set_folding(browser->hists, unfold);
    299 	browser->b.nr_entries = browser->hists->nr_entries;
    300 	/* Go to the start, we may be way after valid entries after a collapse */
    301 	ui_browser__reset_index(&browser->b);
    302 }
    303 
    304 static void ui_browser__warn_lost_events(struct ui_browser *browser)
    305 {
    306 	ui_browser__warning(browser, 4,
    307 		"Events are being lost, check IO/CPU overload!\n\n"
    308 		"You may want to run 'perf' using a RT scheduler policy:\n\n"
    309 		" perf top -r 80\n\n"
    310 		"Or reduce the sampling frequency.");
    311 }
    312 
    313 static void hist_browser__update_pcnt_entries(struct hist_browser *hb);
    314 
    315 static int hist_browser__run(struct hist_browser *browser, const char *ev_name,
    316 			     struct hist_browser_timer *hbt)
    317 {
    318 	int key;
    319 	char title[160];
    320 	int delay_secs = hbt ? hbt->refresh : 0;
    321 
    322 	browser->b.entries = &browser->hists->entries;
    323 	browser->b.nr_entries = browser->hists->nr_entries;
    324 	if (browser->min_pcnt)
    325 		browser->b.nr_entries = browser->nr_pcnt_entries;
    326 
    327 	hist_browser__refresh_dimensions(browser);
    328 	hists__browser_title(browser->hists, title, sizeof(title), ev_name);
    329 
    330 	if (ui_browser__show(&browser->b, title,
    331 			     "Press '?' for help on key bindings") < 0)
    332 		return -1;
    333 
    334 	while (1) {
    335 		key = ui_browser__run(&browser->b, delay_secs);
    336 
    337 		switch (key) {
    338 		case K_TIMER: {
    339 			u64 nr_entries;
    340 			hbt->timer(hbt->arg);
    341 
    342 			if (browser->min_pcnt) {
    343 				hist_browser__update_pcnt_entries(browser);
    344 				nr_entries = browser->nr_pcnt_entries;
    345 			} else {
    346 				nr_entries = browser->hists->nr_entries;
    347 			}
    348 
    349 			ui_browser__update_nr_entries(&browser->b, nr_entries);
    350 
    351 			if (browser->hists->stats.nr_lost_warned !=
    352 			    browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
    353 				browser->hists->stats.nr_lost_warned =
    354 					browser->hists->stats.nr_events[PERF_RECORD_LOST];
    355 				ui_browser__warn_lost_events(&browser->b);
    356 			}
    357 
    358 			hists__browser_title(browser->hists, title, sizeof(title), ev_name);
    359 			ui_browser__show_title(&browser->b, title);
    360 			continue;
    361 		}
    362 		case 'D': { /* Debug */
    363 			static int seq;
    364 			struct hist_entry *h = rb_entry(browser->b.top,
    365 							struct hist_entry, rb_node);
    366 			ui_helpline__pop();
    367 			ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
    368 					   seq++, browser->b.nr_entries,
    369 					   browser->hists->nr_entries,
    370 					   browser->b.height,
    371 					   browser->b.index,
    372 					   browser->b.top_idx,
    373 					   h->row_offset, h->nr_rows);
    374 		}
    375 			break;
    376 		case 'C':
    377 			/* Collapse the whole world. */
    378 			hist_browser__set_folding(browser, false);
    379 			break;
    380 		case 'E':
    381 			/* Expand the whole world. */
    382 			hist_browser__set_folding(browser, true);
    383 			break;
    384 		case K_ENTER:
    385 			if (hist_browser__toggle_fold(browser))
    386 				break;
    387 			/* fall thru */
    388 		default:
    389 			goto out;
    390 		}
    391 	}
    392 out:
    393 	ui_browser__hide(&browser->b);
    394 	return key;
    395 }
    396 
    397 static char *callchain_list__sym_name(struct callchain_list *cl,
    398 				      char *bf, size_t bfsize, bool show_dso)
    399 {
    400 	int printed;
    401 
    402 	if (cl->ms.sym)
    403 		printed = scnprintf(bf, bfsize, "%s", cl->ms.sym->name);
    404 	else
    405 		printed = scnprintf(bf, bfsize, "%#" PRIx64, cl->ip);
    406 
    407 	if (show_dso)
    408 		scnprintf(bf + printed, bfsize - printed, " %s",
    409 			  cl->ms.map ? cl->ms.map->dso->short_name : "unknown");
    410 
    411 	return bf;
    412 }
    413 
    414 #define LEVEL_OFFSET_STEP 3
    415 
    416 static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browser,
    417 						     struct callchain_node *chain_node,
    418 						     u64 total, int level,
    419 						     unsigned short row,
    420 						     off_t *row_offset,
    421 						     bool *is_current_entry)
    422 {
    423 	struct rb_node *node;
    424 	int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
    425 	u64 new_total, remaining;
    426 
    427 	if (callchain_param.mode == CHAIN_GRAPH_REL)
    428 		new_total = chain_node->children_hit;
    429 	else
    430 		new_total = total;
    431 
    432 	remaining = new_total;
    433 	node = rb_first(&chain_node->rb_root);
    434 	while (node) {
    435 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
    436 		struct rb_node *next = rb_next(node);
    437 		u64 cumul = callchain_cumul_hits(child);
    438 		struct callchain_list *chain;
    439 		char folded_sign = ' ';
    440 		int first = true;
    441 		int extra_offset = 0;
    442 
    443 		remaining -= cumul;
    444 
    445 		list_for_each_entry(chain, &child->val, list) {
    446 			char bf[1024], *alloc_str;
    447 			const char *str;
    448 			int color;
    449 			bool was_first = first;
    450 
    451 			if (first)
    452 				first = false;
    453 			else
    454 				extra_offset = LEVEL_OFFSET_STEP;
    455 
    456 			folded_sign = callchain_list__folded(chain);
    457 			if (*row_offset != 0) {
    458 				--*row_offset;
    459 				goto do_next;
    460 			}
    461 
    462 			alloc_str = NULL;
    463 			str = callchain_list__sym_name(chain, bf, sizeof(bf),
    464 						       browser->show_dso);
    465 			if (was_first) {
    466 				double percent = cumul * 100.0 / new_total;
    467 
    468 				if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
    469 					str = "Not enough memory!";
    470 				else
    471 					str = alloc_str;
    472 			}
    473 
    474 			color = HE_COLORSET_NORMAL;
    475 			width = browser->b.width - (offset + extra_offset + 2);
    476 			if (ui_browser__is_current_entry(&browser->b, row)) {
    477 				browser->selection = &chain->ms;
    478 				color = HE_COLORSET_SELECTED;
    479 				*is_current_entry = true;
    480 			}
    481 
    482 			ui_browser__set_color(&browser->b, color);
    483 			ui_browser__gotorc(&browser->b, row, 0);
    484 			slsmg_write_nstring(" ", offset + extra_offset);
    485 			slsmg_printf("%c ", folded_sign);
    486 			slsmg_write_nstring(str, width);
    487 			free(alloc_str);
    488 
    489 			if (++row == browser->b.height)
    490 				goto out;
    491 do_next:
    492 			if (folded_sign == '+')
    493 				break;
    494 		}
    495 
    496 		if (folded_sign == '-') {
    497 			const int new_level = level + (extra_offset ? 2 : 1);
    498 			row += hist_browser__show_callchain_node_rb_tree(browser, child, new_total,
    499 									 new_level, row, row_offset,
    500 									 is_current_entry);
    501 		}
    502 		if (row == browser->b.height)
    503 			goto out;
    504 		node = next;
    505 	}
    506 out:
    507 	return row - first_row;
    508 }
    509 
    510 static int hist_browser__show_callchain_node(struct hist_browser *browser,
    511 					     struct callchain_node *node,
    512 					     int level, unsigned short row,
    513 					     off_t *row_offset,
    514 					     bool *is_current_entry)
    515 {
    516 	struct callchain_list *chain;
    517 	int first_row = row,
    518 	     offset = level * LEVEL_OFFSET_STEP,
    519 	     width = browser->b.width - offset;
    520 	char folded_sign = ' ';
    521 
    522 	list_for_each_entry(chain, &node->val, list) {
    523 		char bf[1024], *s;
    524 		int color;
    525 
    526 		folded_sign = callchain_list__folded(chain);
    527 
    528 		if (*row_offset != 0) {
    529 			--*row_offset;
    530 			continue;
    531 		}
    532 
    533 		color = HE_COLORSET_NORMAL;
    534 		if (ui_browser__is_current_entry(&browser->b, row)) {
    535 			browser->selection = &chain->ms;
    536 			color = HE_COLORSET_SELECTED;
    537 			*is_current_entry = true;
    538 		}
    539 
    540 		s = callchain_list__sym_name(chain, bf, sizeof(bf),
    541 					     browser->show_dso);
    542 		ui_browser__gotorc(&browser->b, row, 0);
    543 		ui_browser__set_color(&browser->b, color);
    544 		slsmg_write_nstring(" ", offset);
    545 		slsmg_printf("%c ", folded_sign);
    546 		slsmg_write_nstring(s, width - 2);
    547 
    548 		if (++row == browser->b.height)
    549 			goto out;
    550 	}
    551 
    552 	if (folded_sign == '-')
    553 		row += hist_browser__show_callchain_node_rb_tree(browser, node,
    554 								 browser->hists->stats.total_period,
    555 								 level + 1, row,
    556 								 row_offset,
    557 								 is_current_entry);
    558 out:
    559 	return row - first_row;
    560 }
    561 
    562 static int hist_browser__show_callchain(struct hist_browser *browser,
    563 					struct rb_root *chain,
    564 					int level, unsigned short row,
    565 					off_t *row_offset,
    566 					bool *is_current_entry)
    567 {
    568 	struct rb_node *nd;
    569 	int first_row = row;
    570 
    571 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
    572 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
    573 
    574 		row += hist_browser__show_callchain_node(browser, node, level,
    575 							 row, row_offset,
    576 							 is_current_entry);
    577 		if (row == browser->b.height)
    578 			break;
    579 	}
    580 
    581 	return row - first_row;
    582 }
    583 
    584 struct hpp_arg {
    585 	struct ui_browser *b;
    586 	char folded_sign;
    587 	bool current_entry;
    588 };
    589 
    590 static int __hpp__color_callchain(struct hpp_arg *arg)
    591 {
    592 	if (!symbol_conf.use_callchain)
    593 		return 0;
    594 
    595 	slsmg_printf("%c ", arg->folded_sign);
    596 	return 2;
    597 }
    598 
    599 static int __hpp__color_fmt(struct perf_hpp *hpp, struct hist_entry *he,
    600 			    u64 (*get_field)(struct hist_entry *),
    601 			    int (*callchain_cb)(struct hpp_arg *))
    602 {
    603 	int ret = 0;
    604 	double percent = 0.0;
    605 	struct hists *hists = he->hists;
    606 	struct hpp_arg *arg = hpp->ptr;
    607 
    608 	if (hists->stats.total_period)
    609 		percent = 100.0 * get_field(he) / hists->stats.total_period;
    610 
    611 	ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
    612 
    613 	if (callchain_cb)
    614 		ret += callchain_cb(arg);
    615 
    616 	ret += scnprintf(hpp->buf, hpp->size, "%6.2f%%", percent);
    617 	slsmg_printf("%s", hpp->buf);
    618 
    619 	if (symbol_conf.event_group) {
    620 		int prev_idx, idx_delta;
    621 		struct perf_evsel *evsel = hists_to_evsel(hists);
    622 		struct hist_entry *pair;
    623 		int nr_members = evsel->nr_members;
    624 
    625 		if (nr_members <= 1)
    626 			goto out;
    627 
    628 		prev_idx = perf_evsel__group_idx(evsel);
    629 
    630 		list_for_each_entry(pair, &he->pairs.head, pairs.node) {
    631 			u64 period = get_field(pair);
    632 			u64 total = pair->hists->stats.total_period;
    633 
    634 			if (!total)
    635 				continue;
    636 
    637 			evsel = hists_to_evsel(pair->hists);
    638 			idx_delta = perf_evsel__group_idx(evsel) - prev_idx - 1;
    639 
    640 			while (idx_delta--) {
    641 				/*
    642 				 * zero-fill group members in the middle which
    643 				 * have no sample
    644 				 */
    645 				ui_browser__set_percent_color(arg->b, 0.0,
    646 							arg->current_entry);
    647 				ret += scnprintf(hpp->buf, hpp->size,
    648 						 " %6.2f%%", 0.0);
    649 				slsmg_printf("%s", hpp->buf);
    650 			}
    651 
    652 			percent = 100.0 * period / total;
    653 			ui_browser__set_percent_color(arg->b, percent,
    654 						      arg->current_entry);
    655 			ret += scnprintf(hpp->buf, hpp->size,
    656 					 " %6.2f%%", percent);
    657 			slsmg_printf("%s", hpp->buf);
    658 
    659 			prev_idx = perf_evsel__group_idx(evsel);
    660 		}
    661 
    662 		idx_delta = nr_members - prev_idx - 1;
    663 
    664 		while (idx_delta--) {
    665 			/*
    666 			 * zero-fill group members at last which have no sample
    667 			 */
    668 			ui_browser__set_percent_color(arg->b, 0.0,
    669 						      arg->current_entry);
    670 			ret += scnprintf(hpp->buf, hpp->size,
    671 					 " %6.2f%%", 0.0);
    672 			slsmg_printf("%s", hpp->buf);
    673 		}
    674 	}
    675 out:
    676 	if (!arg->current_entry || !arg->b->navkeypressed)
    677 		ui_browser__set_color(arg->b, HE_COLORSET_NORMAL);
    678 
    679 	return ret;
    680 }
    681 
    682 #define __HPP_COLOR_PERCENT_FN(_type, _field, _cb)			\
    683 static u64 __hpp_get_##_field(struct hist_entry *he)			\
    684 {									\
    685 	return he->stat._field;						\
    686 }									\
    687 									\
    688 static int								\
    689 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\
    690 				struct perf_hpp *hpp,			\
    691 				struct hist_entry *he)			\
    692 {									\
    693 	return __hpp__color_fmt(hpp, he, __hpp_get_##_field, _cb);	\
    694 }
    695 
    696 __HPP_COLOR_PERCENT_FN(overhead, period, __hpp__color_callchain)
    697 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys, NULL)
    698 __HPP_COLOR_PERCENT_FN(overhead_us, period_us, NULL)
    699 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys, NULL)
    700 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us, NULL)
    701 
    702 #undef __HPP_COLOR_PERCENT_FN
    703 
    704 void hist_browser__init_hpp(void)
    705 {
    706 	perf_hpp__init();
    707 
    708 	perf_hpp__format[PERF_HPP__OVERHEAD].color =
    709 				hist_browser__hpp_color_overhead;
    710 	perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
    711 				hist_browser__hpp_color_overhead_sys;
    712 	perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
    713 				hist_browser__hpp_color_overhead_us;
    714 	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
    715 				hist_browser__hpp_color_overhead_guest_sys;
    716 	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
    717 				hist_browser__hpp_color_overhead_guest_us;
    718 }
    719 
    720 static int hist_browser__show_entry(struct hist_browser *browser,
    721 				    struct hist_entry *entry,
    722 				    unsigned short row)
    723 {
    724 	char s[256];
    725 	int printed = 0;
    726 	int width = browser->b.width;
    727 	char folded_sign = ' ';
    728 	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
    729 	off_t row_offset = entry->row_offset;
    730 	bool first = true;
    731 	struct perf_hpp_fmt *fmt;
    732 
    733 	if (current_entry) {
    734 		browser->he_selection = entry;
    735 		browser->selection = &entry->ms;
    736 	}
    737 
    738 	if (symbol_conf.use_callchain) {
    739 		hist_entry__init_have_children(entry);
    740 		folded_sign = hist_entry__folded(entry);
    741 	}
    742 
    743 	if (row_offset == 0) {
    744 		struct hpp_arg arg = {
    745 			.b 		= &browser->b,
    746 			.folded_sign	= folded_sign,
    747 			.current_entry	= current_entry,
    748 		};
    749 		struct perf_hpp hpp = {
    750 			.buf		= s,
    751 			.size		= sizeof(s),
    752 			.ptr		= &arg,
    753 		};
    754 
    755 		ui_browser__gotorc(&browser->b, row, 0);
    756 
    757 		perf_hpp__for_each_format(fmt) {
    758 			if (!first) {
    759 				slsmg_printf("  ");
    760 				width -= 2;
    761 			}
    762 			first = false;
    763 
    764 			if (fmt->color) {
    765 				width -= fmt->color(fmt, &hpp, entry);
    766 			} else {
    767 				width -= fmt->entry(fmt, &hpp, entry);
    768 				slsmg_printf("%s", s);
    769 			}
    770 		}
    771 
    772 		/* The scroll bar isn't being used */
    773 		if (!browser->b.navkeypressed)
    774 			width += 1;
    775 
    776 		hist_entry__sort_snprintf(entry, s, sizeof(s), browser->hists);
    777 		slsmg_write_nstring(s, width);
    778 		++row;
    779 		++printed;
    780 	} else
    781 		--row_offset;
    782 
    783 	if (folded_sign == '-' && row != browser->b.height) {
    784 		printed += hist_browser__show_callchain(browser, &entry->sorted_chain,
    785 							1, row, &row_offset,
    786 							&current_entry);
    787 		if (current_entry)
    788 			browser->he_selection = entry;
    789 	}
    790 
    791 	return printed;
    792 }
    793 
    794 static void ui_browser__hists_init_top(struct ui_browser *browser)
    795 {
    796 	if (browser->top == NULL) {
    797 		struct hist_browser *hb;
    798 
    799 		hb = container_of(browser, struct hist_browser, b);
    800 		browser->top = rb_first(&hb->hists->entries);
    801 	}
    802 }
    803 
    804 static unsigned int hist_browser__refresh(struct ui_browser *browser)
    805 {
    806 	unsigned row = 0;
    807 	struct rb_node *nd;
    808 	struct hist_browser *hb = container_of(browser, struct hist_browser, b);
    809 
    810 	ui_browser__hists_init_top(browser);
    811 
    812 	for (nd = browser->top; nd; nd = rb_next(nd)) {
    813 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
    814 		float percent = h->stat.period * 100.0 /
    815 					hb->hists->stats.total_period;
    816 
    817 		if (h->filtered)
    818 			continue;
    819 
    820 		if (percent < hb->min_pcnt)
    821 			continue;
    822 
    823 		row += hist_browser__show_entry(hb, h, row);
    824 		if (row == browser->height)
    825 			break;
    826 	}
    827 
    828 	return row;
    829 }
    830 
    831 static struct rb_node *hists__filter_entries(struct rb_node *nd,
    832 					     struct hists *hists,
    833 					     float min_pcnt)
    834 {
    835 	while (nd != NULL) {
    836 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
    837 		float percent = h->stat.period * 100.0 /
    838 					hists->stats.total_period;
    839 
    840 		if (percent < min_pcnt)
    841 			return NULL;
    842 
    843 		if (!h->filtered)
    844 			return nd;
    845 
    846 		nd = rb_next(nd);
    847 	}
    848 
    849 	return NULL;
    850 }
    851 
    852 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
    853 						  struct hists *hists,
    854 						  float min_pcnt)
    855 {
    856 	while (nd != NULL) {
    857 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
    858 		float percent = h->stat.period * 100.0 /
    859 					hists->stats.total_period;
    860 
    861 		if (!h->filtered && percent >= min_pcnt)
    862 			return nd;
    863 
    864 		nd = rb_prev(nd);
    865 	}
    866 
    867 	return NULL;
    868 }
    869 
    870 static void ui_browser__hists_seek(struct ui_browser *browser,
    871 				   off_t offset, int whence)
    872 {
    873 	struct hist_entry *h;
    874 	struct rb_node *nd;
    875 	bool first = true;
    876 	struct hist_browser *hb;
    877 
    878 	hb = container_of(browser, struct hist_browser, b);
    879 
    880 	if (browser->nr_entries == 0)
    881 		return;
    882 
    883 	ui_browser__hists_init_top(browser);
    884 
    885 	switch (whence) {
    886 	case SEEK_SET:
    887 		nd = hists__filter_entries(rb_first(browser->entries),
    888 					   hb->hists, hb->min_pcnt);
    889 		break;
    890 	case SEEK_CUR:
    891 		nd = browser->top;
    892 		goto do_offset;
    893 	case SEEK_END:
    894 		nd = hists__filter_prev_entries(rb_last(browser->entries),
    895 						hb->hists, hb->min_pcnt);
    896 		first = false;
    897 		break;
    898 	default:
    899 		return;
    900 	}
    901 
    902 	/*
    903 	 * Moves not relative to the first visible entry invalidates its
    904 	 * row_offset:
    905 	 */
    906 	h = rb_entry(browser->top, struct hist_entry, rb_node);
    907 	h->row_offset = 0;
    908 
    909 	/*
    910 	 * Here we have to check if nd is expanded (+), if it is we can't go
    911 	 * the next top level hist_entry, instead we must compute an offset of
    912 	 * what _not_ to show and not change the first visible entry.
    913 	 *
    914 	 * This offset increments when we are going from top to bottom and
    915 	 * decreases when we're going from bottom to top.
    916 	 *
    917 	 * As we don't have backpointers to the top level in the callchains
    918 	 * structure, we need to always print the whole hist_entry callchain,
    919 	 * skipping the first ones that are before the first visible entry
    920 	 * and stop when we printed enough lines to fill the screen.
    921 	 */
    922 do_offset:
    923 	if (offset > 0) {
    924 		do {
    925 			h = rb_entry(nd, struct hist_entry, rb_node);
    926 			if (h->ms.unfolded) {
    927 				u16 remaining = h->nr_rows - h->row_offset;
    928 				if (offset > remaining) {
    929 					offset -= remaining;
    930 					h->row_offset = 0;
    931 				} else {
    932 					h->row_offset += offset;
    933 					offset = 0;
    934 					browser->top = nd;
    935 					break;
    936 				}
    937 			}
    938 			nd = hists__filter_entries(rb_next(nd), hb->hists,
    939 						   hb->min_pcnt);
    940 			if (nd == NULL)
    941 				break;
    942 			--offset;
    943 			browser->top = nd;
    944 		} while (offset != 0);
    945 	} else if (offset < 0) {
    946 		while (1) {
    947 			h = rb_entry(nd, struct hist_entry, rb_node);
    948 			if (h->ms.unfolded) {
    949 				if (first) {
    950 					if (-offset > h->row_offset) {
    951 						offset += h->row_offset;
    952 						h->row_offset = 0;
    953 					} else {
    954 						h->row_offset += offset;
    955 						offset = 0;
    956 						browser->top = nd;
    957 						break;
    958 					}
    959 				} else {
    960 					if (-offset > h->nr_rows) {
    961 						offset += h->nr_rows;
    962 						h->row_offset = 0;
    963 					} else {
    964 						h->row_offset = h->nr_rows + offset;
    965 						offset = 0;
    966 						browser->top = nd;
    967 						break;
    968 					}
    969 				}
    970 			}
    971 
    972 			nd = hists__filter_prev_entries(rb_prev(nd), hb->hists,
    973 							hb->min_pcnt);
    974 			if (nd == NULL)
    975 				break;
    976 			++offset;
    977 			browser->top = nd;
    978 			if (offset == 0) {
    979 				/*
    980 				 * Last unfiltered hist_entry, check if it is
    981 				 * unfolded, if it is then we should have
    982 				 * row_offset at its last entry.
    983 				 */
    984 				h = rb_entry(nd, struct hist_entry, rb_node);
    985 				if (h->ms.unfolded)
    986 					h->row_offset = h->nr_rows;
    987 				break;
    988 			}
    989 			first = false;
    990 		}
    991 	} else {
    992 		browser->top = nd;
    993 		h = rb_entry(nd, struct hist_entry, rb_node);
    994 		h->row_offset = 0;
    995 	}
    996 }
    997 
    998 static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser *browser,
    999 							struct callchain_node *chain_node,
   1000 							u64 total, int level,
   1001 							FILE *fp)
   1002 {
   1003 	struct rb_node *node;
   1004 	int offset = level * LEVEL_OFFSET_STEP;
   1005 	u64 new_total, remaining;
   1006 	int printed = 0;
   1007 
   1008 	if (callchain_param.mode == CHAIN_GRAPH_REL)
   1009 		new_total = chain_node->children_hit;
   1010 	else
   1011 		new_total = total;
   1012 
   1013 	remaining = new_total;
   1014 	node = rb_first(&chain_node->rb_root);
   1015 	while (node) {
   1016 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
   1017 		struct rb_node *next = rb_next(node);
   1018 		u64 cumul = callchain_cumul_hits(child);
   1019 		struct callchain_list *chain;
   1020 		char folded_sign = ' ';
   1021 		int first = true;
   1022 		int extra_offset = 0;
   1023 
   1024 		remaining -= cumul;
   1025 
   1026 		list_for_each_entry(chain, &child->val, list) {
   1027 			char bf[1024], *alloc_str;
   1028 			const char *str;
   1029 			bool was_first = first;
   1030 
   1031 			if (first)
   1032 				first = false;
   1033 			else
   1034 				extra_offset = LEVEL_OFFSET_STEP;
   1035 
   1036 			folded_sign = callchain_list__folded(chain);
   1037 
   1038 			alloc_str = NULL;
   1039 			str = callchain_list__sym_name(chain, bf, sizeof(bf),
   1040 						       browser->show_dso);
   1041 			if (was_first) {
   1042 				double percent = cumul * 100.0 / new_total;
   1043 
   1044 				if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
   1045 					str = "Not enough memory!";
   1046 				else
   1047 					str = alloc_str;
   1048 			}
   1049 
   1050 			printed += fprintf(fp, "%*s%c %s\n", offset + extra_offset, " ", folded_sign, str);
   1051 			free(alloc_str);
   1052 			if (folded_sign == '+')
   1053 				break;
   1054 		}
   1055 
   1056 		if (folded_sign == '-') {
   1057 			const int new_level = level + (extra_offset ? 2 : 1);
   1058 			printed += hist_browser__fprintf_callchain_node_rb_tree(browser, child, new_total,
   1059 										new_level, fp);
   1060 		}
   1061 
   1062 		node = next;
   1063 	}
   1064 
   1065 	return printed;
   1066 }
   1067 
   1068 static int hist_browser__fprintf_callchain_node(struct hist_browser *browser,
   1069 						struct callchain_node *node,
   1070 						int level, FILE *fp)
   1071 {
   1072 	struct callchain_list *chain;
   1073 	int offset = level * LEVEL_OFFSET_STEP;
   1074 	char folded_sign = ' ';
   1075 	int printed = 0;
   1076 
   1077 	list_for_each_entry(chain, &node->val, list) {
   1078 		char bf[1024], *s;
   1079 
   1080 		folded_sign = callchain_list__folded(chain);
   1081 		s = callchain_list__sym_name(chain, bf, sizeof(bf), browser->show_dso);
   1082 		printed += fprintf(fp, "%*s%c %s\n", offset, " ", folded_sign, s);
   1083 	}
   1084 
   1085 	if (folded_sign == '-')
   1086 		printed += hist_browser__fprintf_callchain_node_rb_tree(browser, node,
   1087 									browser->hists->stats.total_period,
   1088 									level + 1,  fp);
   1089 	return printed;
   1090 }
   1091 
   1092 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
   1093 					   struct rb_root *chain, int level, FILE *fp)
   1094 {
   1095 	struct rb_node *nd;
   1096 	int printed = 0;
   1097 
   1098 	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
   1099 		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
   1100 
   1101 		printed += hist_browser__fprintf_callchain_node(browser, node, level, fp);
   1102 	}
   1103 
   1104 	return printed;
   1105 }
   1106 
   1107 static int hist_browser__fprintf_entry(struct hist_browser *browser,
   1108 				       struct hist_entry *he, FILE *fp)
   1109 {
   1110 	char s[8192];
   1111 	double percent;
   1112 	int printed = 0;
   1113 	char folded_sign = ' ';
   1114 
   1115 	if (symbol_conf.use_callchain)
   1116 		folded_sign = hist_entry__folded(he);
   1117 
   1118 	hist_entry__sort_snprintf(he, s, sizeof(s), browser->hists);
   1119 	percent = (he->stat.period * 100.0) / browser->hists->stats.total_period;
   1120 
   1121 	if (symbol_conf.use_callchain)
   1122 		printed += fprintf(fp, "%c ", folded_sign);
   1123 
   1124 	printed += fprintf(fp, " %5.2f%%", percent);
   1125 
   1126 	if (symbol_conf.show_nr_samples)
   1127 		printed += fprintf(fp, " %11u", he->stat.nr_events);
   1128 
   1129 	if (symbol_conf.show_total_period)
   1130 		printed += fprintf(fp, " %12" PRIu64, he->stat.period);
   1131 
   1132 	printed += fprintf(fp, "%s\n", rtrim(s));
   1133 
   1134 	if (folded_sign == '-')
   1135 		printed += hist_browser__fprintf_callchain(browser, &he->sorted_chain, 1, fp);
   1136 
   1137 	return printed;
   1138 }
   1139 
   1140 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
   1141 {
   1142 	struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
   1143 						   browser->hists,
   1144 						   browser->min_pcnt);
   1145 	int printed = 0;
   1146 
   1147 	while (nd) {
   1148 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
   1149 
   1150 		printed += hist_browser__fprintf_entry(browser, h, fp);
   1151 		nd = hists__filter_entries(rb_next(nd), browser->hists,
   1152 					   browser->min_pcnt);
   1153 	}
   1154 
   1155 	return printed;
   1156 }
   1157 
   1158 static int hist_browser__dump(struct hist_browser *browser)
   1159 {
   1160 	char filename[64];
   1161 	FILE *fp;
   1162 
   1163 	while (1) {
   1164 		scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
   1165 		if (access(filename, F_OK))
   1166 			break;
   1167 		/*
   1168  		 * XXX: Just an arbitrary lazy upper limit
   1169  		 */
   1170 		if (++browser->print_seq == 8192) {
   1171 			ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
   1172 			return -1;
   1173 		}
   1174 	}
   1175 
   1176 	fp = fopen(filename, "w");
   1177 	if (fp == NULL) {
   1178 		char bf[64];
   1179 		const char *err = strerror_r(errno, bf, sizeof(bf));
   1180 		ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
   1181 		return -1;
   1182 	}
   1183 
   1184 	++browser->print_seq;
   1185 	hist_browser__fprintf(browser, fp);
   1186 	fclose(fp);
   1187 	ui_helpline__fpush("%s written!", filename);
   1188 
   1189 	return 0;
   1190 }
   1191 
   1192 static struct hist_browser *hist_browser__new(struct hists *hists)
   1193 {
   1194 	struct hist_browser *browser = zalloc(sizeof(*browser));
   1195 
   1196 	if (browser) {
   1197 		browser->hists = hists;
   1198 		browser->b.refresh = hist_browser__refresh;
   1199 		browser->b.seek = ui_browser__hists_seek;
   1200 		browser->b.use_navkeypressed = true;
   1201 	}
   1202 
   1203 	return browser;
   1204 }
   1205 
   1206 static void hist_browser__delete(struct hist_browser *browser)
   1207 {
   1208 	free(browser);
   1209 }
   1210 
   1211 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
   1212 {
   1213 	return browser->he_selection;
   1214 }
   1215 
   1216 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
   1217 {
   1218 	return browser->he_selection->thread;
   1219 }
   1220 
   1221 static int hists__browser_title(struct hists *hists, char *bf, size_t size,
   1222 				const char *ev_name)
   1223 {
   1224 	char unit;
   1225 	int printed;
   1226 	const struct dso *dso = hists->dso_filter;
   1227 	const struct thread *thread = hists->thread_filter;
   1228 	unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
   1229 	u64 nr_events = hists->stats.total_period;
   1230 	struct perf_evsel *evsel = hists_to_evsel(hists);
   1231 	char buf[512];
   1232 	size_t buflen = sizeof(buf);
   1233 
   1234 	if (perf_evsel__is_group_event(evsel)) {
   1235 		struct perf_evsel *pos;
   1236 
   1237 		perf_evsel__group_desc(evsel, buf, buflen);
   1238 		ev_name = buf;
   1239 
   1240 		for_each_group_member(pos, evsel) {
   1241 			nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
   1242 			nr_events += pos->hists.stats.total_period;
   1243 		}
   1244 	}
   1245 
   1246 	nr_samples = convert_unit(nr_samples, &unit);
   1247 	printed = scnprintf(bf, size,
   1248 			   "Samples: %lu%c of event '%s', Event count (approx.): %lu",
   1249 			   nr_samples, unit, ev_name, nr_events);
   1250 
   1251 
   1252 	if (hists->uid_filter_str)
   1253 		printed += snprintf(bf + printed, size - printed,
   1254 				    ", UID: %s", hists->uid_filter_str);
   1255 	if (thread)
   1256 		printed += scnprintf(bf + printed, size - printed,
   1257 				    ", Thread: %s(%d)",
   1258 				    (thread->comm_set ? thread->comm : ""),
   1259 				    thread->tid);
   1260 	if (dso)
   1261 		printed += scnprintf(bf + printed, size - printed,
   1262 				    ", DSO: %s", dso->short_name);
   1263 	return printed;
   1264 }
   1265 
   1266 static inline void free_popup_options(char **options, int n)
   1267 {
   1268 	int i;
   1269 
   1270 	for (i = 0; i < n; ++i) {
   1271 		free(options[i]);
   1272 		options[i] = NULL;
   1273 	}
   1274 }
   1275 
   1276 /* Check whether the browser is for 'top' or 'report' */
   1277 static inline bool is_report_browser(void *timer)
   1278 {
   1279 	return timer == NULL;
   1280 }
   1281 
   1282 /*
   1283  * Only runtime switching of perf data file will make "input_name" point
   1284  * to a malloced buffer. So add "is_input_name_malloced" flag to decide
   1285  * whether we need to call free() for current "input_name" during the switch.
   1286  */
   1287 static bool is_input_name_malloced = false;
   1288 
   1289 static int switch_data_file(void)
   1290 {
   1291 	char *pwd, *options[32], *abs_path[32], *tmp;
   1292 	DIR *pwd_dir;
   1293 	int nr_options = 0, choice = -1, ret = -1;
   1294 	struct dirent *dent;
   1295 
   1296 	pwd = getenv("PWD");
   1297 	if (!pwd)
   1298 		return ret;
   1299 
   1300 	pwd_dir = opendir(pwd);
   1301 	if (!pwd_dir)
   1302 		return ret;
   1303 
   1304 	memset(options, 0, sizeof(options));
   1305 	memset(options, 0, sizeof(abs_path));
   1306 
   1307 	while ((dent = readdir(pwd_dir))) {
   1308 		char path[PATH_MAX];
   1309 		u64 magic;
   1310 		char *name = dent->d_name;
   1311 		FILE *file;
   1312 
   1313 		if (!(dent->d_type == DT_REG))
   1314 			continue;
   1315 
   1316 		snprintf(path, sizeof(path), "%s/%s", pwd, name);
   1317 
   1318 		file = fopen(path, "r");
   1319 		if (!file)
   1320 			continue;
   1321 
   1322 		if (fread(&magic, 1, 8, file) < 8)
   1323 			goto close_file_and_continue;
   1324 
   1325 		if (is_perf_magic(magic)) {
   1326 			options[nr_options] = strdup(name);
   1327 			if (!options[nr_options])
   1328 				goto close_file_and_continue;
   1329 
   1330 			abs_path[nr_options] = strdup(path);
   1331 			if (!abs_path[nr_options]) {
   1332 				free(options[nr_options]);
   1333 				ui__warning("Can't search all data files due to memory shortage.\n");
   1334 				fclose(file);
   1335 				break;
   1336 			}
   1337 
   1338 			nr_options++;
   1339 		}
   1340 
   1341 close_file_and_continue:
   1342 		fclose(file);
   1343 		if (nr_options >= 32) {
   1344 			ui__warning("Too many perf data files in PWD!\n"
   1345 				    "Only the first 32 files will be listed.\n");
   1346 			break;
   1347 		}
   1348 	}
   1349 	closedir(pwd_dir);
   1350 
   1351 	if (nr_options) {
   1352 		choice = ui__popup_menu(nr_options, options);
   1353 		if (choice < nr_options && choice >= 0) {
   1354 			tmp = strdup(abs_path[choice]);
   1355 			if (tmp) {
   1356 				if (is_input_name_malloced)
   1357 					free((void *)input_name);
   1358 				input_name = tmp;
   1359 				is_input_name_malloced = true;
   1360 				ret = 0;
   1361 			} else
   1362 				ui__warning("Data switch failed due to memory shortage!\n");
   1363 		}
   1364 	}
   1365 
   1366 	free_popup_options(options, nr_options);
   1367 	free_popup_options(abs_path, nr_options);
   1368 	return ret;
   1369 }
   1370 
   1371 static void hist_browser__update_pcnt_entries(struct hist_browser *hb)
   1372 {
   1373 	u64 nr_entries = 0;
   1374 	struct rb_node *nd = rb_first(&hb->hists->entries);
   1375 
   1376 	while (nd) {
   1377 		nr_entries++;
   1378 		nd = hists__filter_entries(rb_next(nd), hb->hists,
   1379 					   hb->min_pcnt);
   1380 	}
   1381 
   1382 	hb->nr_pcnt_entries = nr_entries;
   1383 }
   1384 
   1385 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
   1386 				    const char *helpline, const char *ev_name,
   1387 				    bool left_exits,
   1388 				    struct hist_browser_timer *hbt,
   1389 				    float min_pcnt,
   1390 				    struct perf_session_env *env)
   1391 {
   1392 	struct hists *hists = &evsel->hists;
   1393 	struct hist_browser *browser = hist_browser__new(hists);
   1394 	struct branch_info *bi;
   1395 	struct pstack *fstack;
   1396 	char *options[16];
   1397 	int nr_options = 0;
   1398 	int key = -1;
   1399 	char buf[64];
   1400 	char script_opt[64];
   1401 	int delay_secs = hbt ? hbt->refresh : 0;
   1402 
   1403 	if (browser == NULL)
   1404 		return -1;
   1405 
   1406 	if (min_pcnt) {
   1407 		browser->min_pcnt = min_pcnt;
   1408 		hist_browser__update_pcnt_entries(browser);
   1409 	}
   1410 
   1411 	fstack = pstack__new(2);
   1412 	if (fstack == NULL)
   1413 		goto out;
   1414 
   1415 	ui_helpline__push(helpline);
   1416 
   1417 	memset(options, 0, sizeof(options));
   1418 
   1419 	while (1) {
   1420 		const struct thread *thread = NULL;
   1421 		const struct dso *dso = NULL;
   1422 		int choice = 0,
   1423 		    annotate = -2, zoom_dso = -2, zoom_thread = -2,
   1424 		    annotate_f = -2, annotate_t = -2, browse_map = -2;
   1425 		int scripts_comm = -2, scripts_symbol = -2,
   1426 		    scripts_all = -2, switch_data = -2;
   1427 
   1428 		nr_options = 0;
   1429 
   1430 		key = hist_browser__run(browser, ev_name, hbt);
   1431 
   1432 		if (browser->he_selection != NULL) {
   1433 			thread = hist_browser__selected_thread(browser);
   1434 			dso = browser->selection->map ? browser->selection->map->dso : NULL;
   1435 		}
   1436 		switch (key) {
   1437 		case K_TAB:
   1438 		case K_UNTAB:
   1439 			if (nr_events == 1)
   1440 				continue;
   1441 			/*
   1442 			 * Exit the browser, let hists__browser_tree
   1443 			 * go to the next or previous
   1444 			 */
   1445 			goto out_free_stack;
   1446 		case 'a':
   1447 			if (!sort__has_sym) {
   1448 				ui_browser__warning(&browser->b, delay_secs * 2,
   1449 			"Annotation is only available for symbolic views, "
   1450 			"include \"sym*\" in --sort to use it.");
   1451 				continue;
   1452 			}
   1453 
   1454 			if (browser->selection == NULL ||
   1455 			    browser->selection->sym == NULL ||
   1456 			    browser->selection->map->dso->annotate_warned)
   1457 				continue;
   1458 			goto do_annotate;
   1459 		case 'P':
   1460 			hist_browser__dump(browser);
   1461 			continue;
   1462 		case 'd':
   1463 			goto zoom_dso;
   1464 		case 'V':
   1465 			browser->show_dso = !browser->show_dso;
   1466 			continue;
   1467 		case 't':
   1468 			goto zoom_thread;
   1469 		case '/':
   1470 			if (ui_browser__input_window("Symbol to show",
   1471 					"Please enter the name of symbol you want to see",
   1472 					buf, "ENTER: OK, ESC: Cancel",
   1473 					delay_secs * 2) == K_ENTER) {
   1474 				hists->symbol_filter_str = *buf ? buf : NULL;
   1475 				hists__filter_by_symbol(hists);
   1476 				hist_browser__reset(browser);
   1477 			}
   1478 			continue;
   1479 		case 'r':
   1480 			if (is_report_browser(hbt))
   1481 				goto do_scripts;
   1482 			continue;
   1483 		case 's':
   1484 			if (is_report_browser(hbt))
   1485 				goto do_data_switch;
   1486 			continue;
   1487 		case K_F1:
   1488 		case 'h':
   1489 		case '?':
   1490 			ui_browser__help_window(&browser->b,
   1491 					"h/?/F1        Show this window\n"
   1492 					"UP/DOWN/PGUP\n"
   1493 					"PGDN/SPACE    Navigate\n"
   1494 					"q/ESC/CTRL+C  Exit browser\n\n"
   1495 					"For multiple event sessions:\n\n"
   1496 					"TAB/UNTAB Switch events\n\n"
   1497 					"For symbolic views (--sort has sym):\n\n"
   1498 					"->            Zoom into DSO/Threads & Annotate current symbol\n"
   1499 					"<-            Zoom out\n"
   1500 					"a             Annotate current symbol\n"
   1501 					"C             Collapse all callchains\n"
   1502 					"E             Expand all callchains\n"
   1503 					"d             Zoom into current DSO\n"
   1504 					"t             Zoom into current Thread\n"
   1505 					"r             Run available scripts('perf report' only)\n"
   1506 					"s             Switch to another data file in PWD ('perf report' only)\n"
   1507 					"P             Print histograms to perf.hist.N\n"
   1508 					"V             Verbose (DSO names in callchains, etc)\n"
   1509 					"/             Filter symbol by name");
   1510 			continue;
   1511 		case K_ENTER:
   1512 		case K_RIGHT:
   1513 			/* menu */
   1514 			break;
   1515 		case K_LEFT: {
   1516 			const void *top;
   1517 
   1518 			if (pstack__empty(fstack)) {
   1519 				/*
   1520 				 * Go back to the perf_evsel_menu__run or other user
   1521 				 */
   1522 				if (left_exits)
   1523 					goto out_free_stack;
   1524 				continue;
   1525 			}
   1526 			top = pstack__pop(fstack);
   1527 			if (top == &browser->hists->dso_filter)
   1528 				goto zoom_out_dso;
   1529 			if (top == &browser->hists->thread_filter)
   1530 				goto zoom_out_thread;
   1531 			continue;
   1532 		}
   1533 		case K_ESC:
   1534 			if (!left_exits &&
   1535 			    !ui_browser__dialog_yesno(&browser->b,
   1536 					       "Do you really want to exit?"))
   1537 				continue;
   1538 			/* Fall thru */
   1539 		case 'q':
   1540 		case CTRL('c'):
   1541 			goto out_free_stack;
   1542 		default:
   1543 			continue;
   1544 		}
   1545 
   1546 		if (!sort__has_sym)
   1547 			goto add_exit_option;
   1548 
   1549 		if (sort__mode == SORT_MODE__BRANCH) {
   1550 			bi = browser->he_selection->branch_info;
   1551 			if (browser->selection != NULL &&
   1552 			    bi &&
   1553 			    bi->from.sym != NULL &&
   1554 			    !bi->from.map->dso->annotate_warned &&
   1555 				asprintf(&options[nr_options], "Annotate %s",
   1556 					 bi->from.sym->name) > 0)
   1557 				annotate_f = nr_options++;
   1558 
   1559 			if (browser->selection != NULL &&
   1560 			    bi &&
   1561 			    bi->to.sym != NULL &&
   1562 			    !bi->to.map->dso->annotate_warned &&
   1563 			    (bi->to.sym != bi->from.sym ||
   1564 			     bi->to.map->dso != bi->from.map->dso) &&
   1565 				asprintf(&options[nr_options], "Annotate %s",
   1566 					 bi->to.sym->name) > 0)
   1567 				annotate_t = nr_options++;
   1568 		} else {
   1569 
   1570 			if (browser->selection != NULL &&
   1571 			    browser->selection->sym != NULL &&
   1572 			    !browser->selection->map->dso->annotate_warned &&
   1573 				asprintf(&options[nr_options], "Annotate %s",
   1574 					 browser->selection->sym->name) > 0)
   1575 				annotate = nr_options++;
   1576 		}
   1577 
   1578 		if (thread != NULL &&
   1579 		    asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
   1580 			     (browser->hists->thread_filter ? "out of" : "into"),
   1581 			     (thread->comm_set ? thread->comm : ""),
   1582 			     thread->tid) > 0)
   1583 			zoom_thread = nr_options++;
   1584 
   1585 		if (dso != NULL &&
   1586 		    asprintf(&options[nr_options], "Zoom %s %s DSO",
   1587 			     (browser->hists->dso_filter ? "out of" : "into"),
   1588 			     (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
   1589 			zoom_dso = nr_options++;
   1590 
   1591 		if (browser->selection != NULL &&
   1592 		    browser->selection->map != NULL &&
   1593 		    asprintf(&options[nr_options], "Browse map details") > 0)
   1594 			browse_map = nr_options++;
   1595 
   1596 		/* perf script support */
   1597 		if (browser->he_selection) {
   1598 			struct symbol *sym;
   1599 
   1600 			if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]",
   1601 				browser->he_selection->thread->comm) > 0)
   1602 				scripts_comm = nr_options++;
   1603 
   1604 			sym = browser->he_selection->ms.sym;
   1605 			if (sym && sym->namelen &&
   1606 				asprintf(&options[nr_options], "Run scripts for samples of symbol [%s]",
   1607 						sym->name) > 0)
   1608 				scripts_symbol = nr_options++;
   1609 		}
   1610 
   1611 		if (asprintf(&options[nr_options], "Run scripts for all samples") > 0)
   1612 			scripts_all = nr_options++;
   1613 
   1614 		if (is_report_browser(hbt) && asprintf(&options[nr_options],
   1615 				"Switch to another data file in PWD") > 0)
   1616 			switch_data = nr_options++;
   1617 add_exit_option:
   1618 		options[nr_options++] = (char *)"Exit";
   1619 retry_popup_menu:
   1620 		choice = ui__popup_menu(nr_options, options);
   1621 
   1622 		if (choice == nr_options - 1)
   1623 			break;
   1624 
   1625 		if (choice == -1) {
   1626 			free_popup_options(options, nr_options - 1);
   1627 			continue;
   1628 		}
   1629 
   1630 		if (choice == annotate || choice == annotate_t || choice == annotate_f) {
   1631 			struct hist_entry *he;
   1632 			int err;
   1633 do_annotate:
   1634 			if (!objdump_path && perf_session_env__lookup_objdump(env))
   1635 				continue;
   1636 
   1637 			he = hist_browser__selected_entry(browser);
   1638 			if (he == NULL)
   1639 				continue;
   1640 
   1641 			/*
   1642 			 * we stash the branch_info symbol + map into the
   1643 			 * the ms so we don't have to rewrite all the annotation
   1644 			 * code to use branch_info.
   1645 			 * in branch mode, the ms struct is not used
   1646 			 */
   1647 			if (choice == annotate_f) {
   1648 				he->ms.sym = he->branch_info->from.sym;
   1649 				he->ms.map = he->branch_info->from.map;
   1650 			}  else if (choice == annotate_t) {
   1651 				he->ms.sym = he->branch_info->to.sym;
   1652 				he->ms.map = he->branch_info->to.map;
   1653 			}
   1654 
   1655 			/*
   1656 			 * Don't let this be freed, say, by hists__decay_entry.
   1657 			 */
   1658 			he->used = true;
   1659 			err = hist_entry__tui_annotate(he, evsel, hbt);
   1660 			he->used = false;
   1661 			/*
   1662 			 * offer option to annotate the other branch source or target
   1663 			 * (if they exists) when returning from annotate
   1664 			 */
   1665 			if ((err == 'q' || err == CTRL('c'))
   1666 			    && annotate_t != -2 && annotate_f != -2)
   1667 				goto retry_popup_menu;
   1668 
   1669 			ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
   1670 			if (err)
   1671 				ui_browser__handle_resize(&browser->b);
   1672 
   1673 		} else if (choice == browse_map)
   1674 			map__browse(browser->selection->map);
   1675 		else if (choice == zoom_dso) {
   1676 zoom_dso:
   1677 			if (browser->hists->dso_filter) {
   1678 				pstack__remove(fstack, &browser->hists->dso_filter);
   1679 zoom_out_dso:
   1680 				ui_helpline__pop();
   1681 				browser->hists->dso_filter = NULL;
   1682 				sort_dso.elide = false;
   1683 			} else {
   1684 				if (dso == NULL)
   1685 					continue;
   1686 				ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
   1687 						   dso->kernel ? "the Kernel" : dso->short_name);
   1688 				browser->hists->dso_filter = dso;
   1689 				sort_dso.elide = true;
   1690 				pstack__push(fstack, &browser->hists->dso_filter);
   1691 			}
   1692 			hists__filter_by_dso(hists);
   1693 			hist_browser__reset(browser);
   1694 		} else if (choice == zoom_thread) {
   1695 zoom_thread:
   1696 			if (browser->hists->thread_filter) {
   1697 				pstack__remove(fstack, &browser->hists->thread_filter);
   1698 zoom_out_thread:
   1699 				ui_helpline__pop();
   1700 				browser->hists->thread_filter = NULL;
   1701 				sort_thread.elide = false;
   1702 			} else {
   1703 				ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
   1704 						   thread->comm_set ? thread->comm : "",
   1705 						   thread->tid);
   1706 				browser->hists->thread_filter = thread;
   1707 				sort_thread.elide = true;
   1708 				pstack__push(fstack, &browser->hists->thread_filter);
   1709 			}
   1710 			hists__filter_by_thread(hists);
   1711 			hist_browser__reset(browser);
   1712 		}
   1713 		/* perf scripts support */
   1714 		else if (choice == scripts_all || choice == scripts_comm ||
   1715 				choice == scripts_symbol) {
   1716 do_scripts:
   1717 			memset(script_opt, 0, 64);
   1718 
   1719 			if (choice == scripts_comm)
   1720 				sprintf(script_opt, " -c %s ", browser->he_selection->thread->comm);
   1721 
   1722 			if (choice == scripts_symbol)
   1723 				sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name);
   1724 
   1725 			script_browse(script_opt);
   1726 		}
   1727 		/* Switch to another data file */
   1728 		else if (choice == switch_data) {
   1729 do_data_switch:
   1730 			if (!switch_data_file()) {
   1731 				key = K_SWITCH_INPUT_DATA;
   1732 				break;
   1733 			} else
   1734 				ui__warning("Won't switch the data files due to\n"
   1735 					"no valid data file get selected!\n");
   1736 		}
   1737 	}
   1738 out_free_stack:
   1739 	pstack__delete(fstack);
   1740 out:
   1741 	hist_browser__delete(browser);
   1742 	free_popup_options(options, nr_options - 1);
   1743 	return key;
   1744 }
   1745 
   1746 struct perf_evsel_menu {
   1747 	struct ui_browser b;
   1748 	struct perf_evsel *selection;
   1749 	bool lost_events, lost_events_warned;
   1750 	float min_pcnt;
   1751 	struct perf_session_env *env;
   1752 };
   1753 
   1754 static void perf_evsel_menu__write(struct ui_browser *browser,
   1755 				   void *entry, int row)
   1756 {
   1757 	struct perf_evsel_menu *menu = container_of(browser,
   1758 						    struct perf_evsel_menu, b);
   1759 	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
   1760 	bool current_entry = ui_browser__is_current_entry(browser, row);
   1761 	unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
   1762 	const char *ev_name = perf_evsel__name(evsel);
   1763 	char bf[256], unit;
   1764 	const char *warn = " ";
   1765 	size_t printed;
   1766 
   1767 	ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
   1768 						       HE_COLORSET_NORMAL);
   1769 
   1770 	if (perf_evsel__is_group_event(evsel)) {
   1771 		struct perf_evsel *pos;
   1772 
   1773 		ev_name = perf_evsel__group_name(evsel);
   1774 
   1775 		for_each_group_member(pos, evsel) {
   1776 			nr_events += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
   1777 		}
   1778 	}
   1779 
   1780 	nr_events = convert_unit(nr_events, &unit);
   1781 	printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
   1782 			   unit, unit == ' ' ? "" : " ", ev_name);
   1783 	slsmg_printf("%s", bf);
   1784 
   1785 	nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST];
   1786 	if (nr_events != 0) {
   1787 		menu->lost_events = true;
   1788 		if (!current_entry)
   1789 			ui_browser__set_color(browser, HE_COLORSET_TOP);
   1790 		nr_events = convert_unit(nr_events, &unit);
   1791 		printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
   1792 				     nr_events, unit, unit == ' ' ? "" : " ");
   1793 		warn = bf;
   1794 	}
   1795 
   1796 	slsmg_write_nstring(warn, browser->width - printed);
   1797 
   1798 	if (current_entry)
   1799 		menu->selection = evsel;
   1800 }
   1801 
   1802 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
   1803 				int nr_events, const char *help,
   1804 				struct hist_browser_timer *hbt)
   1805 {
   1806 	struct perf_evlist *evlist = menu->b.priv;
   1807 	struct perf_evsel *pos;
   1808 	const char *ev_name, *title = "Available samples";
   1809 	int delay_secs = hbt ? hbt->refresh : 0;
   1810 	int key;
   1811 
   1812 	if (ui_browser__show(&menu->b, title,
   1813 			     "ESC: exit, ENTER|->: Browse histograms") < 0)
   1814 		return -1;
   1815 
   1816 	while (1) {
   1817 		key = ui_browser__run(&menu->b, delay_secs);
   1818 
   1819 		switch (key) {
   1820 		case K_TIMER:
   1821 			hbt->timer(hbt->arg);
   1822 
   1823 			if (!menu->lost_events_warned && menu->lost_events) {
   1824 				ui_browser__warn_lost_events(&menu->b);
   1825 				menu->lost_events_warned = true;
   1826 			}
   1827 			continue;
   1828 		case K_RIGHT:
   1829 		case K_ENTER:
   1830 			if (!menu->selection)
   1831 				continue;
   1832 			pos = menu->selection;
   1833 browse_hists:
   1834 			perf_evlist__set_selected(evlist, pos);
   1835 			/*
   1836 			 * Give the calling tool a chance to populate the non
   1837 			 * default evsel resorted hists tree.
   1838 			 */
   1839 			if (hbt)
   1840 				hbt->timer(hbt->arg);
   1841 			ev_name = perf_evsel__name(pos);
   1842 			key = perf_evsel__hists_browse(pos, nr_events, help,
   1843 						       ev_name, true, hbt,
   1844 						       menu->min_pcnt,
   1845 						       menu->env);
   1846 			ui_browser__show_title(&menu->b, title);
   1847 			switch (key) {
   1848 			case K_TAB:
   1849 				if (pos->node.next == &evlist->entries)
   1850 					pos = list_entry(evlist->entries.next, struct perf_evsel, node);
   1851 				else
   1852 					pos = list_entry(pos->node.next, struct perf_evsel, node);
   1853 				goto browse_hists;
   1854 			case K_UNTAB:
   1855 				if (pos->node.prev == &evlist->entries)
   1856 					pos = list_entry(evlist->entries.prev, struct perf_evsel, node);
   1857 				else
   1858 					pos = list_entry(pos->node.prev, struct perf_evsel, node);
   1859 				goto browse_hists;
   1860 			case K_ESC:
   1861 				if (!ui_browser__dialog_yesno(&menu->b,
   1862 						"Do you really want to exit?"))
   1863 					continue;
   1864 				/* Fall thru */
   1865 			case K_SWITCH_INPUT_DATA:
   1866 			case 'q':
   1867 			case CTRL('c'):
   1868 				goto out;
   1869 			default:
   1870 				continue;
   1871 			}
   1872 		case K_LEFT:
   1873 			continue;
   1874 		case K_ESC:
   1875 			if (!ui_browser__dialog_yesno(&menu->b,
   1876 					       "Do you really want to exit?"))
   1877 				continue;
   1878 			/* Fall thru */
   1879 		case 'q':
   1880 		case CTRL('c'):
   1881 			goto out;
   1882 		default:
   1883 			continue;
   1884 		}
   1885 	}
   1886 
   1887 out:
   1888 	ui_browser__hide(&menu->b);
   1889 	return key;
   1890 }
   1891 
   1892 static bool filter_group_entries(struct ui_browser *self __maybe_unused,
   1893 				 void *entry)
   1894 {
   1895 	struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
   1896 
   1897 	if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
   1898 		return true;
   1899 
   1900 	return false;
   1901 }
   1902 
   1903 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
   1904 					   int nr_entries, const char *help,
   1905 					   struct hist_browser_timer *hbt,
   1906 					   float min_pcnt,
   1907 					   struct perf_session_env *env)
   1908 {
   1909 	struct perf_evsel *pos;
   1910 	struct perf_evsel_menu menu = {
   1911 		.b = {
   1912 			.entries    = &evlist->entries,
   1913 			.refresh    = ui_browser__list_head_refresh,
   1914 			.seek	    = ui_browser__list_head_seek,
   1915 			.write	    = perf_evsel_menu__write,
   1916 			.filter	    = filter_group_entries,
   1917 			.nr_entries = nr_entries,
   1918 			.priv	    = evlist,
   1919 		},
   1920 		.min_pcnt = min_pcnt,
   1921 		.env = env,
   1922 	};
   1923 
   1924 	ui_helpline__push("Press ESC to exit");
   1925 
   1926 	list_for_each_entry(pos, &evlist->entries, node) {
   1927 		const char *ev_name = perf_evsel__name(pos);
   1928 		size_t line_len = strlen(ev_name) + 7;
   1929 
   1930 		if (menu.b.width < line_len)
   1931 			menu.b.width = line_len;
   1932 	}
   1933 
   1934 	return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
   1935 }
   1936 
   1937 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
   1938 				  struct hist_browser_timer *hbt,
   1939 				  float min_pcnt,
   1940 				  struct perf_session_env *env)
   1941 {
   1942 	int nr_entries = evlist->nr_entries;
   1943 
   1944 single_entry:
   1945 	if (nr_entries == 1) {
   1946 		struct perf_evsel *first = list_entry(evlist->entries.next,
   1947 						      struct perf_evsel, node);
   1948 		const char *ev_name = perf_evsel__name(first);
   1949 
   1950 		return perf_evsel__hists_browse(first, nr_entries, help,
   1951 						ev_name, false, hbt, min_pcnt,
   1952 						env);
   1953 	}
   1954 
   1955 	if (symbol_conf.event_group) {
   1956 		struct perf_evsel *pos;
   1957 
   1958 		nr_entries = 0;
   1959 		list_for_each_entry(pos, &evlist->entries, node)
   1960 			if (perf_evsel__is_group_leader(pos))
   1961 				nr_entries++;
   1962 
   1963 		if (nr_entries == 1)
   1964 			goto single_entry;
   1965 	}
   1966 
   1967 	return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
   1968 					       hbt, min_pcnt, env);
   1969 }
   1970