Linux Audio

Check our new training course

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