File Index
Files and directories
Sign in
FILE

hists.c

File preview with syntax highlighting.

linux-6.19.14/tools/perf/ui/browsers/hists.c C
   1// SPDX-License-Identifier: GPL-2.0
   2#include <dirent.h>
   3#include <errno.h>
   4#include <inttypes.h>
   5#include <stdio.h>
   6#include <stdlib.h>
   7#include <string.h>
   8#include <linux/rbtree.h>
   9#include <linux/string.h>
  10#include <sys/ttydefaults.h>
  11#include <linux/time64.h>
  12#include <linux/zalloc.h>
  13
  14#include "../../util/debug.h"
  15#include "../../util/dso.h"
  16#include "../../util/callchain.h"
  17#include "../../util/evsel.h"
  18#include "../../util/evlist.h"
  19#include "../../util/header.h"
  20#include "../../util/hist.h"
  21#include "../../util/machine.h"
  22#include "../../util/map.h"
  23#include "../../util/maps.h"
  24#include "../../util/symbol.h"
  25#include "../../util/map_symbol.h"
  26#include "../../util/branch.h"
  27#include "../../util/pstack.h"
  28#include "../../util/sort.h"
  29#include "../../util/top.h"
  30#include "../../util/thread.h"
  31#include "../../util/block-info.h"
  32#include "../../util/util.h"
  33#include "../../arch/common.h"
  34
  35#include "../browsers/hists.h"
  36#include "../helpline.h"
  37#include "../util.h"
  38#include "../ui.h"
  39#include "map.h"
  40#include "annotate.h"
  41#include "annotate-data.h"
  42#include "srcline.h"
  43#include "string2.h"
  44#include "units.h"
  45#include "time-utils.h"
  46
  47#include <linux/ctype.h>
  48
  49extern void hist_browser__init_hpp(void);
  50
  51static int hists_browser__scnprintf_title(struct hist_browser *browser, char *bf, size_t size);
  52static void hist_browser__update_nr_entries(struct hist_browser *hb);
  53
  54static struct rb_node *hists__filter_entries(struct rb_node *nd,
  55					     float min_pcnt);
  56
  57static bool hist_browser__has_filter(struct hist_browser *hb)
  58{
  59	return hists__has_filter(hb->hists) || hb->min_pcnt || symbol_conf.has_filter || hb->c2c_filter;
  60}
  61
  62static int hist_browser__get_folding(struct hist_browser *browser)
  63{
  64	struct rb_node *nd;
  65	struct hists *hists = browser->hists;
  66	int unfolded_rows = 0;
  67
  68	for (nd = rb_first_cached(&hists->entries);
  69	     (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
  70	     nd = rb_hierarchy_next(nd)) {
  71		struct hist_entry *he =
  72			rb_entry(nd, struct hist_entry, rb_node);
  73
  74		if (he->leaf && he->unfolded)
  75			unfolded_rows += he->nr_rows;
  76	}
  77	return unfolded_rows;
  78}
  79
  80static void hist_browser__set_title_space(struct hist_browser *hb)
  81{
  82	struct ui_browser *browser = &hb->b;
  83	struct hists *hists = hb->hists;
  84	struct perf_hpp_list *hpp_list = hists->hpp_list;
  85
  86	browser->extra_title_lines = hb->show_headers ? hpp_list->nr_header_lines : 0;
  87}
  88
  89static u32 hist_browser__nr_entries(struct hist_browser *hb)
  90{
  91	u32 nr_entries;
  92
  93	if (symbol_conf.report_hierarchy)
  94		nr_entries = hb->nr_hierarchy_entries;
  95	else if (hist_browser__has_filter(hb))
  96		nr_entries = hb->nr_non_filtered_entries;
  97	else
  98		nr_entries = hb->hists->nr_entries;
  99
 100	hb->nr_callchain_rows = hist_browser__get_folding(hb);
 101	return nr_entries + hb->nr_callchain_rows;
 102}
 103
 104static void hist_browser__update_rows(struct hist_browser *hb)
 105{
 106	struct ui_browser *browser = &hb->b;
 107	struct hists *hists = hb->hists;
 108	struct perf_hpp_list *hpp_list = hists->hpp_list;
 109	u16 index_row;
 110
 111	if (!hb->show_headers) {
 112		browser->rows += browser->extra_title_lines;
 113		browser->extra_title_lines = 0;
 114		return;
 115	}
 116
 117	browser->extra_title_lines = hpp_list->nr_header_lines;
 118	browser->rows -= browser->extra_title_lines;
 119	/*
 120	 * Verify if we were at the last line and that line isn't
 121	 * visible because we now show the header line(s).
 122	 */
 123	index_row = browser->index - browser->top_idx;
 124	if (index_row >= browser->rows)
 125		browser->index -= index_row - browser->rows + 1;
 126}
 127
 128static void hist_browser__refresh_dimensions(struct ui_browser *browser)
 129{
 130	struct hist_browser *hb = container_of(browser, struct hist_browser, b);
 131
 132	/* 3 == +/- toggle symbol before actual hist_entry rendering */
 133	browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
 134	/*
 135 	 * FIXME: Just keeping existing behaviour, but this really should be
 136 	 *	  before updating browser->width, as it will invalidate the
 137 	 *	  calculation above. Fix this and the fallout in another
 138 	 *	  changeset.
 139 	 */
 140	ui_browser__refresh_dimensions(browser);
 141}
 142
 143static void hist_browser__reset(struct hist_browser *browser)
 144{
 145	/*
 146	 * The hists__remove_entry_filter() already folds non-filtered
 147	 * entries so we can assume it has 0 callchain rows.
 148	 */
 149	browser->nr_callchain_rows = 0;
 150
 151	hist_browser__update_nr_entries(browser);
 152	browser->b.nr_entries = hist_browser__nr_entries(browser);
 153	hist_browser__refresh_dimensions(&browser->b);
 154	ui_browser__reset_index(&browser->b);
 155}
 156
 157static char tree__folded_sign(bool unfolded)
 158{
 159	return unfolded ? '-' : '+';
 160}
 161
 162static char hist_entry__folded(const struct hist_entry *he)
 163{
 164	return he->has_children ? tree__folded_sign(he->unfolded) : ' ';
 165}
 166
 167static char callchain_list__folded(const struct callchain_list *cl)
 168{
 169	return cl->has_children ? tree__folded_sign(cl->unfolded) : ' ';
 170}
 171
 172static void callchain_list__set_folding(struct callchain_list *cl, bool unfold)
 173{
 174	cl->unfolded = unfold ? cl->has_children : false;
 175}
 176
 177static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
 178{
 179	int n = 0;
 180	struct rb_node *nd;
 181
 182	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
 183		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
 184		struct callchain_list *chain;
 185		char folded_sign = ' '; /* No children */
 186
 187		list_for_each_entry(chain, &child->val, list) {
 188			++n;
 189
 190			/* We need this because we may not have children */
 191			folded_sign = callchain_list__folded(chain);
 192			if (folded_sign == '+')
 193				break;
 194		}
 195
 196		if (folded_sign == '-') /* Have children and they're unfolded */
 197			n += callchain_node__count_rows_rb_tree(child);
 198	}
 199
 200	return n;
 201}
 202
 203static int callchain_node__count_flat_rows(struct callchain_node *node)
 204{
 205	struct callchain_list *chain;
 206	char folded_sign = 0;
 207	int n = 0;
 208
 209	list_for_each_entry(chain, &node->parent_val, list) {
 210		if (!folded_sign) {
 211			/* only check first chain list entry */
 212			folded_sign = callchain_list__folded(chain);
 213			if (folded_sign == '+')
 214				return 1;
 215		}
 216		n++;
 217	}
 218
 219	list_for_each_entry(chain, &node->val, list) {
 220		if (!folded_sign) {
 221			/* node->parent_val list might be empty */
 222			folded_sign = callchain_list__folded(chain);
 223			if (folded_sign == '+')
 224				return 1;
 225		}
 226		n++;
 227	}
 228
 229	return n;
 230}
 231
 232static int callchain_node__count_folded_rows(struct callchain_node *node __maybe_unused)
 233{
 234	return 1;
 235}
 236
 237static int callchain_node__count_rows(struct callchain_node *node)
 238{
 239	struct callchain_list *chain;
 240	bool unfolded = false;
 241	int n = 0;
 242
 243	if (callchain_param.mode == CHAIN_FLAT)
 244		return callchain_node__count_flat_rows(node);
 245	else if (callchain_param.mode == CHAIN_FOLDED)
 246		return callchain_node__count_folded_rows(node);
 247
 248	list_for_each_entry(chain, &node->val, list) {
 249		++n;
 250
 251		unfolded = chain->unfolded;
 252	}
 253
 254	if (unfolded)
 255		n += callchain_node__count_rows_rb_tree(node);
 256
 257	return n;
 258}
 259
 260static int callchain__count_rows(struct rb_root *chain)
 261{
 262	struct rb_node *nd;
 263	int n = 0;
 264
 265	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
 266		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
 267		n += callchain_node__count_rows(node);
 268	}
 269
 270	return n;
 271}
 272
 273static int hierarchy_count_rows(struct hist_browser *hb, struct hist_entry *he,
 274				bool include_children)
 275{
 276	int count = 0;
 277	struct rb_node *node;
 278	struct hist_entry *child;
 279
 280	if (he->leaf)
 281		return callchain__count_rows(&he->sorted_chain);
 282
 283	if (he->has_no_entry)
 284		return 1;
 285
 286	node = rb_first_cached(&he->hroot_out);
 287	while (node) {
 288		float percent;
 289
 290		child = rb_entry(node, struct hist_entry, rb_node);
 291		percent = hist_entry__get_percent_limit(child);
 292
 293		if (!child->filtered && percent >= hb->min_pcnt) {
 294			count++;
 295
 296			if (include_children && child->unfolded)
 297				count += hierarchy_count_rows(hb, child, true);
 298		}
 299
 300		node = rb_next(node);
 301	}
 302	return count;
 303}
 304
 305static bool hist_entry__toggle_fold(struct hist_entry *he)
 306{
 307	if (!he)
 308		return false;
 309
 310	if (!he->has_children)
 311		return false;
 312
 313	he->unfolded = !he->unfolded;
 314	return true;
 315}
 316
 317static bool callchain_list__toggle_fold(struct callchain_list *cl)
 318{
 319	if (!cl)
 320		return false;
 321
 322	if (!cl->has_children)
 323		return false;
 324
 325	cl->unfolded = !cl->unfolded;
 326	return true;
 327}
 328
 329static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
 330{
 331	struct rb_node *nd = rb_first(&node->rb_root);
 332
 333	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
 334		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
 335		struct callchain_list *chain;
 336		bool first = true;
 337
 338		list_for_each_entry(chain, &child->val, list) {
 339			if (first) {
 340				first = false;
 341				chain->has_children = chain->list.next != &child->val ||
 342							 !RB_EMPTY_ROOT(&child->rb_root);
 343			} else
 344				chain->has_children = chain->list.next == &child->val &&
 345							 !RB_EMPTY_ROOT(&child->rb_root);
 346		}
 347
 348		callchain_node__init_have_children_rb_tree(child);
 349	}
 350}
 351
 352static void callchain_node__init_have_children(struct callchain_node *node,
 353					       bool has_sibling)
 354{
 355	struct callchain_list *chain;
 356
 357	chain = list_entry(node->val.next, struct callchain_list, list);
 358	chain->has_children = has_sibling;
 359
 360	if (!list_empty(&node->val)) {
 361		chain = list_entry(node->val.prev, struct callchain_list, list);
 362		chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);
 363	}
 364
 365	callchain_node__init_have_children_rb_tree(node);
 366}
 367
 368static void callchain__init_have_children(struct rb_root *root)
 369{
 370	struct rb_node *nd = rb_first(root);
 371	bool has_sibling = nd && rb_next(nd);
 372
 373	for (nd = rb_first(root); nd; nd = rb_next(nd)) {
 374		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
 375		callchain_node__init_have_children(node, has_sibling);
 376		if (callchain_param.mode == CHAIN_FLAT ||
 377		    callchain_param.mode == CHAIN_FOLDED)
 378			callchain_node__make_parent_list(node);
 379	}
 380}
 381
 382static void hist_entry__init_have_children(struct hist_entry *he)
 383{
 384	if (he->init_have_children)
 385		return;
 386
 387	if (he->leaf) {
 388		he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
 389		callchain__init_have_children(&he->sorted_chain);
 390	} else {
 391		he->has_children = !RB_EMPTY_ROOT(&he->hroot_out.rb_root);
 392	}
 393
 394	he->init_have_children = true;
 395}
 396
 397static bool hist_browser__selection_has_children(struct hist_browser *browser)
 398{
 399	struct hist_entry *he = browser->he_selection;
 400	struct map_symbol *ms = browser->selection;
 401
 402	if (!he || !ms)
 403		return false;
 404
 405	if (ms == &he->ms)
 406	       return he->has_children;
 407
 408	return container_of(ms, struct callchain_list, ms)->has_children;
 409}
 410
 411static bool hist_browser__selection_unfolded(struct hist_browser *browser)
 412{
 413	struct hist_entry *he = browser->he_selection;
 414	struct map_symbol *ms = browser->selection;
 415
 416	if (!he || !ms)
 417		return false;
 418
 419	if (ms == &he->ms)
 420	       return he->unfolded;
 421
 422	return container_of(ms, struct callchain_list, ms)->unfolded;
 423}
 424
 425static char *hist_browser__selection_sym_name(struct hist_browser *browser, char *bf, size_t size)
 426{
 427	struct hist_entry *he = browser->he_selection;
 428	struct map_symbol *ms = browser->selection;
 429	struct callchain_list *callchain_entry;
 430
 431	if (!he || !ms)
 432		return NULL;
 433
 434	if (ms == &he->ms) {
 435	       hist_entry__sym_snprintf(he, bf, size, 0);
 436	       return bf + 4; // skip the level, e.g. '[k] '
 437	}
 438
 439	callchain_entry = container_of(ms, struct callchain_list, ms);
 440	return callchain_list__sym_name(callchain_entry, bf, size, browser->show_dso);
 441}
 442
 443static bool hist_browser__toggle_fold(struct hist_browser *browser)
 444{
 445	struct hist_entry *he = browser->he_selection;
 446	struct map_symbol *ms = browser->selection;
 447	struct callchain_list *cl = container_of(ms, struct callchain_list, ms);
 448	bool has_children;
 449
 450	if (!he || !ms)
 451		return false;
 452
 453	if (ms == &he->ms)
 454		has_children = hist_entry__toggle_fold(he);
 455	else
 456		has_children = callchain_list__toggle_fold(cl);
 457
 458	if (has_children) {
 459		int child_rows = 0;
 460
 461		hist_entry__init_have_children(he);
 462		browser->b.nr_entries -= he->nr_rows;
 463
 464		if (he->leaf)
 465			browser->nr_callchain_rows -= he->nr_rows;
 466		else
 467			browser->nr_hierarchy_entries -= he->nr_rows;
 468
 469		if (symbol_conf.report_hierarchy)
 470			child_rows = hierarchy_count_rows(browser, he, true);
 471
 472		if (he->unfolded) {
 473			if (he->leaf)
 474				he->nr_rows = callchain__count_rows(
 475						&he->sorted_chain);
 476			else
 477				he->nr_rows = hierarchy_count_rows(browser, he, false);
 478
 479			/* account grand children */
 480			if (symbol_conf.report_hierarchy)
 481				browser->b.nr_entries += child_rows - he->nr_rows;
 482
 483			if (!he->leaf && he->nr_rows == 0) {
 484				he->has_no_entry = true;
 485				he->nr_rows = 1;
 486			}
 487		} else {
 488			if (symbol_conf.report_hierarchy)
 489				browser->b.nr_entries -= child_rows - he->nr_rows;
 490
 491			if (he->has_no_entry)
 492				he->has_no_entry = false;
 493
 494			he->nr_rows = 0;
 495		}
 496
 497		browser->b.nr_entries += he->nr_rows;
 498
 499		if (he->leaf)
 500			browser->nr_callchain_rows += he->nr_rows;
 501		else
 502			browser->nr_hierarchy_entries += he->nr_rows;
 503
 504		return true;
 505	}
 506
 507	/* If it doesn't have children, no toggling performed */
 508	return false;
 509}
 510
 511static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
 512{
 513	int n = 0;
 514	struct rb_node *nd;
 515
 516	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
 517		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
 518		struct callchain_list *chain;
 519		bool has_children = false;
 520
 521		list_for_each_entry(chain, &child->val, list) {
 522			++n;
 523			callchain_list__set_folding(chain, unfold);
 524			has_children = chain->has_children;
 525		}
 526
 527		if (has_children)
 528			n += callchain_node__set_folding_rb_tree(child, unfold);
 529	}
 530
 531	return n;
 532}
 533
 534static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
 535{
 536	struct callchain_list *chain;
 537	bool has_children = false;
 538	int n = 0;
 539
 540	list_for_each_entry(chain, &node->val, list) {
 541		++n;
 542		callchain_list__set_folding(chain, unfold);
 543		has_children = chain->has_children;
 544	}
 545
 546	if (has_children)
 547		n += callchain_node__set_folding_rb_tree(node, unfold);
 548
 549	return n;
 550}
 551
 552static int callchain__set_folding(struct rb_root *chain, bool unfold)
 553{
 554	struct rb_node *nd;
 555	int n = 0;
 556
 557	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
 558		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
 559		n += callchain_node__set_folding(node, unfold);
 560	}
 561
 562	return n;
 563}
 564
 565static int hierarchy_set_folding(struct hist_browser *hb, struct hist_entry *he,
 566				 bool unfold __maybe_unused)
 567{
 568	float percent;
 569	struct rb_node *nd;
 570	struct hist_entry *child;
 571	int n = 0;
 572
 573	for (nd = rb_first_cached(&he->hroot_out); nd; nd = rb_next(nd)) {
 574		child = rb_entry(nd, struct hist_entry, rb_node);
 575		percent = hist_entry__get_percent_limit(child);
 576		if (!child->filtered && percent >= hb->min_pcnt)
 577			n++;
 578	}
 579
 580	return n;
 581}
 582
 583static void hist_entry__set_folding(struct hist_entry *he,
 584				    struct hist_browser *hb, bool unfold)
 585{
 586	hist_entry__init_have_children(he);
 587	he->unfolded = unfold ? he->has_children : false;
 588
 589	if (he->has_children) {
 590		int n;
 591
 592		if (he->leaf)
 593			n = callchain__set_folding(&he->sorted_chain, unfold);
 594		else
 595			n = hierarchy_set_folding(hb, he, unfold);
 596
 597		he->nr_rows = unfold ? n : 0;
 598	} else
 599		he->nr_rows = 0;
 600}
 601
 602static void
 603__hist_browser__set_folding(struct hist_browser *browser, bool unfold)
 604{
 605	struct rb_node *nd;
 606	struct hist_entry *he;
 607	double percent;
 608
 609	nd = rb_first_cached(&browser->hists->entries);
 610	while (nd) {
 611		he = rb_entry(nd, struct hist_entry, rb_node);
 612
 613		/* set folding state even if it's currently folded */
 614		nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
 615
 616		hist_entry__set_folding(he, browser, unfold);
 617
 618		percent = hist_entry__get_percent_limit(he);
 619		if (he->filtered || percent < browser->min_pcnt)
 620			continue;
 621
 622		if (!he->depth || unfold)
 623			browser->nr_hierarchy_entries++;
 624		if (he->leaf)
 625			browser->nr_callchain_rows += he->nr_rows;
 626		else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) {
 627			browser->nr_hierarchy_entries++;
 628			he->has_no_entry = true;
 629			he->nr_rows = 1;
 630		} else
 631			he->has_no_entry = false;
 632	}
 633}
 634
 635static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
 636{
 637	browser->nr_hierarchy_entries = 0;
 638	browser->nr_callchain_rows = 0;
 639	__hist_browser__set_folding(browser, unfold);
 640
 641	browser->b.nr_entries = hist_browser__nr_entries(browser);
 642	/* Go to the start, we may be way after valid entries after a collapse */
 643	ui_browser__reset_index(&browser->b);
 644}
 645
 646static void hist_browser__set_folding_selected(struct hist_browser *browser, bool unfold)
 647{
 648	if (!browser->he_selection)
 649		return;
 650
 651	if (unfold == browser->he_selection->unfolded)
 652		return;
 653
 654	hist_browser__toggle_fold(browser);
 655}
 656
 657static void ui_browser__warn_lost_events(struct ui_browser *browser)
 658{
 659	ui_browser__warning(browser, 4,
 660		"Events are being lost, check IO/CPU overload!\n\n"
 661		"You may want to run 'perf' using a RT scheduler policy:\n\n"
 662		" perf top -r 80\n\n"
 663		"Or reduce the sampling frequency.");
 664}
 665
 666static int hist_browser__title(struct hist_browser *browser, char *bf, size_t size)
 667{
 668	return browser->title ? browser->title(browser, bf, size) : 0;
 669}
 670
 671static int hist_browser__handle_hotkey(struct hist_browser *browser, bool warn_lost_event, char *title, size_t size, int key)
 672{
 673	switch (key) {
 674	case K_TIMER: {
 675		struct hist_browser_timer *hbt = browser->hbt;
 676		struct evsel *evsel = hists_to_evsel(browser->hists);
 677		u64 nr_entries;
 678
 679		WARN_ON_ONCE(!hbt);
 680
 681		if (hbt)
 682			hbt->timer(hbt->arg);
 683
 684		if (hist_browser__has_filter(browser) || symbol_conf.report_hierarchy)
 685			hist_browser__update_nr_entries(browser);
 686
 687		nr_entries = hist_browser__nr_entries(browser);
 688		ui_browser__update_nr_entries(&browser->b, nr_entries);
 689
 690		if (warn_lost_event &&
 691		    (evsel->evlist->stats.nr_lost_warned !=
 692		     evsel->evlist->stats.nr_events[PERF_RECORD_LOST])) {
 693			evsel->evlist->stats.nr_lost_warned =
 694				evsel->evlist->stats.nr_events[PERF_RECORD_LOST];
 695			ui_browser__warn_lost_events(&browser->b);
 696		}
 697
 698		hist_browser__title(browser, title, size);
 699		ui_browser__show_title(&browser->b, title);
 700		break;
 701	}
 702	case 'D': { /* Debug */
 703		struct hist_entry *h = rb_entry(browser->b.top, struct hist_entry, rb_node);
 704		static int seq;
 705
 706		ui_helpline__pop();
 707		ui_helpline__fpush("%d: nr_ent=(%d,%d), etl: %d, rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
 708				   seq++, browser->b.nr_entries, browser->hists->nr_entries,
 709				   browser->b.extra_title_lines, browser->b.rows,
 710				   browser->b.index, browser->b.top_idx, h->row_offset, h->nr_rows);
 711	}
 712		break;
 713	case 'C':
 714		/* Collapse the whole world. */
 715		hist_browser__set_folding(browser, false);
 716		break;
 717	case 'c':
 718		/* Collapse the selected entry. */
 719		hist_browser__set_folding_selected(browser, false);
 720		break;
 721	case 'E':
 722		/* Expand the whole world. */
 723		hist_browser__set_folding(browser, true);
 724		break;
 725	case 'e':
 726		/* Toggle expand/collapse the selected entry. */
 727		hist_browser__toggle_fold(browser);
 728		break;
 729	case 'H':
 730		browser->show_headers = !browser->show_headers;
 731		hist_browser__update_rows(browser);
 732		break;
 733	case '+':
 734		if (hist_browser__toggle_fold(browser))
 735			break;
 736		/* fall thru */
 737	default:
 738		return -1;
 739	}
 740
 741	return 0;
 742}
 743
 744int hist_browser__run(struct hist_browser *browser, const char *help,
 745		      bool warn_lost_event, int key)
 746{
 747	char title[160];
 748	struct hist_browser_timer *hbt = browser->hbt;
 749	int delay_secs = hbt ? hbt->refresh : 0;
 750
 751	browser->b.entries = &browser->hists->entries;
 752	browser->b.nr_entries = hist_browser__nr_entries(browser);
 753
 754	hist_browser__title(browser, title, sizeof(title));
 755
 756	if (ui_browser__show(&browser->b, title, "%s", help) < 0)
 757		return -1;
 758
 759	if (key && hist_browser__handle_hotkey(browser, warn_lost_event, title, sizeof(title), key))
 760		goto out;
 761
 762	while (1) {
 763		key = ui_browser__run(&browser->b, delay_secs);
 764
 765		if (hist_browser__handle_hotkey(browser, warn_lost_event, title, sizeof(title), key))
 766			break;
 767	}
 768out:
 769	ui_browser__hide(&browser->b);
 770	return key;
 771}
 772
 773struct callchain_print_arg {
 774	/* for hists browser */
 775	off_t	row_offset;
 776	bool	is_current_entry;
 777
 778	/* for file dump */
 779	FILE	*fp;
 780	int	printed;
 781};
 782
 783typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
 784					 struct callchain_list *chain,
 785					 const char *str, int offset,
 786					 unsigned short row,
 787					 struct callchain_print_arg *arg);
 788
 789static void hist_browser__show_callchain_entry(struct hist_browser *browser,
 790					       struct callchain_list *chain,
 791					       const char *str, int offset,
 792					       unsigned short row,
 793					       struct callchain_print_arg *arg)
 794{
 795	int color, width;
 796	char folded_sign = callchain_list__folded(chain);
 797	bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
 798
 799	color = HE_COLORSET_NORMAL;
 800	width = browser->b.width - (offset + 2);
 801	if (ui_browser__is_current_entry(&browser->b, row)) {
 802		browser->selection = &chain->ms;
 803		color = HE_COLORSET_SELECTED;
 804		arg->is_current_entry = true;
 805	}
 806
 807	ui_browser__set_color(&browser->b, color);
 808	ui_browser__gotorc(&browser->b, row, 0);
 809	ui_browser__write_nstring(&browser->b, " ", offset);
 810	ui_browser__printf(&browser->b, "%c", folded_sign);
 811	ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
 812	ui_browser__write_nstring(&browser->b, str, width);
 813}
 814
 815static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
 816						  struct callchain_list *chain,
 817						  const char *str, int offset,
 818						  unsigned short row __maybe_unused,
 819						  struct callchain_print_arg *arg)
 820{
 821	char folded_sign = callchain_list__folded(chain);
 822
 823	arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
 824				folded_sign, str);
 825}
 826
 827typedef bool (*check_output_full_fn)(struct hist_browser *browser,
 828				     unsigned short row);
 829
 830static bool hist_browser__check_output_full(struct hist_browser *browser,
 831					    unsigned short row)
 832{
 833	return browser->b.rows == row;
 834}
 835
 836static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
 837					  unsigned short row __maybe_unused)
 838{
 839	return false;
 840}
 841
 842#define LEVEL_OFFSET_STEP 3
 843
 844static int hist_browser__show_callchain_list(struct hist_browser *browser,
 845					     struct callchain_node *node,
 846					     struct callchain_list *chain,
 847					     unsigned short row, u64 total,
 848					     bool need_percent, int offset,
 849					     print_callchain_entry_fn print,
 850					     struct callchain_print_arg *arg)
 851{
 852	char bf[1024], *alloc_str;
 853	char buf[64], *alloc_str2;
 854	const char *str;
 855	int ret = 1;
 856
 857	if (arg->row_offset != 0) {
 858		arg->row_offset--;
 859		return 0;
 860	}
 861
 862	alloc_str = NULL;
 863	alloc_str2 = NULL;
 864
 865	str = callchain_list__sym_name(chain, bf, sizeof(bf),
 866				       browser->show_dso);
 867
 868	if (symbol_conf.show_branchflag_count) {
 869		callchain_list_counts__printf_value(chain, NULL,
 870						    buf, sizeof(buf));
 871
 872		if (asprintf(&alloc_str2, "%s%s", str, buf) < 0)
 873			str = "Not enough memory!";
 874		else
 875			str = alloc_str2;
 876	}
 877
 878	if (need_percent) {
 879		callchain_node__scnprintf_value(node, buf, sizeof(buf),
 880						total);
 881
 882		if (asprintf(&alloc_str, "%s %s", buf, str) < 0)
 883			str = "Not enough memory!";
 884		else
 885			str = alloc_str;
 886	}
 887
 888	print(browser, chain, str, offset, row, arg);
 889	free(alloc_str);
 890	free(alloc_str2);
 891
 892	return ret;
 893}
 894
 895static bool check_percent_display(struct rb_node *node, u64 parent_total)
 896{
 897	struct callchain_node *child;
 898
 899	if (node == NULL)
 900		return false;
 901
 902	if (rb_next(node))
 903		return true;
 904
 905	child = rb_entry(node, struct callchain_node, rb_node);
 906	return callchain_cumul_hits(child) != parent_total;
 907}
 908
 909static int hist_browser__show_callchain_flat(struct hist_browser *browser,
 910					     struct rb_root *root,
 911					     unsigned short row, u64 total,
 912					     u64 parent_total,
 913					     print_callchain_entry_fn print,
 914					     struct callchain_print_arg *arg,
 915					     check_output_full_fn is_output_full)
 916{
 917	struct rb_node *node;
 918	int first_row = row, offset = LEVEL_OFFSET_STEP;
 919	bool need_percent;
 920
 921	node = rb_first(root);
 922	need_percent = check_percent_display(node, parent_total);
 923
 924	while (node) {
 925		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
 926		struct rb_node *next = rb_next(node);
 927		struct callchain_list *chain;
 928		char folded_sign = ' ';
 929		int first = true;
 930		int extra_offset = 0;
 931
 932		list_for_each_entry(chain, &child->parent_val, list) {
 933			bool was_first = first;
 934
 935			if (first)
 936				first = false;
 937			else if (need_percent)
 938				extra_offset = LEVEL_OFFSET_STEP;
 939
 940			folded_sign = callchain_list__folded(chain);
 941
 942			row += hist_browser__show_callchain_list(browser, child,
 943							chain, row, total,
 944							was_first && need_percent,
 945							offset + extra_offset,
 946							print, arg);
 947
 948			if (is_output_full(browser, row))
 949				goto out;
 950
 951			if (folded_sign == '+')
 952				goto next;
 953		}
 954
 955		list_for_each_entry(chain, &child->val, list) {
 956			bool was_first = first;
 957
 958			if (first)
 959				first = false;
 960			else if (need_percent)
 961				extra_offset = LEVEL_OFFSET_STEP;
 962
 963			folded_sign = callchain_list__folded(chain);
 964
 965			row += hist_browser__show_callchain_list(browser, child,
 966							chain, row, total,
 967							was_first && need_percent,
 968							offset + extra_offset,
 969							print, arg);
 970
 971			if (is_output_full(browser, row))
 972				goto out;
 973
 974			if (folded_sign == '+')
 975				break;
 976		}
 977
 978next:
 979		if (is_output_full(browser, row))
 980			break;
 981		node = next;
 982	}
 983out:
 984	return row - first_row;
 985}
 986
 987static char *hist_browser__folded_callchain_str(struct hist_browser *browser,
 988						struct callchain_list *chain,
 989						char *value_str, char *old_str)
 990{
 991	char bf[1024];
 992	const char *str;
 993	char *new;
 994
 995	str = callchain_list__sym_name(chain, bf, sizeof(bf),
 996				       browser->show_dso);
 997	if (old_str) {
 998		if (asprintf(&new, "%s%s%s", old_str,
 999			     symbol_conf.field_sep ?: ";", str) < 0)
1000			new = NULL;
1001	} else {
1002		if (value_str) {
1003			if (asprintf(&new, "%s %s", value_str, str) < 0)
1004				new = NULL;
1005		} else {
1006			if (asprintf(&new, "%s", str) < 0)
1007				new = NULL;
1008		}
1009	}
1010	return new;
1011}
1012
1013static int hist_browser__show_callchain_folded(struct hist_browser *browser,
1014					       struct rb_root *root,
1015					       unsigned short row, u64 total,
1016					       u64 parent_total,
1017					       print_callchain_entry_fn print,
1018					       struct callchain_print_arg *arg,
1019					       check_output_full_fn is_output_full)
1020{
1021	struct rb_node *node;
1022	int first_row = row, offset = LEVEL_OFFSET_STEP;
1023	bool need_percent;
1024
1025	node = rb_first(root);
1026	need_percent = check_percent_display(node, parent_total);
1027
1028	while (node) {
1029		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1030		struct rb_node *next = rb_next(node);
1031		struct callchain_list *chain, *first_chain = NULL;
1032		int first = true;
1033		char *value_str = NULL, *value_str_alloc = NULL;
1034		char *chain_str = NULL, *chain_str_alloc = NULL;
1035
1036		if (arg->row_offset != 0) {
1037			arg->row_offset--;
1038			goto next;
1039		}
1040
1041		if (need_percent) {
1042			char buf[64];
1043
1044			callchain_node__scnprintf_value(child, buf, sizeof(buf), total);
1045			if (asprintf(&value_str, "%s", buf) < 0) {
1046				value_str = (char *)"<...>";
1047				goto do_print;
1048			}
1049			value_str_alloc = value_str;
1050		}
1051
1052		list_for_each_entry(chain, &child->parent_val, list) {
1053			chain_str = hist_browser__folded_callchain_str(browser,
1054						chain, value_str, chain_str);
1055			if (first) {
1056				first = false;
1057				first_chain = chain;
1058			}
1059
1060			if (chain_str == NULL) {
1061				chain_str = (char *)"Not enough memory!";
1062				goto do_print;
1063			}
1064
1065			chain_str_alloc = chain_str;
1066		}
1067
1068		list_for_each_entry(chain, &child->val, list) {
1069			chain_str = hist_browser__folded_callchain_str(browser,
1070						chain, value_str, chain_str);
1071			if (first) {
1072				first = false;
1073				first_chain = chain;
1074			}
1075
1076			if (chain_str == NULL) {
1077				chain_str = (char *)"Not enough memory!";
1078				goto do_print;
1079			}
1080
1081			chain_str_alloc = chain_str;
1082		}
1083
1084do_print:
1085		print(browser, first_chain, chain_str, offset, row++, arg);
1086		free(value_str_alloc);
1087		free(chain_str_alloc);
1088
1089next:
1090		if (is_output_full(browser, row))
1091			break;
1092		node = next;
1093	}
1094
1095	return row - first_row;
1096}
1097
1098static int hist_browser__show_callchain_graph(struct hist_browser *browser,
1099					struct rb_root *root, int level,
1100					unsigned short row, u64 total,
1101					u64 parent_total,
1102					print_callchain_entry_fn print,
1103					struct callchain_print_arg *arg,
1104					check_output_full_fn is_output_full)
1105{
1106	struct rb_node *node;
1107	int first_row = row, offset = level * LEVEL_OFFSET_STEP;
1108	bool need_percent;
1109	u64 percent_total = total;
1110
1111	if (callchain_param.mode == CHAIN_GRAPH_REL)
1112		percent_total = parent_total;
1113
1114	node = rb_first(root);
1115	need_percent = check_percent_display(node, parent_total);
1116
1117	while (node) {
1118		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1119		struct rb_node *next = rb_next(node);
1120		struct callchain_list *chain;
1121		char folded_sign = ' ';
1122		int first = true;
1123		int extra_offset = 0;
1124
1125		list_for_each_entry(chain, &child->val, list) {
1126			bool was_first = first;
1127
1128			if (first)
1129				first = false;
1130			else if (need_percent)
1131				extra_offset = LEVEL_OFFSET_STEP;
1132
1133			folded_sign = callchain_list__folded(chain);
1134
1135			row += hist_browser__show_callchain_list(browser, child,
1136							chain, row, percent_total,
1137							was_first && need_percent,
1138							offset + extra_offset,
1139							print, arg);
1140
1141			if (is_output_full(browser, row))
1142				goto out;
1143
1144			if (folded_sign == '+')
1145				break;
1146		}
1147
1148		if (folded_sign == '-') {
1149			const int new_level = level + (extra_offset ? 2 : 1);
1150
1151			row += hist_browser__show_callchain_graph(browser, &child->rb_root,
1152							    new_level, row, total,
1153							    child->children_hit,
1154							    print, arg, is_output_full);
1155		}
1156		if (is_output_full(browser, row))
1157			break;
1158		node = next;
1159	}
1160out:
1161	return row - first_row;
1162}
1163
1164static int hist_browser__show_callchain(struct hist_browser *browser,
1165					struct hist_entry *entry, int level,
1166					unsigned short row,
1167					print_callchain_entry_fn print,
1168					struct callchain_print_arg *arg,
1169					check_output_full_fn is_output_full)
1170{
1171	u64 total = hists__total_period(entry->hists);
1172	u64 parent_total;
1173	int printed;
1174
1175	if (symbol_conf.cumulate_callchain)
1176		parent_total = entry->stat_acc->period;
1177	else
1178		parent_total = entry->stat.period;
1179
1180	if (callchain_param.mode == CHAIN_FLAT) {
1181		printed = hist_browser__show_callchain_flat(browser,
1182						&entry->sorted_chain, row,
1183						total, parent_total, print, arg,
1184						is_output_full);
1185	} else if (callchain_param.mode == CHAIN_FOLDED) {
1186		printed = hist_browser__show_callchain_folded(browser,
1187						&entry->sorted_chain, row,
1188						total, parent_total, print, arg,
1189						is_output_full);
1190	} else {
1191		printed = hist_browser__show_callchain_graph(browser,
1192						&entry->sorted_chain, level, row,
1193						total, parent_total, print, arg,
1194						is_output_full);
1195	}
1196
1197	if (arg->is_current_entry)
1198		browser->he_selection = entry;
1199
1200	return printed;
1201}
1202
1203struct hpp_arg {
1204	struct ui_browser *b;
1205	char folded_sign;
1206	bool current_entry;
1207};
1208
1209int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
1210{
1211	struct hpp_arg *arg = hpp->ptr;
1212	int ret, len;
1213	va_list args;
1214	double percent;
1215
1216	va_start(args, fmt);
1217	len = va_arg(args, int);
1218	percent = va_arg(args, double);
1219	va_end(args);
1220
1221	ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
1222
1223	ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
1224	ui_browser__printf(arg->b, "%s", hpp->buf);
1225
1226	return ret;
1227}
1228
1229#define __HPP_COLOR_PERCENT_FN(_type, _field, _fmttype)			\
1230static u64 __hpp_get_##_field(struct hist_entry *he)			\
1231{									\
1232	return he->stat._field;						\
1233}									\
1234									\
1235static int								\
1236hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,		\
1237				struct perf_hpp *hpp,			\
1238				struct hist_entry *he)			\
1239{									\
1240	return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%",	\
1241			__hpp__slsmg_color_printf, _fmttype);		\
1242}
1243
1244#define __HPP_COLOR_ACC_PERCENT_FN(_type, _field, _fmttype)		\
1245static u64 __hpp_get_acc_##_field(struct hist_entry *he)		\
1246{									\
1247	return he->stat_acc->_field;					\
1248}									\
1249									\
1250static int								\
1251hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,		\
1252				struct perf_hpp *hpp,			\
1253				struct hist_entry *he)			\
1254{									\
1255	if (!symbol_conf.cumulate_callchain) {				\
1256		struct hpp_arg *arg = hpp->ptr;				\
1257		int len = fmt->user_len ?: fmt->len;			\
1258		int ret = scnprintf(hpp->buf, hpp->size,		\
1259				    "%*s", len, "N/A");			\
1260		ui_browser__printf(arg->b, "%s", hpp->buf);		\
1261									\
1262		return ret;						\
1263	}								\
1264	return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field,		\
1265			" %*.2f%%", __hpp__slsmg_color_printf,		\
1266			_fmttype);					\
1267}
1268
1269#define __HPP_COLOR_MEM_STAT_FN(_name, _type)				\
1270static int								\
1271hist_browser__hpp_color_mem_stat_##_name(struct perf_hpp_fmt *fmt,	\
1272					 struct perf_hpp *hpp,		\
1273					 struct hist_entry *he)		\
1274{									\
1275	return hpp__fmt_mem_stat(fmt, hpp, he, PERF_MEM_STAT_##_type,	\
1276				 " %5.1f%%", __hpp__slsmg_color_printf);\
1277}
1278
1279__HPP_COLOR_PERCENT_FN(overhead, period, PERF_HPP_FMT_TYPE__PERCENT)
1280__HPP_COLOR_PERCENT_FN(latency, latency, PERF_HPP_FMT_TYPE__LATENCY)
1281__HPP_COLOR_PERCENT_FN(overhead_sys, period_sys, PERF_HPP_FMT_TYPE__PERCENT)
1282__HPP_COLOR_PERCENT_FN(overhead_us, period_us, PERF_HPP_FMT_TYPE__PERCENT)
1283__HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys, PERF_HPP_FMT_TYPE__PERCENT)
1284__HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us, PERF_HPP_FMT_TYPE__PERCENT)
1285__HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period, PERF_HPP_FMT_TYPE__PERCENT)
1286__HPP_COLOR_ACC_PERCENT_FN(latency_acc, latency, PERF_HPP_FMT_TYPE__LATENCY)
1287__HPP_COLOR_MEM_STAT_FN(op, OP)
1288__HPP_COLOR_MEM_STAT_FN(cache, CACHE)
1289__HPP_COLOR_MEM_STAT_FN(memory, MEMORY)
1290__HPP_COLOR_MEM_STAT_FN(snoop, SNOOP)
1291__HPP_COLOR_MEM_STAT_FN(dtlb, DTLB)
1292
1293#undef __HPP_COLOR_PERCENT_FN
1294#undef __HPP_COLOR_ACC_PERCENT_FN
1295#undef __HPP_COLOR_MEM_STAT_FN
1296
1297void hist_browser__init_hpp(void)
1298{
1299	perf_hpp__format[PERF_HPP__OVERHEAD].color =
1300				hist_browser__hpp_color_overhead;
1301	perf_hpp__format[PERF_HPP__LATENCY].color =
1302				hist_browser__hpp_color_latency;
1303	perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
1304				hist_browser__hpp_color_overhead_sys;
1305	perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
1306				hist_browser__hpp_color_overhead_us;
1307	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
1308				hist_browser__hpp_color_overhead_guest_sys;
1309	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
1310				hist_browser__hpp_color_overhead_guest_us;
1311	perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
1312				hist_browser__hpp_color_overhead_acc;
1313	perf_hpp__format[PERF_HPP__LATENCY_ACC].color =
1314				hist_browser__hpp_color_latency_acc;
1315	perf_hpp__format[PERF_HPP__MEM_STAT_OP].color =
1316				hist_browser__hpp_color_mem_stat_op;
1317	perf_hpp__format[PERF_HPP__MEM_STAT_CACHE].color =
1318				hist_browser__hpp_color_mem_stat_cache;
1319	perf_hpp__format[PERF_HPP__MEM_STAT_MEMORY].color =
1320				hist_browser__hpp_color_mem_stat_memory;
1321	perf_hpp__format[PERF_HPP__MEM_STAT_SNOOP].color =
1322				hist_browser__hpp_color_mem_stat_snoop;
1323	perf_hpp__format[PERF_HPP__MEM_STAT_DTLB].color =
1324				hist_browser__hpp_color_mem_stat_dtlb;
1325
1326	res_sample_init();
1327}
1328
1329static int hist_browser__show_entry(struct hist_browser *browser,
1330				    struct hist_entry *entry,
1331				    unsigned short row)
1332{
1333	int printed = 0;
1334	int width = browser->b.width;
1335	char folded_sign = ' ';
1336	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1337	bool use_callchain = hist_entry__has_callchains(entry) && symbol_conf.use_callchain;
1338	off_t row_offset = entry->row_offset;
1339	bool first = true;
1340	struct perf_hpp_fmt *fmt;
1341
1342	if (current_entry) {
1343		browser->he_selection = entry;
1344		browser->selection = &entry->ms;
1345	}
1346
1347	if (use_callchain) {
1348		hist_entry__init_have_children(entry);
1349		folded_sign = hist_entry__folded(entry);
1350	}
1351
1352	if (row_offset == 0) {
1353		struct hpp_arg arg = {
1354			.b		= &browser->b,
1355			.folded_sign	= folded_sign,
1356			.current_entry	= current_entry,
1357		};
1358		int column = 0;
1359
1360		ui_browser__gotorc(&browser->b, row, 0);
1361
1362		hists__for_each_format(browser->hists, fmt) {
1363			char s[2048];
1364			struct perf_hpp hpp = {
1365				.buf	= s,
1366				.size	= sizeof(s),
1367				.ptr	= &arg,
1368			};
1369
1370			if (perf_hpp__should_skip(fmt, entry->hists) ||
1371			    column++ < browser->b.horiz_scroll)
1372				continue;
1373
1374			if (current_entry && browser->b.navkeypressed) {
1375				ui_browser__set_color(&browser->b,
1376						      HE_COLORSET_SELECTED);
1377			} else {
1378				ui_browser__set_color(&browser->b,
1379						      HE_COLORSET_NORMAL);
1380			}
1381
1382			if (first) {
1383				if (use_callchain) {
1384					ui_browser__printf(&browser->b, "%c ", folded_sign);
1385					width -= 2;
1386				}
1387				first = false;
1388			} else {
1389				ui_browser__printf(&browser->b, "  ");
1390				width -= 2;
1391			}
1392
1393			if (fmt->color) {
1394				int ret = fmt->color(fmt, &hpp, entry);
1395				hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1396				/*
1397				 * fmt->color() already used ui_browser to
1398				 * print the non alignment bits, skip it (+ret):
1399				 */
1400				ui_browser__printf(&browser->b, "%s", s + ret);
1401			} else {
1402				hist_entry__snprintf_alignment(entry, &hpp, fmt, fmt->entry(fmt, &hpp, entry));
1403				ui_browser__printf(&browser->b, "%s", s);
1404			}
1405			width -= hpp.buf - s;
1406		}
1407
1408		/* The scroll bar isn't being used */
1409		if (!browser->b.navkeypressed)
1410			width += 1;
1411
1412		ui_browser__write_nstring(&browser->b, "", width);
1413
1414		++row;
1415		++printed;
1416	} else
1417		--row_offset;
1418
1419	if (folded_sign == '-' && row != browser->b.rows) {
1420		struct callchain_print_arg arg = {
1421			.row_offset = row_offset,
1422			.is_current_entry = current_entry,
1423		};
1424
1425		printed += hist_browser__show_callchain(browser,
1426				entry, 1, row,
1427				hist_browser__show_callchain_entry,
1428				&arg,
1429				hist_browser__check_output_full);
1430	}
1431
1432	return printed;
1433}
1434
1435static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
1436					      struct hist_entry *entry,
1437					      unsigned short row,
1438					      int level)
1439{
1440	int printed = 0;
1441	int width = browser->b.width;
1442	char folded_sign = ' ';
1443	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1444	off_t row_offset = entry->row_offset;
1445	bool first = true;
1446	struct perf_hpp_fmt *fmt;
1447	struct perf_hpp_list_node *fmt_node;
1448	struct hpp_arg arg = {
1449		.b		= &browser->b,
1450		.current_entry	= current_entry,
1451	};
1452	int column = 0;
1453	int hierarchy_indent = (entry->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1454
1455	if (current_entry) {
1456		browser->he_selection = entry;
1457		browser->selection = &entry->ms;
1458	}
1459
1460	hist_entry__init_have_children(entry);
1461	folded_sign = hist_entry__folded(entry);
1462	arg.folded_sign = folded_sign;
1463
1464	if (entry->leaf && row_offset) {
1465		row_offset--;
1466		goto show_callchain;
1467	}
1468
1469	ui_browser__gotorc(&browser->b, row, 0);
1470
1471	if (current_entry && browser->b.navkeypressed)
1472		ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1473	else
1474		ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1475
1476	ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1477	width -= level * HIERARCHY_INDENT;
1478
1479	/* the first hpp_list_node is for overhead columns */
1480	fmt_node = list_first_entry(&entry->hists->hpp_formats,
1481				    struct perf_hpp_list_node, list);
1482	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1483		char s[2048];
1484		struct perf_hpp hpp = {
1485			.buf		= s,
1486			.size		= sizeof(s),
1487			.ptr		= &arg,
1488		};
1489
1490		if (perf_hpp__should_skip(fmt, entry->hists) ||
1491		    column++ < browser->b.horiz_scroll)
1492			continue;
1493
1494		if (current_entry && browser->b.navkeypressed) {
1495			ui_browser__set_color(&browser->b,
1496					      HE_COLORSET_SELECTED);
1497		} else {
1498			ui_browser__set_color(&browser->b,
1499					      HE_COLORSET_NORMAL);
1500		}
1501
1502		if (first) {
1503			ui_browser__printf(&browser->b, "%c ", folded_sign);
1504			width -= 2;
1505			first = false;
1506		} else {
1507			ui_browser__printf(&browser->b, "  ");
1508			width -= 2;
1509		}
1510
1511		if (fmt->color) {
1512			int ret = fmt->color(fmt, &hpp, entry);
1513			hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1514			/*
1515			 * fmt->color() already used ui_browser to
1516			 * print the non alignment bits, skip it (+ret):
1517			 */
1518			ui_browser__printf(&browser->b, "%s", s + ret);
1519		} else {
1520			int ret = fmt->entry(fmt, &hpp, entry);
1521			hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1522			ui_browser__printf(&browser->b, "%s", s);
1523		}
1524		width -= hpp.buf - s;
1525	}
1526
1527	if (!first) {
1528		ui_browser__write_nstring(&browser->b, "", hierarchy_indent);
1529		width -= hierarchy_indent;
1530	}
1531
1532	if (column >= browser->b.horiz_scroll) {
1533		char s[2048];
1534		struct perf_hpp hpp = {
1535			.buf		= s,
1536			.size		= sizeof(s),
1537			.ptr		= &arg,
1538		};
1539
1540		if (current_entry && browser->b.navkeypressed) {
1541			ui_browser__set_color(&browser->b,
1542					      HE_COLORSET_SELECTED);
1543		} else {
1544			ui_browser__set_color(&browser->b,
1545					      HE_COLORSET_NORMAL);
1546		}
1547
1548		perf_hpp_list__for_each_format(entry->hpp_list, fmt) {
1549			if (first) {
1550				ui_browser__printf(&browser->b, "%c ", folded_sign);
1551				first = false;
1552			} else {
1553				ui_browser__write_nstring(&browser->b, "", 2);
1554			}
1555
1556			width -= 2;
1557
1558			/*
1559			 * No need to call hist_entry__snprintf_alignment()
1560			 * since this fmt is always the last column in the
1561			 * hierarchy mode.
1562			 */
1563			if (fmt->color) {
1564				width -= fmt->color(fmt, &hpp, entry);
1565			} else {
1566				int i = 0;
1567
1568				width -= fmt->entry(fmt, &hpp, entry);
1569				ui_browser__printf(&browser->b, "%s", skip_spaces(s));
1570
1571				while (isspace(s[i++]))
1572					width++;
1573			}
1574		}
1575	}
1576
1577	/* The scroll bar isn't being used */
1578	if (!browser->b.navkeypressed)
1579		width += 1;
1580
1581	ui_browser__write_nstring(&browser->b, "", width);
1582
1583	++row;
1584	++printed;
1585
1586show_callchain:
1587	if (entry->leaf && folded_sign == '-' && row != browser->b.rows) {
1588		struct callchain_print_arg carg = {
1589			.row_offset = row_offset,
1590		};
1591
1592		printed += hist_browser__show_callchain(browser, entry,
1593					level + 1, row,
1594					hist_browser__show_callchain_entry, &carg,
1595					hist_browser__check_output_full);
1596	}
1597
1598	return printed;
1599}
1600
1601static int hist_browser__show_no_entry(struct hist_browser *browser,
1602				       unsigned short row, int level)
1603{
1604	int width = browser->b.width;
1605	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1606	bool first = true;
1607	int column = 0;
1608	int ret;
1609	struct perf_hpp_fmt *fmt;
1610	struct perf_hpp_list_node *fmt_node;
1611	int indent = browser->hists->nr_hpp_node - 2;
1612
1613	if (current_entry) {
1614		browser->he_selection = NULL;
1615		browser->selection = NULL;
1616	}
1617
1618	ui_browser__gotorc(&browser->b, row, 0);
1619
1620	if (current_entry && browser->b.navkeypressed)
1621		ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1622	else
1623		ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1624
1625	ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1626	width -= level * HIERARCHY_INDENT;
1627
1628	/* the first hpp_list_node is for overhead columns */
1629	fmt_node = list_first_entry(&browser->hists->hpp_formats,
1630				    struct perf_hpp_list_node, list);
1631	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1632		if (perf_hpp__should_skip(fmt, browser->hists) ||
1633		    column++ < browser->b.horiz_scroll)
1634			continue;
1635
1636		ret = fmt->width(fmt, NULL, browser->hists);
1637
1638		if (first) {
1639			/* for folded sign */
1640			first = false;
1641			ret++;
1642		} else {
1643			/* space between columns */
1644			ret += 2;
1645		}
1646
1647		ui_browser__write_nstring(&browser->b, "", ret);
1648		width -= ret;
1649	}
1650
1651	ui_browser__write_nstring(&browser->b, "", indent * HIERARCHY_INDENT);
1652	width -= indent * HIERARCHY_INDENT;
1653
1654	if (column >= browser->b.horiz_scroll) {
1655		char buf[32];
1656
1657		ret = snprintf(buf, sizeof(buf), "no entry >= %.2f%%", browser->min_pcnt);
1658		ui_browser__printf(&browser->b, "  %s", buf);
1659		width -= ret + 2;
1660	}
1661
1662	/* The scroll bar isn't being used */
1663	if (!browser->b.navkeypressed)
1664		width += 1;
1665
1666	ui_browser__write_nstring(&browser->b, "", width);
1667	return 1;
1668}
1669
1670static int advance_hpp_check(struct perf_hpp *hpp, int inc)
1671{
1672	advance_hpp(hpp, inc);
1673	return hpp->size <= 0;
1674}
1675
1676static int
1677hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf,
1678				 size_t size, int line)
1679{
1680	struct hists *hists = browser->hists;
1681	struct perf_hpp dummy_hpp = {
1682		.buf    = buf,
1683		.size   = size,
1684	};
1685	struct perf_hpp_fmt *fmt;
1686	size_t ret = 0;
1687	int column = 0;
1688	int span = 0;
1689
1690	if (hists__has_callchains(hists) && symbol_conf.use_callchain) {
1691		ret = scnprintf(buf, size, "  ");
1692		if (advance_hpp_check(&dummy_hpp, ret))
1693			return ret;
1694	}
1695
1696	hists__for_each_format(browser->hists, fmt) {
1697		if (perf_hpp__should_skip(fmt, hists)  || column++ < browser->b.horiz_scroll)
1698			continue;
1699
1700		ret = fmt->header(fmt, &dummy_hpp, hists, line, &span);
1701		if (advance_hpp_check(&dummy_hpp, ret))
1702			break;
1703
1704		if (span)
1705			continue;
1706
1707		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
1708		if (advance_hpp_check(&dummy_hpp, ret))
1709			break;
1710	}
1711
1712	return ret;
1713}
1714
1715static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser,
1716						      char *buf, size_t size, int line)
1717{
1718	struct hists *hists = browser->hists;
1719	struct perf_hpp dummy_hpp = {
1720		.buf    = buf,
1721		.size   = size,
1722	};
1723	struct perf_hpp_fmt *fmt;
1724	struct perf_hpp_list_node *fmt_node;
1725	size_t ret = 0;
1726	int column = 0;
1727	int indent = hists->nr_hpp_node - 2;
1728	bool first_node, first_col;
1729
1730	ret = scnprintf(buf, size, "  ");
1731	if (advance_hpp_check(&dummy_hpp, ret))
1732		return ret;
1733
1734	first_node = true;
1735	/* the first hpp_list_node is for overhead columns */
1736	fmt_node = list_first_entry(&hists->hpp_formats,
1737				    struct perf_hpp_list_node, list);
1738	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1739		if (column++ < browser->b.horiz_scroll)
1740			continue;
1741
1742		ret = fmt->header(fmt, &dummy_hpp, hists, line, NULL);
1743		if (advance_hpp_check(&dummy_hpp, ret))
1744			break;
1745
1746		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
1747		if (advance_hpp_check(&dummy_hpp, ret))
1748			break;
1749
1750		first_node = false;
1751	}
1752
1753	if (line < hists->hpp_list->nr_header_lines - 1)
1754		return ret;
1755
1756	if (!first_node) {
1757		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s",
1758				indent * HIERARCHY_INDENT, "");
1759		if (advance_hpp_check(&dummy_hpp, ret))
1760			return ret;
1761	}
1762
1763	first_node = true;
1764	list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
1765		if (!first_node) {
1766			ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " / ");
1767			if (advance_hpp_check(&dummy_hpp, ret))
1768				break;
1769		}
1770		first_node = false;
1771
1772		first_col = true;
1773		perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1774			char *start;
1775
1776			if (perf_hpp__should_skip(fmt, hists))
1777				continue;
1778
1779			if (!first_col) {
1780				ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "+");
1781				if (advance_hpp_check(&dummy_hpp, ret))
1782					break;
1783			}
1784			first_col = false;
1785
1786			ret = fmt->header(fmt, &dummy_hpp, hists, line, NULL);
1787			dummy_hpp.buf[ret] = '\0';
1788
1789			start = strim(dummy_hpp.buf);
1790			ret = strlen(start);
1791
1792			if (start != dummy_hpp.buf)
1793				memmove(dummy_hpp.buf, start, ret + 1);
1794
1795			if (advance_hpp_check(&dummy_hpp, ret))
1796				break;
1797		}
1798	}
1799
1800	return ret;
1801}
1802
1803static void hists_browser__hierarchy_headers(struct hist_browser *browser)
1804{
1805	struct perf_hpp_list *hpp_list = browser->hists->hpp_list;
1806	char headers[1024];
1807	int line;
1808
1809	for (line = 0; line < hpp_list->nr_header_lines; line++) {
1810		hists_browser__scnprintf_hierarchy_headers(browser, headers,
1811							   sizeof(headers), line);
1812
1813		ui_browser__gotorc_title(&browser->b, line, 0);
1814		ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1815		ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1816	}
1817}
1818
1819static void hists_browser__headers(struct hist_browser *browser)
1820{
1821	struct hists *hists = browser->hists;
1822	struct perf_hpp_list *hpp_list = hists->hpp_list;
1823
1824	int line;
1825
1826	for (line = 0; line < hpp_list->nr_header_lines; line++) {
1827		char headers[1024];
1828
1829		hists_browser__scnprintf_headers(browser, headers,
1830						 sizeof(headers), line);
1831
1832		ui_browser__gotorc_title(&browser->b, line, 0);
1833		ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1834		ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1835	}
1836}
1837
1838static void hist_browser__show_headers(struct hist_browser *browser)
1839{
1840	if (symbol_conf.report_hierarchy)
1841		hists_browser__hierarchy_headers(browser);
1842	else
1843		hists_browser__headers(browser);
1844}
1845
1846static void ui_browser__hists_init_top(struct ui_browser *browser)
1847{
1848	if (browser->top == NULL) {
1849		struct hist_browser *hb;
1850
1851		hb = container_of(browser, struct hist_browser, b);
1852		browser->top = rb_first_cached(&hb->hists->entries);
1853	}
1854}
1855
1856static unsigned int hist_browser__refresh(struct ui_browser *browser)
1857{
1858	unsigned row = 0;
1859	struct rb_node *nd;
1860	struct hist_browser *hb = container_of(browser, struct hist_browser, b);
1861
1862	if (hb->show_headers)
1863		hist_browser__show_headers(hb);
1864
1865	ui_browser__hists_init_top(browser);
1866	hb->he_selection = NULL;
1867	hb->selection = NULL;
1868
1869	for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) {
1870		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1871		float percent;
1872
1873		if (h->filtered) {
1874			/* let it move to sibling */
1875			h->unfolded = false;
1876			continue;
1877		}
1878
1879		if (symbol_conf.report_individual_block)
1880			percent = block_info__total_cycles_percent(h);
1881		else
1882			percent = hist_entry__get_percent_limit(h);
1883
1884		if (percent < hb->min_pcnt)
1885			continue;
1886
1887		if (symbol_conf.report_hierarchy) {
1888			row += hist_browser__show_hierarchy_entry(hb, h, row,
1889								  h->depth);
1890			if (row == browser->rows)
1891				break;
1892
1893			if (h->has_no_entry) {
1894				hist_browser__show_no_entry(hb, row, h->depth + 1);
1895				row++;
1896			}
1897		} else {
1898			row += hist_browser__show_entry(hb, h, row);
1899		}
1900
1901		if (row == browser->rows)
1902			break;
1903	}
1904
1905	return row;
1906}
1907
1908static struct rb_node *hists__filter_entries(struct rb_node *nd,
1909					     float min_pcnt)
1910{
1911	while (nd != NULL) {
1912		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1913		float percent = hist_entry__get_percent_limit(h);
1914
1915		if (!h->filtered && percent >= min_pcnt)
1916			return nd;
1917
1918		/*
1919		 * If it's filtered, its all children also were filtered.
1920		 * So move to sibling node.
1921		 */
1922		if (rb_next(nd))
1923			nd = rb_next(nd);
1924		else
1925			nd = rb_hierarchy_next(nd);
1926	}
1927
1928	return NULL;
1929}
1930
1931static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
1932						  float min_pcnt)
1933{
1934	while (nd != NULL) {
1935		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1936		float percent = hist_entry__get_percent_limit(h);
1937
1938		if (!h->filtered && percent >= min_pcnt)
1939			return nd;
1940
1941		nd = rb_hierarchy_prev(nd);
1942	}
1943
1944	return NULL;
1945}
1946
1947static void ui_browser__hists_seek(struct ui_browser *browser,
1948				   off_t offset, int whence)
1949{
1950	struct hist_entry *h;
1951	struct rb_node *nd;
1952	bool first = true;
1953	struct hist_browser *hb;
1954
1955	hb = container_of(browser, struct hist_browser, b);
1956
1957	if (browser->nr_entries == 0)
1958		return;
1959
1960	ui_browser__hists_init_top(browser);
1961
1962	switch (whence) {
1963	case SEEK_SET:
1964		nd = hists__filter_entries(rb_first(browser->entries),
1965					   hb->min_pcnt);
1966		break;
1967	case SEEK_CUR:
1968		nd = browser->top;
1969		goto do_offset;
1970	case SEEK_END:
1971		nd = rb_hierarchy_last(rb_last(browser->entries));
1972		nd = hists__filter_prev_entries(nd, hb->min_pcnt);
1973		first = false;
1974		break;
1975	default:
1976		return;
1977	}
1978
1979	/*
1980	 * Moves not relative to the first visible entry invalidates its
1981	 * row_offset:
1982	 */
1983	h = rb_entry(browser->top, struct hist_entry, rb_node);
1984	h->row_offset = 0;
1985
1986	/*
1987	 * Here we have to check if nd is expanded (+), if it is we can't go
1988	 * the next top level hist_entry, instead we must compute an offset of
1989	 * what _not_ to show and not change the first visible entry.
1990	 *
1991	 * This offset increments when we are going from top to bottom and
1992	 * decreases when we're going from bottom to top.
1993	 *
1994	 * As we don't have backpointers to the top level in the callchains
1995	 * structure, we need to always print the whole hist_entry callchain,
1996	 * skipping the first ones that are before the first visible entry
1997	 * and stop when we printed enough lines to fill the screen.
1998	 */
1999do_offset:
2000	if (!nd)
2001		return;
2002
2003	if (offset > 0) {
2004		do {
2005			h = rb_entry(nd, struct hist_entry, rb_node);
2006			if (h->unfolded && h->leaf) {
2007				u16 remaining = h->nr_rows - h->row_offset;
2008				if (offset > remaining) {
2009					offset -= remaining;
2010					h->row_offset = 0;
2011				} else {
2012					h->row_offset += offset;
2013					offset = 0;
2014					browser->top = nd;
2015					break;
2016				}
2017			}
2018			nd = hists__filter_entries(rb_hierarchy_next(nd),
2019						   hb->min_pcnt);
2020			if (nd == NULL)
2021				break;
2022			--offset;
2023			browser->top = nd;
2024		} while (offset != 0);
2025	} else if (offset < 0) {
2026		while (1) {
2027			h = rb_entry(nd, struct hist_entry, rb_node);
2028			if (h->unfolded && h->leaf) {
2029				if (first) {
2030					if (-offset > h->row_offset) {
2031						offset += h->row_offset;
2032						h->row_offset = 0;
2033					} else {
2034						h->row_offset += offset;
2035						offset = 0;
2036						browser->top = nd;
2037						break;
2038					}
2039				} else {
2040					if (-offset > h->nr_rows) {
2041						offset += h->nr_rows;
2042						h->row_offset = 0;
2043					} else {
2044						h->row_offset = h->nr_rows + offset;
2045						offset = 0;
2046						browser->top = nd;
2047						break;
2048					}
2049				}
2050			}
2051
2052			nd = hists__filter_prev_entries(rb_hierarchy_prev(nd),
2053							hb->min_pcnt);
2054			if (nd == NULL)
2055				break;
2056			++offset;
2057			browser->top = nd;
2058			if (offset == 0) {
2059				/*
2060				 * Last unfiltered hist_entry, check if it is
2061				 * unfolded, if it is then we should have
2062				 * row_offset at its last entry.
2063				 */
2064				h = rb_entry(nd, struct hist_entry, rb_node);
2065				if (h->unfolded && h->leaf)
2066					h->row_offset = h->nr_rows;
2067				break;
2068			}
2069			first = false;
2070		}
2071	} else {
2072		browser->top = nd;
2073		h = rb_entry(nd, struct hist_entry, rb_node);
2074		h->row_offset = 0;
2075	}
2076}
2077
2078static int hist_browser__fprintf_callchain(struct hist_browser *browser,
2079					   struct hist_entry *he, FILE *fp,
2080					   int level)
2081{
2082	struct callchain_print_arg arg  = {
2083		.fp = fp,
2084	};
2085
2086	hist_browser__show_callchain(browser, he, level, 0,
2087				     hist_browser__fprintf_callchain_entry, &arg,
2088				     hist_browser__check_dump_full);
2089	return arg.printed;
2090}
2091
2092static int hist_browser__fprintf_entry(struct hist_browser *browser,
2093				       struct hist_entry *he, FILE *fp)
2094{
2095	char s[8192];
2096	int printed = 0;
2097	char folded_sign = ' ';
2098	struct perf_hpp hpp = {
2099		.buf = s,
2100		.size = sizeof(s),
2101	};
2102	struct perf_hpp_fmt *fmt;
2103	bool first = true;
2104	int ret;
2105
2106	if (hist_entry__has_callchains(he) && symbol_conf.use_callchain) {
2107		folded_sign = hist_entry__folded(he);
2108		printed += fprintf(fp, "%c ", folded_sign);
2109	}
2110
2111	hists__for_each_format(browser->hists, fmt) {
2112		if (perf_hpp__should_skip(fmt, he->hists))
2113			continue;
2114
2115		if (!first) {
2116			ret = scnprintf(hpp.buf, hpp.size, "  ");
2117			advance_hpp(&hpp, ret);
2118		} else
2119			first = false;
2120
2121		ret = fmt->entry(fmt, &hpp, he);
2122		ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret);
2123		advance_hpp(&hpp, ret);
2124	}
2125	printed += fprintf(fp, "%s\n", s);
2126
2127	if (folded_sign == '-')
2128		printed += hist_browser__fprintf_callchain(browser, he, fp, 1);
2129
2130	return printed;
2131}
2132
2133
2134static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
2135						 struct hist_entry *he,
2136						 FILE *fp, int level)
2137{
2138	char s[8192];
2139	int printed = 0;
2140	char folded_sign = ' ';
2141	struct perf_hpp hpp = {
2142		.buf = s,
2143		.size = sizeof(s),
2144	};
2145	struct perf_hpp_fmt *fmt;
2146	struct perf_hpp_list_node *fmt_node;
2147	bool first = true;
2148	int ret;
2149	int hierarchy_indent = (he->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
2150
2151	printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, "");
2152
2153	folded_sign = hist_entry__folded(he);
2154	printed += fprintf(fp, "%c", folded_sign);
2155
2156	/* the first hpp_list_node is for overhead columns */
2157	fmt_node = list_first_entry(&he->hists->hpp_formats,
2158				    struct perf_hpp_list_node, list);
2159	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
2160		if (!first) {
2161			ret = scnprintf(hpp.buf, hpp.size, "  ");
2162			advance_hpp(&hpp, ret);
2163		} else
2164			first = false;
2165
2166		ret = fmt->entry(fmt, &hpp, he);
2167		advance_hpp(&hpp, ret);
2168	}
2169
2170	ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, "");
2171	advance_hpp(&hpp, ret);
2172
2173	perf_hpp_list__for_each_format(he->hpp_list, fmt) {
2174		ret = scnprintf(hpp.buf, hpp.size, "  ");
2175		advance_hpp(&hpp, ret);
2176
2177		ret = fmt->entry(fmt, &hpp, he);
2178		advance_hpp(&hpp, ret);
2179	}
2180
2181	strim(s);
2182	printed += fprintf(fp, "%s\n", s);
2183
2184	if (he->leaf && folded_sign == '-') {
2185		printed += hist_browser__fprintf_callchain(browser, he, fp,
2186							   he->depth + 1);
2187	}
2188
2189	return printed;
2190}
2191
2192static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
2193{
2194	struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
2195						   browser->min_pcnt);
2196	int printed = 0;
2197
2198	while (nd) {
2199		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
2200
2201		if (symbol_conf.report_hierarchy) {
2202			printed += hist_browser__fprintf_hierarchy_entry(browser,
2203									 h, fp,
2204									 h->depth);
2205		} else {
2206			printed += hist_browser__fprintf_entry(browser, h, fp);
2207		}
2208
2209		nd = hists__filter_entries(rb_hierarchy_next(nd),
2210					   browser->min_pcnt);
2211	}
2212
2213	return printed;
2214}
2215
2216static int hist_browser__dump(struct hist_browser *browser)
2217{
2218	char filename[64];
2219	FILE *fp;
2220
2221	while (1) {
2222		scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
2223		if (access(filename, F_OK))
2224			break;
2225		/*
2226 		 * XXX: Just an arbitrary lazy upper limit
2227 		 */
2228		if (++browser->print_seq == 8192) {
2229			ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
2230			return -1;
2231		}
2232	}
2233
2234	fp = fopen(filename, "w");
2235	if (fp == NULL) {
2236		char bf[64];
2237		const char *err = str_error_r(errno, bf, sizeof(bf));
2238		ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
2239		return -1;
2240	}
2241
2242	++browser->print_seq;
2243	hist_browser__fprintf(browser, fp);
2244	fclose(fp);
2245	ui_helpline__fpush("%s written!", filename);
2246
2247	return 0;
2248}
2249
2250void hist_browser__init(struct hist_browser *browser,
2251			struct hists *hists)
2252{
2253	struct perf_hpp_fmt *fmt;
2254
2255	browser->hists			= hists;
2256	browser->b.refresh		= hist_browser__refresh;
2257	browser->b.refresh_dimensions	= hist_browser__refresh_dimensions;
2258	browser->b.seek			= ui_browser__hists_seek;
2259	browser->b.use_navkeypressed	= true;
2260	browser->show_headers		= symbol_conf.show_hist_headers;
2261	hist_browser__set_title_space(browser);
2262
2263	if (symbol_conf.report_hierarchy) {
2264		struct perf_hpp_list_node *fmt_node;
2265
2266		/* count overhead columns (in the first node) */
2267		fmt_node = list_first_entry(&hists->hpp_formats,
2268					    struct perf_hpp_list_node, list);
2269		perf_hpp_list__for_each_format(&fmt_node->hpp, fmt)
2270			++browser->b.columns;
2271
2272		/* add a single column for whole hierarchy sort keys*/
2273		++browser->b.columns;
2274	} else {
2275		hists__for_each_format(hists, fmt)
2276			++browser->b.columns;
2277	}
2278
2279	hists__reset_column_width(hists);
2280}
2281
2282struct hist_browser *hist_browser__new(struct hists *hists)
2283{
2284	struct hist_browser *browser = zalloc(sizeof(*browser));
2285
2286	if (browser)
2287		hist_browser__init(browser, hists);
2288
2289	return browser;
2290}
2291
2292static struct hist_browser *
2293perf_evsel_browser__new(struct evsel *evsel,
2294			struct hist_browser_timer *hbt,
2295			struct perf_env *env)
2296{
2297	struct hist_browser *browser = hist_browser__new(evsel__hists(evsel));
2298
2299	if (browser) {
2300		browser->hbt   = hbt;
2301		browser->env   = env;
2302		browser->title = hists_browser__scnprintf_title;
2303	}
2304	return browser;
2305}
2306
2307void hist_browser__delete(struct hist_browser *browser)
2308{
2309	free(browser);
2310}
2311
2312static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
2313{
2314	return browser->he_selection;
2315}
2316
2317static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
2318{
2319	return browser->he_selection->thread;
2320}
2321
2322static struct res_sample *hist_browser__selected_res_sample(struct hist_browser *browser)
2323{
2324	return browser->he_selection ? browser->he_selection->res_samples : NULL;
2325}
2326
2327/* Check whether the browser is for 'top' or 'report' */
2328static inline bool is_report_browser(void *timer)
2329{
2330	return timer == NULL;
2331}
2332
2333static int hists_browser__scnprintf_title(struct hist_browser *browser, char *bf, size_t size)
2334{
2335	struct hist_browser_timer *hbt = browser->hbt;
2336	int printed = __hists__scnprintf_title(browser->hists, bf, size, !is_report_browser(hbt));
2337
2338	if (!is_report_browser(hbt)) {
2339		struct perf_top *top = hbt->arg;
2340
2341		printed += scnprintf(bf + printed, size - printed,
2342				     " lost: %" PRIu64 "/%" PRIu64,
2343				     top->lost, top->lost_total);
2344
2345		printed += scnprintf(bf + printed, size - printed,
2346				     " drop: %" PRIu64 "/%" PRIu64,
2347				     top->drop, top->drop_total);
2348
2349		if (top->zero)
2350			printed += scnprintf(bf + printed, size - printed, " [z]");
2351
2352		perf_top__reset_sample_counters(top);
2353	}
2354
2355
2356	return printed;
2357}
2358
2359static inline void free_popup_options(char **options, int n)
2360{
2361	int i;
2362
2363	for (i = 0; i < n; ++i)
2364		zfree(&options[i]);
2365}
2366
2367/*
2368 * Only runtime switching of perf data file will make "input_name" point
2369 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2370 * whether we need to call free() for current "input_name" during the switch.
2371 */
2372static bool is_input_name_malloced = false;
2373
2374static int switch_data_file(void)
2375{
2376	char *pwd, *options[32], *abs_path[32], *tmp;
2377	DIR *pwd_dir;
2378	int nr_options = 0, choice = -1, ret = -1;
2379	struct dirent *dent;
2380
2381	pwd = getenv("PWD");
2382	if (!pwd)
2383		return ret;
2384
2385	pwd_dir = opendir(pwd);
2386	if (!pwd_dir)
2387		return ret;
2388
2389	memset(options, 0, sizeof(options));
2390	memset(abs_path, 0, sizeof(abs_path));
2391
2392	while ((dent = readdir(pwd_dir))) {
2393		char path[PATH_MAX];
2394		u64 magic;
2395		char *name = dent->d_name;
2396		FILE *file;
2397
2398		if (!(dent->d_type == DT_REG))
2399			continue;
2400
2401		snprintf(path, sizeof(path), "%s/%s", pwd, name);
2402
2403		file = fopen(path, "r");
2404		if (!file)
2405			continue;
2406
2407		if (fread(&magic, 1, 8, file) < 8)
2408			goto close_file_and_continue;
2409
2410		if (is_perf_magic(magic)) {
2411			options[nr_options] = strdup(name);
2412			if (!options[nr_options])
2413				goto close_file_and_continue;
2414
2415			abs_path[nr_options] = strdup(path);
2416			if (!abs_path[nr_options]) {
2417				zfree(&options[nr_options]);
2418				ui__warning("Can't search all data files due to memory shortage.\n");
2419				fclose(file);
2420				break;
2421			}
2422
2423			nr_options++;
2424		}
2425
2426close_file_and_continue:
2427		fclose(file);
2428		if (nr_options >= 32) {
2429			ui__warning("Too many perf data files in PWD!\n"
2430				    "Only the first 32 files will be listed.\n");
2431			break;
2432		}
2433	}
2434	closedir(pwd_dir);
2435
2436	if (nr_options) {
2437		choice = ui__popup_menu(nr_options, options, NULL);
2438		if (choice < nr_options && choice >= 0) {
2439			tmp = strdup(abs_path[choice]);
2440			if (tmp) {
2441				if (is_input_name_malloced)
2442					free((void *)input_name);
2443				input_name = tmp;
2444				is_input_name_malloced = true;
2445				ret = 0;
2446			} else
2447				ui__warning("Data switch failed due to memory shortage!\n");
2448		}
2449	}
2450
2451	free_popup_options(options, nr_options);
2452	free_popup_options(abs_path, nr_options);
2453	return ret;
2454}
2455
2456struct popup_action {
2457	unsigned long		time;
2458	struct thread 		*thread;
2459	int (*fn)(struct hist_browser *browser, struct popup_action *act);
2460	struct map_symbol 	ms;
2461	int			socket;
2462	enum rstype		rstype;
2463
2464};
2465
2466static int
2467do_annotate(struct hist_browser *browser, struct popup_action *act)
2468{
2469	struct evsel *evsel;
2470	struct annotation *notes;
2471	struct hist_entry *he;
2472	int err;
2473
2474	if (!annotate_opts.objdump_path &&
2475	    perf_env__lookup_objdump(browser->env, &annotate_opts.objdump_path))
2476		return 0;
2477
2478	notes = symbol__annotation(act->ms.sym);
2479	if (!notes->src)
2480		return 0;
2481
2482	if (browser->block_evsel)
2483		evsel = browser->block_evsel;
2484	else
2485		evsel = hists_to_evsel(browser->hists);
2486
2487	he = hist_browser__selected_entry(browser);
2488	err = __hist_entry__tui_annotate(he, &act->ms, evsel, browser->hbt, NO_ADDR);
2489	/*
2490	 * offer option to annotate the other branch source or target
2491	 * (if they exists) when returning from annotate
2492	 */
2493	if ((err == 'q' || err == CTRL('c')) && he->branch_info)
2494		return 1;
2495
2496	ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
2497	if (err)
2498		ui_browser__handle_resize(&browser->b);
2499	return 0;
2500}
2501
2502static struct symbol *symbol__new_unresolved(u64 addr, struct map *map)
2503{
2504	struct annotated_source *src;
2505	struct symbol *sym;
2506	char name[64];
2507
2508	snprintf(name, sizeof(name), "%.*" PRIx64, BITS_PER_LONG / 4, addr);
2509
2510	sym = symbol__new(addr, ANNOTATION_DUMMY_LEN, 0, 0, name);
2511	if (sym) {
2512		src = symbol__hists(sym, 1);
2513		if (!src) {
2514			symbol__delete(sym);
2515			return NULL;
2516		}
2517
2518		dso__insert_symbol(map__dso(map), sym);
2519	}
2520
2521	return sym;
2522}
2523
2524static int
2525add_annotate_opt(struct popup_action *act, char **optstr,
2526		 struct map_symbol *ms,
2527		 u64 addr)
2528{
2529	struct dso *dso;
2530
2531	if (!ms->map || (dso = map__dso(ms->map)) == NULL || dso__annotate_warned(dso))
2532		return 0;
2533
2534	if (!ms->sym)
2535		ms->sym = symbol__new_unresolved(addr, ms->map);
2536
2537	if (ms->sym == NULL || symbol__annotation(ms->sym)->src == NULL)
2538		return 0;
2539
2540	if (asprintf(optstr, "Annotate %s", ms->sym->name) < 0)
2541		return 0;
2542
2543	act->ms = *ms;
2544	act->fn = do_annotate;
2545	return 1;
2546}
2547
2548static int
2549do_annotate_type(struct hist_browser *browser, struct popup_action *act __maybe_unused)
2550{
2551	struct hist_entry *he = browser->he_selection;
2552
2553	hist_entry__annotate_data_tui(he, hists_to_evsel(browser->hists), browser->hbt);
2554	ui_browser__handle_resize(&browser->b);
2555	return 0;
2556}
2557
2558static int
2559add_annotate_type_opt(struct popup_action *act, char **optstr,
2560		      struct hist_entry *he)
2561{
2562	if (he == NULL || he->mem_type == NULL || he->mem_type->histograms == NULL)
2563		return 0;
2564
2565	if (asprintf(optstr, "Annotate type %s", he->mem_type->self.type_name) < 0)
2566		return 0;
2567
2568	act->fn = do_annotate_type;
2569	return 1;
2570}
2571
2572static int
2573do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2574{
2575	struct thread *thread = act->thread;
2576
2577	if ((!hists__has(browser->hists, thread) &&
2578	     !hists__has(browser->hists, comm)) || thread == NULL)
2579		return 0;
2580
2581	if (browser->hists->thread_filter) {
2582		pstack__remove(browser->pstack, &browser->hists->thread_filter);
2583		perf_hpp__set_elide(HISTC_THREAD, false);
2584		thread__zput(browser->hists->thread_filter);
2585		ui_helpline__pop();
2586	} else {
2587		const char *comm_set_str =
2588			thread__comm_set(thread) ? thread__comm_str(thread) : "";
2589
2590		if (hists__has(browser->hists, thread)) {
2591			ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2592					   comm_set_str, thread__tid(thread));
2593		} else {
2594			ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
2595					   comm_set_str);
2596		}
2597
2598		browser->hists->thread_filter = thread__get(thread);
2599		perf_hpp__set_elide(HISTC_THREAD, false);
2600		pstack__push(browser->pstack, &browser->hists->thread_filter);
2601	}
2602
2603	hists__filter_by_thread(browser->hists);
2604	hist_browser__reset(browser);
2605	return 0;
2606}
2607
2608static int
2609add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2610	       char **optstr, struct thread *thread)
2611{
2612	int ret;
2613	const char *comm_set_str, *in_out;
2614
2615	if ((!hists__has(browser->hists, thread) &&
2616	     !hists__has(browser->hists, comm)) || thread == NULL)
2617		return 0;
2618
2619	in_out = browser->hists->thread_filter ? "out of" : "into";
2620	comm_set_str = thread__comm_set(thread) ? thread__comm_str(thread) : "";
2621	if (hists__has(browser->hists, thread)) {
2622		ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2623			       in_out, comm_set_str, thread__tid(thread));
2624	} else {
2625		ret = asprintf(optstr, "Zoom %s %s thread", in_out, comm_set_str);
2626	}
2627	if (ret < 0)
2628		return 0;
2629
2630	act->thread = thread;
2631	act->fn = do_zoom_thread;
2632	return 1;
2633}
2634
2635static int hists_browser__zoom_map(struct hist_browser *browser, struct map *map)
2636{
2637	if (!hists__has(browser->hists, dso) || map == NULL)
2638		return 0;
2639
2640	if (browser->hists->dso_filter) {
2641		pstack__remove(browser->pstack, &browser->hists->dso_filter);
2642		perf_hpp__set_elide(HISTC_DSO, false);
2643		browser->hists->dso_filter = NULL;
2644		ui_helpline__pop();
2645	} else {
2646		struct dso *dso = map__dso(map);
2647		ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
2648				   __map__is_kernel(map) ? "the Kernel" : dso__short_name(dso));
2649		browser->hists->dso_filter = dso;
2650		perf_hpp__set_elide(HISTC_DSO, true);
2651		pstack__push(browser->pstack, &browser->hists->dso_filter);
2652	}
2653
2654	hists__filter_by_dso(browser->hists);
2655	hist_browser__reset(browser);
2656	return 0;
2657}
2658
2659static int
2660do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2661{
2662	return hists_browser__zoom_map(browser, act->ms.map);
2663}
2664
2665static int
2666add_dso_opt(struct hist_browser *browser, struct popup_action *act,
2667	    char **optstr, struct map *map)
2668{
2669	if (!hists__has(browser->hists, dso) || map == NULL)
2670		return 0;
2671
2672	if (asprintf(optstr, "Zoom %s %s DSO (use the 'k' hotkey to zoom directly into the kernel)",
2673		     browser->hists->dso_filter ? "out of" : "into",
2674		     __map__is_kernel(map) ? "the Kernel" : dso__short_name(map__dso(map))) < 0)
2675		return 0;
2676
2677	act->ms.map = map;
2678	act->fn = do_zoom_dso;
2679	return 1;
2680}
2681
2682static int do_toggle_callchain(struct hist_browser *browser, struct popup_action *act __maybe_unused)
2683{
2684	hist_browser__toggle_fold(browser);
2685	return 0;
2686}
2687
2688static int add_callchain_toggle_opt(struct hist_browser *browser, struct popup_action *act, char **optstr)
2689{
2690	char sym_name[512];
2691
2692        if (!hist_browser__selection_has_children(browser))
2693                return 0;
2694
2695	if (asprintf(optstr, "%s [%s] callchain (one level, same as '+' hotkey, use 'e'/'c' for the whole main level entry)",
2696		     hist_browser__selection_unfolded(browser) ? "Collapse" : "Expand",
2697		     hist_browser__selection_sym_name(browser, sym_name, sizeof(sym_name))) < 0)
2698		return 0;
2699
2700	act->fn = do_toggle_callchain;
2701	return 1;
2702}
2703
2704static int
2705do_browse_map(struct hist_browser *browser __maybe_unused,
2706	      struct popup_action *act)
2707{
2708	map__browse(act->ms.map);
2709	return 0;
2710}
2711
2712static int
2713add_map_opt(struct hist_browser *browser,
2714	    struct popup_action *act, char **optstr, struct map *map)
2715{
2716	if (!hists__has(browser->hists, dso) || map == NULL)
2717		return 0;
2718
2719	if (asprintf(optstr, "Browse map details") < 0)
2720		return 0;
2721
2722	act->ms.map = map;
2723	act->fn = do_browse_map;
2724	return 1;
2725}
2726
2727static int
2728do_run_script(struct hist_browser *browser,
2729	      struct popup_action *act)
2730{
2731	char *script_opt;
2732	int len;
2733	int n = 0;
2734
2735	len = 100;
2736	if (act->thread)
2737		len += strlen(thread__comm_str(act->thread));
2738	else if (act->ms.sym)
2739		len += strlen(act->ms.sym->name);
2740	script_opt = malloc(len);
2741	if (!script_opt)
2742		return -1;
2743
2744	script_opt[0] = 0;
2745	if (act->thread) {
2746		n = scnprintf(script_opt, len, " -c %s ",
2747			  thread__comm_str(act->thread));
2748	} else if (act->ms.sym) {
2749		n = scnprintf(script_opt, len, " -S %s ",
2750			  act->ms.sym->name);
2751	}
2752
2753	if (act->time) {
2754		char start[32], end[32];
2755		unsigned long starttime = act->time;
2756		unsigned long endtime = act->time + symbol_conf.time_quantum;
2757
2758		if (starttime == endtime) { /* Display 1ms as fallback */
2759			starttime -= 1*NSEC_PER_MSEC;
2760			endtime += 1*NSEC_PER_MSEC;
2761		}
2762		timestamp__scnprintf_usec(starttime, start, sizeof start);
2763		timestamp__scnprintf_usec(endtime, end, sizeof end);
2764		n += snprintf(script_opt + n, len - n, " --time %s,%s", start, end);
2765	}
2766
2767	script_browse(script_opt, hists_to_evsel(browser->hists));
2768	free(script_opt);
2769	return 0;
2770}
2771
2772static int
2773do_res_sample_script(struct hist_browser *browser,
2774		     struct popup_action *act)
2775{
2776	struct hist_entry *he;
2777
2778	he = hist_browser__selected_entry(browser);
2779	res_sample_browse(he->res_samples, he->num_res, hists_to_evsel(browser->hists), act->rstype);
2780	return 0;
2781}
2782
2783static int
2784add_script_opt_2(struct popup_action *act, char **optstr,
2785	       struct thread *thread, struct symbol *sym,
2786	       const char *tstr)
2787{
2788
2789	if (thread) {
2790		if (asprintf(optstr, "Run scripts for samples of thread [%s]%s",
2791			     thread__comm_str(thread), tstr) < 0)
2792			return 0;
2793	} else if (sym) {
2794		if (asprintf(optstr, "Run scripts for samples of symbol [%s]%s",
2795			     sym->name, tstr) < 0)
2796			return 0;
2797	} else {
2798		if (asprintf(optstr, "Run scripts for all samples%s", tstr) < 0)
2799			return 0;
2800	}
2801
2802	act->thread = thread;
2803	act->ms.sym = sym;
2804	act->fn = do_run_script;
2805	return 1;
2806}
2807
2808static int
2809add_script_opt(struct hist_browser *browser,
2810	       struct popup_action *act, char **optstr,
2811	       struct thread *thread, struct symbol *sym)
2812{
2813	int n, j;
2814	struct hist_entry *he;
2815
2816	n = add_script_opt_2(act, optstr, thread, sym, "");
2817
2818	he = hist_browser__selected_entry(browser);
2819	if (sort_order && strstr(sort_order, "time")) {
2820		char tstr[128];
2821
2822		optstr++;
2823		act++;
2824		j = sprintf(tstr, " in ");
2825		j += timestamp__scnprintf_usec(he->time, tstr + j,
2826					       sizeof tstr - j);
2827		j += sprintf(tstr + j, "-");
2828		timestamp__scnprintf_usec(he->time + symbol_conf.time_quantum,
2829				          tstr + j, sizeof tstr - j);
2830		n += add_script_opt_2(act, optstr, thread, sym, tstr);
2831		act->time = he->time;
2832	}
2833	return n;
2834}
2835
2836static int
2837add_res_sample_opt(struct hist_browser *browser __maybe_unused,
2838		   struct popup_action *act, char **optstr,
2839		   struct res_sample *res_sample,
2840		   enum rstype type)
2841{
2842	if (!res_sample)
2843		return 0;
2844
2845	if (asprintf(optstr, "Show context for individual samples %s",
2846		type == A_ASM ? "with assembler" :
2847		type == A_SOURCE ? "with source" : "") < 0)
2848		return 0;
2849
2850	act->fn = do_res_sample_script;
2851	act->rstype = type;
2852	return 1;
2853}
2854
2855static int
2856do_switch_data(struct hist_browser *browser __maybe_unused,
2857	       struct popup_action *act __maybe_unused)
2858{
2859	if (switch_data_file()) {
2860		ui__warning("Won't switch the data files due to\n"
2861			    "no valid data file get selected!\n");
2862		return 0;
2863	}
2864
2865	return K_SWITCH_INPUT_DATA;
2866}
2867
2868static int
2869add_switch_opt(struct hist_browser *browser,
2870	       struct popup_action *act, char **optstr)
2871{
2872	if (!is_report_browser(browser->hbt))
2873		return 0;
2874
2875	if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2876		return 0;
2877
2878	act->fn = do_switch_data;
2879	return 1;
2880}
2881
2882static int
2883do_exit_browser(struct hist_browser *browser __maybe_unused,
2884		struct popup_action *act __maybe_unused)
2885{
2886	return 0;
2887}
2888
2889static int
2890add_exit_opt(struct hist_browser *browser __maybe_unused,
2891	     struct popup_action *act, char **optstr)
2892{
2893	if (asprintf(optstr, "Exit") < 0)
2894		return 0;
2895
2896	act->fn = do_exit_browser;
2897	return 1;
2898}
2899
2900static int
2901do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
2902{
2903	if (!hists__has(browser->hists, socket) || act->socket < 0)
2904		return 0;
2905
2906	if (browser->hists->socket_filter > -1) {
2907		pstack__remove(browser->pstack, &browser->hists->socket_filter);
2908		browser->hists->socket_filter = -1;
2909		perf_hpp__set_elide(HISTC_SOCKET, false);
2910	} else {
2911		browser->hists->socket_filter = act->socket;
2912		perf_hpp__set_elide(HISTC_SOCKET, true);
2913		pstack__push(browser->pstack, &browser->hists->socket_filter);
2914	}
2915
2916	hists__filter_by_socket(browser->hists);
2917	hist_browser__reset(browser);
2918	return 0;
2919}
2920
2921static int
2922add_socket_opt(struct hist_browser *browser, struct popup_action *act,
2923	       char **optstr, int socket_id)
2924{
2925	if (!hists__has(browser->hists, socket) || socket_id < 0)
2926		return 0;
2927
2928	if (asprintf(optstr, "Zoom %s Processor Socket %d",
2929		     (browser->hists->socket_filter > -1) ? "out of" : "into",
2930		     socket_id) < 0)
2931		return 0;
2932
2933	act->socket = socket_id;
2934	act->fn = do_zoom_socket;
2935	return 1;
2936}
2937
2938static void hist_browser__update_nr_entries(struct hist_browser *hb)
2939{
2940	u64 nr_entries = 0;
2941	struct rb_node *nd = rb_first_cached(&hb->hists->entries);
2942
2943	if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
2944		hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2945		return;
2946	}
2947
2948	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2949		nr_entries++;
2950		nd = rb_hierarchy_next(nd);
2951	}
2952
2953	hb->nr_non_filtered_entries = nr_entries;
2954	hb->nr_hierarchy_entries = nr_entries;
2955}
2956
2957static void hist_browser__update_percent_limit(struct hist_browser *hb,
2958					       double percent)
2959{
2960	struct hist_entry *he;
2961	struct rb_node *nd = rb_first_cached(&hb->hists->entries);
2962	u64 total = hists__total_period(hb->hists);
2963	u64 min_callchain_hits = total * (percent / 100);
2964
2965	hb->min_pcnt = callchain_param.min_percent = percent;
2966
2967	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2968		he = rb_entry(nd, struct hist_entry, rb_node);
2969
2970		if (he->has_no_entry) {
2971			he->has_no_entry = false;
2972			he->nr_rows = 0;
2973		}
2974
2975		if (!he->leaf || !hist_entry__has_callchains(he) || !symbol_conf.use_callchain)
2976			goto next;
2977
2978		if (callchain_param.mode == CHAIN_GRAPH_REL) {
2979			total = he->stat.period;
2980
2981			if (symbol_conf.cumulate_callchain)
2982				total = he->stat_acc->period;
2983
2984			min_callchain_hits = total * (percent / 100);
2985		}
2986
2987		callchain_param.sort(&he->sorted_chain, he->callchain,
2988				     min_callchain_hits, &callchain_param);
2989
2990next:
2991		nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
2992
2993		/* force to re-evaluate folding state of callchains */
2994		he->init_have_children = false;
2995		hist_entry__set_folding(he, hb, false);
2996	}
2997}
2998
2999static int evsel__hists_browse(struct evsel *evsel, int nr_events, const char *helpline,
3000			       bool left_exits, struct hist_browser_timer *hbt, float min_pcnt,
3001			       struct perf_env *env, bool warn_lost_event)
3002{
3003	struct hists *hists = evsel__hists(evsel);
3004	struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env);
3005	struct branch_info *bi = NULL;
3006#define MAX_OPTIONS  16
3007	char *options[MAX_OPTIONS];
3008	struct popup_action actions[MAX_OPTIONS];
3009	int nr_options = 0;
3010	int key = -1;
3011	char buf[128];
3012	int delay_secs = hbt ? hbt->refresh : 0;
3013
3014#define HIST_BROWSER_HELP_COMMON					\
3015	"h/?/F1        Show this window\n"				\
3016	"UP/DOWN/PGUP\n"						\
3017	"PGDN/SPACE    Navigate\n"					\
3018	"q/ESC/CTRL+C  Exit browser or go back to previous screen\n\n"	\
3019	"For multiple event sessions:\n\n"				\
3020	"TAB/UNTAB     Switch events\n\n"				\
3021	"For symbolic views (--sort has sym):\n\n"			\
3022	"ENTER         Zoom into DSO/Threads & Annotate current symbol\n" \
3023	"ESC           Zoom out\n"					\
3024	"+             Expand/Collapse one callchain level\n"		\
3025	"a             Annotate current symbol\n"			\
3026	"C             Collapse all callchains\n"			\
3027	"d             Zoom into current DSO\n"				\
3028	"e             Expand/Collapse main entry callchains\n"	\
3029	"E             Expand all callchains\n"				\
3030	"F             Toggle percentage of filtered entries\n"		\
3031	"H             Display column headers\n"			\
3032	"k             Zoom into the kernel map\n"			\
3033	"L             Change percent limit\n"				\
3034	"m             Display context menu\n"				\
3035	"S             Zoom into current Processor Socket\n"		\
3036
3037	/* help messages are sorted by lexical order of the hotkey */
3038	static const char report_help[] = HIST_BROWSER_HELP_COMMON
3039	"i             Show header information\n"
3040	"P             Print histograms to perf.hist.N\n"
3041	"r             Run available scripts\n"
3042	"s             Switch to another data file in PWD\n"
3043	"t             Zoom into current Thread\n"
3044	"V             Verbose (DSO names in callchains, etc)\n"
3045	"/             Filter symbol by name\n"
3046	"0-9           Sort by event n in group";
3047	static const char top_help[] = HIST_BROWSER_HELP_COMMON
3048	"P             Print histograms to perf.hist.N\n"
3049	"t             Zoom into current Thread\n"
3050	"V             Verbose (DSO names in callchains, etc)\n"
3051	"z             Toggle zeroing of samples\n"
3052	"f             Enable/Disable events\n"
3053	"/             Filter symbol by name";
3054
3055	if (browser == NULL)
3056		return -1;
3057
3058	/* reset abort key so that it can get Ctrl-C as a key */
3059	SLang_reset_tty();
3060	SLang_init_tty(0, 0, 0);
3061	SLtty_set_suspend_state(true);
3062
3063	if (min_pcnt)
3064		browser->min_pcnt = min_pcnt;
3065	hist_browser__update_nr_entries(browser);
3066
3067	browser->pstack = pstack__new(3);
3068	if (browser->pstack == NULL)
3069		goto out;
3070
3071	ui_helpline__push(helpline);
3072
3073	memset(options, 0, sizeof(options));
3074	memset(actions, 0, sizeof(actions));
3075
3076	if (symbol_conf.col_width_list_str)
3077		perf_hpp__set_user_width(symbol_conf.col_width_list_str);
3078
3079	if (!is_report_browser(hbt))
3080		browser->b.no_samples_msg = "Collecting samples...";
3081
3082	while (1) {
3083		struct thread *thread = NULL;
3084		struct map *map = NULL;
3085		int choice;
3086		int socked_id = -1;
3087
3088		key = 0; // reset key
3089do_hotkey:		 // key came straight from options ui__popup_menu()
3090		choice = nr_options = 0;
3091		key = hist_browser__run(browser, helpline, warn_lost_event, key);
3092
3093		if (browser->he_selection != NULL) {
3094			thread = hist_browser__selected_thread(browser);
3095			map = browser->selection->map;
3096			socked_id = browser->he_selection->socket;
3097		}
3098		switch (key) {
3099		case K_TAB:
3100		case K_UNTAB:
3101			if (nr_events == 1)
3102				continue;
3103			/*
3104			 * Exit the browser, let hists__browser_tree
3105			 * go to the next or previous
3106			 */
3107			goto out_free_stack;
3108		case '0' ... '9':
3109			if (!symbol_conf.event_group ||
3110			    evsel->core.nr_members < 2) {
3111				snprintf(buf, sizeof(buf),
3112					 "Sort by index only available with group events!");
3113				helpline = buf;
3114				continue;
3115			}
3116
3117			if (key - '0' == symbol_conf.group_sort_idx)
3118				continue;
3119
3120			symbol_conf.group_sort_idx = key - '0';
3121
3122			if (symbol_conf.group_sort_idx >= evsel->core.nr_members) {
3123				snprintf(buf, sizeof(buf),
3124					 "Max event group index to sort is %d (index from 0 to %d)",
3125					 evsel->core.nr_members - 1,
3126					 evsel->core.nr_members - 1);
3127				helpline = buf;
3128				continue;
3129			}
3130
3131			key = K_RELOAD;
3132			goto out_free_stack;
3133		case 'a':
3134			if (!hists__has(hists, sym)) {
3135				ui_browser__warning(&browser->b, delay_secs * 2,
3136			"Annotation is only available for symbolic views, "
3137			"include \"sym*\" in --sort to use it.");
3138				continue;
3139			}
3140
3141			if (!browser->selection ||
3142			    !browser->selection->map ||
3143			    !map__dso(browser->selection->map) ||
3144			    dso__annotate_warned(map__dso(browser->selection->map))) {
3145				continue;
3146			}
3147
3148			if (!browser->selection->sym) {
3149				if (!browser->he_selection)
3150					continue;
3151
3152				if (sort__mode == SORT_MODE__BRANCH) {
3153					bi = browser->he_selection->branch_info;
3154					if (!bi || !bi->to.ms.map)
3155						continue;
3156
3157					actions->ms.sym = symbol__new_unresolved(bi->to.al_addr, bi->to.ms.map);
3158					actions->ms.map = bi->to.ms.map;
3159				} else {
3160					actions->ms.sym = symbol__new_unresolved(browser->he_selection->ip,
3161										 browser->selection->map);
3162					actions->ms.map = browser->selection->map;
3163				}
3164
3165				if (!actions->ms.sym)
3166					continue;
3167			} else {
3168				if (symbol__annotation(browser->selection->sym)->src == NULL) {
3169					ui_browser__warning(&browser->b, delay_secs * 2,
3170						"No samples for the \"%s\" symbol.\n\n"
3171						"Probably appeared just in a callchain",
3172						browser->selection->sym->name);
3173					continue;
3174				}
3175
3176				actions->ms.map = browser->selection->map;
3177				actions->ms.sym = browser->selection->sym;
3178			}
3179
3180			do_annotate(browser, actions);
3181			continue;
3182		case 'P':
3183			hist_browser__dump(browser);
3184			continue;
3185		case 'd':
3186			actions->ms.map = map;
3187			do_zoom_dso(browser, actions);
3188			continue;
3189		case 'k':
3190			if (browser->selection != NULL)
3191				hists_browser__zoom_map(browser,
3192					      maps__machine(browser->selection->maps)->vmlinux_map);
3193			continue;
3194		case 'V':
3195			verbose = (verbose + 1) % 4;
3196			browser->show_dso = verbose > 0;
3197			ui_helpline__fpush("Verbosity level set to %d\n",
3198					   verbose);
3199			continue;
3200		case 't':
3201			actions->thread = thread;
3202			do_zoom_thread(browser, actions);
3203			continue;
3204		case 'S':
3205			actions->socket = socked_id;
3206			do_zoom_socket(browser, actions);
3207			continue;
3208		case '/':
3209			if (ui_browser__input_window("Symbol to show",
3210					"Please enter the name of symbol you want to see.\n"
3211					"To remove the filter later, press / + ENTER.",
3212					buf, "ENTER: OK, ESC: Cancel",
3213					delay_secs * 2) == K_ENTER) {
3214				hists->symbol_filter_str = *buf ? buf : NULL;
3215				hists__filter_by_symbol(hists);
3216				hist_browser__reset(browser);
3217			}
3218			continue;
3219		case 'r':
3220			if (is_report_browser(hbt)) {
3221				actions->thread = NULL;
3222				actions->ms.sym = NULL;
3223				do_run_script(browser, actions);
3224			}
3225			continue;
3226		case 's':
3227			if (is_report_browser(hbt)) {
3228				key = do_switch_data(browser, actions);
3229				if (key == K_SWITCH_INPUT_DATA)
3230					goto out_free_stack;
3231			}
3232			continue;
3233		case 'i':
3234			/* env->arch is NULL for live-mode (i.e. perf top) */
3235			if (env->arch)
3236				tui__header_window(evsel__session(evsel));
3237			continue;
3238		case 'F':
3239			symbol_conf.filter_relative ^= 1;
3240			continue;
3241		case 'z':
3242			if (!is_report_browser(hbt)) {
3243				struct perf_top *top = hbt->arg;
3244
3245				top->zero = !top->zero;
3246			}
3247			continue;
3248		case 'L':
3249			if (ui_browser__input_window("Percent Limit",
3250					"Please enter the value you want to hide entries under that percent.",
3251					buf, "ENTER: OK, ESC: Cancel",
3252					delay_secs * 2) == K_ENTER) {
3253				char *end;
3254				double new_percent = strtod(buf, &end);
3255
3256				if (new_percent < 0 || new_percent > 100) {
3257					ui_browser__warning(&browser->b, delay_secs * 2,
3258						"Invalid percent: %.2f", new_percent);
3259					continue;
3260				}
3261
3262				hist_browser__update_percent_limit(browser, new_percent);
3263				hist_browser__reset(browser);
3264			}
3265			continue;
3266		case K_F1:
3267		case 'h':
3268		case '?':
3269			ui_browser__help_window(&browser->b,
3270				is_report_browser(hbt) ? report_help : top_help);
3271			continue;
3272		case K_ENTER:
3273		case K_RIGHT:
3274		case 'm':
3275			/* menu */
3276			break;
3277		case K_ESC:
3278		case K_LEFT: {
3279			const void *top;
3280
3281			if (pstack__empty(browser->pstack)) {
3282				/*
3283				 * Go back to the perf_evsel_menu__run or other user
3284				 */
3285				if (left_exits)
3286					goto out_free_stack;
3287
3288				if (key == K_ESC &&
3289				    ui_browser__dialog_yesno(&browser->b,
3290							     "Do you really want to exit?"))
3291					goto out_free_stack;
3292
3293				continue;
3294			}
3295			actions->ms.map = map;
3296			top = pstack__peek(browser->pstack);
3297			if (top == &browser->hists->dso_filter) {
3298				/*
3299				 * No need to set actions->dso here since
3300				 * it's just to remove the current filter.
3301				 */
3302				do_zoom_dso(browser, actions);
3303			} else if (top == &browser->hists->thread_filter) {
3304				actions->thread = thread;
3305				do_zoom_thread(browser, actions);
3306			} else if (top == &browser->hists->socket_filter) {
3307				do_zoom_socket(browser, actions);
3308			}
3309			continue;
3310		}
3311		case 'q':
3312		case CTRL('c'):
3313			goto out_free_stack;
3314		case 'f':
3315			if (!is_report_browser(hbt)) {
3316				struct perf_top *top = hbt->arg;
3317
3318				evlist__toggle_enable(top->evlist);
3319				/*
3320				 * No need to refresh, resort/decay histogram
3321				 * entries if we are not collecting samples:
3322				 */
3323				if (top->evlist->enabled) {
3324					helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
3325					hbt->refresh = delay_secs;
3326				} else {
3327					helpline = "Press 'f' again to re-enable the events";
3328					hbt->refresh = 0;
3329				}
3330				continue;
3331			}
3332			/* Fall thru */
3333		default:
3334			helpline = "Press '?' for help on key bindings";
3335			ui_browser__warn_unhandled_hotkey(&browser->b, key, delay_secs,
3336							  ", use 'h'/'?'/F1 to see actions");
3337			continue;
3338		}
3339
3340		if (!hists__has(hists, sym) || browser->selection == NULL)
3341			goto skip_annotation;
3342
3343		if (sort__mode == SORT_MODE__BRANCH) {
3344
3345			if (browser->he_selection)
3346				bi = browser->he_selection->branch_info;
3347
3348			if (bi == NULL)
3349				goto skip_annotation;
3350
3351			nr_options += add_annotate_opt(&actions[nr_options],
3352						       &options[nr_options],
3353						       &bi->from.ms,
3354						       bi->from.al_addr);
3355			if (bi->to.ms.sym != bi->from.ms.sym)
3356				nr_options += add_annotate_opt(&actions[nr_options],
3357							&options[nr_options],
3358							&bi->to.ms,
3359							bi->to.al_addr);
3360		} else if (browser->he_selection) {
3361			nr_options += add_annotate_opt(&actions[nr_options],
3362						       &options[nr_options],
3363						       browser->selection,
3364						       browser->he_selection->ip);
3365		}
3366skip_annotation:
3367		nr_options += add_annotate_type_opt(&actions[nr_options],
3368						    &options[nr_options],
3369						    browser->he_selection);
3370		nr_options += add_thread_opt(browser, &actions[nr_options],
3371					     &options[nr_options], thread);
3372		nr_options += add_dso_opt(browser, &actions[nr_options],
3373					  &options[nr_options], map);
3374		nr_options += add_callchain_toggle_opt(browser, &actions[nr_options], &options[nr_options]);
3375		nr_options += add_map_opt(browser, &actions[nr_options],
3376					  &options[nr_options],
3377					  browser->selection ?
3378						browser->selection->map : NULL);
3379		nr_options += add_socket_opt(browser, &actions[nr_options],
3380					     &options[nr_options],
3381					     socked_id);
3382		/* perf script support */
3383		if (!is_report_browser(hbt))
3384			goto skip_scripting;
3385
3386		if (browser->he_selection) {
3387			if (hists__has(hists, thread) && thread) {
3388				nr_options += add_script_opt(browser,
3389							     &actions[nr_options],
3390							     &options[nr_options],
3391							     thread, NULL);
3392			}
3393			/*
3394			 * Note that browser->selection != NULL
3395			 * when browser->he_selection is not NULL,
3396			 * so we don't need to check browser->selection
3397			 * before fetching browser->selection->sym like what
3398			 * we do before fetching browser->selection->map.
3399			 *
3400			 * See hist_browser__show_entry.
3401			 */
3402			if (hists__has(hists, sym) && browser->selection->sym) {
3403				nr_options += add_script_opt(browser,
3404							     &actions[nr_options],
3405							     &options[nr_options],
3406							     NULL, browser->selection->sym);
3407			}
3408		}
3409		nr_options += add_script_opt(browser, &actions[nr_options],
3410					     &options[nr_options], NULL, NULL);
3411		nr_options += add_res_sample_opt(browser, &actions[nr_options],
3412						 &options[nr_options],
3413						 hist_browser__selected_res_sample(browser),
3414						 A_NORMAL);
3415		nr_options += add_res_sample_opt(browser, &actions[nr_options],
3416						 &options[nr_options],
3417						 hist_browser__selected_res_sample(browser),
3418						 A_ASM);
3419		nr_options += add_res_sample_opt(browser, &actions[nr_options],
3420						 &options[nr_options],
3421						 hist_browser__selected_res_sample(browser),
3422						 A_SOURCE);
3423		nr_options += add_switch_opt(browser, &actions[nr_options],
3424					     &options[nr_options]);
3425skip_scripting:
3426		nr_options += add_exit_opt(browser, &actions[nr_options],
3427					   &options[nr_options]);
3428
3429		do {
3430			struct popup_action *act;
3431
3432			choice = ui__popup_menu(nr_options, options, &key);
3433			if (choice == -1)
3434				break;
3435
3436			if (choice == nr_options)
3437				goto do_hotkey;
3438
3439			act = &actions[choice];
3440			key = act->fn(browser, act);
3441		} while (key == 1);
3442
3443		if (key == K_SWITCH_INPUT_DATA)
3444			break;
3445	}
3446out_free_stack:
3447	pstack__delete(browser->pstack);
3448out:
3449	hist_browser__delete(browser);
3450	free_popup_options(options, MAX_OPTIONS);
3451	return key;
3452}
3453
3454struct evsel_menu {
3455	struct ui_browser b;
3456	struct evsel *selection;
3457	bool lost_events, lost_events_warned;
3458	float min_pcnt;
3459	struct perf_env *env;
3460};
3461
3462static void perf_evsel_menu__write(struct ui_browser *browser,
3463				   void *entry, int row)
3464{
3465	struct evsel_menu *menu = container_of(browser,
3466						    struct evsel_menu, b);
3467	struct evsel *evsel = list_entry(entry, struct evsel, core.node);
3468	struct hists *hists = evsel__hists(evsel);
3469	bool current_entry = ui_browser__is_current_entry(browser, row);
3470	unsigned long nr_events = hists->stats.nr_samples;
3471	const char *ev_name = evsel__name(evsel);
3472	char bf[256], unit;
3473	const char *warn = " ";
3474	size_t printed;
3475
3476	ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3477						       HE_COLORSET_NORMAL);
3478
3479	if (evsel__is_group_event(evsel)) {
3480		struct evsel *pos;
3481
3482		ev_name = evsel__group_name(evsel);
3483
3484		for_each_group_member(pos, evsel) {
3485			struct hists *pos_hists = evsel__hists(pos);
3486			nr_events += pos_hists->stats.nr_samples;
3487		}
3488	}
3489
3490	nr_events = convert_unit(nr_events, &unit);
3491	printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
3492			   unit, unit == ' ' ? "" : " ", ev_name);
3493	ui_browser__printf(browser, "%s", bf);
3494
3495	nr_events = evsel->evlist->stats.nr_events[PERF_RECORD_LOST];
3496	if (nr_events != 0) {
3497		menu->lost_events = true;
3498		if (!current_entry)
3499			ui_browser__set_color(browser, HE_COLORSET_TOP);
3500		nr_events = convert_unit(nr_events, &unit);
3501		printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3502				     nr_events, unit, unit == ' ' ? "" : " ");
3503		warn = bf;
3504	}
3505
3506	ui_browser__write_nstring(browser, warn, browser->width - printed);
3507
3508	if (current_entry)
3509		menu->selection = evsel;
3510}
3511
3512static int perf_evsel_menu__run(struct evsel_menu *menu,
3513				int nr_events, const char *help,
3514				struct hist_browser_timer *hbt,
3515				bool warn_lost_event)
3516{
3517	struct evlist *evlist = menu->b.priv;
3518	struct evsel *pos;
3519	const char *title = "Available samples";
3520	int delay_secs = hbt ? hbt->refresh : 0;
3521	int key;
3522
3523	if (ui_browser__show(&menu->b, title,
3524			     "ESC: exit, ENTER|->: Browse histograms") < 0)
3525		return -1;
3526
3527	while (1) {
3528		key = ui_browser__run(&menu->b, delay_secs);
3529
3530		switch (key) {
3531		case K_TIMER:
3532			if (hbt)
3533				hbt->timer(hbt->arg);
3534
3535			if (!menu->lost_events_warned &&
3536			    menu->lost_events &&
3537			    warn_lost_event) {
3538				ui_browser__warn_lost_events(&menu->b);
3539				menu->lost_events_warned = true;
3540			}
3541			continue;
3542		case K_RIGHT:
3543		case K_ENTER:
3544			if (!menu->selection)
3545				continue;
3546			pos = menu->selection;
3547browse_hists:
3548			evlist__set_selected(evlist, pos);
3549			/*
3550			 * Give the calling tool a chance to populate the non
3551			 * default evsel resorted hists tree.
3552			 */
3553			if (hbt)
3554				hbt->timer(hbt->arg);
3555			key = evsel__hists_browse(pos, nr_events, help, true, hbt,
3556						  menu->min_pcnt, menu->env,
3557						  warn_lost_event);
3558			ui_browser__show_title(&menu->b, title);
3559			switch (key) {
3560			case K_TAB:
3561				if (pos->core.node.next == &evlist->core.entries)
3562					pos = evlist__first(evlist);
3563				else
3564					pos = evsel__next(pos);
3565				goto browse_hists;
3566			case K_UNTAB:
3567				if (pos->core.node.prev == &evlist->core.entries)
3568					pos = evlist__last(evlist);
3569				else
3570					pos = evsel__prev(pos);
3571				goto browse_hists;
3572			case K_SWITCH_INPUT_DATA:
3573			case K_RELOAD:
3574			case 'q':
3575			case CTRL('c'):
3576				goto out;
3577			case K_ESC:
3578			default:
3579				continue;
3580			}
3581		case K_LEFT:
3582			continue;
3583		case K_ESC:
3584			if (!ui_browser__dialog_yesno(&menu->b,
3585					       "Do you really want to exit?"))
3586				continue;
3587			/* Fall thru */
3588		case 'q':
3589		case CTRL('c'):
3590			goto out;
3591		default:
3592			ui_browser__warn_unhandled_hotkey(&menu->b, key, delay_secs, NULL);
3593			continue;
3594		}
3595	}
3596
3597out:
3598	ui_browser__hide(&menu->b);
3599	return key;
3600}
3601
3602static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
3603				 void *entry)
3604{
3605	struct evsel *evsel = list_entry(entry, struct evsel, core.node);
3606
3607	if (symbol_conf.event_group && !evsel__is_group_leader(evsel))
3608		return true;
3609
3610	return false;
3611}
3612
3613static int __evlist__tui_browse_hists(struct evlist *evlist, int nr_entries, const char *help,
3614				      struct hist_browser_timer *hbt, float min_pcnt, struct perf_env *env,
3615				      bool warn_lost_event)
3616{
3617	struct evsel *pos;
3618	struct evsel_menu menu = {
3619		.b = {
3620			.entries    = &evlist->core.entries,
3621			.refresh    = ui_browser__list_head_refresh,
3622			.seek	    = ui_browser__list_head_seek,
3623			.write	    = perf_evsel_menu__write,
3624			.filter	    = filter_group_entries,
3625			.nr_entries = nr_entries,
3626			.priv	    = evlist,
3627		},
3628		.min_pcnt = min_pcnt,
3629		.env = env,
3630	};
3631
3632	ui_helpline__push("Press ESC to exit");
3633
3634	evlist__for_each_entry(evlist, pos) {
3635		const char *ev_name = evsel__name(pos);
3636		size_t line_len = strlen(ev_name) + 7;
3637
3638		if (menu.b.width < line_len)
3639			menu.b.width = line_len;
3640	}
3641
3642	return perf_evsel_menu__run(&menu, nr_entries, help,
3643				    hbt, warn_lost_event);
3644}
3645
3646static bool evlist__single_entry(struct evlist *evlist)
3647{
3648	int nr_entries = evlist->core.nr_entries;
3649
3650	if (nr_entries == 1)
3651	       return true;
3652
3653	if (nr_entries == 2) {
3654		struct evsel *last = evlist__last(evlist);
3655
3656		if (evsel__is_dummy_event(last))
3657			return true;
3658	}
3659
3660	return false;
3661}
3662
3663int evlist__tui_browse_hists(struct evlist *evlist, const char *help, struct hist_browser_timer *hbt,
3664			     float min_pcnt, struct perf_env *env, bool warn_lost_event)
3665{
3666	int nr_entries = evlist->core.nr_entries;
3667
3668	if (evlist__single_entry(evlist)) {
3669single_entry: {
3670		struct evsel *first = evlist__first(evlist);
3671
3672		return evsel__hists_browse(first, nr_entries, help, false, hbt, min_pcnt,
3673					   env, warn_lost_event);
3674	}
3675	}
3676
3677	if (symbol_conf.event_group) {
3678		struct evsel *pos;
3679
3680		nr_entries = 0;
3681		evlist__for_each_entry(evlist, pos) {
3682			if (evsel__is_group_leader(pos))
3683				nr_entries++;
3684		}
3685
3686		if (nr_entries == 1)
3687			goto single_entry;
3688	}
3689
3690	return __evlist__tui_browse_hists(evlist, nr_entries, help, hbt, min_pcnt, env,
3691					  warn_lost_event);
3692}
3693
3694static int block_hists_browser__title(struct hist_browser *browser, char *bf,
3695				      size_t size)
3696{
3697	struct hists *hists = evsel__hists(browser->block_evsel);
3698	const char *evname = evsel__name(browser->block_evsel);
3699	unsigned long nr_samples = hists->stats.nr_samples;
3700	int ret;
3701
3702	ret = scnprintf(bf, size, "# Samples: %lu", nr_samples);
3703	if (evname)
3704		scnprintf(bf + ret, size -  ret, " of event '%s'", evname);
3705
3706	return 0;
3707}
3708
3709int block_hists_tui_browse(struct block_hist *bh, struct evsel *evsel,
3710			   float min_percent, struct perf_env *env)
3711{
3712	struct hists *hists = &bh->block_hists;
3713	struct hist_browser *browser;
3714	int key = -1;
3715	struct popup_action action;
3716	char *br_cntr_text = NULL;
3717	static const char help[] =
3718	" q/ESC         Quit \n"
3719	" B             Branch counter abbr list (Optional)\n";
3720
3721	browser = hist_browser__new(hists);
3722	if (!browser)
3723		return -1;
3724
3725	browser->block_evsel = evsel;
3726	browser->title = block_hists_browser__title;
3727	browser->min_pcnt = min_percent;
3728	browser->env = env;
3729
3730	/* reset abort key so that it can get Ctrl-C as a key */
3731	SLang_reset_tty();
3732	SLang_init_tty(0, 0, 0);
3733	SLtty_set_suspend_state(true);
3734
3735	memset(&action, 0, sizeof(action));
3736
3737	if (!annotation_br_cntr_abbr_list(&br_cntr_text, evsel, false))
3738		annotate_opts.show_br_cntr = true;
3739
3740	while (1) {
3741		key = hist_browser__run(browser, "? - help", true, 0);
3742
3743		switch (key) {
3744		case 'q':
3745		case K_ESC:
3746			goto out;
3747		case '?':
3748			ui_browser__help_window(&browser->b, help);
3749			break;
3750		case 'a':
3751		case K_ENTER:
3752			if (!browser->selection ||
3753			    !browser->selection->sym) {
3754				continue;
3755			}
3756
3757			action.ms.map = browser->selection->map;
3758			action.ms.sym = browser->selection->sym;
3759			do_annotate(browser, &action);
3760			continue;
3761		case 'B':
3762			if (br_cntr_text) {
3763				ui__question_window("Branch counter abbr list",
3764						    br_cntr_text, "Press any key...", 0);
3765			} else {
3766				ui__question_window("Branch counter abbr list",
3767						    "\n The branch counter is not available.\n",
3768						    "Press any key...", 0);
3769			}
3770			continue;
3771		default:
3772			ui_browser__warn_unhandled_hotkey(&browser->b, key, 0,
3773							  ", use '?' to see actions");
3774			continue;
3775		}
3776	}
3777
3778out:
3779	hist_browser__delete(browser);
3780	free(br_cntr_text);
3781	return 0;
3782}
~/ Read-only file preview.