162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <dirent.h>
362306a36Sopenharmony_ci#include <errno.h>
462306a36Sopenharmony_ci#include <inttypes.h>
562306a36Sopenharmony_ci#include <stdio.h>
662306a36Sopenharmony_ci#include <stdlib.h>
762306a36Sopenharmony_ci#include <string.h>
862306a36Sopenharmony_ci#include <linux/rbtree.h>
962306a36Sopenharmony_ci#include <linux/string.h>
1062306a36Sopenharmony_ci#include <sys/ttydefaults.h>
1162306a36Sopenharmony_ci#include <linux/time64.h>
1262306a36Sopenharmony_ci#include <linux/zalloc.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include "../../util/debug.h"
1562306a36Sopenharmony_ci#include "../../util/dso.h"
1662306a36Sopenharmony_ci#include "../../util/callchain.h"
1762306a36Sopenharmony_ci#include "../../util/evsel.h"
1862306a36Sopenharmony_ci#include "../../util/evlist.h"
1962306a36Sopenharmony_ci#include "../../util/header.h"
2062306a36Sopenharmony_ci#include "../../util/hist.h"
2162306a36Sopenharmony_ci#include "../../util/machine.h"
2262306a36Sopenharmony_ci#include "../../util/map.h"
2362306a36Sopenharmony_ci#include "../../util/maps.h"
2462306a36Sopenharmony_ci#include "../../util/symbol.h"
2562306a36Sopenharmony_ci#include "../../util/map_symbol.h"
2662306a36Sopenharmony_ci#include "../../util/branch.h"
2762306a36Sopenharmony_ci#include "../../util/pstack.h"
2862306a36Sopenharmony_ci#include "../../util/sort.h"
2962306a36Sopenharmony_ci#include "../../util/top.h"
3062306a36Sopenharmony_ci#include "../../util/thread.h"
3162306a36Sopenharmony_ci#include "../../util/block-info.h"
3262306a36Sopenharmony_ci#include "../../util/util.h"
3362306a36Sopenharmony_ci#include "../../arch/common.h"
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#include "../browsers/hists.h"
3662306a36Sopenharmony_ci#include "../helpline.h"
3762306a36Sopenharmony_ci#include "../util.h"
3862306a36Sopenharmony_ci#include "../ui.h"
3962306a36Sopenharmony_ci#include "map.h"
4062306a36Sopenharmony_ci#include "annotate.h"
4162306a36Sopenharmony_ci#include "srcline.h"
4262306a36Sopenharmony_ci#include "string2.h"
4362306a36Sopenharmony_ci#include "units.h"
4462306a36Sopenharmony_ci#include "time-utils.h"
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci#include <linux/ctype.h>
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ciextern void hist_browser__init_hpp(void);
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic int hists_browser__scnprintf_title(struct hist_browser *browser, char *bf, size_t size);
5162306a36Sopenharmony_cistatic void hist_browser__update_nr_entries(struct hist_browser *hb);
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistatic struct rb_node *hists__filter_entries(struct rb_node *nd,
5462306a36Sopenharmony_ci					     float min_pcnt);
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic bool hist_browser__has_filter(struct hist_browser *hb)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	return hists__has_filter(hb->hists) || hb->min_pcnt || symbol_conf.has_filter || hb->c2c_filter;
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic int hist_browser__get_folding(struct hist_browser *browser)
6262306a36Sopenharmony_ci{
6362306a36Sopenharmony_ci	struct rb_node *nd;
6462306a36Sopenharmony_ci	struct hists *hists = browser->hists;
6562306a36Sopenharmony_ci	int unfolded_rows = 0;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	for (nd = rb_first_cached(&hists->entries);
6862306a36Sopenharmony_ci	     (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
6962306a36Sopenharmony_ci	     nd = rb_hierarchy_next(nd)) {
7062306a36Sopenharmony_ci		struct hist_entry *he =
7162306a36Sopenharmony_ci			rb_entry(nd, struct hist_entry, rb_node);
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci		if (he->leaf && he->unfolded)
7462306a36Sopenharmony_ci			unfolded_rows += he->nr_rows;
7562306a36Sopenharmony_ci	}
7662306a36Sopenharmony_ci	return unfolded_rows;
7762306a36Sopenharmony_ci}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic void hist_browser__set_title_space(struct hist_browser *hb)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	struct ui_browser *browser = &hb->b;
8262306a36Sopenharmony_ci	struct hists *hists = hb->hists;
8362306a36Sopenharmony_ci	struct perf_hpp_list *hpp_list = hists->hpp_list;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	browser->extra_title_lines = hb->show_headers ? hpp_list->nr_header_lines : 0;
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic u32 hist_browser__nr_entries(struct hist_browser *hb)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	u32 nr_entries;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	if (symbol_conf.report_hierarchy)
9362306a36Sopenharmony_ci		nr_entries = hb->nr_hierarchy_entries;
9462306a36Sopenharmony_ci	else if (hist_browser__has_filter(hb))
9562306a36Sopenharmony_ci		nr_entries = hb->nr_non_filtered_entries;
9662306a36Sopenharmony_ci	else
9762306a36Sopenharmony_ci		nr_entries = hb->hists->nr_entries;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	hb->nr_callchain_rows = hist_browser__get_folding(hb);
10062306a36Sopenharmony_ci	return nr_entries + hb->nr_callchain_rows;
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_cistatic void hist_browser__update_rows(struct hist_browser *hb)
10462306a36Sopenharmony_ci{
10562306a36Sopenharmony_ci	struct ui_browser *browser = &hb->b;
10662306a36Sopenharmony_ci	struct hists *hists = hb->hists;
10762306a36Sopenharmony_ci	struct perf_hpp_list *hpp_list = hists->hpp_list;
10862306a36Sopenharmony_ci	u16 index_row;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	if (!hb->show_headers) {
11162306a36Sopenharmony_ci		browser->rows += browser->extra_title_lines;
11262306a36Sopenharmony_ci		browser->extra_title_lines = 0;
11362306a36Sopenharmony_ci		return;
11462306a36Sopenharmony_ci	}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	browser->extra_title_lines = hpp_list->nr_header_lines;
11762306a36Sopenharmony_ci	browser->rows -= browser->extra_title_lines;
11862306a36Sopenharmony_ci	/*
11962306a36Sopenharmony_ci	 * Verify if we were at the last line and that line isn't
12062306a36Sopenharmony_ci	 * visible because we now show the header line(s).
12162306a36Sopenharmony_ci	 */
12262306a36Sopenharmony_ci	index_row = browser->index - browser->top_idx;
12362306a36Sopenharmony_ci	if (index_row >= browser->rows)
12462306a36Sopenharmony_ci		browser->index -= index_row - browser->rows + 1;
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cistatic void hist_browser__refresh_dimensions(struct ui_browser *browser)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	struct hist_browser *hb = container_of(browser, struct hist_browser, b);
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	/* 3 == +/- toggle symbol before actual hist_entry rendering */
13262306a36Sopenharmony_ci	browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
13362306a36Sopenharmony_ci	/*
13462306a36Sopenharmony_ci 	 * FIXME: Just keeping existing behaviour, but this really should be
13562306a36Sopenharmony_ci 	 *	  before updating browser->width, as it will invalidate the
13662306a36Sopenharmony_ci 	 *	  calculation above. Fix this and the fallout in another
13762306a36Sopenharmony_ci 	 *	  changeset.
13862306a36Sopenharmony_ci 	 */
13962306a36Sopenharmony_ci	ui_browser__refresh_dimensions(browser);
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cistatic void hist_browser__reset(struct hist_browser *browser)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	/*
14562306a36Sopenharmony_ci	 * The hists__remove_entry_filter() already folds non-filtered
14662306a36Sopenharmony_ci	 * entries so we can assume it has 0 callchain rows.
14762306a36Sopenharmony_ci	 */
14862306a36Sopenharmony_ci	browser->nr_callchain_rows = 0;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	hist_browser__update_nr_entries(browser);
15162306a36Sopenharmony_ci	browser->b.nr_entries = hist_browser__nr_entries(browser);
15262306a36Sopenharmony_ci	hist_browser__refresh_dimensions(&browser->b);
15362306a36Sopenharmony_ci	ui_browser__reset_index(&browser->b);
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_cistatic char tree__folded_sign(bool unfolded)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	return unfolded ? '-' : '+';
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_cistatic char hist_entry__folded(const struct hist_entry *he)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	return he->has_children ? tree__folded_sign(he->unfolded) : ' ';
16462306a36Sopenharmony_ci}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_cistatic char callchain_list__folded(const struct callchain_list *cl)
16762306a36Sopenharmony_ci{
16862306a36Sopenharmony_ci	return cl->has_children ? tree__folded_sign(cl->unfolded) : ' ';
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_cistatic void callchain_list__set_folding(struct callchain_list *cl, bool unfold)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	cl->unfolded = unfold ? cl->has_children : false;
17462306a36Sopenharmony_ci}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cistatic int callchain_node__count_rows_rb_tree(struct callchain_node *node)
17762306a36Sopenharmony_ci{
17862306a36Sopenharmony_ci	int n = 0;
17962306a36Sopenharmony_ci	struct rb_node *nd;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
18262306a36Sopenharmony_ci		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
18362306a36Sopenharmony_ci		struct callchain_list *chain;
18462306a36Sopenharmony_ci		char folded_sign = ' '; /* No children */
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci		list_for_each_entry(chain, &child->val, list) {
18762306a36Sopenharmony_ci			++n;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci			/* We need this because we may not have children */
19062306a36Sopenharmony_ci			folded_sign = callchain_list__folded(chain);
19162306a36Sopenharmony_ci			if (folded_sign == '+')
19262306a36Sopenharmony_ci				break;
19362306a36Sopenharmony_ci		}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci		if (folded_sign == '-') /* Have children and they're unfolded */
19662306a36Sopenharmony_ci			n += callchain_node__count_rows_rb_tree(child);
19762306a36Sopenharmony_ci	}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	return n;
20062306a36Sopenharmony_ci}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_cistatic int callchain_node__count_flat_rows(struct callchain_node *node)
20362306a36Sopenharmony_ci{
20462306a36Sopenharmony_ci	struct callchain_list *chain;
20562306a36Sopenharmony_ci	char folded_sign = 0;
20662306a36Sopenharmony_ci	int n = 0;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	list_for_each_entry(chain, &node->parent_val, list) {
20962306a36Sopenharmony_ci		if (!folded_sign) {
21062306a36Sopenharmony_ci			/* only check first chain list entry */
21162306a36Sopenharmony_ci			folded_sign = callchain_list__folded(chain);
21262306a36Sopenharmony_ci			if (folded_sign == '+')
21362306a36Sopenharmony_ci				return 1;
21462306a36Sopenharmony_ci		}
21562306a36Sopenharmony_ci		n++;
21662306a36Sopenharmony_ci	}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	list_for_each_entry(chain, &node->val, list) {
21962306a36Sopenharmony_ci		if (!folded_sign) {
22062306a36Sopenharmony_ci			/* node->parent_val list might be empty */
22162306a36Sopenharmony_ci			folded_sign = callchain_list__folded(chain);
22262306a36Sopenharmony_ci			if (folded_sign == '+')
22362306a36Sopenharmony_ci				return 1;
22462306a36Sopenharmony_ci		}
22562306a36Sopenharmony_ci		n++;
22662306a36Sopenharmony_ci	}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	return n;
22962306a36Sopenharmony_ci}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_cistatic int callchain_node__count_folded_rows(struct callchain_node *node __maybe_unused)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	return 1;
23462306a36Sopenharmony_ci}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_cistatic int callchain_node__count_rows(struct callchain_node *node)
23762306a36Sopenharmony_ci{
23862306a36Sopenharmony_ci	struct callchain_list *chain;
23962306a36Sopenharmony_ci	bool unfolded = false;
24062306a36Sopenharmony_ci	int n = 0;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	if (callchain_param.mode == CHAIN_FLAT)
24362306a36Sopenharmony_ci		return callchain_node__count_flat_rows(node);
24462306a36Sopenharmony_ci	else if (callchain_param.mode == CHAIN_FOLDED)
24562306a36Sopenharmony_ci		return callchain_node__count_folded_rows(node);
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	list_for_each_entry(chain, &node->val, list) {
24862306a36Sopenharmony_ci		++n;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci		unfolded = chain->unfolded;
25162306a36Sopenharmony_ci	}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	if (unfolded)
25462306a36Sopenharmony_ci		n += callchain_node__count_rows_rb_tree(node);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	return n;
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_cistatic int callchain__count_rows(struct rb_root *chain)
26062306a36Sopenharmony_ci{
26162306a36Sopenharmony_ci	struct rb_node *nd;
26262306a36Sopenharmony_ci	int n = 0;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
26562306a36Sopenharmony_ci		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
26662306a36Sopenharmony_ci		n += callchain_node__count_rows(node);
26762306a36Sopenharmony_ci	}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	return n;
27062306a36Sopenharmony_ci}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_cistatic int hierarchy_count_rows(struct hist_browser *hb, struct hist_entry *he,
27362306a36Sopenharmony_ci				bool include_children)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	int count = 0;
27662306a36Sopenharmony_ci	struct rb_node *node;
27762306a36Sopenharmony_ci	struct hist_entry *child;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	if (he->leaf)
28062306a36Sopenharmony_ci		return callchain__count_rows(&he->sorted_chain);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	if (he->has_no_entry)
28362306a36Sopenharmony_ci		return 1;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	node = rb_first_cached(&he->hroot_out);
28662306a36Sopenharmony_ci	while (node) {
28762306a36Sopenharmony_ci		float percent;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci		child = rb_entry(node, struct hist_entry, rb_node);
29062306a36Sopenharmony_ci		percent = hist_entry__get_percent_limit(child);
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci		if (!child->filtered && percent >= hb->min_pcnt) {
29362306a36Sopenharmony_ci			count++;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci			if (include_children && child->unfolded)
29662306a36Sopenharmony_ci				count += hierarchy_count_rows(hb, child, true);
29762306a36Sopenharmony_ci		}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci		node = rb_next(node);
30062306a36Sopenharmony_ci	}
30162306a36Sopenharmony_ci	return count;
30262306a36Sopenharmony_ci}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_cistatic bool hist_entry__toggle_fold(struct hist_entry *he)
30562306a36Sopenharmony_ci{
30662306a36Sopenharmony_ci	if (!he)
30762306a36Sopenharmony_ci		return false;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	if (!he->has_children)
31062306a36Sopenharmony_ci		return false;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	he->unfolded = !he->unfolded;
31362306a36Sopenharmony_ci	return true;
31462306a36Sopenharmony_ci}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_cistatic bool callchain_list__toggle_fold(struct callchain_list *cl)
31762306a36Sopenharmony_ci{
31862306a36Sopenharmony_ci	if (!cl)
31962306a36Sopenharmony_ci		return false;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	if (!cl->has_children)
32262306a36Sopenharmony_ci		return false;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	cl->unfolded = !cl->unfolded;
32562306a36Sopenharmony_ci	return true;
32662306a36Sopenharmony_ci}
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_cistatic void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
32962306a36Sopenharmony_ci{
33062306a36Sopenharmony_ci	struct rb_node *nd = rb_first(&node->rb_root);
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
33362306a36Sopenharmony_ci		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
33462306a36Sopenharmony_ci		struct callchain_list *chain;
33562306a36Sopenharmony_ci		bool first = true;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci		list_for_each_entry(chain, &child->val, list) {
33862306a36Sopenharmony_ci			if (first) {
33962306a36Sopenharmony_ci				first = false;
34062306a36Sopenharmony_ci				chain->has_children = chain->list.next != &child->val ||
34162306a36Sopenharmony_ci							 !RB_EMPTY_ROOT(&child->rb_root);
34262306a36Sopenharmony_ci			} else
34362306a36Sopenharmony_ci				chain->has_children = chain->list.next == &child->val &&
34462306a36Sopenharmony_ci							 !RB_EMPTY_ROOT(&child->rb_root);
34562306a36Sopenharmony_ci		}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci		callchain_node__init_have_children_rb_tree(child);
34862306a36Sopenharmony_ci	}
34962306a36Sopenharmony_ci}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_cistatic void callchain_node__init_have_children(struct callchain_node *node,
35262306a36Sopenharmony_ci					       bool has_sibling)
35362306a36Sopenharmony_ci{
35462306a36Sopenharmony_ci	struct callchain_list *chain;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	chain = list_entry(node->val.next, struct callchain_list, list);
35762306a36Sopenharmony_ci	chain->has_children = has_sibling;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	if (!list_empty(&node->val)) {
36062306a36Sopenharmony_ci		chain = list_entry(node->val.prev, struct callchain_list, list);
36162306a36Sopenharmony_ci		chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);
36262306a36Sopenharmony_ci	}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	callchain_node__init_have_children_rb_tree(node);
36562306a36Sopenharmony_ci}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_cistatic void callchain__init_have_children(struct rb_root *root)
36862306a36Sopenharmony_ci{
36962306a36Sopenharmony_ci	struct rb_node *nd = rb_first(root);
37062306a36Sopenharmony_ci	bool has_sibling = nd && rb_next(nd);
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	for (nd = rb_first(root); nd; nd = rb_next(nd)) {
37362306a36Sopenharmony_ci		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
37462306a36Sopenharmony_ci		callchain_node__init_have_children(node, has_sibling);
37562306a36Sopenharmony_ci		if (callchain_param.mode == CHAIN_FLAT ||
37662306a36Sopenharmony_ci		    callchain_param.mode == CHAIN_FOLDED)
37762306a36Sopenharmony_ci			callchain_node__make_parent_list(node);
37862306a36Sopenharmony_ci	}
37962306a36Sopenharmony_ci}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_cistatic void hist_entry__init_have_children(struct hist_entry *he)
38262306a36Sopenharmony_ci{
38362306a36Sopenharmony_ci	if (he->init_have_children)
38462306a36Sopenharmony_ci		return;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	if (he->leaf) {
38762306a36Sopenharmony_ci		he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
38862306a36Sopenharmony_ci		callchain__init_have_children(&he->sorted_chain);
38962306a36Sopenharmony_ci	} else {
39062306a36Sopenharmony_ci		he->has_children = !RB_EMPTY_ROOT(&he->hroot_out.rb_root);
39162306a36Sopenharmony_ci	}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	he->init_have_children = true;
39462306a36Sopenharmony_ci}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_cistatic bool hist_browser__selection_has_children(struct hist_browser *browser)
39762306a36Sopenharmony_ci{
39862306a36Sopenharmony_ci	struct hist_entry *he = browser->he_selection;
39962306a36Sopenharmony_ci	struct map_symbol *ms = browser->selection;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	if (!he || !ms)
40262306a36Sopenharmony_ci		return false;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	if (ms == &he->ms)
40562306a36Sopenharmony_ci	       return he->has_children;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	return container_of(ms, struct callchain_list, ms)->has_children;
40862306a36Sopenharmony_ci}
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_cistatic bool hist_browser__selection_unfolded(struct hist_browser *browser)
41162306a36Sopenharmony_ci{
41262306a36Sopenharmony_ci	struct hist_entry *he = browser->he_selection;
41362306a36Sopenharmony_ci	struct map_symbol *ms = browser->selection;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	if (!he || !ms)
41662306a36Sopenharmony_ci		return false;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	if (ms == &he->ms)
41962306a36Sopenharmony_ci	       return he->unfolded;
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	return container_of(ms, struct callchain_list, ms)->unfolded;
42262306a36Sopenharmony_ci}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_cistatic char *hist_browser__selection_sym_name(struct hist_browser *browser, char *bf, size_t size)
42562306a36Sopenharmony_ci{
42662306a36Sopenharmony_ci	struct hist_entry *he = browser->he_selection;
42762306a36Sopenharmony_ci	struct map_symbol *ms = browser->selection;
42862306a36Sopenharmony_ci	struct callchain_list *callchain_entry;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	if (!he || !ms)
43162306a36Sopenharmony_ci		return NULL;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	if (ms == &he->ms) {
43462306a36Sopenharmony_ci	       hist_entry__sym_snprintf(he, bf, size, 0);
43562306a36Sopenharmony_ci	       return bf + 4; // skip the level, e.g. '[k] '
43662306a36Sopenharmony_ci	}
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	callchain_entry = container_of(ms, struct callchain_list, ms);
43962306a36Sopenharmony_ci	return callchain_list__sym_name(callchain_entry, bf, size, browser->show_dso);
44062306a36Sopenharmony_ci}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_cistatic bool hist_browser__toggle_fold(struct hist_browser *browser)
44362306a36Sopenharmony_ci{
44462306a36Sopenharmony_ci	struct hist_entry *he = browser->he_selection;
44562306a36Sopenharmony_ci	struct map_symbol *ms = browser->selection;
44662306a36Sopenharmony_ci	struct callchain_list *cl = container_of(ms, struct callchain_list, ms);
44762306a36Sopenharmony_ci	bool has_children;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	if (!he || !ms)
45062306a36Sopenharmony_ci		return false;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	if (ms == &he->ms)
45362306a36Sopenharmony_ci		has_children = hist_entry__toggle_fold(he);
45462306a36Sopenharmony_ci	else
45562306a36Sopenharmony_ci		has_children = callchain_list__toggle_fold(cl);
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	if (has_children) {
45862306a36Sopenharmony_ci		int child_rows = 0;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci		hist_entry__init_have_children(he);
46162306a36Sopenharmony_ci		browser->b.nr_entries -= he->nr_rows;
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci		if (he->leaf)
46462306a36Sopenharmony_ci			browser->nr_callchain_rows -= he->nr_rows;
46562306a36Sopenharmony_ci		else
46662306a36Sopenharmony_ci			browser->nr_hierarchy_entries -= he->nr_rows;
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci		if (symbol_conf.report_hierarchy)
46962306a36Sopenharmony_ci			child_rows = hierarchy_count_rows(browser, he, true);
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci		if (he->unfolded) {
47262306a36Sopenharmony_ci			if (he->leaf)
47362306a36Sopenharmony_ci				he->nr_rows = callchain__count_rows(
47462306a36Sopenharmony_ci						&he->sorted_chain);
47562306a36Sopenharmony_ci			else
47662306a36Sopenharmony_ci				he->nr_rows = hierarchy_count_rows(browser, he, false);
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci			/* account grand children */
47962306a36Sopenharmony_ci			if (symbol_conf.report_hierarchy)
48062306a36Sopenharmony_ci				browser->b.nr_entries += child_rows - he->nr_rows;
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci			if (!he->leaf && he->nr_rows == 0) {
48362306a36Sopenharmony_ci				he->has_no_entry = true;
48462306a36Sopenharmony_ci				he->nr_rows = 1;
48562306a36Sopenharmony_ci			}
48662306a36Sopenharmony_ci		} else {
48762306a36Sopenharmony_ci			if (symbol_conf.report_hierarchy)
48862306a36Sopenharmony_ci				browser->b.nr_entries -= child_rows - he->nr_rows;
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci			if (he->has_no_entry)
49162306a36Sopenharmony_ci				he->has_no_entry = false;
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci			he->nr_rows = 0;
49462306a36Sopenharmony_ci		}
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci		browser->b.nr_entries += he->nr_rows;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci		if (he->leaf)
49962306a36Sopenharmony_ci			browser->nr_callchain_rows += he->nr_rows;
50062306a36Sopenharmony_ci		else
50162306a36Sopenharmony_ci			browser->nr_hierarchy_entries += he->nr_rows;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci		return true;
50462306a36Sopenharmony_ci	}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	/* If it doesn't have children, no toggling performed */
50762306a36Sopenharmony_ci	return false;
50862306a36Sopenharmony_ci}
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_cistatic int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
51162306a36Sopenharmony_ci{
51262306a36Sopenharmony_ci	int n = 0;
51362306a36Sopenharmony_ci	struct rb_node *nd;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
51662306a36Sopenharmony_ci		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
51762306a36Sopenharmony_ci		struct callchain_list *chain;
51862306a36Sopenharmony_ci		bool has_children = false;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci		list_for_each_entry(chain, &child->val, list) {
52162306a36Sopenharmony_ci			++n;
52262306a36Sopenharmony_ci			callchain_list__set_folding(chain, unfold);
52362306a36Sopenharmony_ci			has_children = chain->has_children;
52462306a36Sopenharmony_ci		}
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci		if (has_children)
52762306a36Sopenharmony_ci			n += callchain_node__set_folding_rb_tree(child, unfold);
52862306a36Sopenharmony_ci	}
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	return n;
53162306a36Sopenharmony_ci}
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_cistatic int callchain_node__set_folding(struct callchain_node *node, bool unfold)
53462306a36Sopenharmony_ci{
53562306a36Sopenharmony_ci	struct callchain_list *chain;
53662306a36Sopenharmony_ci	bool has_children = false;
53762306a36Sopenharmony_ci	int n = 0;
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	list_for_each_entry(chain, &node->val, list) {
54062306a36Sopenharmony_ci		++n;
54162306a36Sopenharmony_ci		callchain_list__set_folding(chain, unfold);
54262306a36Sopenharmony_ci		has_children = chain->has_children;
54362306a36Sopenharmony_ci	}
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	if (has_children)
54662306a36Sopenharmony_ci		n += callchain_node__set_folding_rb_tree(node, unfold);
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	return n;
54962306a36Sopenharmony_ci}
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_cistatic int callchain__set_folding(struct rb_root *chain, bool unfold)
55262306a36Sopenharmony_ci{
55362306a36Sopenharmony_ci	struct rb_node *nd;
55462306a36Sopenharmony_ci	int n = 0;
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
55762306a36Sopenharmony_ci		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
55862306a36Sopenharmony_ci		n += callchain_node__set_folding(node, unfold);
55962306a36Sopenharmony_ci	}
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	return n;
56262306a36Sopenharmony_ci}
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_cistatic int hierarchy_set_folding(struct hist_browser *hb, struct hist_entry *he,
56562306a36Sopenharmony_ci				 bool unfold __maybe_unused)
56662306a36Sopenharmony_ci{
56762306a36Sopenharmony_ci	float percent;
56862306a36Sopenharmony_ci	struct rb_node *nd;
56962306a36Sopenharmony_ci	struct hist_entry *child;
57062306a36Sopenharmony_ci	int n = 0;
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	for (nd = rb_first_cached(&he->hroot_out); nd; nd = rb_next(nd)) {
57362306a36Sopenharmony_ci		child = rb_entry(nd, struct hist_entry, rb_node);
57462306a36Sopenharmony_ci		percent = hist_entry__get_percent_limit(child);
57562306a36Sopenharmony_ci		if (!child->filtered && percent >= hb->min_pcnt)
57662306a36Sopenharmony_ci			n++;
57762306a36Sopenharmony_ci	}
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	return n;
58062306a36Sopenharmony_ci}
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_cistatic void hist_entry__set_folding(struct hist_entry *he,
58362306a36Sopenharmony_ci				    struct hist_browser *hb, bool unfold)
58462306a36Sopenharmony_ci{
58562306a36Sopenharmony_ci	hist_entry__init_have_children(he);
58662306a36Sopenharmony_ci	he->unfolded = unfold ? he->has_children : false;
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	if (he->has_children) {
58962306a36Sopenharmony_ci		int n;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci		if (he->leaf)
59262306a36Sopenharmony_ci			n = callchain__set_folding(&he->sorted_chain, unfold);
59362306a36Sopenharmony_ci		else
59462306a36Sopenharmony_ci			n = hierarchy_set_folding(hb, he, unfold);
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci		he->nr_rows = unfold ? n : 0;
59762306a36Sopenharmony_ci	} else
59862306a36Sopenharmony_ci		he->nr_rows = 0;
59962306a36Sopenharmony_ci}
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_cistatic void
60262306a36Sopenharmony_ci__hist_browser__set_folding(struct hist_browser *browser, bool unfold)
60362306a36Sopenharmony_ci{
60462306a36Sopenharmony_ci	struct rb_node *nd;
60562306a36Sopenharmony_ci	struct hist_entry *he;
60662306a36Sopenharmony_ci	double percent;
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	nd = rb_first_cached(&browser->hists->entries);
60962306a36Sopenharmony_ci	while (nd) {
61062306a36Sopenharmony_ci		he = rb_entry(nd, struct hist_entry, rb_node);
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci		/* set folding state even if it's currently folded */
61362306a36Sopenharmony_ci		nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci		hist_entry__set_folding(he, browser, unfold);
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci		percent = hist_entry__get_percent_limit(he);
61862306a36Sopenharmony_ci		if (he->filtered || percent < browser->min_pcnt)
61962306a36Sopenharmony_ci			continue;
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci		if (!he->depth || unfold)
62262306a36Sopenharmony_ci			browser->nr_hierarchy_entries++;
62362306a36Sopenharmony_ci		if (he->leaf)
62462306a36Sopenharmony_ci			browser->nr_callchain_rows += he->nr_rows;
62562306a36Sopenharmony_ci		else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) {
62662306a36Sopenharmony_ci			browser->nr_hierarchy_entries++;
62762306a36Sopenharmony_ci			he->has_no_entry = true;
62862306a36Sopenharmony_ci			he->nr_rows = 1;
62962306a36Sopenharmony_ci		} else
63062306a36Sopenharmony_ci			he->has_no_entry = false;
63162306a36Sopenharmony_ci	}
63262306a36Sopenharmony_ci}
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_cistatic void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
63562306a36Sopenharmony_ci{
63662306a36Sopenharmony_ci	browser->nr_hierarchy_entries = 0;
63762306a36Sopenharmony_ci	browser->nr_callchain_rows = 0;
63862306a36Sopenharmony_ci	__hist_browser__set_folding(browser, unfold);
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	browser->b.nr_entries = hist_browser__nr_entries(browser);
64162306a36Sopenharmony_ci	/* Go to the start, we may be way after valid entries after a collapse */
64262306a36Sopenharmony_ci	ui_browser__reset_index(&browser->b);
64362306a36Sopenharmony_ci}
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_cistatic void hist_browser__set_folding_selected(struct hist_browser *browser, bool unfold)
64662306a36Sopenharmony_ci{
64762306a36Sopenharmony_ci	if (!browser->he_selection)
64862306a36Sopenharmony_ci		return;
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	if (unfold == browser->he_selection->unfolded)
65162306a36Sopenharmony_ci		return;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	hist_browser__toggle_fold(browser);
65462306a36Sopenharmony_ci}
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_cistatic void ui_browser__warn_lost_events(struct ui_browser *browser)
65762306a36Sopenharmony_ci{
65862306a36Sopenharmony_ci	ui_browser__warning(browser, 4,
65962306a36Sopenharmony_ci		"Events are being lost, check IO/CPU overload!\n\n"
66062306a36Sopenharmony_ci		"You may want to run 'perf' using a RT scheduler policy:\n\n"
66162306a36Sopenharmony_ci		" perf top -r 80\n\n"
66262306a36Sopenharmony_ci		"Or reduce the sampling frequency.");
66362306a36Sopenharmony_ci}
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_cistatic int hist_browser__title(struct hist_browser *browser, char *bf, size_t size)
66662306a36Sopenharmony_ci{
66762306a36Sopenharmony_ci	return browser->title ? browser->title(browser, bf, size) : 0;
66862306a36Sopenharmony_ci}
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_cistatic int hist_browser__handle_hotkey(struct hist_browser *browser, bool warn_lost_event, char *title, size_t size, int key)
67162306a36Sopenharmony_ci{
67262306a36Sopenharmony_ci	switch (key) {
67362306a36Sopenharmony_ci	case K_TIMER: {
67462306a36Sopenharmony_ci		struct hist_browser_timer *hbt = browser->hbt;
67562306a36Sopenharmony_ci		struct evsel *evsel = hists_to_evsel(browser->hists);
67662306a36Sopenharmony_ci		u64 nr_entries;
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci		WARN_ON_ONCE(!hbt);
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci		if (hbt)
68162306a36Sopenharmony_ci			hbt->timer(hbt->arg);
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci		if (hist_browser__has_filter(browser) || symbol_conf.report_hierarchy)
68462306a36Sopenharmony_ci			hist_browser__update_nr_entries(browser);
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci		nr_entries = hist_browser__nr_entries(browser);
68762306a36Sopenharmony_ci		ui_browser__update_nr_entries(&browser->b, nr_entries);
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci		if (warn_lost_event &&
69062306a36Sopenharmony_ci		    (evsel->evlist->stats.nr_lost_warned !=
69162306a36Sopenharmony_ci		     evsel->evlist->stats.nr_events[PERF_RECORD_LOST])) {
69262306a36Sopenharmony_ci			evsel->evlist->stats.nr_lost_warned =
69362306a36Sopenharmony_ci				evsel->evlist->stats.nr_events[PERF_RECORD_LOST];
69462306a36Sopenharmony_ci			ui_browser__warn_lost_events(&browser->b);
69562306a36Sopenharmony_ci		}
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci		hist_browser__title(browser, title, size);
69862306a36Sopenharmony_ci		ui_browser__show_title(&browser->b, title);
69962306a36Sopenharmony_ci		break;
70062306a36Sopenharmony_ci	}
70162306a36Sopenharmony_ci	case 'D': { /* Debug */
70262306a36Sopenharmony_ci		struct hist_entry *h = rb_entry(browser->b.top, struct hist_entry, rb_node);
70362306a36Sopenharmony_ci		static int seq;
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci		ui_helpline__pop();
70662306a36Sopenharmony_ci		ui_helpline__fpush("%d: nr_ent=(%d,%d), etl: %d, rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
70762306a36Sopenharmony_ci				   seq++, browser->b.nr_entries, browser->hists->nr_entries,
70862306a36Sopenharmony_ci				   browser->b.extra_title_lines, browser->b.rows,
70962306a36Sopenharmony_ci				   browser->b.index, browser->b.top_idx, h->row_offset, h->nr_rows);
71062306a36Sopenharmony_ci	}
71162306a36Sopenharmony_ci		break;
71262306a36Sopenharmony_ci	case 'C':
71362306a36Sopenharmony_ci		/* Collapse the whole world. */
71462306a36Sopenharmony_ci		hist_browser__set_folding(browser, false);
71562306a36Sopenharmony_ci		break;
71662306a36Sopenharmony_ci	case 'c':
71762306a36Sopenharmony_ci		/* Collapse the selected entry. */
71862306a36Sopenharmony_ci		hist_browser__set_folding_selected(browser, false);
71962306a36Sopenharmony_ci		break;
72062306a36Sopenharmony_ci	case 'E':
72162306a36Sopenharmony_ci		/* Expand the whole world. */
72262306a36Sopenharmony_ci		hist_browser__set_folding(browser, true);
72362306a36Sopenharmony_ci		break;
72462306a36Sopenharmony_ci	case 'e':
72562306a36Sopenharmony_ci		/* Toggle expand/collapse the selected entry. */
72662306a36Sopenharmony_ci		hist_browser__toggle_fold(browser);
72762306a36Sopenharmony_ci		break;
72862306a36Sopenharmony_ci	case 'H':
72962306a36Sopenharmony_ci		browser->show_headers = !browser->show_headers;
73062306a36Sopenharmony_ci		hist_browser__update_rows(browser);
73162306a36Sopenharmony_ci		break;
73262306a36Sopenharmony_ci	case '+':
73362306a36Sopenharmony_ci		if (hist_browser__toggle_fold(browser))
73462306a36Sopenharmony_ci			break;
73562306a36Sopenharmony_ci		/* fall thru */
73662306a36Sopenharmony_ci	default:
73762306a36Sopenharmony_ci		return -1;
73862306a36Sopenharmony_ci	}
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	return 0;
74162306a36Sopenharmony_ci}
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ciint hist_browser__run(struct hist_browser *browser, const char *help,
74462306a36Sopenharmony_ci		      bool warn_lost_event, int key)
74562306a36Sopenharmony_ci{
74662306a36Sopenharmony_ci	char title[160];
74762306a36Sopenharmony_ci	struct hist_browser_timer *hbt = browser->hbt;
74862306a36Sopenharmony_ci	int delay_secs = hbt ? hbt->refresh : 0;
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	browser->b.entries = &browser->hists->entries;
75162306a36Sopenharmony_ci	browser->b.nr_entries = hist_browser__nr_entries(browser);
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	hist_browser__title(browser, title, sizeof(title));
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	if (ui_browser__show(&browser->b, title, "%s", help) < 0)
75662306a36Sopenharmony_ci		return -1;
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	if (key && hist_browser__handle_hotkey(browser, warn_lost_event, title, sizeof(title), key))
75962306a36Sopenharmony_ci		goto out;
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	while (1) {
76262306a36Sopenharmony_ci		key = ui_browser__run(&browser->b, delay_secs);
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci		if (hist_browser__handle_hotkey(browser, warn_lost_event, title, sizeof(title), key))
76562306a36Sopenharmony_ci			break;
76662306a36Sopenharmony_ci	}
76762306a36Sopenharmony_ciout:
76862306a36Sopenharmony_ci	ui_browser__hide(&browser->b);
76962306a36Sopenharmony_ci	return key;
77062306a36Sopenharmony_ci}
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_cistruct callchain_print_arg {
77362306a36Sopenharmony_ci	/* for hists browser */
77462306a36Sopenharmony_ci	off_t	row_offset;
77562306a36Sopenharmony_ci	bool	is_current_entry;
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	/* for file dump */
77862306a36Sopenharmony_ci	FILE	*fp;
77962306a36Sopenharmony_ci	int	printed;
78062306a36Sopenharmony_ci};
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_citypedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
78362306a36Sopenharmony_ci					 struct callchain_list *chain,
78462306a36Sopenharmony_ci					 const char *str, int offset,
78562306a36Sopenharmony_ci					 unsigned short row,
78662306a36Sopenharmony_ci					 struct callchain_print_arg *arg);
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_cistatic void hist_browser__show_callchain_entry(struct hist_browser *browser,
78962306a36Sopenharmony_ci					       struct callchain_list *chain,
79062306a36Sopenharmony_ci					       const char *str, int offset,
79162306a36Sopenharmony_ci					       unsigned short row,
79262306a36Sopenharmony_ci					       struct callchain_print_arg *arg)
79362306a36Sopenharmony_ci{
79462306a36Sopenharmony_ci	int color, width;
79562306a36Sopenharmony_ci	char folded_sign = callchain_list__folded(chain);
79662306a36Sopenharmony_ci	bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	color = HE_COLORSET_NORMAL;
79962306a36Sopenharmony_ci	width = browser->b.width - (offset + 2);
80062306a36Sopenharmony_ci	if (ui_browser__is_current_entry(&browser->b, row)) {
80162306a36Sopenharmony_ci		browser->selection = &chain->ms;
80262306a36Sopenharmony_ci		color = HE_COLORSET_SELECTED;
80362306a36Sopenharmony_ci		arg->is_current_entry = true;
80462306a36Sopenharmony_ci	}
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	ui_browser__set_color(&browser->b, color);
80762306a36Sopenharmony_ci	ui_browser__gotorc(&browser->b, row, 0);
80862306a36Sopenharmony_ci	ui_browser__write_nstring(&browser->b, " ", offset);
80962306a36Sopenharmony_ci	ui_browser__printf(&browser->b, "%c", folded_sign);
81062306a36Sopenharmony_ci	ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
81162306a36Sopenharmony_ci	ui_browser__write_nstring(&browser->b, str, width);
81262306a36Sopenharmony_ci}
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_cistatic void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
81562306a36Sopenharmony_ci						  struct callchain_list *chain,
81662306a36Sopenharmony_ci						  const char *str, int offset,
81762306a36Sopenharmony_ci						  unsigned short row __maybe_unused,
81862306a36Sopenharmony_ci						  struct callchain_print_arg *arg)
81962306a36Sopenharmony_ci{
82062306a36Sopenharmony_ci	char folded_sign = callchain_list__folded(chain);
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
82362306a36Sopenharmony_ci				folded_sign, str);
82462306a36Sopenharmony_ci}
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_citypedef bool (*check_output_full_fn)(struct hist_browser *browser,
82762306a36Sopenharmony_ci				     unsigned short row);
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_cistatic bool hist_browser__check_output_full(struct hist_browser *browser,
83062306a36Sopenharmony_ci					    unsigned short row)
83162306a36Sopenharmony_ci{
83262306a36Sopenharmony_ci	return browser->b.rows == row;
83362306a36Sopenharmony_ci}
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_cistatic bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
83662306a36Sopenharmony_ci					  unsigned short row __maybe_unused)
83762306a36Sopenharmony_ci{
83862306a36Sopenharmony_ci	return false;
83962306a36Sopenharmony_ci}
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci#define LEVEL_OFFSET_STEP 3
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_cistatic int hist_browser__show_callchain_list(struct hist_browser *browser,
84462306a36Sopenharmony_ci					     struct callchain_node *node,
84562306a36Sopenharmony_ci					     struct callchain_list *chain,
84662306a36Sopenharmony_ci					     unsigned short row, u64 total,
84762306a36Sopenharmony_ci					     bool need_percent, int offset,
84862306a36Sopenharmony_ci					     print_callchain_entry_fn print,
84962306a36Sopenharmony_ci					     struct callchain_print_arg *arg)
85062306a36Sopenharmony_ci{
85162306a36Sopenharmony_ci	char bf[1024], *alloc_str;
85262306a36Sopenharmony_ci	char buf[64], *alloc_str2;
85362306a36Sopenharmony_ci	const char *str;
85462306a36Sopenharmony_ci	int ret = 1;
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci	if (arg->row_offset != 0) {
85762306a36Sopenharmony_ci		arg->row_offset--;
85862306a36Sopenharmony_ci		return 0;
85962306a36Sopenharmony_ci	}
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	alloc_str = NULL;
86262306a36Sopenharmony_ci	alloc_str2 = NULL;
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci	str = callchain_list__sym_name(chain, bf, sizeof(bf),
86562306a36Sopenharmony_ci				       browser->show_dso);
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	if (symbol_conf.show_branchflag_count) {
86862306a36Sopenharmony_ci		callchain_list_counts__printf_value(chain, NULL,
86962306a36Sopenharmony_ci						    buf, sizeof(buf));
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci		if (asprintf(&alloc_str2, "%s%s", str, buf) < 0)
87262306a36Sopenharmony_ci			str = "Not enough memory!";
87362306a36Sopenharmony_ci		else
87462306a36Sopenharmony_ci			str = alloc_str2;
87562306a36Sopenharmony_ci	}
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	if (need_percent) {
87862306a36Sopenharmony_ci		callchain_node__scnprintf_value(node, buf, sizeof(buf),
87962306a36Sopenharmony_ci						total);
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci		if (asprintf(&alloc_str, "%s %s", buf, str) < 0)
88262306a36Sopenharmony_ci			str = "Not enough memory!";
88362306a36Sopenharmony_ci		else
88462306a36Sopenharmony_ci			str = alloc_str;
88562306a36Sopenharmony_ci	}
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci	print(browser, chain, str, offset, row, arg);
88862306a36Sopenharmony_ci	free(alloc_str);
88962306a36Sopenharmony_ci	free(alloc_str2);
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	return ret;
89262306a36Sopenharmony_ci}
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_cistatic bool check_percent_display(struct rb_node *node, u64 parent_total)
89562306a36Sopenharmony_ci{
89662306a36Sopenharmony_ci	struct callchain_node *child;
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	if (node == NULL)
89962306a36Sopenharmony_ci		return false;
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	if (rb_next(node))
90262306a36Sopenharmony_ci		return true;
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	child = rb_entry(node, struct callchain_node, rb_node);
90562306a36Sopenharmony_ci	return callchain_cumul_hits(child) != parent_total;
90662306a36Sopenharmony_ci}
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_cistatic int hist_browser__show_callchain_flat(struct hist_browser *browser,
90962306a36Sopenharmony_ci					     struct rb_root *root,
91062306a36Sopenharmony_ci					     unsigned short row, u64 total,
91162306a36Sopenharmony_ci					     u64 parent_total,
91262306a36Sopenharmony_ci					     print_callchain_entry_fn print,
91362306a36Sopenharmony_ci					     struct callchain_print_arg *arg,
91462306a36Sopenharmony_ci					     check_output_full_fn is_output_full)
91562306a36Sopenharmony_ci{
91662306a36Sopenharmony_ci	struct rb_node *node;
91762306a36Sopenharmony_ci	int first_row = row, offset = LEVEL_OFFSET_STEP;
91862306a36Sopenharmony_ci	bool need_percent;
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	node = rb_first(root);
92162306a36Sopenharmony_ci	need_percent = check_percent_display(node, parent_total);
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	while (node) {
92462306a36Sopenharmony_ci		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
92562306a36Sopenharmony_ci		struct rb_node *next = rb_next(node);
92662306a36Sopenharmony_ci		struct callchain_list *chain;
92762306a36Sopenharmony_ci		char folded_sign = ' ';
92862306a36Sopenharmony_ci		int first = true;
92962306a36Sopenharmony_ci		int extra_offset = 0;
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci		list_for_each_entry(chain, &child->parent_val, list) {
93262306a36Sopenharmony_ci			bool was_first = first;
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci			if (first)
93562306a36Sopenharmony_ci				first = false;
93662306a36Sopenharmony_ci			else if (need_percent)
93762306a36Sopenharmony_ci				extra_offset = LEVEL_OFFSET_STEP;
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci			folded_sign = callchain_list__folded(chain);
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci			row += hist_browser__show_callchain_list(browser, child,
94262306a36Sopenharmony_ci							chain, row, total,
94362306a36Sopenharmony_ci							was_first && need_percent,
94462306a36Sopenharmony_ci							offset + extra_offset,
94562306a36Sopenharmony_ci							print, arg);
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci			if (is_output_full(browser, row))
94862306a36Sopenharmony_ci				goto out;
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci			if (folded_sign == '+')
95162306a36Sopenharmony_ci				goto next;
95262306a36Sopenharmony_ci		}
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci		list_for_each_entry(chain, &child->val, list) {
95562306a36Sopenharmony_ci			bool was_first = first;
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci			if (first)
95862306a36Sopenharmony_ci				first = false;
95962306a36Sopenharmony_ci			else if (need_percent)
96062306a36Sopenharmony_ci				extra_offset = LEVEL_OFFSET_STEP;
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci			folded_sign = callchain_list__folded(chain);
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci			row += hist_browser__show_callchain_list(browser, child,
96562306a36Sopenharmony_ci							chain, row, total,
96662306a36Sopenharmony_ci							was_first && need_percent,
96762306a36Sopenharmony_ci							offset + extra_offset,
96862306a36Sopenharmony_ci							print, arg);
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci			if (is_output_full(browser, row))
97162306a36Sopenharmony_ci				goto out;
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci			if (folded_sign == '+')
97462306a36Sopenharmony_ci				break;
97562306a36Sopenharmony_ci		}
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_cinext:
97862306a36Sopenharmony_ci		if (is_output_full(browser, row))
97962306a36Sopenharmony_ci			break;
98062306a36Sopenharmony_ci		node = next;
98162306a36Sopenharmony_ci	}
98262306a36Sopenharmony_ciout:
98362306a36Sopenharmony_ci	return row - first_row;
98462306a36Sopenharmony_ci}
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_cistatic char *hist_browser__folded_callchain_str(struct hist_browser *browser,
98762306a36Sopenharmony_ci						struct callchain_list *chain,
98862306a36Sopenharmony_ci						char *value_str, char *old_str)
98962306a36Sopenharmony_ci{
99062306a36Sopenharmony_ci	char bf[1024];
99162306a36Sopenharmony_ci	const char *str;
99262306a36Sopenharmony_ci	char *new;
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	str = callchain_list__sym_name(chain, bf, sizeof(bf),
99562306a36Sopenharmony_ci				       browser->show_dso);
99662306a36Sopenharmony_ci	if (old_str) {
99762306a36Sopenharmony_ci		if (asprintf(&new, "%s%s%s", old_str,
99862306a36Sopenharmony_ci			     symbol_conf.field_sep ?: ";", str) < 0)
99962306a36Sopenharmony_ci			new = NULL;
100062306a36Sopenharmony_ci	} else {
100162306a36Sopenharmony_ci		if (value_str) {
100262306a36Sopenharmony_ci			if (asprintf(&new, "%s %s", value_str, str) < 0)
100362306a36Sopenharmony_ci				new = NULL;
100462306a36Sopenharmony_ci		} else {
100562306a36Sopenharmony_ci			if (asprintf(&new, "%s", str) < 0)
100662306a36Sopenharmony_ci				new = NULL;
100762306a36Sopenharmony_ci		}
100862306a36Sopenharmony_ci	}
100962306a36Sopenharmony_ci	return new;
101062306a36Sopenharmony_ci}
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_cistatic int hist_browser__show_callchain_folded(struct hist_browser *browser,
101362306a36Sopenharmony_ci					       struct rb_root *root,
101462306a36Sopenharmony_ci					       unsigned short row, u64 total,
101562306a36Sopenharmony_ci					       u64 parent_total,
101662306a36Sopenharmony_ci					       print_callchain_entry_fn print,
101762306a36Sopenharmony_ci					       struct callchain_print_arg *arg,
101862306a36Sopenharmony_ci					       check_output_full_fn is_output_full)
101962306a36Sopenharmony_ci{
102062306a36Sopenharmony_ci	struct rb_node *node;
102162306a36Sopenharmony_ci	int first_row = row, offset = LEVEL_OFFSET_STEP;
102262306a36Sopenharmony_ci	bool need_percent;
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci	node = rb_first(root);
102562306a36Sopenharmony_ci	need_percent = check_percent_display(node, parent_total);
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci	while (node) {
102862306a36Sopenharmony_ci		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
102962306a36Sopenharmony_ci		struct rb_node *next = rb_next(node);
103062306a36Sopenharmony_ci		struct callchain_list *chain, *first_chain = NULL;
103162306a36Sopenharmony_ci		int first = true;
103262306a36Sopenharmony_ci		char *value_str = NULL, *value_str_alloc = NULL;
103362306a36Sopenharmony_ci		char *chain_str = NULL, *chain_str_alloc = NULL;
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci		if (arg->row_offset != 0) {
103662306a36Sopenharmony_ci			arg->row_offset--;
103762306a36Sopenharmony_ci			goto next;
103862306a36Sopenharmony_ci		}
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci		if (need_percent) {
104162306a36Sopenharmony_ci			char buf[64];
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci			callchain_node__scnprintf_value(child, buf, sizeof(buf), total);
104462306a36Sopenharmony_ci			if (asprintf(&value_str, "%s", buf) < 0) {
104562306a36Sopenharmony_ci				value_str = (char *)"<...>";
104662306a36Sopenharmony_ci				goto do_print;
104762306a36Sopenharmony_ci			}
104862306a36Sopenharmony_ci			value_str_alloc = value_str;
104962306a36Sopenharmony_ci		}
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci		list_for_each_entry(chain, &child->parent_val, list) {
105262306a36Sopenharmony_ci			chain_str = hist_browser__folded_callchain_str(browser,
105362306a36Sopenharmony_ci						chain, value_str, chain_str);
105462306a36Sopenharmony_ci			if (first) {
105562306a36Sopenharmony_ci				first = false;
105662306a36Sopenharmony_ci				first_chain = chain;
105762306a36Sopenharmony_ci			}
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci			if (chain_str == NULL) {
106062306a36Sopenharmony_ci				chain_str = (char *)"Not enough memory!";
106162306a36Sopenharmony_ci				goto do_print;
106262306a36Sopenharmony_ci			}
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci			chain_str_alloc = chain_str;
106562306a36Sopenharmony_ci		}
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci		list_for_each_entry(chain, &child->val, list) {
106862306a36Sopenharmony_ci			chain_str = hist_browser__folded_callchain_str(browser,
106962306a36Sopenharmony_ci						chain, value_str, chain_str);
107062306a36Sopenharmony_ci			if (first) {
107162306a36Sopenharmony_ci				first = false;
107262306a36Sopenharmony_ci				first_chain = chain;
107362306a36Sopenharmony_ci			}
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci			if (chain_str == NULL) {
107662306a36Sopenharmony_ci				chain_str = (char *)"Not enough memory!";
107762306a36Sopenharmony_ci				goto do_print;
107862306a36Sopenharmony_ci			}
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci			chain_str_alloc = chain_str;
108162306a36Sopenharmony_ci		}
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_cido_print:
108462306a36Sopenharmony_ci		print(browser, first_chain, chain_str, offset, row++, arg);
108562306a36Sopenharmony_ci		free(value_str_alloc);
108662306a36Sopenharmony_ci		free(chain_str_alloc);
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_cinext:
108962306a36Sopenharmony_ci		if (is_output_full(browser, row))
109062306a36Sopenharmony_ci			break;
109162306a36Sopenharmony_ci		node = next;
109262306a36Sopenharmony_ci	}
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci	return row - first_row;
109562306a36Sopenharmony_ci}
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_cistatic int hist_browser__show_callchain_graph(struct hist_browser *browser,
109862306a36Sopenharmony_ci					struct rb_root *root, int level,
109962306a36Sopenharmony_ci					unsigned short row, u64 total,
110062306a36Sopenharmony_ci					u64 parent_total,
110162306a36Sopenharmony_ci					print_callchain_entry_fn print,
110262306a36Sopenharmony_ci					struct callchain_print_arg *arg,
110362306a36Sopenharmony_ci					check_output_full_fn is_output_full)
110462306a36Sopenharmony_ci{
110562306a36Sopenharmony_ci	struct rb_node *node;
110662306a36Sopenharmony_ci	int first_row = row, offset = level * LEVEL_OFFSET_STEP;
110762306a36Sopenharmony_ci	bool need_percent;
110862306a36Sopenharmony_ci	u64 percent_total = total;
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci	if (callchain_param.mode == CHAIN_GRAPH_REL)
111162306a36Sopenharmony_ci		percent_total = parent_total;
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci	node = rb_first(root);
111462306a36Sopenharmony_ci	need_percent = check_percent_display(node, parent_total);
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	while (node) {
111762306a36Sopenharmony_ci		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
111862306a36Sopenharmony_ci		struct rb_node *next = rb_next(node);
111962306a36Sopenharmony_ci		struct callchain_list *chain;
112062306a36Sopenharmony_ci		char folded_sign = ' ';
112162306a36Sopenharmony_ci		int first = true;
112262306a36Sopenharmony_ci		int extra_offset = 0;
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci		list_for_each_entry(chain, &child->val, list) {
112562306a36Sopenharmony_ci			bool was_first = first;
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci			if (first)
112862306a36Sopenharmony_ci				first = false;
112962306a36Sopenharmony_ci			else if (need_percent)
113062306a36Sopenharmony_ci				extra_offset = LEVEL_OFFSET_STEP;
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci			folded_sign = callchain_list__folded(chain);
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ci			row += hist_browser__show_callchain_list(browser, child,
113562306a36Sopenharmony_ci							chain, row, percent_total,
113662306a36Sopenharmony_ci							was_first && need_percent,
113762306a36Sopenharmony_ci							offset + extra_offset,
113862306a36Sopenharmony_ci							print, arg);
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_ci			if (is_output_full(browser, row))
114162306a36Sopenharmony_ci				goto out;
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci			if (folded_sign == '+')
114462306a36Sopenharmony_ci				break;
114562306a36Sopenharmony_ci		}
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci		if (folded_sign == '-') {
114862306a36Sopenharmony_ci			const int new_level = level + (extra_offset ? 2 : 1);
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci			row += hist_browser__show_callchain_graph(browser, &child->rb_root,
115162306a36Sopenharmony_ci							    new_level, row, total,
115262306a36Sopenharmony_ci							    child->children_hit,
115362306a36Sopenharmony_ci							    print, arg, is_output_full);
115462306a36Sopenharmony_ci		}
115562306a36Sopenharmony_ci		if (is_output_full(browser, row))
115662306a36Sopenharmony_ci			break;
115762306a36Sopenharmony_ci		node = next;
115862306a36Sopenharmony_ci	}
115962306a36Sopenharmony_ciout:
116062306a36Sopenharmony_ci	return row - first_row;
116162306a36Sopenharmony_ci}
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_cistatic int hist_browser__show_callchain(struct hist_browser *browser,
116462306a36Sopenharmony_ci					struct hist_entry *entry, int level,
116562306a36Sopenharmony_ci					unsigned short row,
116662306a36Sopenharmony_ci					print_callchain_entry_fn print,
116762306a36Sopenharmony_ci					struct callchain_print_arg *arg,
116862306a36Sopenharmony_ci					check_output_full_fn is_output_full)
116962306a36Sopenharmony_ci{
117062306a36Sopenharmony_ci	u64 total = hists__total_period(entry->hists);
117162306a36Sopenharmony_ci	u64 parent_total;
117262306a36Sopenharmony_ci	int printed;
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci	if (symbol_conf.cumulate_callchain)
117562306a36Sopenharmony_ci		parent_total = entry->stat_acc->period;
117662306a36Sopenharmony_ci	else
117762306a36Sopenharmony_ci		parent_total = entry->stat.period;
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci	if (callchain_param.mode == CHAIN_FLAT) {
118062306a36Sopenharmony_ci		printed = hist_browser__show_callchain_flat(browser,
118162306a36Sopenharmony_ci						&entry->sorted_chain, row,
118262306a36Sopenharmony_ci						total, parent_total, print, arg,
118362306a36Sopenharmony_ci						is_output_full);
118462306a36Sopenharmony_ci	} else if (callchain_param.mode == CHAIN_FOLDED) {
118562306a36Sopenharmony_ci		printed = hist_browser__show_callchain_folded(browser,
118662306a36Sopenharmony_ci						&entry->sorted_chain, row,
118762306a36Sopenharmony_ci						total, parent_total, print, arg,
118862306a36Sopenharmony_ci						is_output_full);
118962306a36Sopenharmony_ci	} else {
119062306a36Sopenharmony_ci		printed = hist_browser__show_callchain_graph(browser,
119162306a36Sopenharmony_ci						&entry->sorted_chain, level, row,
119262306a36Sopenharmony_ci						total, parent_total, print, arg,
119362306a36Sopenharmony_ci						is_output_full);
119462306a36Sopenharmony_ci	}
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci	if (arg->is_current_entry)
119762306a36Sopenharmony_ci		browser->he_selection = entry;
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_ci	return printed;
120062306a36Sopenharmony_ci}
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_cistruct hpp_arg {
120362306a36Sopenharmony_ci	struct ui_browser *b;
120462306a36Sopenharmony_ci	char folded_sign;
120562306a36Sopenharmony_ci	bool current_entry;
120662306a36Sopenharmony_ci};
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ciint __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
120962306a36Sopenharmony_ci{
121062306a36Sopenharmony_ci	struct hpp_arg *arg = hpp->ptr;
121162306a36Sopenharmony_ci	int ret, len;
121262306a36Sopenharmony_ci	va_list args;
121362306a36Sopenharmony_ci	double percent;
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci	va_start(args, fmt);
121662306a36Sopenharmony_ci	len = va_arg(args, int);
121762306a36Sopenharmony_ci	percent = va_arg(args, double);
121862306a36Sopenharmony_ci	va_end(args);
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci	ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci	ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
122362306a36Sopenharmony_ci	ui_browser__printf(arg->b, "%s", hpp->buf);
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_ci	return ret;
122662306a36Sopenharmony_ci}
122762306a36Sopenharmony_ci
122862306a36Sopenharmony_ci#define __HPP_COLOR_PERCENT_FN(_type, _field)				\
122962306a36Sopenharmony_cistatic u64 __hpp_get_##_field(struct hist_entry *he)			\
123062306a36Sopenharmony_ci{									\
123162306a36Sopenharmony_ci	return he->stat._field;						\
123262306a36Sopenharmony_ci}									\
123362306a36Sopenharmony_ci									\
123462306a36Sopenharmony_cistatic int								\
123562306a36Sopenharmony_cihist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,		\
123662306a36Sopenharmony_ci				struct perf_hpp *hpp,			\
123762306a36Sopenharmony_ci				struct hist_entry *he)			\
123862306a36Sopenharmony_ci{									\
123962306a36Sopenharmony_ci	return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%",	\
124062306a36Sopenharmony_ci			__hpp__slsmg_color_printf, true);		\
124162306a36Sopenharmony_ci}
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci#define __HPP_COLOR_ACC_PERCENT_FN(_type, _field)			\
124462306a36Sopenharmony_cistatic u64 __hpp_get_acc_##_field(struct hist_entry *he)		\
124562306a36Sopenharmony_ci{									\
124662306a36Sopenharmony_ci	return he->stat_acc->_field;					\
124762306a36Sopenharmony_ci}									\
124862306a36Sopenharmony_ci									\
124962306a36Sopenharmony_cistatic int								\
125062306a36Sopenharmony_cihist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,		\
125162306a36Sopenharmony_ci				struct perf_hpp *hpp,			\
125262306a36Sopenharmony_ci				struct hist_entry *he)			\
125362306a36Sopenharmony_ci{									\
125462306a36Sopenharmony_ci	if (!symbol_conf.cumulate_callchain) {				\
125562306a36Sopenharmony_ci		struct hpp_arg *arg = hpp->ptr;				\
125662306a36Sopenharmony_ci		int len = fmt->user_len ?: fmt->len;			\
125762306a36Sopenharmony_ci		int ret = scnprintf(hpp->buf, hpp->size,		\
125862306a36Sopenharmony_ci				    "%*s", len, "N/A");			\
125962306a36Sopenharmony_ci		ui_browser__printf(arg->b, "%s", hpp->buf);		\
126062306a36Sopenharmony_ci									\
126162306a36Sopenharmony_ci		return ret;						\
126262306a36Sopenharmony_ci	}								\
126362306a36Sopenharmony_ci	return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field,		\
126462306a36Sopenharmony_ci			" %*.2f%%", __hpp__slsmg_color_printf, true);	\
126562306a36Sopenharmony_ci}
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ci__HPP_COLOR_PERCENT_FN(overhead, period)
126862306a36Sopenharmony_ci__HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
126962306a36Sopenharmony_ci__HPP_COLOR_PERCENT_FN(overhead_us, period_us)
127062306a36Sopenharmony_ci__HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
127162306a36Sopenharmony_ci__HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
127262306a36Sopenharmony_ci__HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci#undef __HPP_COLOR_PERCENT_FN
127562306a36Sopenharmony_ci#undef __HPP_COLOR_ACC_PERCENT_FN
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_civoid hist_browser__init_hpp(void)
127862306a36Sopenharmony_ci{
127962306a36Sopenharmony_ci	perf_hpp__format[PERF_HPP__OVERHEAD].color =
128062306a36Sopenharmony_ci				hist_browser__hpp_color_overhead;
128162306a36Sopenharmony_ci	perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
128262306a36Sopenharmony_ci				hist_browser__hpp_color_overhead_sys;
128362306a36Sopenharmony_ci	perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
128462306a36Sopenharmony_ci				hist_browser__hpp_color_overhead_us;
128562306a36Sopenharmony_ci	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
128662306a36Sopenharmony_ci				hist_browser__hpp_color_overhead_guest_sys;
128762306a36Sopenharmony_ci	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
128862306a36Sopenharmony_ci				hist_browser__hpp_color_overhead_guest_us;
128962306a36Sopenharmony_ci	perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
129062306a36Sopenharmony_ci				hist_browser__hpp_color_overhead_acc;
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ci	res_sample_init();
129362306a36Sopenharmony_ci}
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_cistatic int hist_browser__show_entry(struct hist_browser *browser,
129662306a36Sopenharmony_ci				    struct hist_entry *entry,
129762306a36Sopenharmony_ci				    unsigned short row)
129862306a36Sopenharmony_ci{
129962306a36Sopenharmony_ci	int printed = 0;
130062306a36Sopenharmony_ci	int width = browser->b.width;
130162306a36Sopenharmony_ci	char folded_sign = ' ';
130262306a36Sopenharmony_ci	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
130362306a36Sopenharmony_ci	bool use_callchain = hist_entry__has_callchains(entry) && symbol_conf.use_callchain;
130462306a36Sopenharmony_ci	off_t row_offset = entry->row_offset;
130562306a36Sopenharmony_ci	bool first = true;
130662306a36Sopenharmony_ci	struct perf_hpp_fmt *fmt;
130762306a36Sopenharmony_ci
130862306a36Sopenharmony_ci	if (current_entry) {
130962306a36Sopenharmony_ci		browser->he_selection = entry;
131062306a36Sopenharmony_ci		browser->selection = &entry->ms;
131162306a36Sopenharmony_ci	}
131262306a36Sopenharmony_ci
131362306a36Sopenharmony_ci	if (use_callchain) {
131462306a36Sopenharmony_ci		hist_entry__init_have_children(entry);
131562306a36Sopenharmony_ci		folded_sign = hist_entry__folded(entry);
131662306a36Sopenharmony_ci	}
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ci	if (row_offset == 0) {
131962306a36Sopenharmony_ci		struct hpp_arg arg = {
132062306a36Sopenharmony_ci			.b		= &browser->b,
132162306a36Sopenharmony_ci			.folded_sign	= folded_sign,
132262306a36Sopenharmony_ci			.current_entry	= current_entry,
132362306a36Sopenharmony_ci		};
132462306a36Sopenharmony_ci		int column = 0;
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci		ui_browser__gotorc(&browser->b, row, 0);
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ci		hists__for_each_format(browser->hists, fmt) {
132962306a36Sopenharmony_ci			char s[2048];
133062306a36Sopenharmony_ci			struct perf_hpp hpp = {
133162306a36Sopenharmony_ci				.buf	= s,
133262306a36Sopenharmony_ci				.size	= sizeof(s),
133362306a36Sopenharmony_ci				.ptr	= &arg,
133462306a36Sopenharmony_ci			};
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci			if (perf_hpp__should_skip(fmt, entry->hists) ||
133762306a36Sopenharmony_ci			    column++ < browser->b.horiz_scroll)
133862306a36Sopenharmony_ci				continue;
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_ci			if (current_entry && browser->b.navkeypressed) {
134162306a36Sopenharmony_ci				ui_browser__set_color(&browser->b,
134262306a36Sopenharmony_ci						      HE_COLORSET_SELECTED);
134362306a36Sopenharmony_ci			} else {
134462306a36Sopenharmony_ci				ui_browser__set_color(&browser->b,
134562306a36Sopenharmony_ci						      HE_COLORSET_NORMAL);
134662306a36Sopenharmony_ci			}
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_ci			if (first) {
134962306a36Sopenharmony_ci				if (use_callchain) {
135062306a36Sopenharmony_ci					ui_browser__printf(&browser->b, "%c ", folded_sign);
135162306a36Sopenharmony_ci					width -= 2;
135262306a36Sopenharmony_ci				}
135362306a36Sopenharmony_ci				first = false;
135462306a36Sopenharmony_ci			} else {
135562306a36Sopenharmony_ci				ui_browser__printf(&browser->b, "  ");
135662306a36Sopenharmony_ci				width -= 2;
135762306a36Sopenharmony_ci			}
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_ci			if (fmt->color) {
136062306a36Sopenharmony_ci				int ret = fmt->color(fmt, &hpp, entry);
136162306a36Sopenharmony_ci				hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
136262306a36Sopenharmony_ci				/*
136362306a36Sopenharmony_ci				 * fmt->color() already used ui_browser to
136462306a36Sopenharmony_ci				 * print the non alignment bits, skip it (+ret):
136562306a36Sopenharmony_ci				 */
136662306a36Sopenharmony_ci				ui_browser__printf(&browser->b, "%s", s + ret);
136762306a36Sopenharmony_ci			} else {
136862306a36Sopenharmony_ci				hist_entry__snprintf_alignment(entry, &hpp, fmt, fmt->entry(fmt, &hpp, entry));
136962306a36Sopenharmony_ci				ui_browser__printf(&browser->b, "%s", s);
137062306a36Sopenharmony_ci			}
137162306a36Sopenharmony_ci			width -= hpp.buf - s;
137262306a36Sopenharmony_ci		}
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci		/* The scroll bar isn't being used */
137562306a36Sopenharmony_ci		if (!browser->b.navkeypressed)
137662306a36Sopenharmony_ci			width += 1;
137762306a36Sopenharmony_ci
137862306a36Sopenharmony_ci		ui_browser__write_nstring(&browser->b, "", width);
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_ci		++row;
138162306a36Sopenharmony_ci		++printed;
138262306a36Sopenharmony_ci	} else
138362306a36Sopenharmony_ci		--row_offset;
138462306a36Sopenharmony_ci
138562306a36Sopenharmony_ci	if (folded_sign == '-' && row != browser->b.rows) {
138662306a36Sopenharmony_ci		struct callchain_print_arg arg = {
138762306a36Sopenharmony_ci			.row_offset = row_offset,
138862306a36Sopenharmony_ci			.is_current_entry = current_entry,
138962306a36Sopenharmony_ci		};
139062306a36Sopenharmony_ci
139162306a36Sopenharmony_ci		printed += hist_browser__show_callchain(browser,
139262306a36Sopenharmony_ci				entry, 1, row,
139362306a36Sopenharmony_ci				hist_browser__show_callchain_entry,
139462306a36Sopenharmony_ci				&arg,
139562306a36Sopenharmony_ci				hist_browser__check_output_full);
139662306a36Sopenharmony_ci	}
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_ci	return printed;
139962306a36Sopenharmony_ci}
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_cistatic int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
140262306a36Sopenharmony_ci					      struct hist_entry *entry,
140362306a36Sopenharmony_ci					      unsigned short row,
140462306a36Sopenharmony_ci					      int level)
140562306a36Sopenharmony_ci{
140662306a36Sopenharmony_ci	int printed = 0;
140762306a36Sopenharmony_ci	int width = browser->b.width;
140862306a36Sopenharmony_ci	char folded_sign = ' ';
140962306a36Sopenharmony_ci	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
141062306a36Sopenharmony_ci	off_t row_offset = entry->row_offset;
141162306a36Sopenharmony_ci	bool first = true;
141262306a36Sopenharmony_ci	struct perf_hpp_fmt *fmt;
141362306a36Sopenharmony_ci	struct perf_hpp_list_node *fmt_node;
141462306a36Sopenharmony_ci	struct hpp_arg arg = {
141562306a36Sopenharmony_ci		.b		= &browser->b,
141662306a36Sopenharmony_ci		.current_entry	= current_entry,
141762306a36Sopenharmony_ci	};
141862306a36Sopenharmony_ci	int column = 0;
141962306a36Sopenharmony_ci	int hierarchy_indent = (entry->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
142062306a36Sopenharmony_ci
142162306a36Sopenharmony_ci	if (current_entry) {
142262306a36Sopenharmony_ci		browser->he_selection = entry;
142362306a36Sopenharmony_ci		browser->selection = &entry->ms;
142462306a36Sopenharmony_ci	}
142562306a36Sopenharmony_ci
142662306a36Sopenharmony_ci	hist_entry__init_have_children(entry);
142762306a36Sopenharmony_ci	folded_sign = hist_entry__folded(entry);
142862306a36Sopenharmony_ci	arg.folded_sign = folded_sign;
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_ci	if (entry->leaf && row_offset) {
143162306a36Sopenharmony_ci		row_offset--;
143262306a36Sopenharmony_ci		goto show_callchain;
143362306a36Sopenharmony_ci	}
143462306a36Sopenharmony_ci
143562306a36Sopenharmony_ci	ui_browser__gotorc(&browser->b, row, 0);
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_ci	if (current_entry && browser->b.navkeypressed)
143862306a36Sopenharmony_ci		ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
143962306a36Sopenharmony_ci	else
144062306a36Sopenharmony_ci		ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_ci	ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
144362306a36Sopenharmony_ci	width -= level * HIERARCHY_INDENT;
144462306a36Sopenharmony_ci
144562306a36Sopenharmony_ci	/* the first hpp_list_node is for overhead columns */
144662306a36Sopenharmony_ci	fmt_node = list_first_entry(&entry->hists->hpp_formats,
144762306a36Sopenharmony_ci				    struct perf_hpp_list_node, list);
144862306a36Sopenharmony_ci	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
144962306a36Sopenharmony_ci		char s[2048];
145062306a36Sopenharmony_ci		struct perf_hpp hpp = {
145162306a36Sopenharmony_ci			.buf		= s,
145262306a36Sopenharmony_ci			.size		= sizeof(s),
145362306a36Sopenharmony_ci			.ptr		= &arg,
145462306a36Sopenharmony_ci		};
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_ci		if (perf_hpp__should_skip(fmt, entry->hists) ||
145762306a36Sopenharmony_ci		    column++ < browser->b.horiz_scroll)
145862306a36Sopenharmony_ci			continue;
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci		if (current_entry && browser->b.navkeypressed) {
146162306a36Sopenharmony_ci			ui_browser__set_color(&browser->b,
146262306a36Sopenharmony_ci					      HE_COLORSET_SELECTED);
146362306a36Sopenharmony_ci		} else {
146462306a36Sopenharmony_ci			ui_browser__set_color(&browser->b,
146562306a36Sopenharmony_ci					      HE_COLORSET_NORMAL);
146662306a36Sopenharmony_ci		}
146762306a36Sopenharmony_ci
146862306a36Sopenharmony_ci		if (first) {
146962306a36Sopenharmony_ci			ui_browser__printf(&browser->b, "%c ", folded_sign);
147062306a36Sopenharmony_ci			width -= 2;
147162306a36Sopenharmony_ci			first = false;
147262306a36Sopenharmony_ci		} else {
147362306a36Sopenharmony_ci			ui_browser__printf(&browser->b, "  ");
147462306a36Sopenharmony_ci			width -= 2;
147562306a36Sopenharmony_ci		}
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_ci		if (fmt->color) {
147862306a36Sopenharmony_ci			int ret = fmt->color(fmt, &hpp, entry);
147962306a36Sopenharmony_ci			hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
148062306a36Sopenharmony_ci			/*
148162306a36Sopenharmony_ci			 * fmt->color() already used ui_browser to
148262306a36Sopenharmony_ci			 * print the non alignment bits, skip it (+ret):
148362306a36Sopenharmony_ci			 */
148462306a36Sopenharmony_ci			ui_browser__printf(&browser->b, "%s", s + ret);
148562306a36Sopenharmony_ci		} else {
148662306a36Sopenharmony_ci			int ret = fmt->entry(fmt, &hpp, entry);
148762306a36Sopenharmony_ci			hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
148862306a36Sopenharmony_ci			ui_browser__printf(&browser->b, "%s", s);
148962306a36Sopenharmony_ci		}
149062306a36Sopenharmony_ci		width -= hpp.buf - s;
149162306a36Sopenharmony_ci	}
149262306a36Sopenharmony_ci
149362306a36Sopenharmony_ci	if (!first) {
149462306a36Sopenharmony_ci		ui_browser__write_nstring(&browser->b, "", hierarchy_indent);
149562306a36Sopenharmony_ci		width -= hierarchy_indent;
149662306a36Sopenharmony_ci	}
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_ci	if (column >= browser->b.horiz_scroll) {
149962306a36Sopenharmony_ci		char s[2048];
150062306a36Sopenharmony_ci		struct perf_hpp hpp = {
150162306a36Sopenharmony_ci			.buf		= s,
150262306a36Sopenharmony_ci			.size		= sizeof(s),
150362306a36Sopenharmony_ci			.ptr		= &arg,
150462306a36Sopenharmony_ci		};
150562306a36Sopenharmony_ci
150662306a36Sopenharmony_ci		if (current_entry && browser->b.navkeypressed) {
150762306a36Sopenharmony_ci			ui_browser__set_color(&browser->b,
150862306a36Sopenharmony_ci					      HE_COLORSET_SELECTED);
150962306a36Sopenharmony_ci		} else {
151062306a36Sopenharmony_ci			ui_browser__set_color(&browser->b,
151162306a36Sopenharmony_ci					      HE_COLORSET_NORMAL);
151262306a36Sopenharmony_ci		}
151362306a36Sopenharmony_ci
151462306a36Sopenharmony_ci		perf_hpp_list__for_each_format(entry->hpp_list, fmt) {
151562306a36Sopenharmony_ci			if (first) {
151662306a36Sopenharmony_ci				ui_browser__printf(&browser->b, "%c ", folded_sign);
151762306a36Sopenharmony_ci				first = false;
151862306a36Sopenharmony_ci			} else {
151962306a36Sopenharmony_ci				ui_browser__write_nstring(&browser->b, "", 2);
152062306a36Sopenharmony_ci			}
152162306a36Sopenharmony_ci
152262306a36Sopenharmony_ci			width -= 2;
152362306a36Sopenharmony_ci
152462306a36Sopenharmony_ci			/*
152562306a36Sopenharmony_ci			 * No need to call hist_entry__snprintf_alignment()
152662306a36Sopenharmony_ci			 * since this fmt is always the last column in the
152762306a36Sopenharmony_ci			 * hierarchy mode.
152862306a36Sopenharmony_ci			 */
152962306a36Sopenharmony_ci			if (fmt->color) {
153062306a36Sopenharmony_ci				width -= fmt->color(fmt, &hpp, entry);
153162306a36Sopenharmony_ci			} else {
153262306a36Sopenharmony_ci				int i = 0;
153362306a36Sopenharmony_ci
153462306a36Sopenharmony_ci				width -= fmt->entry(fmt, &hpp, entry);
153562306a36Sopenharmony_ci				ui_browser__printf(&browser->b, "%s", skip_spaces(s));
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_ci				while (isspace(s[i++]))
153862306a36Sopenharmony_ci					width++;
153962306a36Sopenharmony_ci			}
154062306a36Sopenharmony_ci		}
154162306a36Sopenharmony_ci	}
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_ci	/* The scroll bar isn't being used */
154462306a36Sopenharmony_ci	if (!browser->b.navkeypressed)
154562306a36Sopenharmony_ci		width += 1;
154662306a36Sopenharmony_ci
154762306a36Sopenharmony_ci	ui_browser__write_nstring(&browser->b, "", width);
154862306a36Sopenharmony_ci
154962306a36Sopenharmony_ci	++row;
155062306a36Sopenharmony_ci	++printed;
155162306a36Sopenharmony_ci
155262306a36Sopenharmony_cishow_callchain:
155362306a36Sopenharmony_ci	if (entry->leaf && folded_sign == '-' && row != browser->b.rows) {
155462306a36Sopenharmony_ci		struct callchain_print_arg carg = {
155562306a36Sopenharmony_ci			.row_offset = row_offset,
155662306a36Sopenharmony_ci		};
155762306a36Sopenharmony_ci
155862306a36Sopenharmony_ci		printed += hist_browser__show_callchain(browser, entry,
155962306a36Sopenharmony_ci					level + 1, row,
156062306a36Sopenharmony_ci					hist_browser__show_callchain_entry, &carg,
156162306a36Sopenharmony_ci					hist_browser__check_output_full);
156262306a36Sopenharmony_ci	}
156362306a36Sopenharmony_ci
156462306a36Sopenharmony_ci	return printed;
156562306a36Sopenharmony_ci}
156662306a36Sopenharmony_ci
156762306a36Sopenharmony_cistatic int hist_browser__show_no_entry(struct hist_browser *browser,
156862306a36Sopenharmony_ci				       unsigned short row, int level)
156962306a36Sopenharmony_ci{
157062306a36Sopenharmony_ci	int width = browser->b.width;
157162306a36Sopenharmony_ci	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
157262306a36Sopenharmony_ci	bool first = true;
157362306a36Sopenharmony_ci	int column = 0;
157462306a36Sopenharmony_ci	int ret;
157562306a36Sopenharmony_ci	struct perf_hpp_fmt *fmt;
157662306a36Sopenharmony_ci	struct perf_hpp_list_node *fmt_node;
157762306a36Sopenharmony_ci	int indent = browser->hists->nr_hpp_node - 2;
157862306a36Sopenharmony_ci
157962306a36Sopenharmony_ci	if (current_entry) {
158062306a36Sopenharmony_ci		browser->he_selection = NULL;
158162306a36Sopenharmony_ci		browser->selection = NULL;
158262306a36Sopenharmony_ci	}
158362306a36Sopenharmony_ci
158462306a36Sopenharmony_ci	ui_browser__gotorc(&browser->b, row, 0);
158562306a36Sopenharmony_ci
158662306a36Sopenharmony_ci	if (current_entry && browser->b.navkeypressed)
158762306a36Sopenharmony_ci		ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
158862306a36Sopenharmony_ci	else
158962306a36Sopenharmony_ci		ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
159062306a36Sopenharmony_ci
159162306a36Sopenharmony_ci	ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
159262306a36Sopenharmony_ci	width -= level * HIERARCHY_INDENT;
159362306a36Sopenharmony_ci
159462306a36Sopenharmony_ci	/* the first hpp_list_node is for overhead columns */
159562306a36Sopenharmony_ci	fmt_node = list_first_entry(&browser->hists->hpp_formats,
159662306a36Sopenharmony_ci				    struct perf_hpp_list_node, list);
159762306a36Sopenharmony_ci	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
159862306a36Sopenharmony_ci		if (perf_hpp__should_skip(fmt, browser->hists) ||
159962306a36Sopenharmony_ci		    column++ < browser->b.horiz_scroll)
160062306a36Sopenharmony_ci			continue;
160162306a36Sopenharmony_ci
160262306a36Sopenharmony_ci		ret = fmt->width(fmt, NULL, browser->hists);
160362306a36Sopenharmony_ci
160462306a36Sopenharmony_ci		if (first) {
160562306a36Sopenharmony_ci			/* for folded sign */
160662306a36Sopenharmony_ci			first = false;
160762306a36Sopenharmony_ci			ret++;
160862306a36Sopenharmony_ci		} else {
160962306a36Sopenharmony_ci			/* space between columns */
161062306a36Sopenharmony_ci			ret += 2;
161162306a36Sopenharmony_ci		}
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_ci		ui_browser__write_nstring(&browser->b, "", ret);
161462306a36Sopenharmony_ci		width -= ret;
161562306a36Sopenharmony_ci	}
161662306a36Sopenharmony_ci
161762306a36Sopenharmony_ci	ui_browser__write_nstring(&browser->b, "", indent * HIERARCHY_INDENT);
161862306a36Sopenharmony_ci	width -= indent * HIERARCHY_INDENT;
161962306a36Sopenharmony_ci
162062306a36Sopenharmony_ci	if (column >= browser->b.horiz_scroll) {
162162306a36Sopenharmony_ci		char buf[32];
162262306a36Sopenharmony_ci
162362306a36Sopenharmony_ci		ret = snprintf(buf, sizeof(buf), "no entry >= %.2f%%", browser->min_pcnt);
162462306a36Sopenharmony_ci		ui_browser__printf(&browser->b, "  %s", buf);
162562306a36Sopenharmony_ci		width -= ret + 2;
162662306a36Sopenharmony_ci	}
162762306a36Sopenharmony_ci
162862306a36Sopenharmony_ci	/* The scroll bar isn't being used */
162962306a36Sopenharmony_ci	if (!browser->b.navkeypressed)
163062306a36Sopenharmony_ci		width += 1;
163162306a36Sopenharmony_ci
163262306a36Sopenharmony_ci	ui_browser__write_nstring(&browser->b, "", width);
163362306a36Sopenharmony_ci	return 1;
163462306a36Sopenharmony_ci}
163562306a36Sopenharmony_ci
163662306a36Sopenharmony_cistatic int advance_hpp_check(struct perf_hpp *hpp, int inc)
163762306a36Sopenharmony_ci{
163862306a36Sopenharmony_ci	advance_hpp(hpp, inc);
163962306a36Sopenharmony_ci	return hpp->size <= 0;
164062306a36Sopenharmony_ci}
164162306a36Sopenharmony_ci
164262306a36Sopenharmony_cistatic int
164362306a36Sopenharmony_cihists_browser__scnprintf_headers(struct hist_browser *browser, char *buf,
164462306a36Sopenharmony_ci				 size_t size, int line)
164562306a36Sopenharmony_ci{
164662306a36Sopenharmony_ci	struct hists *hists = browser->hists;
164762306a36Sopenharmony_ci	struct perf_hpp dummy_hpp = {
164862306a36Sopenharmony_ci		.buf    = buf,
164962306a36Sopenharmony_ci		.size   = size,
165062306a36Sopenharmony_ci	};
165162306a36Sopenharmony_ci	struct perf_hpp_fmt *fmt;
165262306a36Sopenharmony_ci	size_t ret = 0;
165362306a36Sopenharmony_ci	int column = 0;
165462306a36Sopenharmony_ci	int span = 0;
165562306a36Sopenharmony_ci
165662306a36Sopenharmony_ci	if (hists__has_callchains(hists) && symbol_conf.use_callchain) {
165762306a36Sopenharmony_ci		ret = scnprintf(buf, size, "  ");
165862306a36Sopenharmony_ci		if (advance_hpp_check(&dummy_hpp, ret))
165962306a36Sopenharmony_ci			return ret;
166062306a36Sopenharmony_ci	}
166162306a36Sopenharmony_ci
166262306a36Sopenharmony_ci	hists__for_each_format(browser->hists, fmt) {
166362306a36Sopenharmony_ci		if (perf_hpp__should_skip(fmt, hists)  || column++ < browser->b.horiz_scroll)
166462306a36Sopenharmony_ci			continue;
166562306a36Sopenharmony_ci
166662306a36Sopenharmony_ci		ret = fmt->header(fmt, &dummy_hpp, hists, line, &span);
166762306a36Sopenharmony_ci		if (advance_hpp_check(&dummy_hpp, ret))
166862306a36Sopenharmony_ci			break;
166962306a36Sopenharmony_ci
167062306a36Sopenharmony_ci		if (span)
167162306a36Sopenharmony_ci			continue;
167262306a36Sopenharmony_ci
167362306a36Sopenharmony_ci		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
167462306a36Sopenharmony_ci		if (advance_hpp_check(&dummy_hpp, ret))
167562306a36Sopenharmony_ci			break;
167662306a36Sopenharmony_ci	}
167762306a36Sopenharmony_ci
167862306a36Sopenharmony_ci	return ret;
167962306a36Sopenharmony_ci}
168062306a36Sopenharmony_ci
168162306a36Sopenharmony_cistatic int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size)
168262306a36Sopenharmony_ci{
168362306a36Sopenharmony_ci	struct hists *hists = browser->hists;
168462306a36Sopenharmony_ci	struct perf_hpp dummy_hpp = {
168562306a36Sopenharmony_ci		.buf    = buf,
168662306a36Sopenharmony_ci		.size   = size,
168762306a36Sopenharmony_ci	};
168862306a36Sopenharmony_ci	struct perf_hpp_fmt *fmt;
168962306a36Sopenharmony_ci	struct perf_hpp_list_node *fmt_node;
169062306a36Sopenharmony_ci	size_t ret = 0;
169162306a36Sopenharmony_ci	int column = 0;
169262306a36Sopenharmony_ci	int indent = hists->nr_hpp_node - 2;
169362306a36Sopenharmony_ci	bool first_node, first_col;
169462306a36Sopenharmony_ci
169562306a36Sopenharmony_ci	ret = scnprintf(buf, size, "  ");
169662306a36Sopenharmony_ci	if (advance_hpp_check(&dummy_hpp, ret))
169762306a36Sopenharmony_ci		return ret;
169862306a36Sopenharmony_ci
169962306a36Sopenharmony_ci	first_node = true;
170062306a36Sopenharmony_ci	/* the first hpp_list_node is for overhead columns */
170162306a36Sopenharmony_ci	fmt_node = list_first_entry(&hists->hpp_formats,
170262306a36Sopenharmony_ci				    struct perf_hpp_list_node, list);
170362306a36Sopenharmony_ci	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
170462306a36Sopenharmony_ci		if (column++ < browser->b.horiz_scroll)
170562306a36Sopenharmony_ci			continue;
170662306a36Sopenharmony_ci
170762306a36Sopenharmony_ci		ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
170862306a36Sopenharmony_ci		if (advance_hpp_check(&dummy_hpp, ret))
170962306a36Sopenharmony_ci			break;
171062306a36Sopenharmony_ci
171162306a36Sopenharmony_ci		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
171262306a36Sopenharmony_ci		if (advance_hpp_check(&dummy_hpp, ret))
171362306a36Sopenharmony_ci			break;
171462306a36Sopenharmony_ci
171562306a36Sopenharmony_ci		first_node = false;
171662306a36Sopenharmony_ci	}
171762306a36Sopenharmony_ci
171862306a36Sopenharmony_ci	if (!first_node) {
171962306a36Sopenharmony_ci		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s",
172062306a36Sopenharmony_ci				indent * HIERARCHY_INDENT, "");
172162306a36Sopenharmony_ci		if (advance_hpp_check(&dummy_hpp, ret))
172262306a36Sopenharmony_ci			return ret;
172362306a36Sopenharmony_ci	}
172462306a36Sopenharmony_ci
172562306a36Sopenharmony_ci	first_node = true;
172662306a36Sopenharmony_ci	list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
172762306a36Sopenharmony_ci		if (!first_node) {
172862306a36Sopenharmony_ci			ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " / ");
172962306a36Sopenharmony_ci			if (advance_hpp_check(&dummy_hpp, ret))
173062306a36Sopenharmony_ci				break;
173162306a36Sopenharmony_ci		}
173262306a36Sopenharmony_ci		first_node = false;
173362306a36Sopenharmony_ci
173462306a36Sopenharmony_ci		first_col = true;
173562306a36Sopenharmony_ci		perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
173662306a36Sopenharmony_ci			char *start;
173762306a36Sopenharmony_ci
173862306a36Sopenharmony_ci			if (perf_hpp__should_skip(fmt, hists))
173962306a36Sopenharmony_ci				continue;
174062306a36Sopenharmony_ci
174162306a36Sopenharmony_ci			if (!first_col) {
174262306a36Sopenharmony_ci				ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "+");
174362306a36Sopenharmony_ci				if (advance_hpp_check(&dummy_hpp, ret))
174462306a36Sopenharmony_ci					break;
174562306a36Sopenharmony_ci			}
174662306a36Sopenharmony_ci			first_col = false;
174762306a36Sopenharmony_ci
174862306a36Sopenharmony_ci			ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
174962306a36Sopenharmony_ci			dummy_hpp.buf[ret] = '\0';
175062306a36Sopenharmony_ci
175162306a36Sopenharmony_ci			start = strim(dummy_hpp.buf);
175262306a36Sopenharmony_ci			ret = strlen(start);
175362306a36Sopenharmony_ci
175462306a36Sopenharmony_ci			if (start != dummy_hpp.buf)
175562306a36Sopenharmony_ci				memmove(dummy_hpp.buf, start, ret + 1);
175662306a36Sopenharmony_ci
175762306a36Sopenharmony_ci			if (advance_hpp_check(&dummy_hpp, ret))
175862306a36Sopenharmony_ci				break;
175962306a36Sopenharmony_ci		}
176062306a36Sopenharmony_ci	}
176162306a36Sopenharmony_ci
176262306a36Sopenharmony_ci	return ret;
176362306a36Sopenharmony_ci}
176462306a36Sopenharmony_ci
176562306a36Sopenharmony_cistatic void hists_browser__hierarchy_headers(struct hist_browser *browser)
176662306a36Sopenharmony_ci{
176762306a36Sopenharmony_ci	char headers[1024];
176862306a36Sopenharmony_ci
176962306a36Sopenharmony_ci	hists_browser__scnprintf_hierarchy_headers(browser, headers,
177062306a36Sopenharmony_ci						   sizeof(headers));
177162306a36Sopenharmony_ci
177262306a36Sopenharmony_ci	ui_browser__gotorc_title(&browser->b, 0, 0);
177362306a36Sopenharmony_ci	ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
177462306a36Sopenharmony_ci	ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
177562306a36Sopenharmony_ci}
177662306a36Sopenharmony_ci
177762306a36Sopenharmony_cistatic void hists_browser__headers(struct hist_browser *browser)
177862306a36Sopenharmony_ci{
177962306a36Sopenharmony_ci	struct hists *hists = browser->hists;
178062306a36Sopenharmony_ci	struct perf_hpp_list *hpp_list = hists->hpp_list;
178162306a36Sopenharmony_ci
178262306a36Sopenharmony_ci	int line;
178362306a36Sopenharmony_ci
178462306a36Sopenharmony_ci	for (line = 0; line < hpp_list->nr_header_lines; line++) {
178562306a36Sopenharmony_ci		char headers[1024];
178662306a36Sopenharmony_ci
178762306a36Sopenharmony_ci		hists_browser__scnprintf_headers(browser, headers,
178862306a36Sopenharmony_ci						 sizeof(headers), line);
178962306a36Sopenharmony_ci
179062306a36Sopenharmony_ci		ui_browser__gotorc_title(&browser->b, line, 0);
179162306a36Sopenharmony_ci		ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
179262306a36Sopenharmony_ci		ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
179362306a36Sopenharmony_ci	}
179462306a36Sopenharmony_ci}
179562306a36Sopenharmony_ci
179662306a36Sopenharmony_cistatic void hist_browser__show_headers(struct hist_browser *browser)
179762306a36Sopenharmony_ci{
179862306a36Sopenharmony_ci	if (symbol_conf.report_hierarchy)
179962306a36Sopenharmony_ci		hists_browser__hierarchy_headers(browser);
180062306a36Sopenharmony_ci	else
180162306a36Sopenharmony_ci		hists_browser__headers(browser);
180262306a36Sopenharmony_ci}
180362306a36Sopenharmony_ci
180462306a36Sopenharmony_cistatic void ui_browser__hists_init_top(struct ui_browser *browser)
180562306a36Sopenharmony_ci{
180662306a36Sopenharmony_ci	if (browser->top == NULL) {
180762306a36Sopenharmony_ci		struct hist_browser *hb;
180862306a36Sopenharmony_ci
180962306a36Sopenharmony_ci		hb = container_of(browser, struct hist_browser, b);
181062306a36Sopenharmony_ci		browser->top = rb_first_cached(&hb->hists->entries);
181162306a36Sopenharmony_ci	}
181262306a36Sopenharmony_ci}
181362306a36Sopenharmony_ci
181462306a36Sopenharmony_cistatic unsigned int hist_browser__refresh(struct ui_browser *browser)
181562306a36Sopenharmony_ci{
181662306a36Sopenharmony_ci	unsigned row = 0;
181762306a36Sopenharmony_ci	struct rb_node *nd;
181862306a36Sopenharmony_ci	struct hist_browser *hb = container_of(browser, struct hist_browser, b);
181962306a36Sopenharmony_ci
182062306a36Sopenharmony_ci	if (hb->show_headers)
182162306a36Sopenharmony_ci		hist_browser__show_headers(hb);
182262306a36Sopenharmony_ci
182362306a36Sopenharmony_ci	ui_browser__hists_init_top(browser);
182462306a36Sopenharmony_ci	hb->he_selection = NULL;
182562306a36Sopenharmony_ci	hb->selection = NULL;
182662306a36Sopenharmony_ci
182762306a36Sopenharmony_ci	for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) {
182862306a36Sopenharmony_ci		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
182962306a36Sopenharmony_ci		float percent;
183062306a36Sopenharmony_ci
183162306a36Sopenharmony_ci		if (h->filtered) {
183262306a36Sopenharmony_ci			/* let it move to sibling */
183362306a36Sopenharmony_ci			h->unfolded = false;
183462306a36Sopenharmony_ci			continue;
183562306a36Sopenharmony_ci		}
183662306a36Sopenharmony_ci
183762306a36Sopenharmony_ci		if (symbol_conf.report_individual_block)
183862306a36Sopenharmony_ci			percent = block_info__total_cycles_percent(h);
183962306a36Sopenharmony_ci		else
184062306a36Sopenharmony_ci			percent = hist_entry__get_percent_limit(h);
184162306a36Sopenharmony_ci
184262306a36Sopenharmony_ci		if (percent < hb->min_pcnt)
184362306a36Sopenharmony_ci			continue;
184462306a36Sopenharmony_ci
184562306a36Sopenharmony_ci		if (symbol_conf.report_hierarchy) {
184662306a36Sopenharmony_ci			row += hist_browser__show_hierarchy_entry(hb, h, row,
184762306a36Sopenharmony_ci								  h->depth);
184862306a36Sopenharmony_ci			if (row == browser->rows)
184962306a36Sopenharmony_ci				break;
185062306a36Sopenharmony_ci
185162306a36Sopenharmony_ci			if (h->has_no_entry) {
185262306a36Sopenharmony_ci				hist_browser__show_no_entry(hb, row, h->depth + 1);
185362306a36Sopenharmony_ci				row++;
185462306a36Sopenharmony_ci			}
185562306a36Sopenharmony_ci		} else {
185662306a36Sopenharmony_ci			row += hist_browser__show_entry(hb, h, row);
185762306a36Sopenharmony_ci		}
185862306a36Sopenharmony_ci
185962306a36Sopenharmony_ci		if (row == browser->rows)
186062306a36Sopenharmony_ci			break;
186162306a36Sopenharmony_ci	}
186262306a36Sopenharmony_ci
186362306a36Sopenharmony_ci	return row;
186462306a36Sopenharmony_ci}
186562306a36Sopenharmony_ci
186662306a36Sopenharmony_cistatic struct rb_node *hists__filter_entries(struct rb_node *nd,
186762306a36Sopenharmony_ci					     float min_pcnt)
186862306a36Sopenharmony_ci{
186962306a36Sopenharmony_ci	while (nd != NULL) {
187062306a36Sopenharmony_ci		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
187162306a36Sopenharmony_ci		float percent = hist_entry__get_percent_limit(h);
187262306a36Sopenharmony_ci
187362306a36Sopenharmony_ci		if (!h->filtered && percent >= min_pcnt)
187462306a36Sopenharmony_ci			return nd;
187562306a36Sopenharmony_ci
187662306a36Sopenharmony_ci		/*
187762306a36Sopenharmony_ci		 * If it's filtered, its all children also were filtered.
187862306a36Sopenharmony_ci		 * So move to sibling node.
187962306a36Sopenharmony_ci		 */
188062306a36Sopenharmony_ci		if (rb_next(nd))
188162306a36Sopenharmony_ci			nd = rb_next(nd);
188262306a36Sopenharmony_ci		else
188362306a36Sopenharmony_ci			nd = rb_hierarchy_next(nd);
188462306a36Sopenharmony_ci	}
188562306a36Sopenharmony_ci
188662306a36Sopenharmony_ci	return NULL;
188762306a36Sopenharmony_ci}
188862306a36Sopenharmony_ci
188962306a36Sopenharmony_cistatic struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
189062306a36Sopenharmony_ci						  float min_pcnt)
189162306a36Sopenharmony_ci{
189262306a36Sopenharmony_ci	while (nd != NULL) {
189362306a36Sopenharmony_ci		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
189462306a36Sopenharmony_ci		float percent = hist_entry__get_percent_limit(h);
189562306a36Sopenharmony_ci
189662306a36Sopenharmony_ci		if (!h->filtered && percent >= min_pcnt)
189762306a36Sopenharmony_ci			return nd;
189862306a36Sopenharmony_ci
189962306a36Sopenharmony_ci		nd = rb_hierarchy_prev(nd);
190062306a36Sopenharmony_ci	}
190162306a36Sopenharmony_ci
190262306a36Sopenharmony_ci	return NULL;
190362306a36Sopenharmony_ci}
190462306a36Sopenharmony_ci
190562306a36Sopenharmony_cistatic void ui_browser__hists_seek(struct ui_browser *browser,
190662306a36Sopenharmony_ci				   off_t offset, int whence)
190762306a36Sopenharmony_ci{
190862306a36Sopenharmony_ci	struct hist_entry *h;
190962306a36Sopenharmony_ci	struct rb_node *nd;
191062306a36Sopenharmony_ci	bool first = true;
191162306a36Sopenharmony_ci	struct hist_browser *hb;
191262306a36Sopenharmony_ci
191362306a36Sopenharmony_ci	hb = container_of(browser, struct hist_browser, b);
191462306a36Sopenharmony_ci
191562306a36Sopenharmony_ci	if (browser->nr_entries == 0)
191662306a36Sopenharmony_ci		return;
191762306a36Sopenharmony_ci
191862306a36Sopenharmony_ci	ui_browser__hists_init_top(browser);
191962306a36Sopenharmony_ci
192062306a36Sopenharmony_ci	switch (whence) {
192162306a36Sopenharmony_ci	case SEEK_SET:
192262306a36Sopenharmony_ci		nd = hists__filter_entries(rb_first(browser->entries),
192362306a36Sopenharmony_ci					   hb->min_pcnt);
192462306a36Sopenharmony_ci		break;
192562306a36Sopenharmony_ci	case SEEK_CUR:
192662306a36Sopenharmony_ci		nd = browser->top;
192762306a36Sopenharmony_ci		goto do_offset;
192862306a36Sopenharmony_ci	case SEEK_END:
192962306a36Sopenharmony_ci		nd = rb_hierarchy_last(rb_last(browser->entries));
193062306a36Sopenharmony_ci		nd = hists__filter_prev_entries(nd, hb->min_pcnt);
193162306a36Sopenharmony_ci		first = false;
193262306a36Sopenharmony_ci		break;
193362306a36Sopenharmony_ci	default:
193462306a36Sopenharmony_ci		return;
193562306a36Sopenharmony_ci	}
193662306a36Sopenharmony_ci
193762306a36Sopenharmony_ci	/*
193862306a36Sopenharmony_ci	 * Moves not relative to the first visible entry invalidates its
193962306a36Sopenharmony_ci	 * row_offset:
194062306a36Sopenharmony_ci	 */
194162306a36Sopenharmony_ci	h = rb_entry(browser->top, struct hist_entry, rb_node);
194262306a36Sopenharmony_ci	h->row_offset = 0;
194362306a36Sopenharmony_ci
194462306a36Sopenharmony_ci	/*
194562306a36Sopenharmony_ci	 * Here we have to check if nd is expanded (+), if it is we can't go
194662306a36Sopenharmony_ci	 * the next top level hist_entry, instead we must compute an offset of
194762306a36Sopenharmony_ci	 * what _not_ to show and not change the first visible entry.
194862306a36Sopenharmony_ci	 *
194962306a36Sopenharmony_ci	 * This offset increments when we are going from top to bottom and
195062306a36Sopenharmony_ci	 * decreases when we're going from bottom to top.
195162306a36Sopenharmony_ci	 *
195262306a36Sopenharmony_ci	 * As we don't have backpointers to the top level in the callchains
195362306a36Sopenharmony_ci	 * structure, we need to always print the whole hist_entry callchain,
195462306a36Sopenharmony_ci	 * skipping the first ones that are before the first visible entry
195562306a36Sopenharmony_ci	 * and stop when we printed enough lines to fill the screen.
195662306a36Sopenharmony_ci	 */
195762306a36Sopenharmony_cido_offset:
195862306a36Sopenharmony_ci	if (!nd)
195962306a36Sopenharmony_ci		return;
196062306a36Sopenharmony_ci
196162306a36Sopenharmony_ci	if (offset > 0) {
196262306a36Sopenharmony_ci		do {
196362306a36Sopenharmony_ci			h = rb_entry(nd, struct hist_entry, rb_node);
196462306a36Sopenharmony_ci			if (h->unfolded && h->leaf) {
196562306a36Sopenharmony_ci				u16 remaining = h->nr_rows - h->row_offset;
196662306a36Sopenharmony_ci				if (offset > remaining) {
196762306a36Sopenharmony_ci					offset -= remaining;
196862306a36Sopenharmony_ci					h->row_offset = 0;
196962306a36Sopenharmony_ci				} else {
197062306a36Sopenharmony_ci					h->row_offset += offset;
197162306a36Sopenharmony_ci					offset = 0;
197262306a36Sopenharmony_ci					browser->top = nd;
197362306a36Sopenharmony_ci					break;
197462306a36Sopenharmony_ci				}
197562306a36Sopenharmony_ci			}
197662306a36Sopenharmony_ci			nd = hists__filter_entries(rb_hierarchy_next(nd),
197762306a36Sopenharmony_ci						   hb->min_pcnt);
197862306a36Sopenharmony_ci			if (nd == NULL)
197962306a36Sopenharmony_ci				break;
198062306a36Sopenharmony_ci			--offset;
198162306a36Sopenharmony_ci			browser->top = nd;
198262306a36Sopenharmony_ci		} while (offset != 0);
198362306a36Sopenharmony_ci	} else if (offset < 0) {
198462306a36Sopenharmony_ci		while (1) {
198562306a36Sopenharmony_ci			h = rb_entry(nd, struct hist_entry, rb_node);
198662306a36Sopenharmony_ci			if (h->unfolded && h->leaf) {
198762306a36Sopenharmony_ci				if (first) {
198862306a36Sopenharmony_ci					if (-offset > h->row_offset) {
198962306a36Sopenharmony_ci						offset += h->row_offset;
199062306a36Sopenharmony_ci						h->row_offset = 0;
199162306a36Sopenharmony_ci					} else {
199262306a36Sopenharmony_ci						h->row_offset += offset;
199362306a36Sopenharmony_ci						offset = 0;
199462306a36Sopenharmony_ci						browser->top = nd;
199562306a36Sopenharmony_ci						break;
199662306a36Sopenharmony_ci					}
199762306a36Sopenharmony_ci				} else {
199862306a36Sopenharmony_ci					if (-offset > h->nr_rows) {
199962306a36Sopenharmony_ci						offset += h->nr_rows;
200062306a36Sopenharmony_ci						h->row_offset = 0;
200162306a36Sopenharmony_ci					} else {
200262306a36Sopenharmony_ci						h->row_offset = h->nr_rows + offset;
200362306a36Sopenharmony_ci						offset = 0;
200462306a36Sopenharmony_ci						browser->top = nd;
200562306a36Sopenharmony_ci						break;
200662306a36Sopenharmony_ci					}
200762306a36Sopenharmony_ci				}
200862306a36Sopenharmony_ci			}
200962306a36Sopenharmony_ci
201062306a36Sopenharmony_ci			nd = hists__filter_prev_entries(rb_hierarchy_prev(nd),
201162306a36Sopenharmony_ci							hb->min_pcnt);
201262306a36Sopenharmony_ci			if (nd == NULL)
201362306a36Sopenharmony_ci				break;
201462306a36Sopenharmony_ci			++offset;
201562306a36Sopenharmony_ci			browser->top = nd;
201662306a36Sopenharmony_ci			if (offset == 0) {
201762306a36Sopenharmony_ci				/*
201862306a36Sopenharmony_ci				 * Last unfiltered hist_entry, check if it is
201962306a36Sopenharmony_ci				 * unfolded, if it is then we should have
202062306a36Sopenharmony_ci				 * row_offset at its last entry.
202162306a36Sopenharmony_ci				 */
202262306a36Sopenharmony_ci				h = rb_entry(nd, struct hist_entry, rb_node);
202362306a36Sopenharmony_ci				if (h->unfolded && h->leaf)
202462306a36Sopenharmony_ci					h->row_offset = h->nr_rows;
202562306a36Sopenharmony_ci				break;
202662306a36Sopenharmony_ci			}
202762306a36Sopenharmony_ci			first = false;
202862306a36Sopenharmony_ci		}
202962306a36Sopenharmony_ci	} else {
203062306a36Sopenharmony_ci		browser->top = nd;
203162306a36Sopenharmony_ci		h = rb_entry(nd, struct hist_entry, rb_node);
203262306a36Sopenharmony_ci		h->row_offset = 0;
203362306a36Sopenharmony_ci	}
203462306a36Sopenharmony_ci}
203562306a36Sopenharmony_ci
203662306a36Sopenharmony_cistatic int hist_browser__fprintf_callchain(struct hist_browser *browser,
203762306a36Sopenharmony_ci					   struct hist_entry *he, FILE *fp,
203862306a36Sopenharmony_ci					   int level)
203962306a36Sopenharmony_ci{
204062306a36Sopenharmony_ci	struct callchain_print_arg arg  = {
204162306a36Sopenharmony_ci		.fp = fp,
204262306a36Sopenharmony_ci	};
204362306a36Sopenharmony_ci
204462306a36Sopenharmony_ci	hist_browser__show_callchain(browser, he, level, 0,
204562306a36Sopenharmony_ci				     hist_browser__fprintf_callchain_entry, &arg,
204662306a36Sopenharmony_ci				     hist_browser__check_dump_full);
204762306a36Sopenharmony_ci	return arg.printed;
204862306a36Sopenharmony_ci}
204962306a36Sopenharmony_ci
205062306a36Sopenharmony_cistatic int hist_browser__fprintf_entry(struct hist_browser *browser,
205162306a36Sopenharmony_ci				       struct hist_entry *he, FILE *fp)
205262306a36Sopenharmony_ci{
205362306a36Sopenharmony_ci	char s[8192];
205462306a36Sopenharmony_ci	int printed = 0;
205562306a36Sopenharmony_ci	char folded_sign = ' ';
205662306a36Sopenharmony_ci	struct perf_hpp hpp = {
205762306a36Sopenharmony_ci		.buf = s,
205862306a36Sopenharmony_ci		.size = sizeof(s),
205962306a36Sopenharmony_ci	};
206062306a36Sopenharmony_ci	struct perf_hpp_fmt *fmt;
206162306a36Sopenharmony_ci	bool first = true;
206262306a36Sopenharmony_ci	int ret;
206362306a36Sopenharmony_ci
206462306a36Sopenharmony_ci	if (hist_entry__has_callchains(he) && symbol_conf.use_callchain) {
206562306a36Sopenharmony_ci		folded_sign = hist_entry__folded(he);
206662306a36Sopenharmony_ci		printed += fprintf(fp, "%c ", folded_sign);
206762306a36Sopenharmony_ci	}
206862306a36Sopenharmony_ci
206962306a36Sopenharmony_ci	hists__for_each_format(browser->hists, fmt) {
207062306a36Sopenharmony_ci		if (perf_hpp__should_skip(fmt, he->hists))
207162306a36Sopenharmony_ci			continue;
207262306a36Sopenharmony_ci
207362306a36Sopenharmony_ci		if (!first) {
207462306a36Sopenharmony_ci			ret = scnprintf(hpp.buf, hpp.size, "  ");
207562306a36Sopenharmony_ci			advance_hpp(&hpp, ret);
207662306a36Sopenharmony_ci		} else
207762306a36Sopenharmony_ci			first = false;
207862306a36Sopenharmony_ci
207962306a36Sopenharmony_ci		ret = fmt->entry(fmt, &hpp, he);
208062306a36Sopenharmony_ci		ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret);
208162306a36Sopenharmony_ci		advance_hpp(&hpp, ret);
208262306a36Sopenharmony_ci	}
208362306a36Sopenharmony_ci	printed += fprintf(fp, "%s\n", s);
208462306a36Sopenharmony_ci
208562306a36Sopenharmony_ci	if (folded_sign == '-')
208662306a36Sopenharmony_ci		printed += hist_browser__fprintf_callchain(browser, he, fp, 1);
208762306a36Sopenharmony_ci
208862306a36Sopenharmony_ci	return printed;
208962306a36Sopenharmony_ci}
209062306a36Sopenharmony_ci
209162306a36Sopenharmony_ci
209262306a36Sopenharmony_cistatic int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
209362306a36Sopenharmony_ci						 struct hist_entry *he,
209462306a36Sopenharmony_ci						 FILE *fp, int level)
209562306a36Sopenharmony_ci{
209662306a36Sopenharmony_ci	char s[8192];
209762306a36Sopenharmony_ci	int printed = 0;
209862306a36Sopenharmony_ci	char folded_sign = ' ';
209962306a36Sopenharmony_ci	struct perf_hpp hpp = {
210062306a36Sopenharmony_ci		.buf = s,
210162306a36Sopenharmony_ci		.size = sizeof(s),
210262306a36Sopenharmony_ci	};
210362306a36Sopenharmony_ci	struct perf_hpp_fmt *fmt;
210462306a36Sopenharmony_ci	struct perf_hpp_list_node *fmt_node;
210562306a36Sopenharmony_ci	bool first = true;
210662306a36Sopenharmony_ci	int ret;
210762306a36Sopenharmony_ci	int hierarchy_indent = (he->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
210862306a36Sopenharmony_ci
210962306a36Sopenharmony_ci	printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, "");
211062306a36Sopenharmony_ci
211162306a36Sopenharmony_ci	folded_sign = hist_entry__folded(he);
211262306a36Sopenharmony_ci	printed += fprintf(fp, "%c", folded_sign);
211362306a36Sopenharmony_ci
211462306a36Sopenharmony_ci	/* the first hpp_list_node is for overhead columns */
211562306a36Sopenharmony_ci	fmt_node = list_first_entry(&he->hists->hpp_formats,
211662306a36Sopenharmony_ci				    struct perf_hpp_list_node, list);
211762306a36Sopenharmony_ci	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
211862306a36Sopenharmony_ci		if (!first) {
211962306a36Sopenharmony_ci			ret = scnprintf(hpp.buf, hpp.size, "  ");
212062306a36Sopenharmony_ci			advance_hpp(&hpp, ret);
212162306a36Sopenharmony_ci		} else
212262306a36Sopenharmony_ci			first = false;
212362306a36Sopenharmony_ci
212462306a36Sopenharmony_ci		ret = fmt->entry(fmt, &hpp, he);
212562306a36Sopenharmony_ci		advance_hpp(&hpp, ret);
212662306a36Sopenharmony_ci	}
212762306a36Sopenharmony_ci
212862306a36Sopenharmony_ci	ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, "");
212962306a36Sopenharmony_ci	advance_hpp(&hpp, ret);
213062306a36Sopenharmony_ci
213162306a36Sopenharmony_ci	perf_hpp_list__for_each_format(he->hpp_list, fmt) {
213262306a36Sopenharmony_ci		ret = scnprintf(hpp.buf, hpp.size, "  ");
213362306a36Sopenharmony_ci		advance_hpp(&hpp, ret);
213462306a36Sopenharmony_ci
213562306a36Sopenharmony_ci		ret = fmt->entry(fmt, &hpp, he);
213662306a36Sopenharmony_ci		advance_hpp(&hpp, ret);
213762306a36Sopenharmony_ci	}
213862306a36Sopenharmony_ci
213962306a36Sopenharmony_ci	strim(s);
214062306a36Sopenharmony_ci	printed += fprintf(fp, "%s\n", s);
214162306a36Sopenharmony_ci
214262306a36Sopenharmony_ci	if (he->leaf && folded_sign == '-') {
214362306a36Sopenharmony_ci		printed += hist_browser__fprintf_callchain(browser, he, fp,
214462306a36Sopenharmony_ci							   he->depth + 1);
214562306a36Sopenharmony_ci	}
214662306a36Sopenharmony_ci
214762306a36Sopenharmony_ci	return printed;
214862306a36Sopenharmony_ci}
214962306a36Sopenharmony_ci
215062306a36Sopenharmony_cistatic int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
215162306a36Sopenharmony_ci{
215262306a36Sopenharmony_ci	struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
215362306a36Sopenharmony_ci						   browser->min_pcnt);
215462306a36Sopenharmony_ci	int printed = 0;
215562306a36Sopenharmony_ci
215662306a36Sopenharmony_ci	while (nd) {
215762306a36Sopenharmony_ci		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
215862306a36Sopenharmony_ci
215962306a36Sopenharmony_ci		if (symbol_conf.report_hierarchy) {
216062306a36Sopenharmony_ci			printed += hist_browser__fprintf_hierarchy_entry(browser,
216162306a36Sopenharmony_ci									 h, fp,
216262306a36Sopenharmony_ci									 h->depth);
216362306a36Sopenharmony_ci		} else {
216462306a36Sopenharmony_ci			printed += hist_browser__fprintf_entry(browser, h, fp);
216562306a36Sopenharmony_ci		}
216662306a36Sopenharmony_ci
216762306a36Sopenharmony_ci		nd = hists__filter_entries(rb_hierarchy_next(nd),
216862306a36Sopenharmony_ci					   browser->min_pcnt);
216962306a36Sopenharmony_ci	}
217062306a36Sopenharmony_ci
217162306a36Sopenharmony_ci	return printed;
217262306a36Sopenharmony_ci}
217362306a36Sopenharmony_ci
217462306a36Sopenharmony_cistatic int hist_browser__dump(struct hist_browser *browser)
217562306a36Sopenharmony_ci{
217662306a36Sopenharmony_ci	char filename[64];
217762306a36Sopenharmony_ci	FILE *fp;
217862306a36Sopenharmony_ci
217962306a36Sopenharmony_ci	while (1) {
218062306a36Sopenharmony_ci		scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
218162306a36Sopenharmony_ci		if (access(filename, F_OK))
218262306a36Sopenharmony_ci			break;
218362306a36Sopenharmony_ci		/*
218462306a36Sopenharmony_ci 		 * XXX: Just an arbitrary lazy upper limit
218562306a36Sopenharmony_ci 		 */
218662306a36Sopenharmony_ci		if (++browser->print_seq == 8192) {
218762306a36Sopenharmony_ci			ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
218862306a36Sopenharmony_ci			return -1;
218962306a36Sopenharmony_ci		}
219062306a36Sopenharmony_ci	}
219162306a36Sopenharmony_ci
219262306a36Sopenharmony_ci	fp = fopen(filename, "w");
219362306a36Sopenharmony_ci	if (fp == NULL) {
219462306a36Sopenharmony_ci		char bf[64];
219562306a36Sopenharmony_ci		const char *err = str_error_r(errno, bf, sizeof(bf));
219662306a36Sopenharmony_ci		ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
219762306a36Sopenharmony_ci		return -1;
219862306a36Sopenharmony_ci	}
219962306a36Sopenharmony_ci
220062306a36Sopenharmony_ci	++browser->print_seq;
220162306a36Sopenharmony_ci	hist_browser__fprintf(browser, fp);
220262306a36Sopenharmony_ci	fclose(fp);
220362306a36Sopenharmony_ci	ui_helpline__fpush("%s written!", filename);
220462306a36Sopenharmony_ci
220562306a36Sopenharmony_ci	return 0;
220662306a36Sopenharmony_ci}
220762306a36Sopenharmony_ci
220862306a36Sopenharmony_civoid hist_browser__init(struct hist_browser *browser,
220962306a36Sopenharmony_ci			struct hists *hists)
221062306a36Sopenharmony_ci{
221162306a36Sopenharmony_ci	struct perf_hpp_fmt *fmt;
221262306a36Sopenharmony_ci
221362306a36Sopenharmony_ci	browser->hists			= hists;
221462306a36Sopenharmony_ci	browser->b.refresh		= hist_browser__refresh;
221562306a36Sopenharmony_ci	browser->b.refresh_dimensions	= hist_browser__refresh_dimensions;
221662306a36Sopenharmony_ci	browser->b.seek			= ui_browser__hists_seek;
221762306a36Sopenharmony_ci	browser->b.use_navkeypressed	= true;
221862306a36Sopenharmony_ci	browser->show_headers		= symbol_conf.show_hist_headers;
221962306a36Sopenharmony_ci	hist_browser__set_title_space(browser);
222062306a36Sopenharmony_ci
222162306a36Sopenharmony_ci	if (symbol_conf.report_hierarchy) {
222262306a36Sopenharmony_ci		struct perf_hpp_list_node *fmt_node;
222362306a36Sopenharmony_ci
222462306a36Sopenharmony_ci		/* count overhead columns (in the first node) */
222562306a36Sopenharmony_ci		fmt_node = list_first_entry(&hists->hpp_formats,
222662306a36Sopenharmony_ci					    struct perf_hpp_list_node, list);
222762306a36Sopenharmony_ci		perf_hpp_list__for_each_format(&fmt_node->hpp, fmt)
222862306a36Sopenharmony_ci			++browser->b.columns;
222962306a36Sopenharmony_ci
223062306a36Sopenharmony_ci		/* add a single column for whole hierarchy sort keys*/
223162306a36Sopenharmony_ci		++browser->b.columns;
223262306a36Sopenharmony_ci	} else {
223362306a36Sopenharmony_ci		hists__for_each_format(hists, fmt)
223462306a36Sopenharmony_ci			++browser->b.columns;
223562306a36Sopenharmony_ci	}
223662306a36Sopenharmony_ci
223762306a36Sopenharmony_ci	hists__reset_column_width(hists);
223862306a36Sopenharmony_ci}
223962306a36Sopenharmony_ci
224062306a36Sopenharmony_cistruct hist_browser *hist_browser__new(struct hists *hists)
224162306a36Sopenharmony_ci{
224262306a36Sopenharmony_ci	struct hist_browser *browser = zalloc(sizeof(*browser));
224362306a36Sopenharmony_ci
224462306a36Sopenharmony_ci	if (browser)
224562306a36Sopenharmony_ci		hist_browser__init(browser, hists);
224662306a36Sopenharmony_ci
224762306a36Sopenharmony_ci	return browser;
224862306a36Sopenharmony_ci}
224962306a36Sopenharmony_ci
225062306a36Sopenharmony_cistatic struct hist_browser *
225162306a36Sopenharmony_ciperf_evsel_browser__new(struct evsel *evsel,
225262306a36Sopenharmony_ci			struct hist_browser_timer *hbt,
225362306a36Sopenharmony_ci			struct perf_env *env,
225462306a36Sopenharmony_ci			struct annotation_options *annotation_opts)
225562306a36Sopenharmony_ci{
225662306a36Sopenharmony_ci	struct hist_browser *browser = hist_browser__new(evsel__hists(evsel));
225762306a36Sopenharmony_ci
225862306a36Sopenharmony_ci	if (browser) {
225962306a36Sopenharmony_ci		browser->hbt   = hbt;
226062306a36Sopenharmony_ci		browser->env   = env;
226162306a36Sopenharmony_ci		browser->title = hists_browser__scnprintf_title;
226262306a36Sopenharmony_ci		browser->annotation_opts = annotation_opts;
226362306a36Sopenharmony_ci	}
226462306a36Sopenharmony_ci	return browser;
226562306a36Sopenharmony_ci}
226662306a36Sopenharmony_ci
226762306a36Sopenharmony_civoid hist_browser__delete(struct hist_browser *browser)
226862306a36Sopenharmony_ci{
226962306a36Sopenharmony_ci	free(browser);
227062306a36Sopenharmony_ci}
227162306a36Sopenharmony_ci
227262306a36Sopenharmony_cistatic struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
227362306a36Sopenharmony_ci{
227462306a36Sopenharmony_ci	return browser->he_selection;
227562306a36Sopenharmony_ci}
227662306a36Sopenharmony_ci
227762306a36Sopenharmony_cistatic struct thread *hist_browser__selected_thread(struct hist_browser *browser)
227862306a36Sopenharmony_ci{
227962306a36Sopenharmony_ci	return browser->he_selection->thread;
228062306a36Sopenharmony_ci}
228162306a36Sopenharmony_ci
228262306a36Sopenharmony_cistatic struct res_sample *hist_browser__selected_res_sample(struct hist_browser *browser)
228362306a36Sopenharmony_ci{
228462306a36Sopenharmony_ci	return browser->he_selection ? browser->he_selection->res_samples : NULL;
228562306a36Sopenharmony_ci}
228662306a36Sopenharmony_ci
228762306a36Sopenharmony_ci/* Check whether the browser is for 'top' or 'report' */
228862306a36Sopenharmony_cistatic inline bool is_report_browser(void *timer)
228962306a36Sopenharmony_ci{
229062306a36Sopenharmony_ci	return timer == NULL;
229162306a36Sopenharmony_ci}
229262306a36Sopenharmony_ci
229362306a36Sopenharmony_cistatic int hists_browser__scnprintf_title(struct hist_browser *browser, char *bf, size_t size)
229462306a36Sopenharmony_ci{
229562306a36Sopenharmony_ci	struct hist_browser_timer *hbt = browser->hbt;
229662306a36Sopenharmony_ci	int printed = __hists__scnprintf_title(browser->hists, bf, size, !is_report_browser(hbt));
229762306a36Sopenharmony_ci
229862306a36Sopenharmony_ci	if (!is_report_browser(hbt)) {
229962306a36Sopenharmony_ci		struct perf_top *top = hbt->arg;
230062306a36Sopenharmony_ci
230162306a36Sopenharmony_ci		printed += scnprintf(bf + printed, size - printed,
230262306a36Sopenharmony_ci				     " lost: %" PRIu64 "/%" PRIu64,
230362306a36Sopenharmony_ci				     top->lost, top->lost_total);
230462306a36Sopenharmony_ci
230562306a36Sopenharmony_ci		printed += scnprintf(bf + printed, size - printed,
230662306a36Sopenharmony_ci				     " drop: %" PRIu64 "/%" PRIu64,
230762306a36Sopenharmony_ci				     top->drop, top->drop_total);
230862306a36Sopenharmony_ci
230962306a36Sopenharmony_ci		if (top->zero)
231062306a36Sopenharmony_ci			printed += scnprintf(bf + printed, size - printed, " [z]");
231162306a36Sopenharmony_ci
231262306a36Sopenharmony_ci		perf_top__reset_sample_counters(top);
231362306a36Sopenharmony_ci	}
231462306a36Sopenharmony_ci
231562306a36Sopenharmony_ci
231662306a36Sopenharmony_ci	return printed;
231762306a36Sopenharmony_ci}
231862306a36Sopenharmony_ci
231962306a36Sopenharmony_cistatic inline void free_popup_options(char **options, int n)
232062306a36Sopenharmony_ci{
232162306a36Sopenharmony_ci	int i;
232262306a36Sopenharmony_ci
232362306a36Sopenharmony_ci	for (i = 0; i < n; ++i)
232462306a36Sopenharmony_ci		zfree(&options[i]);
232562306a36Sopenharmony_ci}
232662306a36Sopenharmony_ci
232762306a36Sopenharmony_ci/*
232862306a36Sopenharmony_ci * Only runtime switching of perf data file will make "input_name" point
232962306a36Sopenharmony_ci * to a malloced buffer. So add "is_input_name_malloced" flag to decide
233062306a36Sopenharmony_ci * whether we need to call free() for current "input_name" during the switch.
233162306a36Sopenharmony_ci */
233262306a36Sopenharmony_cistatic bool is_input_name_malloced = false;
233362306a36Sopenharmony_ci
233462306a36Sopenharmony_cistatic int switch_data_file(void)
233562306a36Sopenharmony_ci{
233662306a36Sopenharmony_ci	char *pwd, *options[32], *abs_path[32], *tmp;
233762306a36Sopenharmony_ci	DIR *pwd_dir;
233862306a36Sopenharmony_ci	int nr_options = 0, choice = -1, ret = -1;
233962306a36Sopenharmony_ci	struct dirent *dent;
234062306a36Sopenharmony_ci
234162306a36Sopenharmony_ci	pwd = getenv("PWD");
234262306a36Sopenharmony_ci	if (!pwd)
234362306a36Sopenharmony_ci		return ret;
234462306a36Sopenharmony_ci
234562306a36Sopenharmony_ci	pwd_dir = opendir(pwd);
234662306a36Sopenharmony_ci	if (!pwd_dir)
234762306a36Sopenharmony_ci		return ret;
234862306a36Sopenharmony_ci
234962306a36Sopenharmony_ci	memset(options, 0, sizeof(options));
235062306a36Sopenharmony_ci	memset(abs_path, 0, sizeof(abs_path));
235162306a36Sopenharmony_ci
235262306a36Sopenharmony_ci	while ((dent = readdir(pwd_dir))) {
235362306a36Sopenharmony_ci		char path[PATH_MAX];
235462306a36Sopenharmony_ci		u64 magic;
235562306a36Sopenharmony_ci		char *name = dent->d_name;
235662306a36Sopenharmony_ci		FILE *file;
235762306a36Sopenharmony_ci
235862306a36Sopenharmony_ci		if (!(dent->d_type == DT_REG))
235962306a36Sopenharmony_ci			continue;
236062306a36Sopenharmony_ci
236162306a36Sopenharmony_ci		snprintf(path, sizeof(path), "%s/%s", pwd, name);
236262306a36Sopenharmony_ci
236362306a36Sopenharmony_ci		file = fopen(path, "r");
236462306a36Sopenharmony_ci		if (!file)
236562306a36Sopenharmony_ci			continue;
236662306a36Sopenharmony_ci
236762306a36Sopenharmony_ci		if (fread(&magic, 1, 8, file) < 8)
236862306a36Sopenharmony_ci			goto close_file_and_continue;
236962306a36Sopenharmony_ci
237062306a36Sopenharmony_ci		if (is_perf_magic(magic)) {
237162306a36Sopenharmony_ci			options[nr_options] = strdup(name);
237262306a36Sopenharmony_ci			if (!options[nr_options])
237362306a36Sopenharmony_ci				goto close_file_and_continue;
237462306a36Sopenharmony_ci
237562306a36Sopenharmony_ci			abs_path[nr_options] = strdup(path);
237662306a36Sopenharmony_ci			if (!abs_path[nr_options]) {
237762306a36Sopenharmony_ci				zfree(&options[nr_options]);
237862306a36Sopenharmony_ci				ui__warning("Can't search all data files due to memory shortage.\n");
237962306a36Sopenharmony_ci				fclose(file);
238062306a36Sopenharmony_ci				break;
238162306a36Sopenharmony_ci			}
238262306a36Sopenharmony_ci
238362306a36Sopenharmony_ci			nr_options++;
238462306a36Sopenharmony_ci		}
238562306a36Sopenharmony_ci
238662306a36Sopenharmony_ciclose_file_and_continue:
238762306a36Sopenharmony_ci		fclose(file);
238862306a36Sopenharmony_ci		if (nr_options >= 32) {
238962306a36Sopenharmony_ci			ui__warning("Too many perf data files in PWD!\n"
239062306a36Sopenharmony_ci				    "Only the first 32 files will be listed.\n");
239162306a36Sopenharmony_ci			break;
239262306a36Sopenharmony_ci		}
239362306a36Sopenharmony_ci	}
239462306a36Sopenharmony_ci	closedir(pwd_dir);
239562306a36Sopenharmony_ci
239662306a36Sopenharmony_ci	if (nr_options) {
239762306a36Sopenharmony_ci		choice = ui__popup_menu(nr_options, options, NULL);
239862306a36Sopenharmony_ci		if (choice < nr_options && choice >= 0) {
239962306a36Sopenharmony_ci			tmp = strdup(abs_path[choice]);
240062306a36Sopenharmony_ci			if (tmp) {
240162306a36Sopenharmony_ci				if (is_input_name_malloced)
240262306a36Sopenharmony_ci					free((void *)input_name);
240362306a36Sopenharmony_ci				input_name = tmp;
240462306a36Sopenharmony_ci				is_input_name_malloced = true;
240562306a36Sopenharmony_ci				ret = 0;
240662306a36Sopenharmony_ci			} else
240762306a36Sopenharmony_ci				ui__warning("Data switch failed due to memory shortage!\n");
240862306a36Sopenharmony_ci		}
240962306a36Sopenharmony_ci	}
241062306a36Sopenharmony_ci
241162306a36Sopenharmony_ci	free_popup_options(options, nr_options);
241262306a36Sopenharmony_ci	free_popup_options(abs_path, nr_options);
241362306a36Sopenharmony_ci	return ret;
241462306a36Sopenharmony_ci}
241562306a36Sopenharmony_ci
241662306a36Sopenharmony_cistruct popup_action {
241762306a36Sopenharmony_ci	unsigned long		time;
241862306a36Sopenharmony_ci	struct thread 		*thread;
241962306a36Sopenharmony_ci	struct map_symbol 	ms;
242062306a36Sopenharmony_ci	int			socket;
242162306a36Sopenharmony_ci	struct evsel	*evsel;
242262306a36Sopenharmony_ci	enum rstype		rstype;
242362306a36Sopenharmony_ci
242462306a36Sopenharmony_ci	int (*fn)(struct hist_browser *browser, struct popup_action *act);
242562306a36Sopenharmony_ci};
242662306a36Sopenharmony_ci
242762306a36Sopenharmony_cistatic int
242862306a36Sopenharmony_cido_annotate(struct hist_browser *browser, struct popup_action *act)
242962306a36Sopenharmony_ci{
243062306a36Sopenharmony_ci	struct evsel *evsel;
243162306a36Sopenharmony_ci	struct annotation *notes;
243262306a36Sopenharmony_ci	struct hist_entry *he;
243362306a36Sopenharmony_ci	int err;
243462306a36Sopenharmony_ci
243562306a36Sopenharmony_ci	if (!browser->annotation_opts->objdump_path &&
243662306a36Sopenharmony_ci	    perf_env__lookup_objdump(browser->env, &browser->annotation_opts->objdump_path))
243762306a36Sopenharmony_ci		return 0;
243862306a36Sopenharmony_ci
243962306a36Sopenharmony_ci	notes = symbol__annotation(act->ms.sym);
244062306a36Sopenharmony_ci	if (!notes->src)
244162306a36Sopenharmony_ci		return 0;
244262306a36Sopenharmony_ci
244362306a36Sopenharmony_ci	if (browser->block_evsel)
244462306a36Sopenharmony_ci		evsel = browser->block_evsel;
244562306a36Sopenharmony_ci	else
244662306a36Sopenharmony_ci		evsel = hists_to_evsel(browser->hists);
244762306a36Sopenharmony_ci
244862306a36Sopenharmony_ci	err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt,
244962306a36Sopenharmony_ci				       browser->annotation_opts);
245062306a36Sopenharmony_ci	he = hist_browser__selected_entry(browser);
245162306a36Sopenharmony_ci	/*
245262306a36Sopenharmony_ci	 * offer option to annotate the other branch source or target
245362306a36Sopenharmony_ci	 * (if they exists) when returning from annotate
245462306a36Sopenharmony_ci	 */
245562306a36Sopenharmony_ci	if ((err == 'q' || err == CTRL('c')) && he->branch_info)
245662306a36Sopenharmony_ci		return 1;
245762306a36Sopenharmony_ci
245862306a36Sopenharmony_ci	ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
245962306a36Sopenharmony_ci	if (err)
246062306a36Sopenharmony_ci		ui_browser__handle_resize(&browser->b);
246162306a36Sopenharmony_ci	return 0;
246262306a36Sopenharmony_ci}
246362306a36Sopenharmony_ci
246462306a36Sopenharmony_cistatic struct symbol *symbol__new_unresolved(u64 addr, struct map *map)
246562306a36Sopenharmony_ci{
246662306a36Sopenharmony_ci	struct annotated_source *src;
246762306a36Sopenharmony_ci	struct symbol *sym;
246862306a36Sopenharmony_ci	char name[64];
246962306a36Sopenharmony_ci
247062306a36Sopenharmony_ci	snprintf(name, sizeof(name), "%.*" PRIx64, BITS_PER_LONG / 4, addr);
247162306a36Sopenharmony_ci
247262306a36Sopenharmony_ci	sym = symbol__new(addr, ANNOTATION_DUMMY_LEN, 0, 0, name);
247362306a36Sopenharmony_ci	if (sym) {
247462306a36Sopenharmony_ci		src = symbol__hists(sym, 1);
247562306a36Sopenharmony_ci		if (!src) {
247662306a36Sopenharmony_ci			symbol__delete(sym);
247762306a36Sopenharmony_ci			return NULL;
247862306a36Sopenharmony_ci		}
247962306a36Sopenharmony_ci
248062306a36Sopenharmony_ci		dso__insert_symbol(map__dso(map), sym);
248162306a36Sopenharmony_ci	}
248262306a36Sopenharmony_ci
248362306a36Sopenharmony_ci	return sym;
248462306a36Sopenharmony_ci}
248562306a36Sopenharmony_ci
248662306a36Sopenharmony_cistatic int
248762306a36Sopenharmony_ciadd_annotate_opt(struct hist_browser *browser __maybe_unused,
248862306a36Sopenharmony_ci		 struct popup_action *act, char **optstr,
248962306a36Sopenharmony_ci		 struct map_symbol *ms,
249062306a36Sopenharmony_ci		 u64 addr)
249162306a36Sopenharmony_ci{
249262306a36Sopenharmony_ci	struct dso *dso;
249362306a36Sopenharmony_ci
249462306a36Sopenharmony_ci	if (!ms->map || (dso = map__dso(ms->map)) == NULL || dso->annotate_warned)
249562306a36Sopenharmony_ci		return 0;
249662306a36Sopenharmony_ci
249762306a36Sopenharmony_ci	if (!ms->sym)
249862306a36Sopenharmony_ci		ms->sym = symbol__new_unresolved(addr, ms->map);
249962306a36Sopenharmony_ci
250062306a36Sopenharmony_ci	if (ms->sym == NULL || symbol__annotation(ms->sym)->src == NULL)
250162306a36Sopenharmony_ci		return 0;
250262306a36Sopenharmony_ci
250362306a36Sopenharmony_ci	if (asprintf(optstr, "Annotate %s", ms->sym->name) < 0)
250462306a36Sopenharmony_ci		return 0;
250562306a36Sopenharmony_ci
250662306a36Sopenharmony_ci	act->ms = *ms;
250762306a36Sopenharmony_ci	act->fn = do_annotate;
250862306a36Sopenharmony_ci	return 1;
250962306a36Sopenharmony_ci}
251062306a36Sopenharmony_ci
251162306a36Sopenharmony_cistatic int
251262306a36Sopenharmony_cido_zoom_thread(struct hist_browser *browser, struct popup_action *act)
251362306a36Sopenharmony_ci{
251462306a36Sopenharmony_ci	struct thread *thread = act->thread;
251562306a36Sopenharmony_ci
251662306a36Sopenharmony_ci	if ((!hists__has(browser->hists, thread) &&
251762306a36Sopenharmony_ci	     !hists__has(browser->hists, comm)) || thread == NULL)
251862306a36Sopenharmony_ci		return 0;
251962306a36Sopenharmony_ci
252062306a36Sopenharmony_ci	if (browser->hists->thread_filter) {
252162306a36Sopenharmony_ci		pstack__remove(browser->pstack, &browser->hists->thread_filter);
252262306a36Sopenharmony_ci		perf_hpp__set_elide(HISTC_THREAD, false);
252362306a36Sopenharmony_ci		thread__zput(browser->hists->thread_filter);
252462306a36Sopenharmony_ci		ui_helpline__pop();
252562306a36Sopenharmony_ci	} else {
252662306a36Sopenharmony_ci		const char *comm_set_str =
252762306a36Sopenharmony_ci			thread__comm_set(thread) ? thread__comm_str(thread) : "";
252862306a36Sopenharmony_ci
252962306a36Sopenharmony_ci		if (hists__has(browser->hists, thread)) {
253062306a36Sopenharmony_ci			ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
253162306a36Sopenharmony_ci					   comm_set_str, thread__tid(thread));
253262306a36Sopenharmony_ci		} else {
253362306a36Sopenharmony_ci			ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
253462306a36Sopenharmony_ci					   comm_set_str);
253562306a36Sopenharmony_ci		}
253662306a36Sopenharmony_ci
253762306a36Sopenharmony_ci		browser->hists->thread_filter = thread__get(thread);
253862306a36Sopenharmony_ci		perf_hpp__set_elide(HISTC_THREAD, false);
253962306a36Sopenharmony_ci		pstack__push(browser->pstack, &browser->hists->thread_filter);
254062306a36Sopenharmony_ci	}
254162306a36Sopenharmony_ci
254262306a36Sopenharmony_ci	hists__filter_by_thread(browser->hists);
254362306a36Sopenharmony_ci	hist_browser__reset(browser);
254462306a36Sopenharmony_ci	return 0;
254562306a36Sopenharmony_ci}
254662306a36Sopenharmony_ci
254762306a36Sopenharmony_cistatic int
254862306a36Sopenharmony_ciadd_thread_opt(struct hist_browser *browser, struct popup_action *act,
254962306a36Sopenharmony_ci	       char **optstr, struct thread *thread)
255062306a36Sopenharmony_ci{
255162306a36Sopenharmony_ci	int ret;
255262306a36Sopenharmony_ci	const char *comm_set_str, *in_out;
255362306a36Sopenharmony_ci
255462306a36Sopenharmony_ci	if ((!hists__has(browser->hists, thread) &&
255562306a36Sopenharmony_ci	     !hists__has(browser->hists, comm)) || thread == NULL)
255662306a36Sopenharmony_ci		return 0;
255762306a36Sopenharmony_ci
255862306a36Sopenharmony_ci	in_out = browser->hists->thread_filter ? "out of" : "into";
255962306a36Sopenharmony_ci	comm_set_str = thread__comm_set(thread) ? thread__comm_str(thread) : "";
256062306a36Sopenharmony_ci	if (hists__has(browser->hists, thread)) {
256162306a36Sopenharmony_ci		ret = asprintf(optstr, "Zoom %s %s(%d) thread",
256262306a36Sopenharmony_ci			       in_out, comm_set_str, thread__tid(thread));
256362306a36Sopenharmony_ci	} else {
256462306a36Sopenharmony_ci		ret = asprintf(optstr, "Zoom %s %s thread", in_out, comm_set_str);
256562306a36Sopenharmony_ci	}
256662306a36Sopenharmony_ci	if (ret < 0)
256762306a36Sopenharmony_ci		return 0;
256862306a36Sopenharmony_ci
256962306a36Sopenharmony_ci	act->thread = thread;
257062306a36Sopenharmony_ci	act->fn = do_zoom_thread;
257162306a36Sopenharmony_ci	return 1;
257262306a36Sopenharmony_ci}
257362306a36Sopenharmony_ci
257462306a36Sopenharmony_cistatic int hists_browser__zoom_map(struct hist_browser *browser, struct map *map)
257562306a36Sopenharmony_ci{
257662306a36Sopenharmony_ci	if (!hists__has(browser->hists, dso) || map == NULL)
257762306a36Sopenharmony_ci		return 0;
257862306a36Sopenharmony_ci
257962306a36Sopenharmony_ci	if (browser->hists->dso_filter) {
258062306a36Sopenharmony_ci		pstack__remove(browser->pstack, &browser->hists->dso_filter);
258162306a36Sopenharmony_ci		perf_hpp__set_elide(HISTC_DSO, false);
258262306a36Sopenharmony_ci		browser->hists->dso_filter = NULL;
258362306a36Sopenharmony_ci		ui_helpline__pop();
258462306a36Sopenharmony_ci	} else {
258562306a36Sopenharmony_ci		struct dso *dso = map__dso(map);
258662306a36Sopenharmony_ci		ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
258762306a36Sopenharmony_ci				   __map__is_kernel(map) ? "the Kernel" : dso->short_name);
258862306a36Sopenharmony_ci		browser->hists->dso_filter = dso;
258962306a36Sopenharmony_ci		perf_hpp__set_elide(HISTC_DSO, true);
259062306a36Sopenharmony_ci		pstack__push(browser->pstack, &browser->hists->dso_filter);
259162306a36Sopenharmony_ci	}
259262306a36Sopenharmony_ci
259362306a36Sopenharmony_ci	hists__filter_by_dso(browser->hists);
259462306a36Sopenharmony_ci	hist_browser__reset(browser);
259562306a36Sopenharmony_ci	return 0;
259662306a36Sopenharmony_ci}
259762306a36Sopenharmony_ci
259862306a36Sopenharmony_cistatic int
259962306a36Sopenharmony_cido_zoom_dso(struct hist_browser *browser, struct popup_action *act)
260062306a36Sopenharmony_ci{
260162306a36Sopenharmony_ci	return hists_browser__zoom_map(browser, act->ms.map);
260262306a36Sopenharmony_ci}
260362306a36Sopenharmony_ci
260462306a36Sopenharmony_cistatic int
260562306a36Sopenharmony_ciadd_dso_opt(struct hist_browser *browser, struct popup_action *act,
260662306a36Sopenharmony_ci	    char **optstr, struct map *map)
260762306a36Sopenharmony_ci{
260862306a36Sopenharmony_ci	if (!hists__has(browser->hists, dso) || map == NULL)
260962306a36Sopenharmony_ci		return 0;
261062306a36Sopenharmony_ci
261162306a36Sopenharmony_ci	if (asprintf(optstr, "Zoom %s %s DSO (use the 'k' hotkey to zoom directly into the kernel)",
261262306a36Sopenharmony_ci		     browser->hists->dso_filter ? "out of" : "into",
261362306a36Sopenharmony_ci		     __map__is_kernel(map) ? "the Kernel" : map__dso(map)->short_name) < 0)
261462306a36Sopenharmony_ci		return 0;
261562306a36Sopenharmony_ci
261662306a36Sopenharmony_ci	act->ms.map = map;
261762306a36Sopenharmony_ci	act->fn = do_zoom_dso;
261862306a36Sopenharmony_ci	return 1;
261962306a36Sopenharmony_ci}
262062306a36Sopenharmony_ci
262162306a36Sopenharmony_cistatic int do_toggle_callchain(struct hist_browser *browser, struct popup_action *act __maybe_unused)
262262306a36Sopenharmony_ci{
262362306a36Sopenharmony_ci	hist_browser__toggle_fold(browser);
262462306a36Sopenharmony_ci	return 0;
262562306a36Sopenharmony_ci}
262662306a36Sopenharmony_ci
262762306a36Sopenharmony_cistatic int add_callchain_toggle_opt(struct hist_browser *browser, struct popup_action *act, char **optstr)
262862306a36Sopenharmony_ci{
262962306a36Sopenharmony_ci	char sym_name[512];
263062306a36Sopenharmony_ci
263162306a36Sopenharmony_ci        if (!hist_browser__selection_has_children(browser))
263262306a36Sopenharmony_ci                return 0;
263362306a36Sopenharmony_ci
263462306a36Sopenharmony_ci	if (asprintf(optstr, "%s [%s] callchain (one level, same as '+' hotkey, use 'e'/'c' for the whole main level entry)",
263562306a36Sopenharmony_ci		     hist_browser__selection_unfolded(browser) ? "Collapse" : "Expand",
263662306a36Sopenharmony_ci		     hist_browser__selection_sym_name(browser, sym_name, sizeof(sym_name))) < 0)
263762306a36Sopenharmony_ci		return 0;
263862306a36Sopenharmony_ci
263962306a36Sopenharmony_ci	act->fn = do_toggle_callchain;
264062306a36Sopenharmony_ci	return 1;
264162306a36Sopenharmony_ci}
264262306a36Sopenharmony_ci
264362306a36Sopenharmony_cistatic int
264462306a36Sopenharmony_cido_browse_map(struct hist_browser *browser __maybe_unused,
264562306a36Sopenharmony_ci	      struct popup_action *act)
264662306a36Sopenharmony_ci{
264762306a36Sopenharmony_ci	map__browse(act->ms.map);
264862306a36Sopenharmony_ci	return 0;
264962306a36Sopenharmony_ci}
265062306a36Sopenharmony_ci
265162306a36Sopenharmony_cistatic int
265262306a36Sopenharmony_ciadd_map_opt(struct hist_browser *browser,
265362306a36Sopenharmony_ci	    struct popup_action *act, char **optstr, struct map *map)
265462306a36Sopenharmony_ci{
265562306a36Sopenharmony_ci	if (!hists__has(browser->hists, dso) || map == NULL)
265662306a36Sopenharmony_ci		return 0;
265762306a36Sopenharmony_ci
265862306a36Sopenharmony_ci	if (asprintf(optstr, "Browse map details") < 0)
265962306a36Sopenharmony_ci		return 0;
266062306a36Sopenharmony_ci
266162306a36Sopenharmony_ci	act->ms.map = map;
266262306a36Sopenharmony_ci	act->fn = do_browse_map;
266362306a36Sopenharmony_ci	return 1;
266462306a36Sopenharmony_ci}
266562306a36Sopenharmony_ci
266662306a36Sopenharmony_cistatic int
266762306a36Sopenharmony_cido_run_script(struct hist_browser *browser __maybe_unused,
266862306a36Sopenharmony_ci	      struct popup_action *act)
266962306a36Sopenharmony_ci{
267062306a36Sopenharmony_ci	char *script_opt;
267162306a36Sopenharmony_ci	int len;
267262306a36Sopenharmony_ci	int n = 0;
267362306a36Sopenharmony_ci
267462306a36Sopenharmony_ci	len = 100;
267562306a36Sopenharmony_ci	if (act->thread)
267662306a36Sopenharmony_ci		len += strlen(thread__comm_str(act->thread));
267762306a36Sopenharmony_ci	else if (act->ms.sym)
267862306a36Sopenharmony_ci		len += strlen(act->ms.sym->name);
267962306a36Sopenharmony_ci	script_opt = malloc(len);
268062306a36Sopenharmony_ci	if (!script_opt)
268162306a36Sopenharmony_ci		return -1;
268262306a36Sopenharmony_ci
268362306a36Sopenharmony_ci	script_opt[0] = 0;
268462306a36Sopenharmony_ci	if (act->thread) {
268562306a36Sopenharmony_ci		n = scnprintf(script_opt, len, " -c %s ",
268662306a36Sopenharmony_ci			  thread__comm_str(act->thread));
268762306a36Sopenharmony_ci	} else if (act->ms.sym) {
268862306a36Sopenharmony_ci		n = scnprintf(script_opt, len, " -S %s ",
268962306a36Sopenharmony_ci			  act->ms.sym->name);
269062306a36Sopenharmony_ci	}
269162306a36Sopenharmony_ci
269262306a36Sopenharmony_ci	if (act->time) {
269362306a36Sopenharmony_ci		char start[32], end[32];
269462306a36Sopenharmony_ci		unsigned long starttime = act->time;
269562306a36Sopenharmony_ci		unsigned long endtime = act->time + symbol_conf.time_quantum;
269662306a36Sopenharmony_ci
269762306a36Sopenharmony_ci		if (starttime == endtime) { /* Display 1ms as fallback */
269862306a36Sopenharmony_ci			starttime -= 1*NSEC_PER_MSEC;
269962306a36Sopenharmony_ci			endtime += 1*NSEC_PER_MSEC;
270062306a36Sopenharmony_ci		}
270162306a36Sopenharmony_ci		timestamp__scnprintf_usec(starttime, start, sizeof start);
270262306a36Sopenharmony_ci		timestamp__scnprintf_usec(endtime, end, sizeof end);
270362306a36Sopenharmony_ci		n += snprintf(script_opt + n, len - n, " --time %s,%s", start, end);
270462306a36Sopenharmony_ci	}
270562306a36Sopenharmony_ci
270662306a36Sopenharmony_ci	script_browse(script_opt, act->evsel);
270762306a36Sopenharmony_ci	free(script_opt);
270862306a36Sopenharmony_ci	return 0;
270962306a36Sopenharmony_ci}
271062306a36Sopenharmony_ci
271162306a36Sopenharmony_cistatic int
271262306a36Sopenharmony_cido_res_sample_script(struct hist_browser *browser __maybe_unused,
271362306a36Sopenharmony_ci		     struct popup_action *act)
271462306a36Sopenharmony_ci{
271562306a36Sopenharmony_ci	struct hist_entry *he;
271662306a36Sopenharmony_ci
271762306a36Sopenharmony_ci	he = hist_browser__selected_entry(browser);
271862306a36Sopenharmony_ci	res_sample_browse(he->res_samples, he->num_res, act->evsel, act->rstype);
271962306a36Sopenharmony_ci	return 0;
272062306a36Sopenharmony_ci}
272162306a36Sopenharmony_ci
272262306a36Sopenharmony_cistatic int
272362306a36Sopenharmony_ciadd_script_opt_2(struct hist_browser *browser __maybe_unused,
272462306a36Sopenharmony_ci	       struct popup_action *act, char **optstr,
272562306a36Sopenharmony_ci	       struct thread *thread, struct symbol *sym,
272662306a36Sopenharmony_ci	       struct evsel *evsel, const char *tstr)
272762306a36Sopenharmony_ci{
272862306a36Sopenharmony_ci
272962306a36Sopenharmony_ci	if (thread) {
273062306a36Sopenharmony_ci		if (asprintf(optstr, "Run scripts for samples of thread [%s]%s",
273162306a36Sopenharmony_ci			     thread__comm_str(thread), tstr) < 0)
273262306a36Sopenharmony_ci			return 0;
273362306a36Sopenharmony_ci	} else if (sym) {
273462306a36Sopenharmony_ci		if (asprintf(optstr, "Run scripts for samples of symbol [%s]%s",
273562306a36Sopenharmony_ci			     sym->name, tstr) < 0)
273662306a36Sopenharmony_ci			return 0;
273762306a36Sopenharmony_ci	} else {
273862306a36Sopenharmony_ci		if (asprintf(optstr, "Run scripts for all samples%s", tstr) < 0)
273962306a36Sopenharmony_ci			return 0;
274062306a36Sopenharmony_ci	}
274162306a36Sopenharmony_ci
274262306a36Sopenharmony_ci	act->thread = thread;
274362306a36Sopenharmony_ci	act->ms.sym = sym;
274462306a36Sopenharmony_ci	act->evsel = evsel;
274562306a36Sopenharmony_ci	act->fn = do_run_script;
274662306a36Sopenharmony_ci	return 1;
274762306a36Sopenharmony_ci}
274862306a36Sopenharmony_ci
274962306a36Sopenharmony_cistatic int
275062306a36Sopenharmony_ciadd_script_opt(struct hist_browser *browser,
275162306a36Sopenharmony_ci	       struct popup_action *act, char **optstr,
275262306a36Sopenharmony_ci	       struct thread *thread, struct symbol *sym,
275362306a36Sopenharmony_ci	       struct evsel *evsel)
275462306a36Sopenharmony_ci{
275562306a36Sopenharmony_ci	int n, j;
275662306a36Sopenharmony_ci	struct hist_entry *he;
275762306a36Sopenharmony_ci
275862306a36Sopenharmony_ci	n = add_script_opt_2(browser, act, optstr, thread, sym, evsel, "");
275962306a36Sopenharmony_ci
276062306a36Sopenharmony_ci	he = hist_browser__selected_entry(browser);
276162306a36Sopenharmony_ci	if (sort_order && strstr(sort_order, "time")) {
276262306a36Sopenharmony_ci		char tstr[128];
276362306a36Sopenharmony_ci
276462306a36Sopenharmony_ci		optstr++;
276562306a36Sopenharmony_ci		act++;
276662306a36Sopenharmony_ci		j = sprintf(tstr, " in ");
276762306a36Sopenharmony_ci		j += timestamp__scnprintf_usec(he->time, tstr + j,
276862306a36Sopenharmony_ci					       sizeof tstr - j);
276962306a36Sopenharmony_ci		j += sprintf(tstr + j, "-");
277062306a36Sopenharmony_ci		timestamp__scnprintf_usec(he->time + symbol_conf.time_quantum,
277162306a36Sopenharmony_ci				          tstr + j, sizeof tstr - j);
277262306a36Sopenharmony_ci		n += add_script_opt_2(browser, act, optstr, thread, sym,
277362306a36Sopenharmony_ci					  evsel, tstr);
277462306a36Sopenharmony_ci		act->time = he->time;
277562306a36Sopenharmony_ci	}
277662306a36Sopenharmony_ci	return n;
277762306a36Sopenharmony_ci}
277862306a36Sopenharmony_ci
277962306a36Sopenharmony_cistatic int
278062306a36Sopenharmony_ciadd_res_sample_opt(struct hist_browser *browser __maybe_unused,
278162306a36Sopenharmony_ci		   struct popup_action *act, char **optstr,
278262306a36Sopenharmony_ci		   struct res_sample *res_sample,
278362306a36Sopenharmony_ci		   struct evsel *evsel,
278462306a36Sopenharmony_ci		   enum rstype type)
278562306a36Sopenharmony_ci{
278662306a36Sopenharmony_ci	if (!res_sample)
278762306a36Sopenharmony_ci		return 0;
278862306a36Sopenharmony_ci
278962306a36Sopenharmony_ci	if (asprintf(optstr, "Show context for individual samples %s",
279062306a36Sopenharmony_ci		type == A_ASM ? "with assembler" :
279162306a36Sopenharmony_ci		type == A_SOURCE ? "with source" : "") < 0)
279262306a36Sopenharmony_ci		return 0;
279362306a36Sopenharmony_ci
279462306a36Sopenharmony_ci	act->fn = do_res_sample_script;
279562306a36Sopenharmony_ci	act->evsel = evsel;
279662306a36Sopenharmony_ci	act->rstype = type;
279762306a36Sopenharmony_ci	return 1;
279862306a36Sopenharmony_ci}
279962306a36Sopenharmony_ci
280062306a36Sopenharmony_cistatic int
280162306a36Sopenharmony_cido_switch_data(struct hist_browser *browser __maybe_unused,
280262306a36Sopenharmony_ci	       struct popup_action *act __maybe_unused)
280362306a36Sopenharmony_ci{
280462306a36Sopenharmony_ci	if (switch_data_file()) {
280562306a36Sopenharmony_ci		ui__warning("Won't switch the data files due to\n"
280662306a36Sopenharmony_ci			    "no valid data file get selected!\n");
280762306a36Sopenharmony_ci		return 0;
280862306a36Sopenharmony_ci	}
280962306a36Sopenharmony_ci
281062306a36Sopenharmony_ci	return K_SWITCH_INPUT_DATA;
281162306a36Sopenharmony_ci}
281262306a36Sopenharmony_ci
281362306a36Sopenharmony_cistatic int
281462306a36Sopenharmony_ciadd_switch_opt(struct hist_browser *browser,
281562306a36Sopenharmony_ci	       struct popup_action *act, char **optstr)
281662306a36Sopenharmony_ci{
281762306a36Sopenharmony_ci	if (!is_report_browser(browser->hbt))
281862306a36Sopenharmony_ci		return 0;
281962306a36Sopenharmony_ci
282062306a36Sopenharmony_ci	if (asprintf(optstr, "Switch to another data file in PWD") < 0)
282162306a36Sopenharmony_ci		return 0;
282262306a36Sopenharmony_ci
282362306a36Sopenharmony_ci	act->fn = do_switch_data;
282462306a36Sopenharmony_ci	return 1;
282562306a36Sopenharmony_ci}
282662306a36Sopenharmony_ci
282762306a36Sopenharmony_cistatic int
282862306a36Sopenharmony_cido_exit_browser(struct hist_browser *browser __maybe_unused,
282962306a36Sopenharmony_ci		struct popup_action *act __maybe_unused)
283062306a36Sopenharmony_ci{
283162306a36Sopenharmony_ci	return 0;
283262306a36Sopenharmony_ci}
283362306a36Sopenharmony_ci
283462306a36Sopenharmony_cistatic int
283562306a36Sopenharmony_ciadd_exit_opt(struct hist_browser *browser __maybe_unused,
283662306a36Sopenharmony_ci	     struct popup_action *act, char **optstr)
283762306a36Sopenharmony_ci{
283862306a36Sopenharmony_ci	if (asprintf(optstr, "Exit") < 0)
283962306a36Sopenharmony_ci		return 0;
284062306a36Sopenharmony_ci
284162306a36Sopenharmony_ci	act->fn = do_exit_browser;
284262306a36Sopenharmony_ci	return 1;
284362306a36Sopenharmony_ci}
284462306a36Sopenharmony_ci
284562306a36Sopenharmony_cistatic int
284662306a36Sopenharmony_cido_zoom_socket(struct hist_browser *browser, struct popup_action *act)
284762306a36Sopenharmony_ci{
284862306a36Sopenharmony_ci	if (!hists__has(browser->hists, socket) || act->socket < 0)
284962306a36Sopenharmony_ci		return 0;
285062306a36Sopenharmony_ci
285162306a36Sopenharmony_ci	if (browser->hists->socket_filter > -1) {
285262306a36Sopenharmony_ci		pstack__remove(browser->pstack, &browser->hists->socket_filter);
285362306a36Sopenharmony_ci		browser->hists->socket_filter = -1;
285462306a36Sopenharmony_ci		perf_hpp__set_elide(HISTC_SOCKET, false);
285562306a36Sopenharmony_ci	} else {
285662306a36Sopenharmony_ci		browser->hists->socket_filter = act->socket;
285762306a36Sopenharmony_ci		perf_hpp__set_elide(HISTC_SOCKET, true);
285862306a36Sopenharmony_ci		pstack__push(browser->pstack, &browser->hists->socket_filter);
285962306a36Sopenharmony_ci	}
286062306a36Sopenharmony_ci
286162306a36Sopenharmony_ci	hists__filter_by_socket(browser->hists);
286262306a36Sopenharmony_ci	hist_browser__reset(browser);
286362306a36Sopenharmony_ci	return 0;
286462306a36Sopenharmony_ci}
286562306a36Sopenharmony_ci
286662306a36Sopenharmony_cistatic int
286762306a36Sopenharmony_ciadd_socket_opt(struct hist_browser *browser, struct popup_action *act,
286862306a36Sopenharmony_ci	       char **optstr, int socket_id)
286962306a36Sopenharmony_ci{
287062306a36Sopenharmony_ci	if (!hists__has(browser->hists, socket) || socket_id < 0)
287162306a36Sopenharmony_ci		return 0;
287262306a36Sopenharmony_ci
287362306a36Sopenharmony_ci	if (asprintf(optstr, "Zoom %s Processor Socket %d",
287462306a36Sopenharmony_ci		     (browser->hists->socket_filter > -1) ? "out of" : "into",
287562306a36Sopenharmony_ci		     socket_id) < 0)
287662306a36Sopenharmony_ci		return 0;
287762306a36Sopenharmony_ci
287862306a36Sopenharmony_ci	act->socket = socket_id;
287962306a36Sopenharmony_ci	act->fn = do_zoom_socket;
288062306a36Sopenharmony_ci	return 1;
288162306a36Sopenharmony_ci}
288262306a36Sopenharmony_ci
288362306a36Sopenharmony_cistatic void hist_browser__update_nr_entries(struct hist_browser *hb)
288462306a36Sopenharmony_ci{
288562306a36Sopenharmony_ci	u64 nr_entries = 0;
288662306a36Sopenharmony_ci	struct rb_node *nd = rb_first_cached(&hb->hists->entries);
288762306a36Sopenharmony_ci
288862306a36Sopenharmony_ci	if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
288962306a36Sopenharmony_ci		hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
289062306a36Sopenharmony_ci		return;
289162306a36Sopenharmony_ci	}
289262306a36Sopenharmony_ci
289362306a36Sopenharmony_ci	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
289462306a36Sopenharmony_ci		nr_entries++;
289562306a36Sopenharmony_ci		nd = rb_hierarchy_next(nd);
289662306a36Sopenharmony_ci	}
289762306a36Sopenharmony_ci
289862306a36Sopenharmony_ci	hb->nr_non_filtered_entries = nr_entries;
289962306a36Sopenharmony_ci	hb->nr_hierarchy_entries = nr_entries;
290062306a36Sopenharmony_ci}
290162306a36Sopenharmony_ci
290262306a36Sopenharmony_cistatic void hist_browser__update_percent_limit(struct hist_browser *hb,
290362306a36Sopenharmony_ci					       double percent)
290462306a36Sopenharmony_ci{
290562306a36Sopenharmony_ci	struct hist_entry *he;
290662306a36Sopenharmony_ci	struct rb_node *nd = rb_first_cached(&hb->hists->entries);
290762306a36Sopenharmony_ci	u64 total = hists__total_period(hb->hists);
290862306a36Sopenharmony_ci	u64 min_callchain_hits = total * (percent / 100);
290962306a36Sopenharmony_ci
291062306a36Sopenharmony_ci	hb->min_pcnt = callchain_param.min_percent = percent;
291162306a36Sopenharmony_ci
291262306a36Sopenharmony_ci	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
291362306a36Sopenharmony_ci		he = rb_entry(nd, struct hist_entry, rb_node);
291462306a36Sopenharmony_ci
291562306a36Sopenharmony_ci		if (he->has_no_entry) {
291662306a36Sopenharmony_ci			he->has_no_entry = false;
291762306a36Sopenharmony_ci			he->nr_rows = 0;
291862306a36Sopenharmony_ci		}
291962306a36Sopenharmony_ci
292062306a36Sopenharmony_ci		if (!he->leaf || !hist_entry__has_callchains(he) || !symbol_conf.use_callchain)
292162306a36Sopenharmony_ci			goto next;
292262306a36Sopenharmony_ci
292362306a36Sopenharmony_ci		if (callchain_param.mode == CHAIN_GRAPH_REL) {
292462306a36Sopenharmony_ci			total = he->stat.period;
292562306a36Sopenharmony_ci
292662306a36Sopenharmony_ci			if (symbol_conf.cumulate_callchain)
292762306a36Sopenharmony_ci				total = he->stat_acc->period;
292862306a36Sopenharmony_ci
292962306a36Sopenharmony_ci			min_callchain_hits = total * (percent / 100);
293062306a36Sopenharmony_ci		}
293162306a36Sopenharmony_ci
293262306a36Sopenharmony_ci		callchain_param.sort(&he->sorted_chain, he->callchain,
293362306a36Sopenharmony_ci				     min_callchain_hits, &callchain_param);
293462306a36Sopenharmony_ci
293562306a36Sopenharmony_cinext:
293662306a36Sopenharmony_ci		nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
293762306a36Sopenharmony_ci
293862306a36Sopenharmony_ci		/* force to re-evaluate folding state of callchains */
293962306a36Sopenharmony_ci		he->init_have_children = false;
294062306a36Sopenharmony_ci		hist_entry__set_folding(he, hb, false);
294162306a36Sopenharmony_ci	}
294262306a36Sopenharmony_ci}
294362306a36Sopenharmony_ci
294462306a36Sopenharmony_cistatic int evsel__hists_browse(struct evsel *evsel, int nr_events, const char *helpline,
294562306a36Sopenharmony_ci			       bool left_exits, struct hist_browser_timer *hbt, float min_pcnt,
294662306a36Sopenharmony_ci			       struct perf_env *env, bool warn_lost_event,
294762306a36Sopenharmony_ci			       struct annotation_options *annotation_opts)
294862306a36Sopenharmony_ci{
294962306a36Sopenharmony_ci	struct hists *hists = evsel__hists(evsel);
295062306a36Sopenharmony_ci	struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env, annotation_opts);
295162306a36Sopenharmony_ci	struct branch_info *bi = NULL;
295262306a36Sopenharmony_ci#define MAX_OPTIONS  16
295362306a36Sopenharmony_ci	char *options[MAX_OPTIONS];
295462306a36Sopenharmony_ci	struct popup_action actions[MAX_OPTIONS];
295562306a36Sopenharmony_ci	int nr_options = 0;
295662306a36Sopenharmony_ci	int key = -1;
295762306a36Sopenharmony_ci	char buf[128];
295862306a36Sopenharmony_ci	int delay_secs = hbt ? hbt->refresh : 0;
295962306a36Sopenharmony_ci
296062306a36Sopenharmony_ci#define HIST_BROWSER_HELP_COMMON					\
296162306a36Sopenharmony_ci	"h/?/F1        Show this window\n"				\
296262306a36Sopenharmony_ci	"UP/DOWN/PGUP\n"						\
296362306a36Sopenharmony_ci	"PGDN/SPACE    Navigate\n"					\
296462306a36Sopenharmony_ci	"q/ESC/CTRL+C  Exit browser or go back to previous screen\n\n"	\
296562306a36Sopenharmony_ci	"For multiple event sessions:\n\n"				\
296662306a36Sopenharmony_ci	"TAB/UNTAB     Switch events\n\n"				\
296762306a36Sopenharmony_ci	"For symbolic views (--sort has sym):\n\n"			\
296862306a36Sopenharmony_ci	"ENTER         Zoom into DSO/Threads & Annotate current symbol\n" \
296962306a36Sopenharmony_ci	"ESC           Zoom out\n"					\
297062306a36Sopenharmony_ci	"+             Expand/Collapse one callchain level\n"		\
297162306a36Sopenharmony_ci	"a             Annotate current symbol\n"			\
297262306a36Sopenharmony_ci	"C             Collapse all callchains\n"			\
297362306a36Sopenharmony_ci	"d             Zoom into current DSO\n"				\
297462306a36Sopenharmony_ci	"e             Expand/Collapse main entry callchains\n"	\
297562306a36Sopenharmony_ci	"E             Expand all callchains\n"				\
297662306a36Sopenharmony_ci	"F             Toggle percentage of filtered entries\n"		\
297762306a36Sopenharmony_ci	"H             Display column headers\n"			\
297862306a36Sopenharmony_ci	"k             Zoom into the kernel map\n"			\
297962306a36Sopenharmony_ci	"L             Change percent limit\n"				\
298062306a36Sopenharmony_ci	"m             Display context menu\n"				\
298162306a36Sopenharmony_ci	"S             Zoom into current Processor Socket\n"		\
298262306a36Sopenharmony_ci
298362306a36Sopenharmony_ci	/* help messages are sorted by lexical order of the hotkey */
298462306a36Sopenharmony_ci	static const char report_help[] = HIST_BROWSER_HELP_COMMON
298562306a36Sopenharmony_ci	"i             Show header information\n"
298662306a36Sopenharmony_ci	"P             Print histograms to perf.hist.N\n"
298762306a36Sopenharmony_ci	"r             Run available scripts\n"
298862306a36Sopenharmony_ci	"s             Switch to another data file in PWD\n"
298962306a36Sopenharmony_ci	"t             Zoom into current Thread\n"
299062306a36Sopenharmony_ci	"V             Verbose (DSO names in callchains, etc)\n"
299162306a36Sopenharmony_ci	"/             Filter symbol by name\n"
299262306a36Sopenharmony_ci	"0-9           Sort by event n in group";
299362306a36Sopenharmony_ci	static const char top_help[] = HIST_BROWSER_HELP_COMMON
299462306a36Sopenharmony_ci	"P             Print histograms to perf.hist.N\n"
299562306a36Sopenharmony_ci	"t             Zoom into current Thread\n"
299662306a36Sopenharmony_ci	"V             Verbose (DSO names in callchains, etc)\n"
299762306a36Sopenharmony_ci	"z             Toggle zeroing of samples\n"
299862306a36Sopenharmony_ci	"f             Enable/Disable events\n"
299962306a36Sopenharmony_ci	"/             Filter symbol by name";
300062306a36Sopenharmony_ci
300162306a36Sopenharmony_ci	if (browser == NULL)
300262306a36Sopenharmony_ci		return -1;
300362306a36Sopenharmony_ci
300462306a36Sopenharmony_ci	/* reset abort key so that it can get Ctrl-C as a key */
300562306a36Sopenharmony_ci	SLang_reset_tty();
300662306a36Sopenharmony_ci	SLang_init_tty(0, 0, 0);
300762306a36Sopenharmony_ci
300862306a36Sopenharmony_ci	if (min_pcnt)
300962306a36Sopenharmony_ci		browser->min_pcnt = min_pcnt;
301062306a36Sopenharmony_ci	hist_browser__update_nr_entries(browser);
301162306a36Sopenharmony_ci
301262306a36Sopenharmony_ci	browser->pstack = pstack__new(3);
301362306a36Sopenharmony_ci	if (browser->pstack == NULL)
301462306a36Sopenharmony_ci		goto out;
301562306a36Sopenharmony_ci
301662306a36Sopenharmony_ci	ui_helpline__push(helpline);
301762306a36Sopenharmony_ci
301862306a36Sopenharmony_ci	memset(options, 0, sizeof(options));
301962306a36Sopenharmony_ci	memset(actions, 0, sizeof(actions));
302062306a36Sopenharmony_ci
302162306a36Sopenharmony_ci	if (symbol_conf.col_width_list_str)
302262306a36Sopenharmony_ci		perf_hpp__set_user_width(symbol_conf.col_width_list_str);
302362306a36Sopenharmony_ci
302462306a36Sopenharmony_ci	if (!is_report_browser(hbt))
302562306a36Sopenharmony_ci		browser->b.no_samples_msg = "Collecting samples...";
302662306a36Sopenharmony_ci
302762306a36Sopenharmony_ci	while (1) {
302862306a36Sopenharmony_ci		struct thread *thread = NULL;
302962306a36Sopenharmony_ci		struct map *map = NULL;
303062306a36Sopenharmony_ci		int choice;
303162306a36Sopenharmony_ci		int socked_id = -1;
303262306a36Sopenharmony_ci
303362306a36Sopenharmony_ci		key = 0; // reset key
303462306a36Sopenharmony_cido_hotkey:		 // key came straight from options ui__popup_menu()
303562306a36Sopenharmony_ci		choice = nr_options = 0;
303662306a36Sopenharmony_ci		key = hist_browser__run(browser, helpline, warn_lost_event, key);
303762306a36Sopenharmony_ci
303862306a36Sopenharmony_ci		if (browser->he_selection != NULL) {
303962306a36Sopenharmony_ci			thread = hist_browser__selected_thread(browser);
304062306a36Sopenharmony_ci			map = browser->selection->map;
304162306a36Sopenharmony_ci			socked_id = browser->he_selection->socket;
304262306a36Sopenharmony_ci		}
304362306a36Sopenharmony_ci		switch (key) {
304462306a36Sopenharmony_ci		case K_TAB:
304562306a36Sopenharmony_ci		case K_UNTAB:
304662306a36Sopenharmony_ci			if (nr_events == 1)
304762306a36Sopenharmony_ci				continue;
304862306a36Sopenharmony_ci			/*
304962306a36Sopenharmony_ci			 * Exit the browser, let hists__browser_tree
305062306a36Sopenharmony_ci			 * go to the next or previous
305162306a36Sopenharmony_ci			 */
305262306a36Sopenharmony_ci			goto out_free_stack;
305362306a36Sopenharmony_ci		case '0' ... '9':
305462306a36Sopenharmony_ci			if (!symbol_conf.event_group ||
305562306a36Sopenharmony_ci			    evsel->core.nr_members < 2) {
305662306a36Sopenharmony_ci				snprintf(buf, sizeof(buf),
305762306a36Sopenharmony_ci					 "Sort by index only available with group events!");
305862306a36Sopenharmony_ci				helpline = buf;
305962306a36Sopenharmony_ci				continue;
306062306a36Sopenharmony_ci			}
306162306a36Sopenharmony_ci
306262306a36Sopenharmony_ci			if (key - '0' == symbol_conf.group_sort_idx)
306362306a36Sopenharmony_ci				continue;
306462306a36Sopenharmony_ci
306562306a36Sopenharmony_ci			symbol_conf.group_sort_idx = key - '0';
306662306a36Sopenharmony_ci
306762306a36Sopenharmony_ci			if (symbol_conf.group_sort_idx >= evsel->core.nr_members) {
306862306a36Sopenharmony_ci				snprintf(buf, sizeof(buf),
306962306a36Sopenharmony_ci					 "Max event group index to sort is %d (index from 0 to %d)",
307062306a36Sopenharmony_ci					 evsel->core.nr_members - 1,
307162306a36Sopenharmony_ci					 evsel->core.nr_members - 1);
307262306a36Sopenharmony_ci				helpline = buf;
307362306a36Sopenharmony_ci				continue;
307462306a36Sopenharmony_ci			}
307562306a36Sopenharmony_ci
307662306a36Sopenharmony_ci			key = K_RELOAD;
307762306a36Sopenharmony_ci			goto out_free_stack;
307862306a36Sopenharmony_ci		case 'a':
307962306a36Sopenharmony_ci			if (!hists__has(hists, sym)) {
308062306a36Sopenharmony_ci				ui_browser__warning(&browser->b, delay_secs * 2,
308162306a36Sopenharmony_ci			"Annotation is only available for symbolic views, "
308262306a36Sopenharmony_ci			"include \"sym*\" in --sort to use it.");
308362306a36Sopenharmony_ci				continue;
308462306a36Sopenharmony_ci			}
308562306a36Sopenharmony_ci
308662306a36Sopenharmony_ci			if (!browser->selection ||
308762306a36Sopenharmony_ci			    !browser->selection->map ||
308862306a36Sopenharmony_ci			    !map__dso(browser->selection->map) ||
308962306a36Sopenharmony_ci			    map__dso(browser->selection->map)->annotate_warned) {
309062306a36Sopenharmony_ci				continue;
309162306a36Sopenharmony_ci			}
309262306a36Sopenharmony_ci
309362306a36Sopenharmony_ci			if (!browser->selection->sym) {
309462306a36Sopenharmony_ci				if (!browser->he_selection)
309562306a36Sopenharmony_ci					continue;
309662306a36Sopenharmony_ci
309762306a36Sopenharmony_ci				if (sort__mode == SORT_MODE__BRANCH) {
309862306a36Sopenharmony_ci					bi = browser->he_selection->branch_info;
309962306a36Sopenharmony_ci					if (!bi || !bi->to.ms.map)
310062306a36Sopenharmony_ci						continue;
310162306a36Sopenharmony_ci
310262306a36Sopenharmony_ci					actions->ms.sym = symbol__new_unresolved(bi->to.al_addr, bi->to.ms.map);
310362306a36Sopenharmony_ci					actions->ms.map = bi->to.ms.map;
310462306a36Sopenharmony_ci				} else {
310562306a36Sopenharmony_ci					actions->ms.sym = symbol__new_unresolved(browser->he_selection->ip,
310662306a36Sopenharmony_ci										 browser->selection->map);
310762306a36Sopenharmony_ci					actions->ms.map = browser->selection->map;
310862306a36Sopenharmony_ci				}
310962306a36Sopenharmony_ci
311062306a36Sopenharmony_ci				if (!actions->ms.sym)
311162306a36Sopenharmony_ci					continue;
311262306a36Sopenharmony_ci			} else {
311362306a36Sopenharmony_ci				if (symbol__annotation(browser->selection->sym)->src == NULL) {
311462306a36Sopenharmony_ci					ui_browser__warning(&browser->b, delay_secs * 2,
311562306a36Sopenharmony_ci						"No samples for the \"%s\" symbol.\n\n"
311662306a36Sopenharmony_ci						"Probably appeared just in a callchain",
311762306a36Sopenharmony_ci						browser->selection->sym->name);
311862306a36Sopenharmony_ci					continue;
311962306a36Sopenharmony_ci				}
312062306a36Sopenharmony_ci
312162306a36Sopenharmony_ci				actions->ms.map = browser->selection->map;
312262306a36Sopenharmony_ci				actions->ms.sym = browser->selection->sym;
312362306a36Sopenharmony_ci			}
312462306a36Sopenharmony_ci
312562306a36Sopenharmony_ci			do_annotate(browser, actions);
312662306a36Sopenharmony_ci			continue;
312762306a36Sopenharmony_ci		case 'P':
312862306a36Sopenharmony_ci			hist_browser__dump(browser);
312962306a36Sopenharmony_ci			continue;
313062306a36Sopenharmony_ci		case 'd':
313162306a36Sopenharmony_ci			actions->ms.map = map;
313262306a36Sopenharmony_ci			do_zoom_dso(browser, actions);
313362306a36Sopenharmony_ci			continue;
313462306a36Sopenharmony_ci		case 'k':
313562306a36Sopenharmony_ci			if (browser->selection != NULL)
313662306a36Sopenharmony_ci				hists_browser__zoom_map(browser,
313762306a36Sopenharmony_ci					      maps__machine(browser->selection->maps)->vmlinux_map);
313862306a36Sopenharmony_ci			continue;
313962306a36Sopenharmony_ci		case 'V':
314062306a36Sopenharmony_ci			verbose = (verbose + 1) % 4;
314162306a36Sopenharmony_ci			browser->show_dso = verbose > 0;
314262306a36Sopenharmony_ci			ui_helpline__fpush("Verbosity level set to %d\n",
314362306a36Sopenharmony_ci					   verbose);
314462306a36Sopenharmony_ci			continue;
314562306a36Sopenharmony_ci		case 't':
314662306a36Sopenharmony_ci			actions->thread = thread;
314762306a36Sopenharmony_ci			do_zoom_thread(browser, actions);
314862306a36Sopenharmony_ci			continue;
314962306a36Sopenharmony_ci		case 'S':
315062306a36Sopenharmony_ci			actions->socket = socked_id;
315162306a36Sopenharmony_ci			do_zoom_socket(browser, actions);
315262306a36Sopenharmony_ci			continue;
315362306a36Sopenharmony_ci		case '/':
315462306a36Sopenharmony_ci			if (ui_browser__input_window("Symbol to show",
315562306a36Sopenharmony_ci					"Please enter the name of symbol you want to see.\n"
315662306a36Sopenharmony_ci					"To remove the filter later, press / + ENTER.",
315762306a36Sopenharmony_ci					buf, "ENTER: OK, ESC: Cancel",
315862306a36Sopenharmony_ci					delay_secs * 2) == K_ENTER) {
315962306a36Sopenharmony_ci				hists->symbol_filter_str = *buf ? buf : NULL;
316062306a36Sopenharmony_ci				hists__filter_by_symbol(hists);
316162306a36Sopenharmony_ci				hist_browser__reset(browser);
316262306a36Sopenharmony_ci			}
316362306a36Sopenharmony_ci			continue;
316462306a36Sopenharmony_ci		case 'r':
316562306a36Sopenharmony_ci			if (is_report_browser(hbt)) {
316662306a36Sopenharmony_ci				actions->thread = NULL;
316762306a36Sopenharmony_ci				actions->ms.sym = NULL;
316862306a36Sopenharmony_ci				do_run_script(browser, actions);
316962306a36Sopenharmony_ci			}
317062306a36Sopenharmony_ci			continue;
317162306a36Sopenharmony_ci		case 's':
317262306a36Sopenharmony_ci			if (is_report_browser(hbt)) {
317362306a36Sopenharmony_ci				key = do_switch_data(browser, actions);
317462306a36Sopenharmony_ci				if (key == K_SWITCH_INPUT_DATA)
317562306a36Sopenharmony_ci					goto out_free_stack;
317662306a36Sopenharmony_ci			}
317762306a36Sopenharmony_ci			continue;
317862306a36Sopenharmony_ci		case 'i':
317962306a36Sopenharmony_ci			/* env->arch is NULL for live-mode (i.e. perf top) */
318062306a36Sopenharmony_ci			if (env->arch)
318162306a36Sopenharmony_ci				tui__header_window(env);
318262306a36Sopenharmony_ci			continue;
318362306a36Sopenharmony_ci		case 'F':
318462306a36Sopenharmony_ci			symbol_conf.filter_relative ^= 1;
318562306a36Sopenharmony_ci			continue;
318662306a36Sopenharmony_ci		case 'z':
318762306a36Sopenharmony_ci			if (!is_report_browser(hbt)) {
318862306a36Sopenharmony_ci				struct perf_top *top = hbt->arg;
318962306a36Sopenharmony_ci
319062306a36Sopenharmony_ci				top->zero = !top->zero;
319162306a36Sopenharmony_ci			}
319262306a36Sopenharmony_ci			continue;
319362306a36Sopenharmony_ci		case 'L':
319462306a36Sopenharmony_ci			if (ui_browser__input_window("Percent Limit",
319562306a36Sopenharmony_ci					"Please enter the value you want to hide entries under that percent.",
319662306a36Sopenharmony_ci					buf, "ENTER: OK, ESC: Cancel",
319762306a36Sopenharmony_ci					delay_secs * 2) == K_ENTER) {
319862306a36Sopenharmony_ci				char *end;
319962306a36Sopenharmony_ci				double new_percent = strtod(buf, &end);
320062306a36Sopenharmony_ci
320162306a36Sopenharmony_ci				if (new_percent < 0 || new_percent > 100) {
320262306a36Sopenharmony_ci					ui_browser__warning(&browser->b, delay_secs * 2,
320362306a36Sopenharmony_ci						"Invalid percent: %.2f", new_percent);
320462306a36Sopenharmony_ci					continue;
320562306a36Sopenharmony_ci				}
320662306a36Sopenharmony_ci
320762306a36Sopenharmony_ci				hist_browser__update_percent_limit(browser, new_percent);
320862306a36Sopenharmony_ci				hist_browser__reset(browser);
320962306a36Sopenharmony_ci			}
321062306a36Sopenharmony_ci			continue;
321162306a36Sopenharmony_ci		case K_F1:
321262306a36Sopenharmony_ci		case 'h':
321362306a36Sopenharmony_ci		case '?':
321462306a36Sopenharmony_ci			ui_browser__help_window(&browser->b,
321562306a36Sopenharmony_ci				is_report_browser(hbt) ? report_help : top_help);
321662306a36Sopenharmony_ci			continue;
321762306a36Sopenharmony_ci		case K_ENTER:
321862306a36Sopenharmony_ci		case K_RIGHT:
321962306a36Sopenharmony_ci		case 'm':
322062306a36Sopenharmony_ci			/* menu */
322162306a36Sopenharmony_ci			break;
322262306a36Sopenharmony_ci		case K_ESC:
322362306a36Sopenharmony_ci		case K_LEFT: {
322462306a36Sopenharmony_ci			const void *top;
322562306a36Sopenharmony_ci
322662306a36Sopenharmony_ci			if (pstack__empty(browser->pstack)) {
322762306a36Sopenharmony_ci				/*
322862306a36Sopenharmony_ci				 * Go back to the perf_evsel_menu__run or other user
322962306a36Sopenharmony_ci				 */
323062306a36Sopenharmony_ci				if (left_exits)
323162306a36Sopenharmony_ci					goto out_free_stack;
323262306a36Sopenharmony_ci
323362306a36Sopenharmony_ci				if (key == K_ESC &&
323462306a36Sopenharmony_ci				    ui_browser__dialog_yesno(&browser->b,
323562306a36Sopenharmony_ci							     "Do you really want to exit?"))
323662306a36Sopenharmony_ci					goto out_free_stack;
323762306a36Sopenharmony_ci
323862306a36Sopenharmony_ci				continue;
323962306a36Sopenharmony_ci			}
324062306a36Sopenharmony_ci			actions->ms.map = map;
324162306a36Sopenharmony_ci			top = pstack__peek(browser->pstack);
324262306a36Sopenharmony_ci			if (top == &browser->hists->dso_filter) {
324362306a36Sopenharmony_ci				/*
324462306a36Sopenharmony_ci				 * No need to set actions->dso here since
324562306a36Sopenharmony_ci				 * it's just to remove the current filter.
324662306a36Sopenharmony_ci				 * Ditto for thread below.
324762306a36Sopenharmony_ci				 */
324862306a36Sopenharmony_ci				do_zoom_dso(browser, actions);
324962306a36Sopenharmony_ci			} else if (top == &browser->hists->thread_filter) {
325062306a36Sopenharmony_ci				do_zoom_thread(browser, actions);
325162306a36Sopenharmony_ci			} else if (top == &browser->hists->socket_filter) {
325262306a36Sopenharmony_ci				do_zoom_socket(browser, actions);
325362306a36Sopenharmony_ci			}
325462306a36Sopenharmony_ci			continue;
325562306a36Sopenharmony_ci		}
325662306a36Sopenharmony_ci		case 'q':
325762306a36Sopenharmony_ci		case CTRL('c'):
325862306a36Sopenharmony_ci			goto out_free_stack;
325962306a36Sopenharmony_ci		case 'f':
326062306a36Sopenharmony_ci			if (!is_report_browser(hbt)) {
326162306a36Sopenharmony_ci				struct perf_top *top = hbt->arg;
326262306a36Sopenharmony_ci
326362306a36Sopenharmony_ci				evlist__toggle_enable(top->evlist);
326462306a36Sopenharmony_ci				/*
326562306a36Sopenharmony_ci				 * No need to refresh, resort/decay histogram
326662306a36Sopenharmony_ci				 * entries if we are not collecting samples:
326762306a36Sopenharmony_ci				 */
326862306a36Sopenharmony_ci				if (top->evlist->enabled) {
326962306a36Sopenharmony_ci					helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
327062306a36Sopenharmony_ci					hbt->refresh = delay_secs;
327162306a36Sopenharmony_ci				} else {
327262306a36Sopenharmony_ci					helpline = "Press 'f' again to re-enable the events";
327362306a36Sopenharmony_ci					hbt->refresh = 0;
327462306a36Sopenharmony_ci				}
327562306a36Sopenharmony_ci				continue;
327662306a36Sopenharmony_ci			}
327762306a36Sopenharmony_ci			/* Fall thru */
327862306a36Sopenharmony_ci		default:
327962306a36Sopenharmony_ci			helpline = "Press '?' for help on key bindings";
328062306a36Sopenharmony_ci			continue;
328162306a36Sopenharmony_ci		}
328262306a36Sopenharmony_ci
328362306a36Sopenharmony_ci		if (!hists__has(hists, sym) || browser->selection == NULL)
328462306a36Sopenharmony_ci			goto skip_annotation;
328562306a36Sopenharmony_ci
328662306a36Sopenharmony_ci		if (sort__mode == SORT_MODE__BRANCH) {
328762306a36Sopenharmony_ci
328862306a36Sopenharmony_ci			if (browser->he_selection)
328962306a36Sopenharmony_ci				bi = browser->he_selection->branch_info;
329062306a36Sopenharmony_ci
329162306a36Sopenharmony_ci			if (bi == NULL)
329262306a36Sopenharmony_ci				goto skip_annotation;
329362306a36Sopenharmony_ci
329462306a36Sopenharmony_ci			nr_options += add_annotate_opt(browser,
329562306a36Sopenharmony_ci						       &actions[nr_options],
329662306a36Sopenharmony_ci						       &options[nr_options],
329762306a36Sopenharmony_ci						       &bi->from.ms,
329862306a36Sopenharmony_ci						       bi->from.al_addr);
329962306a36Sopenharmony_ci			if (bi->to.ms.sym != bi->from.ms.sym)
330062306a36Sopenharmony_ci				nr_options += add_annotate_opt(browser,
330162306a36Sopenharmony_ci							&actions[nr_options],
330262306a36Sopenharmony_ci							&options[nr_options],
330362306a36Sopenharmony_ci							&bi->to.ms,
330462306a36Sopenharmony_ci							bi->to.al_addr);
330562306a36Sopenharmony_ci		} else {
330662306a36Sopenharmony_ci			nr_options += add_annotate_opt(browser,
330762306a36Sopenharmony_ci						       &actions[nr_options],
330862306a36Sopenharmony_ci						       &options[nr_options],
330962306a36Sopenharmony_ci						       browser->selection,
331062306a36Sopenharmony_ci						       browser->he_selection->ip);
331162306a36Sopenharmony_ci		}
331262306a36Sopenharmony_ciskip_annotation:
331362306a36Sopenharmony_ci		nr_options += add_thread_opt(browser, &actions[nr_options],
331462306a36Sopenharmony_ci					     &options[nr_options], thread);
331562306a36Sopenharmony_ci		nr_options += add_dso_opt(browser, &actions[nr_options],
331662306a36Sopenharmony_ci					  &options[nr_options], map);
331762306a36Sopenharmony_ci		nr_options += add_callchain_toggle_opt(browser, &actions[nr_options], &options[nr_options]);
331862306a36Sopenharmony_ci		nr_options += add_map_opt(browser, &actions[nr_options],
331962306a36Sopenharmony_ci					  &options[nr_options],
332062306a36Sopenharmony_ci					  browser->selection ?
332162306a36Sopenharmony_ci						browser->selection->map : NULL);
332262306a36Sopenharmony_ci		nr_options += add_socket_opt(browser, &actions[nr_options],
332362306a36Sopenharmony_ci					     &options[nr_options],
332462306a36Sopenharmony_ci					     socked_id);
332562306a36Sopenharmony_ci		/* perf script support */
332662306a36Sopenharmony_ci		if (!is_report_browser(hbt))
332762306a36Sopenharmony_ci			goto skip_scripting;
332862306a36Sopenharmony_ci
332962306a36Sopenharmony_ci		if (browser->he_selection) {
333062306a36Sopenharmony_ci			if (hists__has(hists, thread) && thread) {
333162306a36Sopenharmony_ci				nr_options += add_script_opt(browser,
333262306a36Sopenharmony_ci							     &actions[nr_options],
333362306a36Sopenharmony_ci							     &options[nr_options],
333462306a36Sopenharmony_ci							     thread, NULL, evsel);
333562306a36Sopenharmony_ci			}
333662306a36Sopenharmony_ci			/*
333762306a36Sopenharmony_ci			 * Note that browser->selection != NULL
333862306a36Sopenharmony_ci			 * when browser->he_selection is not NULL,
333962306a36Sopenharmony_ci			 * so we don't need to check browser->selection
334062306a36Sopenharmony_ci			 * before fetching browser->selection->sym like what
334162306a36Sopenharmony_ci			 * we do before fetching browser->selection->map.
334262306a36Sopenharmony_ci			 *
334362306a36Sopenharmony_ci			 * See hist_browser__show_entry.
334462306a36Sopenharmony_ci			 */
334562306a36Sopenharmony_ci			if (hists__has(hists, sym) && browser->selection->sym) {
334662306a36Sopenharmony_ci				nr_options += add_script_opt(browser,
334762306a36Sopenharmony_ci							     &actions[nr_options],
334862306a36Sopenharmony_ci							     &options[nr_options],
334962306a36Sopenharmony_ci							     NULL, browser->selection->sym,
335062306a36Sopenharmony_ci							     evsel);
335162306a36Sopenharmony_ci			}
335262306a36Sopenharmony_ci		}
335362306a36Sopenharmony_ci		nr_options += add_script_opt(browser, &actions[nr_options],
335462306a36Sopenharmony_ci					     &options[nr_options], NULL, NULL, evsel);
335562306a36Sopenharmony_ci		nr_options += add_res_sample_opt(browser, &actions[nr_options],
335662306a36Sopenharmony_ci						 &options[nr_options],
335762306a36Sopenharmony_ci						 hist_browser__selected_res_sample(browser),
335862306a36Sopenharmony_ci						 evsel, A_NORMAL);
335962306a36Sopenharmony_ci		nr_options += add_res_sample_opt(browser, &actions[nr_options],
336062306a36Sopenharmony_ci						 &options[nr_options],
336162306a36Sopenharmony_ci						 hist_browser__selected_res_sample(browser),
336262306a36Sopenharmony_ci						 evsel, A_ASM);
336362306a36Sopenharmony_ci		nr_options += add_res_sample_opt(browser, &actions[nr_options],
336462306a36Sopenharmony_ci						 &options[nr_options],
336562306a36Sopenharmony_ci						 hist_browser__selected_res_sample(browser),
336662306a36Sopenharmony_ci						 evsel, A_SOURCE);
336762306a36Sopenharmony_ci		nr_options += add_switch_opt(browser, &actions[nr_options],
336862306a36Sopenharmony_ci					     &options[nr_options]);
336962306a36Sopenharmony_ciskip_scripting:
337062306a36Sopenharmony_ci		nr_options += add_exit_opt(browser, &actions[nr_options],
337162306a36Sopenharmony_ci					   &options[nr_options]);
337262306a36Sopenharmony_ci
337362306a36Sopenharmony_ci		do {
337462306a36Sopenharmony_ci			struct popup_action *act;
337562306a36Sopenharmony_ci
337662306a36Sopenharmony_ci			choice = ui__popup_menu(nr_options, options, &key);
337762306a36Sopenharmony_ci			if (choice == -1)
337862306a36Sopenharmony_ci				break;
337962306a36Sopenharmony_ci
338062306a36Sopenharmony_ci			if (choice == nr_options)
338162306a36Sopenharmony_ci				goto do_hotkey;
338262306a36Sopenharmony_ci
338362306a36Sopenharmony_ci			act = &actions[choice];
338462306a36Sopenharmony_ci			key = act->fn(browser, act);
338562306a36Sopenharmony_ci		} while (key == 1);
338662306a36Sopenharmony_ci
338762306a36Sopenharmony_ci		if (key == K_SWITCH_INPUT_DATA)
338862306a36Sopenharmony_ci			break;
338962306a36Sopenharmony_ci	}
339062306a36Sopenharmony_ciout_free_stack:
339162306a36Sopenharmony_ci	pstack__delete(browser->pstack);
339262306a36Sopenharmony_ciout:
339362306a36Sopenharmony_ci	hist_browser__delete(browser);
339462306a36Sopenharmony_ci	free_popup_options(options, MAX_OPTIONS);
339562306a36Sopenharmony_ci	return key;
339662306a36Sopenharmony_ci}
339762306a36Sopenharmony_ci
339862306a36Sopenharmony_cistruct evsel_menu {
339962306a36Sopenharmony_ci	struct ui_browser b;
340062306a36Sopenharmony_ci	struct evsel *selection;
340162306a36Sopenharmony_ci	struct annotation_options *annotation_opts;
340262306a36Sopenharmony_ci	bool lost_events, lost_events_warned;
340362306a36Sopenharmony_ci	float min_pcnt;
340462306a36Sopenharmony_ci	struct perf_env *env;
340562306a36Sopenharmony_ci};
340662306a36Sopenharmony_ci
340762306a36Sopenharmony_cistatic void perf_evsel_menu__write(struct ui_browser *browser,
340862306a36Sopenharmony_ci				   void *entry, int row)
340962306a36Sopenharmony_ci{
341062306a36Sopenharmony_ci	struct evsel_menu *menu = container_of(browser,
341162306a36Sopenharmony_ci						    struct evsel_menu, b);
341262306a36Sopenharmony_ci	struct evsel *evsel = list_entry(entry, struct evsel, core.node);
341362306a36Sopenharmony_ci	struct hists *hists = evsel__hists(evsel);
341462306a36Sopenharmony_ci	bool current_entry = ui_browser__is_current_entry(browser, row);
341562306a36Sopenharmony_ci	unsigned long nr_events = hists->stats.nr_samples;
341662306a36Sopenharmony_ci	const char *ev_name = evsel__name(evsel);
341762306a36Sopenharmony_ci	char bf[256], unit;
341862306a36Sopenharmony_ci	const char *warn = " ";
341962306a36Sopenharmony_ci	size_t printed;
342062306a36Sopenharmony_ci
342162306a36Sopenharmony_ci	ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
342262306a36Sopenharmony_ci						       HE_COLORSET_NORMAL);
342362306a36Sopenharmony_ci
342462306a36Sopenharmony_ci	if (evsel__is_group_event(evsel)) {
342562306a36Sopenharmony_ci		struct evsel *pos;
342662306a36Sopenharmony_ci
342762306a36Sopenharmony_ci		ev_name = evsel__group_name(evsel);
342862306a36Sopenharmony_ci
342962306a36Sopenharmony_ci		for_each_group_member(pos, evsel) {
343062306a36Sopenharmony_ci			struct hists *pos_hists = evsel__hists(pos);
343162306a36Sopenharmony_ci			nr_events += pos_hists->stats.nr_samples;
343262306a36Sopenharmony_ci		}
343362306a36Sopenharmony_ci	}
343462306a36Sopenharmony_ci
343562306a36Sopenharmony_ci	nr_events = convert_unit(nr_events, &unit);
343662306a36Sopenharmony_ci	printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
343762306a36Sopenharmony_ci			   unit, unit == ' ' ? "" : " ", ev_name);
343862306a36Sopenharmony_ci	ui_browser__printf(browser, "%s", bf);
343962306a36Sopenharmony_ci
344062306a36Sopenharmony_ci	nr_events = evsel->evlist->stats.nr_events[PERF_RECORD_LOST];
344162306a36Sopenharmony_ci	if (nr_events != 0) {
344262306a36Sopenharmony_ci		menu->lost_events = true;
344362306a36Sopenharmony_ci		if (!current_entry)
344462306a36Sopenharmony_ci			ui_browser__set_color(browser, HE_COLORSET_TOP);
344562306a36Sopenharmony_ci		nr_events = convert_unit(nr_events, &unit);
344662306a36Sopenharmony_ci		printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
344762306a36Sopenharmony_ci				     nr_events, unit, unit == ' ' ? "" : " ");
344862306a36Sopenharmony_ci		warn = bf;
344962306a36Sopenharmony_ci	}
345062306a36Sopenharmony_ci
345162306a36Sopenharmony_ci	ui_browser__write_nstring(browser, warn, browser->width - printed);
345262306a36Sopenharmony_ci
345362306a36Sopenharmony_ci	if (current_entry)
345462306a36Sopenharmony_ci		menu->selection = evsel;
345562306a36Sopenharmony_ci}
345662306a36Sopenharmony_ci
345762306a36Sopenharmony_cistatic int perf_evsel_menu__run(struct evsel_menu *menu,
345862306a36Sopenharmony_ci				int nr_events, const char *help,
345962306a36Sopenharmony_ci				struct hist_browser_timer *hbt,
346062306a36Sopenharmony_ci				bool warn_lost_event)
346162306a36Sopenharmony_ci{
346262306a36Sopenharmony_ci	struct evlist *evlist = menu->b.priv;
346362306a36Sopenharmony_ci	struct evsel *pos;
346462306a36Sopenharmony_ci	const char *title = "Available samples";
346562306a36Sopenharmony_ci	int delay_secs = hbt ? hbt->refresh : 0;
346662306a36Sopenharmony_ci	int key;
346762306a36Sopenharmony_ci
346862306a36Sopenharmony_ci	if (ui_browser__show(&menu->b, title,
346962306a36Sopenharmony_ci			     "ESC: exit, ENTER|->: Browse histograms") < 0)
347062306a36Sopenharmony_ci		return -1;
347162306a36Sopenharmony_ci
347262306a36Sopenharmony_ci	while (1) {
347362306a36Sopenharmony_ci		key = ui_browser__run(&menu->b, delay_secs);
347462306a36Sopenharmony_ci
347562306a36Sopenharmony_ci		switch (key) {
347662306a36Sopenharmony_ci		case K_TIMER:
347762306a36Sopenharmony_ci			if (hbt)
347862306a36Sopenharmony_ci				hbt->timer(hbt->arg);
347962306a36Sopenharmony_ci
348062306a36Sopenharmony_ci			if (!menu->lost_events_warned &&
348162306a36Sopenharmony_ci			    menu->lost_events &&
348262306a36Sopenharmony_ci			    warn_lost_event) {
348362306a36Sopenharmony_ci				ui_browser__warn_lost_events(&menu->b);
348462306a36Sopenharmony_ci				menu->lost_events_warned = true;
348562306a36Sopenharmony_ci			}
348662306a36Sopenharmony_ci			continue;
348762306a36Sopenharmony_ci		case K_RIGHT:
348862306a36Sopenharmony_ci		case K_ENTER:
348962306a36Sopenharmony_ci			if (!menu->selection)
349062306a36Sopenharmony_ci				continue;
349162306a36Sopenharmony_ci			pos = menu->selection;
349262306a36Sopenharmony_cibrowse_hists:
349362306a36Sopenharmony_ci			evlist__set_selected(evlist, pos);
349462306a36Sopenharmony_ci			/*
349562306a36Sopenharmony_ci			 * Give the calling tool a chance to populate the non
349662306a36Sopenharmony_ci			 * default evsel resorted hists tree.
349762306a36Sopenharmony_ci			 */
349862306a36Sopenharmony_ci			if (hbt)
349962306a36Sopenharmony_ci				hbt->timer(hbt->arg);
350062306a36Sopenharmony_ci			key = evsel__hists_browse(pos, nr_events, help, true, hbt,
350162306a36Sopenharmony_ci						  menu->min_pcnt, menu->env,
350262306a36Sopenharmony_ci						  warn_lost_event,
350362306a36Sopenharmony_ci						  menu->annotation_opts);
350462306a36Sopenharmony_ci			ui_browser__show_title(&menu->b, title);
350562306a36Sopenharmony_ci			switch (key) {
350662306a36Sopenharmony_ci			case K_TAB:
350762306a36Sopenharmony_ci				if (pos->core.node.next == &evlist->core.entries)
350862306a36Sopenharmony_ci					pos = evlist__first(evlist);
350962306a36Sopenharmony_ci				else
351062306a36Sopenharmony_ci					pos = evsel__next(pos);
351162306a36Sopenharmony_ci				goto browse_hists;
351262306a36Sopenharmony_ci			case K_UNTAB:
351362306a36Sopenharmony_ci				if (pos->core.node.prev == &evlist->core.entries)
351462306a36Sopenharmony_ci					pos = evlist__last(evlist);
351562306a36Sopenharmony_ci				else
351662306a36Sopenharmony_ci					pos = evsel__prev(pos);
351762306a36Sopenharmony_ci				goto browse_hists;
351862306a36Sopenharmony_ci			case K_SWITCH_INPUT_DATA:
351962306a36Sopenharmony_ci			case K_RELOAD:
352062306a36Sopenharmony_ci			case 'q':
352162306a36Sopenharmony_ci			case CTRL('c'):
352262306a36Sopenharmony_ci				goto out;
352362306a36Sopenharmony_ci			case K_ESC:
352462306a36Sopenharmony_ci			default:
352562306a36Sopenharmony_ci				continue;
352662306a36Sopenharmony_ci			}
352762306a36Sopenharmony_ci		case K_LEFT:
352862306a36Sopenharmony_ci			continue;
352962306a36Sopenharmony_ci		case K_ESC:
353062306a36Sopenharmony_ci			if (!ui_browser__dialog_yesno(&menu->b,
353162306a36Sopenharmony_ci					       "Do you really want to exit?"))
353262306a36Sopenharmony_ci				continue;
353362306a36Sopenharmony_ci			/* Fall thru */
353462306a36Sopenharmony_ci		case 'q':
353562306a36Sopenharmony_ci		case CTRL('c'):
353662306a36Sopenharmony_ci			goto out;
353762306a36Sopenharmony_ci		default:
353862306a36Sopenharmony_ci			continue;
353962306a36Sopenharmony_ci		}
354062306a36Sopenharmony_ci	}
354162306a36Sopenharmony_ci
354262306a36Sopenharmony_ciout:
354362306a36Sopenharmony_ci	ui_browser__hide(&menu->b);
354462306a36Sopenharmony_ci	return key;
354562306a36Sopenharmony_ci}
354662306a36Sopenharmony_ci
354762306a36Sopenharmony_cistatic bool filter_group_entries(struct ui_browser *browser __maybe_unused,
354862306a36Sopenharmony_ci				 void *entry)
354962306a36Sopenharmony_ci{
355062306a36Sopenharmony_ci	struct evsel *evsel = list_entry(entry, struct evsel, core.node);
355162306a36Sopenharmony_ci
355262306a36Sopenharmony_ci	if (symbol_conf.event_group && !evsel__is_group_leader(evsel))
355362306a36Sopenharmony_ci		return true;
355462306a36Sopenharmony_ci
355562306a36Sopenharmony_ci	return false;
355662306a36Sopenharmony_ci}
355762306a36Sopenharmony_ci
355862306a36Sopenharmony_cistatic int __evlist__tui_browse_hists(struct evlist *evlist, int nr_entries, const char *help,
355962306a36Sopenharmony_ci				      struct hist_browser_timer *hbt, float min_pcnt, struct perf_env *env,
356062306a36Sopenharmony_ci				      bool warn_lost_event, struct annotation_options *annotation_opts)
356162306a36Sopenharmony_ci{
356262306a36Sopenharmony_ci	struct evsel *pos;
356362306a36Sopenharmony_ci	struct evsel_menu menu = {
356462306a36Sopenharmony_ci		.b = {
356562306a36Sopenharmony_ci			.entries    = &evlist->core.entries,
356662306a36Sopenharmony_ci			.refresh    = ui_browser__list_head_refresh,
356762306a36Sopenharmony_ci			.seek	    = ui_browser__list_head_seek,
356862306a36Sopenharmony_ci			.write	    = perf_evsel_menu__write,
356962306a36Sopenharmony_ci			.filter	    = filter_group_entries,
357062306a36Sopenharmony_ci			.nr_entries = nr_entries,
357162306a36Sopenharmony_ci			.priv	    = evlist,
357262306a36Sopenharmony_ci		},
357362306a36Sopenharmony_ci		.min_pcnt = min_pcnt,
357462306a36Sopenharmony_ci		.env = env,
357562306a36Sopenharmony_ci		.annotation_opts = annotation_opts,
357662306a36Sopenharmony_ci	};
357762306a36Sopenharmony_ci
357862306a36Sopenharmony_ci	ui_helpline__push("Press ESC to exit");
357962306a36Sopenharmony_ci
358062306a36Sopenharmony_ci	evlist__for_each_entry(evlist, pos) {
358162306a36Sopenharmony_ci		const char *ev_name = evsel__name(pos);
358262306a36Sopenharmony_ci		size_t line_len = strlen(ev_name) + 7;
358362306a36Sopenharmony_ci
358462306a36Sopenharmony_ci		if (menu.b.width < line_len)
358562306a36Sopenharmony_ci			menu.b.width = line_len;
358662306a36Sopenharmony_ci	}
358762306a36Sopenharmony_ci
358862306a36Sopenharmony_ci	return perf_evsel_menu__run(&menu, nr_entries, help,
358962306a36Sopenharmony_ci				    hbt, warn_lost_event);
359062306a36Sopenharmony_ci}
359162306a36Sopenharmony_ci
359262306a36Sopenharmony_cistatic bool evlist__single_entry(struct evlist *evlist)
359362306a36Sopenharmony_ci{
359462306a36Sopenharmony_ci	int nr_entries = evlist->core.nr_entries;
359562306a36Sopenharmony_ci
359662306a36Sopenharmony_ci	if (nr_entries == 1)
359762306a36Sopenharmony_ci	       return true;
359862306a36Sopenharmony_ci
359962306a36Sopenharmony_ci	if (nr_entries == 2) {
360062306a36Sopenharmony_ci		struct evsel *last = evlist__last(evlist);
360162306a36Sopenharmony_ci
360262306a36Sopenharmony_ci		if (evsel__is_dummy_event(last))
360362306a36Sopenharmony_ci			return true;
360462306a36Sopenharmony_ci	}
360562306a36Sopenharmony_ci
360662306a36Sopenharmony_ci	return false;
360762306a36Sopenharmony_ci}
360862306a36Sopenharmony_ci
360962306a36Sopenharmony_ciint evlist__tui_browse_hists(struct evlist *evlist, const char *help, struct hist_browser_timer *hbt,
361062306a36Sopenharmony_ci			     float min_pcnt, struct perf_env *env, bool warn_lost_event,
361162306a36Sopenharmony_ci			     struct annotation_options *annotation_opts)
361262306a36Sopenharmony_ci{
361362306a36Sopenharmony_ci	int nr_entries = evlist->core.nr_entries;
361462306a36Sopenharmony_ci
361562306a36Sopenharmony_ci	if (evlist__single_entry(evlist)) {
361662306a36Sopenharmony_cisingle_entry: {
361762306a36Sopenharmony_ci		struct evsel *first = evlist__first(evlist);
361862306a36Sopenharmony_ci
361962306a36Sopenharmony_ci		return evsel__hists_browse(first, nr_entries, help, false, hbt, min_pcnt,
362062306a36Sopenharmony_ci					   env, warn_lost_event, annotation_opts);
362162306a36Sopenharmony_ci	}
362262306a36Sopenharmony_ci	}
362362306a36Sopenharmony_ci
362462306a36Sopenharmony_ci	if (symbol_conf.event_group) {
362562306a36Sopenharmony_ci		struct evsel *pos;
362662306a36Sopenharmony_ci
362762306a36Sopenharmony_ci		nr_entries = 0;
362862306a36Sopenharmony_ci		evlist__for_each_entry(evlist, pos) {
362962306a36Sopenharmony_ci			if (evsel__is_group_leader(pos))
363062306a36Sopenharmony_ci				nr_entries++;
363162306a36Sopenharmony_ci		}
363262306a36Sopenharmony_ci
363362306a36Sopenharmony_ci		if (nr_entries == 1)
363462306a36Sopenharmony_ci			goto single_entry;
363562306a36Sopenharmony_ci	}
363662306a36Sopenharmony_ci
363762306a36Sopenharmony_ci	return __evlist__tui_browse_hists(evlist, nr_entries, help, hbt, min_pcnt, env,
363862306a36Sopenharmony_ci					  warn_lost_event, annotation_opts);
363962306a36Sopenharmony_ci}
364062306a36Sopenharmony_ci
364162306a36Sopenharmony_cistatic int block_hists_browser__title(struct hist_browser *browser, char *bf,
364262306a36Sopenharmony_ci				      size_t size)
364362306a36Sopenharmony_ci{
364462306a36Sopenharmony_ci	struct hists *hists = evsel__hists(browser->block_evsel);
364562306a36Sopenharmony_ci	const char *evname = evsel__name(browser->block_evsel);
364662306a36Sopenharmony_ci	unsigned long nr_samples = hists->stats.nr_samples;
364762306a36Sopenharmony_ci	int ret;
364862306a36Sopenharmony_ci
364962306a36Sopenharmony_ci	ret = scnprintf(bf, size, "# Samples: %lu", nr_samples);
365062306a36Sopenharmony_ci	if (evname)
365162306a36Sopenharmony_ci		scnprintf(bf + ret, size -  ret, " of event '%s'", evname);
365262306a36Sopenharmony_ci
365362306a36Sopenharmony_ci	return 0;
365462306a36Sopenharmony_ci}
365562306a36Sopenharmony_ci
365662306a36Sopenharmony_ciint block_hists_tui_browse(struct block_hist *bh, struct evsel *evsel,
365762306a36Sopenharmony_ci			   float min_percent, struct perf_env *env,
365862306a36Sopenharmony_ci			   struct annotation_options *annotation_opts)
365962306a36Sopenharmony_ci{
366062306a36Sopenharmony_ci	struct hists *hists = &bh->block_hists;
366162306a36Sopenharmony_ci	struct hist_browser *browser;
366262306a36Sopenharmony_ci	int key = -1;
366362306a36Sopenharmony_ci	struct popup_action action;
366462306a36Sopenharmony_ci	static const char help[] =
366562306a36Sopenharmony_ci	" q             Quit \n";
366662306a36Sopenharmony_ci
366762306a36Sopenharmony_ci	browser = hist_browser__new(hists);
366862306a36Sopenharmony_ci	if (!browser)
366962306a36Sopenharmony_ci		return -1;
367062306a36Sopenharmony_ci
367162306a36Sopenharmony_ci	browser->block_evsel = evsel;
367262306a36Sopenharmony_ci	browser->title = block_hists_browser__title;
367362306a36Sopenharmony_ci	browser->min_pcnt = min_percent;
367462306a36Sopenharmony_ci	browser->env = env;
367562306a36Sopenharmony_ci	browser->annotation_opts = annotation_opts;
367662306a36Sopenharmony_ci
367762306a36Sopenharmony_ci	/* reset abort key so that it can get Ctrl-C as a key */
367862306a36Sopenharmony_ci	SLang_reset_tty();
367962306a36Sopenharmony_ci	SLang_init_tty(0, 0, 0);
368062306a36Sopenharmony_ci
368162306a36Sopenharmony_ci	memset(&action, 0, sizeof(action));
368262306a36Sopenharmony_ci
368362306a36Sopenharmony_ci	while (1) {
368462306a36Sopenharmony_ci		key = hist_browser__run(browser, "? - help", true, 0);
368562306a36Sopenharmony_ci
368662306a36Sopenharmony_ci		switch (key) {
368762306a36Sopenharmony_ci		case 'q':
368862306a36Sopenharmony_ci			goto out;
368962306a36Sopenharmony_ci		case '?':
369062306a36Sopenharmony_ci			ui_browser__help_window(&browser->b, help);
369162306a36Sopenharmony_ci			break;
369262306a36Sopenharmony_ci		case 'a':
369362306a36Sopenharmony_ci		case K_ENTER:
369462306a36Sopenharmony_ci			if (!browser->selection ||
369562306a36Sopenharmony_ci			    !browser->selection->sym) {
369662306a36Sopenharmony_ci				continue;
369762306a36Sopenharmony_ci			}
369862306a36Sopenharmony_ci
369962306a36Sopenharmony_ci			action.ms.map = browser->selection->map;
370062306a36Sopenharmony_ci			action.ms.sym = browser->selection->sym;
370162306a36Sopenharmony_ci			do_annotate(browser, &action);
370262306a36Sopenharmony_ci			continue;
370362306a36Sopenharmony_ci		default:
370462306a36Sopenharmony_ci			break;
370562306a36Sopenharmony_ci		}
370662306a36Sopenharmony_ci	}
370762306a36Sopenharmony_ci
370862306a36Sopenharmony_ciout:
370962306a36Sopenharmony_ci	hist_browser__delete(browser);
371062306a36Sopenharmony_ci	return 0;
371162306a36Sopenharmony_ci}
3712