hists.c 85.6 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0
2
#include <dirent.h>
3
#include <errno.h>
4
#include <inttypes.h>
5 6 7 8
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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 18 19
#include "../../util/evsel.h"
#include "../../util/evlist.h"
#include "../../util/hist.h"
20
#include "../../util/map.h"
21
#include "../../util/symbol.h"
22 23
#include "../../util/pstack.h"
#include "../../util/sort.h"
24
#include "../../util/top.h"
25
#include "../../util/thread.h"
26
#include "../../arch/common.h"
27
#include "../../perf.h"
28

29
#include "../browsers/hists.h"
30 31
#include "../helpline.h"
#include "../util.h"
32
#include "../ui.h"
33
#include "map.h"
34
#include "annotate.h"
35
#include "srcline.h"
36
#include "string2.h"
37
#include "units.h"
38
#include "time-utils.h"
39

40
#include <linux/ctype.h>
41

42 43
extern void hist_browser__init_hpp(void);

44
static int hists_browser__scnprintf_title(struct hist_browser *browser, char *bf, size_t size);
45
static void hist_browser__update_nr_entries(struct hist_browser *hb);
46

47 48 49
static struct rb_node *hists__filter_entries(struct rb_node *nd,
					     float min_pcnt);

50 51
static bool hist_browser__has_filter(struct hist_browser *hb)
{
52
	return hists__has_filter(hb->hists) || hb->min_pcnt || symbol_conf.has_filter || hb->c2c_filter;
53 54
}

55 56 57 58 59 60
static int hist_browser__get_folding(struct hist_browser *browser)
{
	struct rb_node *nd;
	struct hists *hists = browser->hists;
	int unfolded_rows = 0;

61
	for (nd = rb_first_cached(&hists->entries);
62
	     (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
63
	     nd = rb_hierarchy_next(nd)) {
64 65 66
		struct hist_entry *he =
			rb_entry(nd, struct hist_entry, rb_node);

67
		if (he->leaf && he->unfolded)
68 69 70 71 72
			unfolded_rows += he->nr_rows;
	}
	return unfolded_rows;
}

73 74 75 76 77 78 79 80 81
static void hist_browser__set_title_space(struct hist_browser *hb)
{
	struct ui_browser *browser = &hb->b;
	struct hists *hists = hb->hists;
	struct perf_hpp_list *hpp_list = hists->hpp_list;

	browser->extra_title_lines = hb->show_headers ? hpp_list->nr_header_lines : 0;
}

82 83 84 85
static u32 hist_browser__nr_entries(struct hist_browser *hb)
{
	u32 nr_entries;

86 87 88
	if (symbol_conf.report_hierarchy)
		nr_entries = hb->nr_hierarchy_entries;
	else if (hist_browser__has_filter(hb))
89 90 91 92
		nr_entries = hb->nr_non_filtered_entries;
	else
		nr_entries = hb->hists->nr_entries;

93
	hb->nr_callchain_rows = hist_browser__get_folding(hb);
94 95 96
	return nr_entries + hb->nr_callchain_rows;
}

97 98 99
static void hist_browser__update_rows(struct hist_browser *hb)
{
	struct ui_browser *browser = &hb->b;
100 101
	struct hists *hists = hb->hists;
	struct perf_hpp_list *hpp_list = hists->hpp_list;
102 103 104 105 106 107 108
	u16 index_row;

	if (!hb->show_headers) {
		browser->rows += browser->extra_title_lines;
		browser->extra_title_lines = 0;
		return;
	}
109

110 111
	browser->extra_title_lines = hpp_list->nr_header_lines;
	browser->rows -= browser->extra_title_lines;
112 113 114 115 116 117 118 119 120
	/*
	 * Verify if we were at the last line and that line isn't
	 * visibe because we now show the header line(s).
	 */
	index_row = browser->index - browser->top_idx;
	if (index_row >= browser->rows)
		browser->index -= index_row - browser->rows + 1;
}

121
static void hist_browser__refresh_dimensions(struct ui_browser *browser)
122
{
123 124
	struct hist_browser *hb = container_of(browser, struct hist_browser, b);

125
	/* 3 == +/- toggle symbol before actual hist_entry rendering */
126 127 128 129 130 131 132 133
	browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
	/*
 	 * FIXME: Just keeping existing behaviour, but this really should be
 	 *	  before updating browser->width, as it will invalidate the
 	 *	  calculation above. Fix this and the fallout in another
 	 *	  changeset.
 	 */
	ui_browser__refresh_dimensions(browser);
134 135
}

136
static void hist_browser__reset(struct hist_browser *browser)
137
{
138 139 140 141 142 143
	/*
	 * The hists__remove_entry_filter() already folds non-filtered
	 * entries so we can assume it has 0 callchain rows.
	 */
	browser->nr_callchain_rows = 0;

144
	hist_browser__update_nr_entries(browser);
145
	browser->b.nr_entries = hist_browser__nr_entries(browser);
146
	hist_browser__refresh_dimensions(&browser->b);
147
	ui_browser__reset_index(&browser->b);
148 149 150 151 152 153 154
}

static char tree__folded_sign(bool unfolded)
{
	return unfolded ? '-' : '+';
}

155
static char hist_entry__folded(const struct hist_entry *he)
156
{
157
	return he->has_children ? tree__folded_sign(he->unfolded) : ' ';
158 159
}

160
static char callchain_list__folded(const struct callchain_list *cl)
161
{
162
	return cl->has_children ? tree__folded_sign(cl->unfolded) : ' ';
163 164
}

165
static void callchain_list__set_folding(struct callchain_list *cl, bool unfold)
166
{
167
	cl->unfolded = unfold ? cl->has_children : false;
168 169
}

170
static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
171
{
172
	int n = 0;
173 174
	struct rb_node *nd;

175
	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
176 177 178 179 180 181
		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
		struct callchain_list *chain;
		char folded_sign = ' '; /* No children */

		list_for_each_entry(chain, &child->val, list) {
			++n;
182

183 184 185 186 187 188 189 190 191 192 193 194 195
			/* We need this because we may not have children */
			folded_sign = callchain_list__folded(chain);
			if (folded_sign == '+')
				break;
		}

		if (folded_sign == '-') /* Have children and they're unfolded */
			n += callchain_node__count_rows_rb_tree(child);
	}

	return n;
}

196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
static int callchain_node__count_flat_rows(struct callchain_node *node)
{
	struct callchain_list *chain;
	char folded_sign = 0;
	int n = 0;

	list_for_each_entry(chain, &node->parent_val, list) {
		if (!folded_sign) {
			/* only check first chain list entry */
			folded_sign = callchain_list__folded(chain);
			if (folded_sign == '+')
				return 1;
		}
		n++;
	}

	list_for_each_entry(chain, &node->val, list) {
		if (!folded_sign) {
			/* node->parent_val list might be empty */
			folded_sign = callchain_list__folded(chain);
			if (folded_sign == '+')
				return 1;
		}
		n++;
	}

	return n;
}

225 226 227 228 229
static int callchain_node__count_folded_rows(struct callchain_node *node __maybe_unused)
{
	return 1;
}

230 231 232 233
static int callchain_node__count_rows(struct callchain_node *node)
{
	struct callchain_list *chain;
	bool unfolded = false;
234
	int n = 0;
235

236 237
	if (callchain_param.mode == CHAIN_FLAT)
		return callchain_node__count_flat_rows(node);
238 239
	else if (callchain_param.mode == CHAIN_FOLDED)
		return callchain_node__count_folded_rows(node);
240

241 242
	list_for_each_entry(chain, &node->val, list) {
		++n;
243

244
		unfolded = chain->unfolded;
245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265
	}

	if (unfolded)
		n += callchain_node__count_rows_rb_tree(node);

	return n;
}

static int callchain__count_rows(struct rb_root *chain)
{
	struct rb_node *nd;
	int n = 0;

	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
		n += callchain_node__count_rows(node);
	}

	return n;
}

266 267 268 269 270 271 272 273 274 275
static int hierarchy_count_rows(struct hist_browser *hb, struct hist_entry *he,
				bool include_children)
{
	int count = 0;
	struct rb_node *node;
	struct hist_entry *child;

	if (he->leaf)
		return callchain__count_rows(&he->sorted_chain);

276 277 278
	if (he->has_no_entry)
		return 1;

279
	node = rb_first_cached(&he->hroot_out);
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297
	while (node) {
		float percent;

		child = rb_entry(node, struct hist_entry, rb_node);
		percent = hist_entry__get_percent_limit(child);

		if (!child->filtered && percent >= hb->min_pcnt) {
			count++;

			if (include_children && child->unfolded)
				count += hierarchy_count_rows(hb, child, true);
		}

		node = rb_next(node);
	}
	return count;
}

298
static bool hist_entry__toggle_fold(struct hist_entry *he)
299
{
300
	if (!he)
301 302
		return false;

303
	if (!he->has_children)
304 305
		return false;

306 307 308 309 310 311 312 313 314 315 316 317 318
	he->unfolded = !he->unfolded;
	return true;
}

static bool callchain_list__toggle_fold(struct callchain_list *cl)
{
	if (!cl)
		return false;

	if (!cl->has_children)
		return false;

	cl->unfolded = !cl->unfolded;
319 320 321
	return true;
}

322
static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
323
{
324
	struct rb_node *nd = rb_first(&node->rb_root);
325

326
	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
327 328
		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
		struct callchain_list *chain;
329
		bool first = true;
330 331 332 333

		list_for_each_entry(chain, &child->val, list) {
			if (first) {
				first = false;
334
				chain->has_children = chain->list.next != &child->val ||
335
							 !RB_EMPTY_ROOT(&child->rb_root);
336
			} else
337
				chain->has_children = chain->list.next == &child->val &&
338
							 !RB_EMPTY_ROOT(&child->rb_root);
339 340 341 342 343 344
		}

		callchain_node__init_have_children_rb_tree(child);
	}
}

345 346
static void callchain_node__init_have_children(struct callchain_node *node,
					       bool has_sibling)
347 348 349
{
	struct callchain_list *chain;

350
	chain = list_entry(node->val.next, struct callchain_list, list);
351
	chain->has_children = has_sibling;
352

353
	if (!list_empty(&node->val)) {
354
		chain = list_entry(node->val.prev, struct callchain_list, list);
355
		chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);
356
	}
357

358
	callchain_node__init_have_children_rb_tree(node);
359 360
}

361
static void callchain__init_have_children(struct rb_root *root)
362
{
363 364
	struct rb_node *nd = rb_first(root);
	bool has_sibling = nd && rb_next(nd);
365

366
	for (nd = rb_first(root); nd; nd = rb_next(nd)) {
367
		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
368
		callchain_node__init_have_children(node, has_sibling);
369 370
		if (callchain_param.mode == CHAIN_FLAT ||
		    callchain_param.mode == CHAIN_FOLDED)
371
			callchain_node__make_parent_list(node);
372 373 374
	}
}

375
static void hist_entry__init_have_children(struct hist_entry *he)
376
{
377 378 379 380
	if (he->init_have_children)
		return;

	if (he->leaf) {
381
		he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
382
		callchain__init_have_children(&he->sorted_chain);
383
	} else {
384
		he->has_children = !RB_EMPTY_ROOT(&he->hroot_out.rb_root);
385
	}
386 387

	he->init_have_children = true;
388 389
}

390
static bool hist_browser__toggle_fold(struct hist_browser *browser)
391
{
392 393 394 395 396
	struct hist_entry *he = browser->he_selection;
	struct map_symbol *ms = browser->selection;
	struct callchain_list *cl = container_of(ms, struct callchain_list, ms);
	bool has_children;

397 398 399
	if (!he || !ms)
		return false;

400 401 402 403
	if (ms == &he->ms)
		has_children = hist_entry__toggle_fold(he);
	else
		has_children = callchain_list__toggle_fold(cl);
404

405
	if (has_children) {
406 407
		int child_rows = 0;

408
		hist_entry__init_have_children(he);
409
		browser->b.nr_entries -= he->nr_rows;
410

411 412
		if (he->leaf)
			browser->nr_callchain_rows -= he->nr_rows;
413
		else
414 415 416 417 418 419 420
			browser->nr_hierarchy_entries -= he->nr_rows;

		if (symbol_conf.report_hierarchy)
			child_rows = hierarchy_count_rows(browser, he, true);

		if (he->unfolded) {
			if (he->leaf)
421 422
				he->nr_rows = callchain__count_rows(
						&he->sorted_chain);
423 424 425 426 427 428
			else
				he->nr_rows = hierarchy_count_rows(browser, he, false);

			/* account grand children */
			if (symbol_conf.report_hierarchy)
				browser->b.nr_entries += child_rows - he->nr_rows;
429 430 431 432 433

			if (!he->leaf && he->nr_rows == 0) {
				he->has_no_entry = true;
				he->nr_rows = 1;
			}
434 435 436 437
		} else {
			if (symbol_conf.report_hierarchy)
				browser->b.nr_entries -= child_rows - he->nr_rows;

438 439 440
			if (he->has_no_entry)
				he->has_no_entry = false;

441
			he->nr_rows = 0;
442
		}
443 444

		browser->b.nr_entries += he->nr_rows;
445 446 447 448 449

		if (he->leaf)
			browser->nr_callchain_rows += he->nr_rows;
		else
			browser->nr_hierarchy_entries += he->nr_rows;
450 451 452 453 454 455 456 457

		return true;
	}

	/* If it doesn't have children, no toggling performed */
	return false;
}

458
static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
459 460 461 462
{
	int n = 0;
	struct rb_node *nd;

463
	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
464 465 466 467 468 469
		struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
		struct callchain_list *chain;
		bool has_children = false;

		list_for_each_entry(chain, &child->val, list) {
			++n;
470 471
			callchain_list__set_folding(chain, unfold);
			has_children = chain->has_children;
472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488
		}

		if (has_children)
			n += callchain_node__set_folding_rb_tree(child, unfold);
	}

	return n;
}

static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
{
	struct callchain_list *chain;
	bool has_children = false;
	int n = 0;

	list_for_each_entry(chain, &node->val, list) {
		++n;
489 490
		callchain_list__set_folding(chain, unfold);
		has_children = chain->has_children;
491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511
	}

	if (has_children)
		n += callchain_node__set_folding_rb_tree(node, unfold);

	return n;
}

static int callchain__set_folding(struct rb_root *chain, bool unfold)
{
	struct rb_node *nd;
	int n = 0;

	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
		n += callchain_node__set_folding(node, unfold);
	}

	return n;
}

512 513 514 515 516 517 518 519
static int hierarchy_set_folding(struct hist_browser *hb, struct hist_entry *he,
				 bool unfold __maybe_unused)
{
	float percent;
	struct rb_node *nd;
	struct hist_entry *child;
	int n = 0;

520
	for (nd = rb_first_cached(&he->hroot_out); nd; nd = rb_next(nd)) {
521 522 523 524 525 526 527 528 529
		child = rb_entry(nd, struct hist_entry, rb_node);
		percent = hist_entry__get_percent_limit(child);
		if (!child->filtered && percent >= hb->min_pcnt)
			n++;
	}

	return n;
}

530 531
static void __hist_entry__set_folding(struct hist_entry *he,
				      struct hist_browser *hb, bool unfold)
532
{
533
	hist_entry__init_have_children(he);
534
	he->unfolded = unfold ? he->has_children : false;
535

536
	if (he->has_children) {
537 538 539 540 541 542 543
		int n;

		if (he->leaf)
			n = callchain__set_folding(&he->sorted_chain, unfold);
		else
			n = hierarchy_set_folding(hb, he, unfold);

544
		he->nr_rows = unfold ? n : 0;
545
	} else
546
		he->nr_rows = 0;
547 548
}

549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571
static void hist_entry__set_folding(struct hist_entry *he,
				    struct hist_browser *browser, bool unfold)
{
	double percent;

	percent = hist_entry__get_percent_limit(he);
	if (he->filtered || percent < browser->min_pcnt)
		return;

	__hist_entry__set_folding(he, browser, unfold);

	if (!he->depth || unfold)
		browser->nr_hierarchy_entries++;
	if (he->leaf)
		browser->nr_callchain_rows += he->nr_rows;
	else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) {
		browser->nr_hierarchy_entries++;
		he->has_no_entry = true;
		he->nr_rows = 1;
	} else
		he->has_no_entry = false;
}

572 573
static void
__hist_browser__set_folding(struct hist_browser *browser, bool unfold)
574 575
{
	struct rb_node *nd;
576
	struct hist_entry *he;
577

578
	nd = rb_first_cached(&browser->hists->entries);
579 580 581 582 583 584 585
	while (nd) {
		he = rb_entry(nd, struct hist_entry, rb_node);

		/* set folding state even if it's currently folded */
		nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);

		hist_entry__set_folding(he, browser, unfold);
586 587 588
	}
}

589
static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
590
{
591
	browser->nr_hierarchy_entries = 0;
592 593 594 595
	browser->nr_callchain_rows = 0;
	__hist_browser__set_folding(browser, unfold);

	browser->b.nr_entries = hist_browser__nr_entries(browser);
596
	/* Go to the start, we may be way after valid entries after a collapse */
597
	ui_browser__reset_index(&browser->b);
598 599
}

600 601 602 603 604 605 606 607 608
static void hist_browser__set_folding_selected(struct hist_browser *browser, bool unfold)
{
	if (!browser->he_selection)
		return;

	hist_entry__set_folding(browser->he_selection, browser, unfold);
	browser->b.nr_entries = hist_browser__nr_entries(browser);
}

609 610 611 612 613 614 615 616 617
static void ui_browser__warn_lost_events(struct ui_browser *browser)
{
	ui_browser__warning(browser, 4,
		"Events are being lost, check IO/CPU overload!\n\n"
		"You may want to run 'perf' using a RT scheduler policy:\n\n"
		" perf top -r 80\n\n"
		"Or reduce the sampling frequency.");
}

618 619 620 621 622
static int hist_browser__title(struct hist_browser *browser, char *bf, size_t size)
{
	return browser->title ? browser->title(browser, bf, size) : 0;
}

623 624
int hist_browser__run(struct hist_browser *browser, const char *help,
		      bool warn_lost_event)
625
{
626
	int key;
627
	char title[160];
628
	struct hist_browser_timer *hbt = browser->hbt;
629
	int delay_secs = hbt ? hbt->refresh : 0;
630

631
	browser->b.entries = &browser->hists->entries;
632
	browser->b.nr_entries = hist_browser__nr_entries(browser);
633

634
	hist_browser__title(browser, title, sizeof(title));
635

636
	if (ui_browser__show(&browser->b, title, "%s", help) < 0)
637 638 639
		return -1;

	while (1) {
640
		key = ui_browser__run(&browser->b, delay_secs);
641

642
		switch (key) {
643 644
		case K_TIMER: {
			u64 nr_entries;
645 646 647 648 649

			WARN_ON_ONCE(!hbt);

			if (hbt)
				hbt->timer(hbt->arg);
650

651 652
			if (hist_browser__has_filter(browser) ||
			    symbol_conf.report_hierarchy)
653
				hist_browser__update_nr_entries(browser);
654

655
			nr_entries = hist_browser__nr_entries(browser);
656
			ui_browser__update_nr_entries(&browser->b, nr_entries);
657

658 659 660
			if (warn_lost_event &&
			    (browser->hists->stats.nr_lost_warned !=
			    browser->hists->stats.nr_events[PERF_RECORD_LOST])) {
661 662 663
				browser->hists->stats.nr_lost_warned =
					browser->hists->stats.nr_events[PERF_RECORD_LOST];
				ui_browser__warn_lost_events(&browser->b);
664 665
			}

666
			hist_browser__title(browser, title, sizeof(title));
667
			ui_browser__show_title(&browser->b, title);
668
			continue;
669
		}
670
		case 'D': { /* Debug */
671
			static int seq;
672
			struct hist_entry *h = rb_entry(browser->b.top,
673 674
							struct hist_entry, rb_node);
			ui_helpline__pop();
675
			ui_helpline__fpush("%d: nr_ent=(%d,%d), etl: %d, rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
676 677
					   seq++, browser->b.nr_entries,
					   browser->hists->nr_entries,
678
					   browser->b.extra_title_lines,
679
					   browser->b.rows,
680 681
					   browser->b.index,
					   browser->b.top_idx,
682 683
					   h->row_offset, h->nr_rows);
		}
684 685 686
			break;
		case 'C':
			/* Collapse the whole world. */
687
			hist_browser__set_folding(browser, false);
688
			break;
689 690 691 692
		case 'c':
			/* Collapse the selected entry. */
			hist_browser__set_folding_selected(browser, false);
			break;
693 694
		case 'E':
			/* Expand the whole world. */
695
			hist_browser__set_folding(browser, true);
696
			break;
697 698 699 700
		case 'e':
			/* Expand the selected entry. */
			hist_browser__set_folding_selected(browser, true);
			break;
701 702 703 704
		case 'H':
			browser->show_headers = !browser->show_headers;
			hist_browser__update_rows(browser);
			break;
705
		case K_ENTER:
706
			if (hist_browser__toggle_fold(browser))
707 708 709
				break;
			/* fall thru */
		default:
710
			goto out;
711 712
		}
	}
713
out:
714
	ui_browser__hide(&browser->b);
715
	return key;
716 717
}

718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733
struct callchain_print_arg {
	/* for hists browser */
	off_t	row_offset;
	bool	is_current_entry;

	/* for file dump */
	FILE	*fp;
	int	printed;
};

typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
					 struct callchain_list *chain,
					 const char *str, int offset,
					 unsigned short row,
					 struct callchain_print_arg *arg);

734 735
static void hist_browser__show_callchain_entry(struct hist_browser *browser,
					       struct callchain_list *chain,
736 737 738
					       const char *str, int offset,
					       unsigned short row,
					       struct callchain_print_arg *arg)
739 740
{
	int color, width;
741
	char folded_sign = callchain_list__folded(chain);
742
	bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
743 744 745 746 747 748

	color = HE_COLORSET_NORMAL;
	width = browser->b.width - (offset + 2);
	if (ui_browser__is_current_entry(&browser->b, row)) {
		browser->selection = &chain->ms;
		color = HE_COLORSET_SELECTED;
749
		arg->is_current_entry = true;
750 751 752
	}

	ui_browser__set_color(&browser->b, color);
753
	ui_browser__gotorc(&browser->b, row, 0);
754
	ui_browser__write_nstring(&browser->b, " ", offset);
755
	ui_browser__printf(&browser->b, "%c", folded_sign);
756
	ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
757
	ui_browser__write_nstring(&browser->b, str, width);
758 759
}

760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786
static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
						  struct callchain_list *chain,
						  const char *str, int offset,
						  unsigned short row __maybe_unused,
						  struct callchain_print_arg *arg)
{
	char folded_sign = callchain_list__folded(chain);

	arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
				folded_sign, str);
}

typedef bool (*check_output_full_fn)(struct hist_browser *browser,
				     unsigned short row);

static bool hist_browser__check_output_full(struct hist_browser *browser,
					    unsigned short row)
{
	return browser->b.rows == row;
}

static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
					  unsigned short row __maybe_unused)
{
	return false;
}

787 788
#define LEVEL_OFFSET_STEP 3

789 790 791 792 793 794 795 796 797
static int hist_browser__show_callchain_list(struct hist_browser *browser,
					     struct callchain_node *node,
					     struct callchain_list *chain,
					     unsigned short row, u64 total,
					     bool need_percent, int offset,
					     print_callchain_entry_fn print,
					     struct callchain_print_arg *arg)
{
	char bf[1024], *alloc_str;
798
	char buf[64], *alloc_str2;
799
	const char *str;
800
	int ret = 1;
801 802 803 804 805 806 807

	if (arg->row_offset != 0) {
		arg->row_offset--;
		return 0;
	}

	alloc_str = NULL;
808 809
	alloc_str2 = NULL;

810 811 812
	str = callchain_list__sym_name(chain, bf, sizeof(bf),
				       browser->show_dso);

813
	if (symbol_conf.show_branchflag_count) {
814 815
		callchain_list_counts__printf_value(chain, NULL,
						    buf, sizeof(buf));
816 817 818 819 820 821

		if (asprintf(&alloc_str2, "%s%s", str, buf) < 0)
			str = "Not enough memory!";
		else
			str = alloc_str2;
	}
822

823
	if (need_percent) {
824 825 826 827 828 829 830 831 832 833 834
		callchain_node__scnprintf_value(node, buf, sizeof(buf),
						total);

		if (asprintf(&alloc_str, "%s %s", buf, str) < 0)
			str = "Not enough memory!";
		else
			str = alloc_str;
	}

	print(browser, chain, str, offset, row, arg);
	free(alloc_str);
835
	free(alloc_str2);
836

837
	return ret;
838 839
}

840 841 842 843 844 845 846 847 848 849 850 851 852 853
static bool check_percent_display(struct rb_node *node, u64 parent_total)
{
	struct callchain_node *child;

	if (node == NULL)
		return false;

	if (rb_next(node))
		return true;

	child = rb_entry(node, struct callchain_node, rb_node);
	return callchain_cumul_hits(child) != parent_total;
}

854 855 856
static int hist_browser__show_callchain_flat(struct hist_browser *browser,
					     struct rb_root *root,
					     unsigned short row, u64 total,
857
					     u64 parent_total,
858 859 860 861 862 863 864 865 866
					     print_callchain_entry_fn print,
					     struct callchain_print_arg *arg,
					     check_output_full_fn is_output_full)
{
	struct rb_node *node;
	int first_row = row, offset = LEVEL_OFFSET_STEP;
	bool need_percent;

	node = rb_first(root);
867
	need_percent = check_percent_display(node, parent_total);
868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931

	while (node) {
		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
		struct rb_node *next = rb_next(node);
		struct callchain_list *chain;
		char folded_sign = ' ';
		int first = true;
		int extra_offset = 0;

		list_for_each_entry(chain, &child->parent_val, list) {
			bool was_first = first;

			if (first)
				first = false;
			else if (need_percent)
				extra_offset = LEVEL_OFFSET_STEP;

			folded_sign = callchain_list__folded(chain);

			row += hist_browser__show_callchain_list(browser, child,
							chain, row, total,
							was_first && need_percent,
							offset + extra_offset,
							print, arg);

			if (is_output_full(browser, row))
				goto out;

			if (folded_sign == '+')
				goto next;
		}

		list_for_each_entry(chain, &child->val, list) {
			bool was_first = first;

			if (first)
				first = false;
			else if (need_percent)
				extra_offset = LEVEL_OFFSET_STEP;

			folded_sign = callchain_list__folded(chain);

			row += hist_browser__show_callchain_list(browser, child,
							chain, row, total,
							was_first && need_percent,
							offset + extra_offset,
							print, arg);

			if (is_output_full(browser, row))
				goto out;

			if (folded_sign == '+')
				break;
		}

next:
		if (is_output_full(browser, row))
			break;
		node = next;
	}
out:
	return row - first_row;
}

932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960
static char *hist_browser__folded_callchain_str(struct hist_browser *browser,
						struct callchain_list *chain,
						char *value_str, char *old_str)
{
	char bf[1024];
	const char *str;
	char *new;

	str = callchain_list__sym_name(chain, bf, sizeof(bf),
				       browser->show_dso);
	if (old_str) {
		if (asprintf(&new, "%s%s%s", old_str,
			     symbol_conf.field_sep ?: ";", str) < 0)
			new = NULL;
	} else {
		if (value_str) {
			if (asprintf(&new, "%s %s", value_str, str) < 0)
				new = NULL;
		} else {
			if (asprintf(&new, "%s", str) < 0)
				new = NULL;
		}
	}
	return new;
}

static int hist_browser__show_callchain_folded(struct hist_browser *browser,
					       struct rb_root *root,
					       unsigned short row, u64 total,
961
					       u64 parent_total,
962 963 964 965 966 967 968 969 970
					       print_callchain_entry_fn print,
					       struct callchain_print_arg *arg,
					       check_output_full_fn is_output_full)
{
	struct rb_node *node;
	int first_row = row, offset = LEVEL_OFFSET_STEP;
	bool need_percent;

	node = rb_first(root);
971
	need_percent = check_percent_display(node, parent_total);
972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042

	while (node) {
		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
		struct rb_node *next = rb_next(node);
		struct callchain_list *chain, *first_chain = NULL;
		int first = true;
		char *value_str = NULL, *value_str_alloc = NULL;
		char *chain_str = NULL, *chain_str_alloc = NULL;

		if (arg->row_offset != 0) {
			arg->row_offset--;
			goto next;
		}

		if (need_percent) {
			char buf[64];

			callchain_node__scnprintf_value(child, buf, sizeof(buf), total);
			if (asprintf(&value_str, "%s", buf) < 0) {
				value_str = (char *)"<...>";
				goto do_print;
			}
			value_str_alloc = value_str;
		}

		list_for_each_entry(chain, &child->parent_val, list) {
			chain_str = hist_browser__folded_callchain_str(browser,
						chain, value_str, chain_str);
			if (first) {
				first = false;
				first_chain = chain;
			}

			if (chain_str == NULL) {
				chain_str = (char *)"Not enough memory!";
				goto do_print;
			}

			chain_str_alloc = chain_str;
		}

		list_for_each_entry(chain, &child->val, list) {
			chain_str = hist_browser__folded_callchain_str(browser,
						chain, value_str, chain_str);
			if (first) {
				first = false;
				first_chain = chain;
			}

			if (chain_str == NULL) {
				chain_str = (char *)"Not enough memory!";
				goto do_print;
			}

			chain_str_alloc = chain_str;
		}

do_print:
		print(browser, first_chain, chain_str, offset, row++, arg);
		free(value_str_alloc);
		free(chain_str_alloc);

next:
		if (is_output_full(browser, row))
			break;
		node = next;
	}

	return row - first_row;
}

1043
static int hist_browser__show_callchain_graph(struct hist_browser *browser,
1044
					struct rb_root *root, int level,
1045
					unsigned short row, u64 total,
1046
					u64 parent_total,
1047 1048 1049
					print_callchain_entry_fn print,
					struct callchain_print_arg *arg,
					check_output_full_fn is_output_full)
1050 1051
{
	struct rb_node *node;
1052
	int first_row = row, offset = level * LEVEL_OFFSET_STEP;
1053
	bool need_percent;
1054 1055 1056 1057
	u64 percent_total = total;

	if (callchain_param.mode == CHAIN_GRAPH_REL)
		percent_total = parent_total;
1058

1059
	node = rb_first(root);
1060
	need_percent = check_percent_display(node, parent_total);
1061

1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072
	while (node) {
		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
		struct rb_node *next = rb_next(node);
		struct callchain_list *chain;
		char folded_sign = ' ';
		int first = true;
		int extra_offset = 0;

		list_for_each_entry(chain, &child->val, list) {
			bool was_first = first;

1073
			if (first)
1074
				first = false;
1075
			else if (need_percent)
1076 1077 1078
				extra_offset = LEVEL_OFFSET_STEP;

			folded_sign = callchain_list__folded(chain);
1079

1080
			row += hist_browser__show_callchain_list(browser, child,
1081
							chain, row, percent_total,
1082 1083 1084
							was_first && need_percent,
							offset + extra_offset,
							print, arg);
1085

1086
			if (is_output_full(browser, row))
1087
				goto out;
1088

1089 1090 1091 1092 1093 1094 1095
			if (folded_sign == '+')
				break;
		}

		if (folded_sign == '-') {
			const int new_level = level + (extra_offset ? 2 : 1);

1096
			row += hist_browser__show_callchain_graph(browser, &child->rb_root,
1097 1098
							    new_level, row, total,
							    child->children_hit,
1099
							    print, arg, is_output_full);
1100
		}
1101
		if (is_output_full(browser, row))
1102
			break;
1103
		node = next;
1104
	}
1105
out:
1106 1107 1108
	return row - first_row;
}

1109 1110 1111 1112 1113 1114 1115 1116
static int hist_browser__show_callchain(struct hist_browser *browser,
					struct hist_entry *entry, int level,
					unsigned short row,
					print_callchain_entry_fn print,
					struct callchain_print_arg *arg,
					check_output_full_fn is_output_full)
{
	u64 total = hists__total_period(entry->hists);
1117
	u64 parent_total;
1118 1119
	int printed;

1120 1121 1122 1123
	if (symbol_conf.cumulate_callchain)
		parent_total = entry->stat_acc->period;
	else
		parent_total = entry->stat.period;
1124 1125 1126

	if (callchain_param.mode == CHAIN_FLAT) {
		printed = hist_browser__show_callchain_flat(browser,
1127 1128 1129
						&entry->sorted_chain, row,
						total, parent_total, print, arg,
						is_output_full);
1130 1131
	} else if (callchain_param.mode == CHAIN_FOLDED) {
		printed = hist_browser__show_callchain_folded(browser,
1132 1133 1134
						&entry->sorted_chain, row,
						total, parent_total, print, arg,
						is_output_full);
1135 1136
	} else {
		printed = hist_browser__show_callchain_graph(browser,
1137 1138 1139
						&entry->sorted_chain, level, row,
						total, parent_total, print, arg,
						is_output_full);
1140 1141 1142 1143 1144 1145 1146 1147
	}

	if (arg->is_current_entry)
		browser->he_selection = entry;

	return printed;
}

1148 1149 1150 1151 1152 1153
struct hpp_arg {
	struct ui_browser *b;
	char folded_sign;
	bool current_entry;
};

1154
int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
1155 1156
{
	struct hpp_arg *arg = hpp->ptr;
1157
	int ret, len;
1158 1159
	va_list args;
	double percent;
1160

1161
	va_start(args, fmt);
1162
	len = va_arg(args, int);
1163 1164
	percent = va_arg(args, double);
	va_end(args);
1165

1166
	ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
1167

1168
	ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
1169
	ui_browser__printf(arg->b, "%s", hpp->buf);
1170 1171 1172 1173

	return ret;
}

1174
#define __HPP_COLOR_PERCENT_FN(_type, _field)				\
1175 1176 1177 1178 1179
static u64 __hpp_get_##_field(struct hist_entry *he)			\
{									\
	return he->stat._field;						\
}									\
									\
1180
static int								\
1181
hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,		\
1182 1183
				struct perf_hpp *hpp,			\
				struct hist_entry *he)			\
1184
{									\
1185 1186
	return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%",	\
			__hpp__slsmg_color_printf, true);		\
1187 1188
}

1189 1190 1191 1192 1193 1194 1195
#define __HPP_COLOR_ACC_PERCENT_FN(_type, _field)			\
static u64 __hpp_get_acc_##_field(struct hist_entry *he)		\
{									\
	return he->stat_acc->_field;					\
}									\
									\
static int								\
1196
hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,		\
1197 1198 1199 1200
				struct perf_hpp *hpp,			\
				struct hist_entry *he)			\
{									\
	if (!symbol_conf.cumulate_callchain) {				\
1201
		struct hpp_arg *arg = hpp->ptr;				\
1202
		int len = fmt->user_len ?: fmt->len;			\
1203
		int ret = scnprintf(hpp->buf, hpp->size,		\
1204
				    "%*s", len, "N/A");			\
1205
		ui_browser__printf(arg->b, "%s", hpp->buf);		\
1206 1207 1208
									\
		return ret;						\
	}								\
1209 1210
	return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field,		\
			" %*.2f%%", __hpp__slsmg_color_printf, true);	\
1211 1212
}

1213 1214 1215 1216 1217
__HPP_COLOR_PERCENT_FN(overhead, period)
__HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
__HPP_COLOR_PERCENT_FN(overhead_us, period_us)
__HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
__HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
1218
__HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
1219

1220
#undef __HPP_COLOR_PERCENT_FN
1221
#undef __HPP_COLOR_ACC_PERCENT_FN
1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234

void hist_browser__init_hpp(void)
{
	perf_hpp__format[PERF_HPP__OVERHEAD].color =
				hist_browser__hpp_color_overhead;
	perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
				hist_browser__hpp_color_overhead_sys;
	perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
				hist_browser__hpp_color_overhead_us;
	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
				hist_browser__hpp_color_overhead_guest_sys;
	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
				hist_browser__hpp_color_overhead_guest_us;
1235 1236
	perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
				hist_browser__hpp_color_overhead_acc;
1237 1238

	res_sample_init();
1239 1240
}

1241
static int hist_browser__show_entry(struct hist_browser *browser,
1242 1243 1244
				    struct hist_entry *entry,
				    unsigned short row)
{
1245
	int printed = 0;
1246
	int width = browser->b.width;
1247
	char folded_sign = ' ';
1248
	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1249
	bool use_callchain = hist_entry__has_callchains(entry) && symbol_conf.use_callchain;
1250
	off_t row_offset = entry->row_offset;
1251
	bool first = true;
1252
	struct perf_hpp_fmt *fmt;
1253 1254

	if (current_entry) {
1255 1256
		browser->he_selection = entry;
		browser->selection = &entry->ms;
1257 1258
	}

1259
	if (use_callchain) {
1260
		hist_entry__init_have_children(entry);
1261 1262 1263 1264
		folded_sign = hist_entry__folded(entry);
	}

	if (row_offset == 0) {
1265
		struct hpp_arg arg = {
1266
			.b		= &browser->b,
1267 1268 1269
			.folded_sign	= folded_sign,
			.current_entry	= current_entry,
		};
1270
		int column = 0;
1271

1272
		ui_browser__gotorc(&browser->b, row, 0);
1273

1274
		hists__for_each_format(browser->hists, fmt) {
1275 1276 1277 1278 1279 1280 1281
			char s[2048];
			struct perf_hpp hpp = {
				.buf	= s,
				.size	= sizeof(s),
				.ptr	= &arg,
			};

1282 1283
			if (perf_hpp__should_skip(fmt, entry->hists) ||
			    column++ < browser->b.horiz_scroll)
1284 1285
				continue;

1286 1287 1288 1289 1290 1291 1292 1293 1294
			if (current_entry && browser->b.navkeypressed) {
				ui_browser__set_color(&browser->b,
						      HE_COLORSET_SELECTED);
			} else {
				ui_browser__set_color(&browser->b,
						      HE_COLORSET_NORMAL);
			}

			if (first) {
1295
				if (use_callchain) {
1296
					ui_browser__printf(&browser->b, "%c ", folded_sign);
1297 1298 1299 1300
					width -= 2;
				}
				first = false;
			} else {
1301
				ui_browser__printf(&browser->b, "  ");
1302 1303
				width -= 2;
			}
1304

1305
			if (fmt->color) {
1306 1307 1308 1309 1310 1311 1312
				int ret = fmt->color(fmt, &hpp, entry);
				hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
				/*
				 * fmt->color() already used ui_browser to
				 * print the non alignment bits, skip it (+ret):
				 */
				ui_browser__printf(&browser->b, "%s", s + ret);
1313
			} else {
1314
				hist_entry__snprintf_alignment(entry, &hpp, fmt, fmt->entry(fmt, &hpp, entry));
1315
				ui_browser__printf(&browser->b, "%s", s);
1316
			}
1317
			width -= hpp.buf - s;
1318 1319
		}

1320 1321 1322 1323
		/* The scroll bar isn't being used */
		if (!browser->b.navkeypressed)
			width += 1;

1324
		ui_browser__write_nstring(&browser->b, "", width);
1325

1326 1327 1328 1329 1330
		++row;
		++printed;
	} else
		--row_offset;

1331
	if (folded_sign == '-' && row != browser->b.rows) {
1332 1333 1334 1335
		struct callchain_print_arg arg = {
			.row_offset = row_offset,
			.is_current_entry = current_entry,
		};
1336

1337 1338 1339 1340 1341
		printed += hist_browser__show_callchain(browser,
				entry, 1, row,
				hist_browser__show_callchain_entry,
				&arg,
				hist_browser__check_output_full);
1342 1343 1344 1345 1346
	}

	return printed;
}

1347 1348 1349
static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
					      struct hist_entry *entry,
					      unsigned short row,
1350
					      int level)
1351 1352 1353 1354 1355 1356 1357 1358
{
	int printed = 0;
	int width = browser->b.width;
	char folded_sign = ' ';
	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
	off_t row_offset = entry->row_offset;
	bool first = true;
	struct perf_hpp_fmt *fmt;
1359
	struct perf_hpp_list_node *fmt_node;
1360 1361 1362 1363 1364
	struct hpp_arg arg = {
		.b		= &browser->b,
		.current_entry	= current_entry,
	};
	int column = 0;
1365
	int hierarchy_indent = (entry->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380

	if (current_entry) {
		browser->he_selection = entry;
		browser->selection = &entry->ms;
	}

	hist_entry__init_have_children(entry);
	folded_sign = hist_entry__folded(entry);
	arg.folded_sign = folded_sign;

	if (entry->leaf && row_offset) {
		row_offset--;
		goto show_callchain;
	}

1381
	ui_browser__gotorc(&browser->b, row, 0);
1382 1383 1384 1385 1386 1387 1388 1389 1390

	if (current_entry && browser->b.navkeypressed)
		ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
	else
		ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);

	ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
	width -= level * HIERARCHY_INDENT;

1391 1392 1393 1394
	/* the first hpp_list_node is for overhead columns */
	fmt_node = list_first_entry(&entry->hists->hpp_formats,
				    struct perf_hpp_list_node, list);
	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414
		char s[2048];
		struct perf_hpp hpp = {
			.buf		= s,
			.size		= sizeof(s),
			.ptr		= &arg,
		};

		if (perf_hpp__should_skip(fmt, entry->hists) ||
		    column++ < browser->b.horiz_scroll)
			continue;

		if (current_entry && browser->b.navkeypressed) {
			ui_browser__set_color(&browser->b,
					      HE_COLORSET_SELECTED);
		} else {
			ui_browser__set_color(&browser->b,
					      HE_COLORSET_NORMAL);
		}

		if (first) {
1415 1416
			ui_browser__printf(&browser->b, "%c ", folded_sign);
			width -= 2;
1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438
			first = false;
		} else {
			ui_browser__printf(&browser->b, "  ");
			width -= 2;
		}

		if (fmt->color) {
			int ret = fmt->color(fmt, &hpp, entry);
			hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
			/*
			 * fmt->color() already used ui_browser to
			 * print the non alignment bits, skip it (+ret):
			 */
			ui_browser__printf(&browser->b, "%s", s + ret);
		} else {
			int ret = fmt->entry(fmt, &hpp, entry);
			hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
			ui_browser__printf(&browser->b, "%s", s);
		}
		width -= hpp.buf - s;
	}

1439 1440 1441 1442
	if (!first) {
		ui_browser__write_nstring(&browser->b, "", hierarchy_indent);
		width -= hierarchy_indent;
	}
1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459

	if (column >= browser->b.horiz_scroll) {
		char s[2048];
		struct perf_hpp hpp = {
			.buf		= s,
			.size		= sizeof(s),
			.ptr		= &arg,
		};

		if (current_entry && browser->b.navkeypressed) {
			ui_browser__set_color(&browser->b,
					      HE_COLORSET_SELECTED);
		} else {
			ui_browser__set_color(&browser->b,
					      HE_COLORSET_NORMAL);
		}

1460
		perf_hpp_list__for_each_format(entry->hpp_list, fmt) {
1461 1462 1463 1464 1465 1466 1467
			if (first) {
				ui_browser__printf(&browser->b, "%c ", folded_sign);
				first = false;
			} else {
				ui_browser__write_nstring(&browser->b, "", 2);
			}

1468
			width -= 2;
1469

1470 1471 1472 1473 1474 1475 1476 1477 1478
			/*
			 * No need to call hist_entry__snprintf_alignment()
			 * since this fmt is always the last column in the
			 * hierarchy mode.
			 */
			if (fmt->color) {
				width -= fmt->color(fmt, &hpp, entry);
			} else {
				int i = 0;
1479

1480
				width -= fmt->entry(fmt, &hpp, entry);
1481
				ui_browser__printf(&browser->b, "%s", skip_spaces(s));
1482

1483 1484 1485
				while (isspace(s[i++]))
					width++;
			}
1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512
		}
	}

	/* The scroll bar isn't being used */
	if (!browser->b.navkeypressed)
		width += 1;

	ui_browser__write_nstring(&browser->b, "", width);

	++row;
	++printed;

show_callchain:
	if (entry->leaf && folded_sign == '-' && row != browser->b.rows) {
		struct callchain_print_arg carg = {
			.row_offset = row_offset,
		};

		printed += hist_browser__show_callchain(browser, entry,
					level + 1, row,
					hist_browser__show_callchain_entry, &carg,
					hist_browser__check_output_full);
	}

	return printed;
}

1513
static int hist_browser__show_no_entry(struct hist_browser *browser,
1514
				       unsigned short row, int level)
1515 1516 1517 1518 1519 1520 1521
{
	int width = browser->b.width;
	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
	bool first = true;
	int column = 0;
	int ret;
	struct perf_hpp_fmt *fmt;
1522
	struct perf_hpp_list_node *fmt_node;
1523
	int indent = browser->hists->nr_hpp_node - 2;
1524 1525 1526 1527 1528 1529

	if (current_entry) {
		browser->he_selection = NULL;
		browser->selection = NULL;
	}

1530
	ui_browser__gotorc(&browser->b, row, 0);
1531 1532 1533 1534 1535 1536 1537 1538 1539

	if (current_entry && browser->b.navkeypressed)
		ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
	else
		ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);

	ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
	width -= level * HIERARCHY_INDENT;

1540 1541 1542 1543
	/* the first hpp_list_node is for overhead columns */
	fmt_node = list_first_entry(&browser->hists->hpp_formats,
				    struct perf_hpp_list_node, list);
	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1544 1545 1546 1547
		if (perf_hpp__should_skip(fmt, browser->hists) ||
		    column++ < browser->b.horiz_scroll)
			continue;

1548
		ret = fmt->width(fmt, NULL, browser->hists);
1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562

		if (first) {
			/* for folded sign */
			first = false;
			ret++;
		} else {
			/* space between columns */
			ret += 2;
		}

		ui_browser__write_nstring(&browser->b, "", ret);
		width -= ret;
	}

1563 1564
	ui_browser__write_nstring(&browser->b, "", indent * HIERARCHY_INDENT);
	width -= indent * HIERARCHY_INDENT;
1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581

	if (column >= browser->b.horiz_scroll) {
		char buf[32];

		ret = snprintf(buf, sizeof(buf), "no entry >= %.2f%%", browser->min_pcnt);
		ui_browser__printf(&browser->b, "  %s", buf);
		width -= ret + 2;
	}

	/* The scroll bar isn't being used */
	if (!browser->b.navkeypressed)
		width += 1;

	ui_browser__write_nstring(&browser->b, "", width);
	return 1;
}

1582 1583 1584 1585 1586 1587
static int advance_hpp_check(struct perf_hpp *hpp, int inc)
{
	advance_hpp(hpp, inc);
	return hpp->size <= 0;
}

1588 1589 1590
static int
hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf,
				 size_t size, int line)
1591
{
1592
	struct hists *hists = browser->hists;
1593 1594 1595 1596 1597 1598
	struct perf_hpp dummy_hpp = {
		.buf    = buf,
		.size   = size,
	};
	struct perf_hpp_fmt *fmt;
	size_t ret = 0;
1599
	int column = 0;
1600
	int span = 0;
1601

1602
	if (hists__has_callchains(hists) && symbol_conf.use_callchain) {
1603 1604 1605 1606 1607
		ret = scnprintf(buf, size, "  ");
		if (advance_hpp_check(&dummy_hpp, ret))
			return ret;
	}

1608
	hists__for_each_format(browser->hists, fmt) {
1609
		if (perf_hpp__should_skip(fmt, hists)  || column++ < browser->b.horiz_scroll)
1610 1611
			continue;

1612
		ret = fmt->header(fmt, &dummy_hpp, hists, line, &span);
1613 1614 1615
		if (advance_hpp_check(&dummy_hpp, ret))
			break;

1616 1617 1618
		if (span)
			continue;

1619 1620 1621 1622 1623 1624 1625 1626
		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
		if (advance_hpp_check(&dummy_hpp, ret))
			break;
	}

	return ret;
}

1627 1628 1629 1630 1631 1632 1633 1634
static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size)
{
	struct hists *hists = browser->hists;
	struct perf_hpp dummy_hpp = {
		.buf    = buf,
		.size   = size,
	};
	struct perf_hpp_fmt *fmt;
1635
	struct perf_hpp_list_node *fmt_node;
1636 1637
	size_t ret = 0;
	int column = 0;
1638
	int indent = hists->nr_hpp_node - 2;
1639
	bool first_node, first_col;
1640

1641
	ret = scnprintf(buf, size, "  ");
1642 1643 1644
	if (advance_hpp_check(&dummy_hpp, ret))
		return ret;

1645
	first_node = true;
1646 1647 1648 1649
	/* the first hpp_list_node is for overhead columns */
	fmt_node = list_first_entry(&hists->hpp_formats,
				    struct perf_hpp_list_node, list);
	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1650 1651 1652
		if (column++ < browser->b.horiz_scroll)
			continue;

1653
		ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1654 1655 1656 1657 1658 1659
		if (advance_hpp_check(&dummy_hpp, ret))
			break;

		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
		if (advance_hpp_check(&dummy_hpp, ret))
			break;
1660 1661

		first_node = false;
1662 1663
	}

1664 1665 1666 1667 1668 1669
	if (!first_node) {
		ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s",
				indent * HIERARCHY_INDENT, "");
		if (advance_hpp_check(&dummy_hpp, ret))
			return ret;
	}
1670

1671 1672 1673
	first_node = true;
	list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
		if (!first_node) {
1674 1675 1676 1677
			ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " / ");
			if (advance_hpp_check(&dummy_hpp, ret))
				break;
		}
1678
		first_node = false;
1679

1680 1681 1682
		first_col = true;
		perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
			char *start;
1683

1684 1685
			if (perf_hpp__should_skip(fmt, hists))
				continue;
1686

1687 1688 1689 1690 1691 1692
			if (!first_col) {
				ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "+");
				if (advance_hpp_check(&dummy_hpp, ret))
					break;
			}
			first_col = false;
1693

1694
			ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1695 1696
			dummy_hpp.buf[ret] = '\0';

1697
			start = strim(dummy_hpp.buf);
1698 1699 1700 1701 1702 1703 1704 1705
			ret = strlen(start);

			if (start != dummy_hpp.buf)
				memmove(dummy_hpp.buf, start, ret + 1);

			if (advance_hpp_check(&dummy_hpp, ret))
				break;
		}
1706 1707 1708 1709 1710
	}

	return ret;
}

1711
static void hists_browser__hierarchy_headers(struct hist_browser *browser)
1712
{
1713 1714
	char headers[1024];

1715 1716 1717
	hists_browser__scnprintf_hierarchy_headers(browser, headers,
						   sizeof(headers));

1718 1719
	ui_browser__gotorc(&browser->b, 0, 0);
	ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1720
	ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1721 1722
}

1723 1724
static void hists_browser__headers(struct hist_browser *browser)
{
1725 1726
	struct hists *hists = browser->hists;
	struct perf_hpp_list *hpp_list = hists->hpp_list;
1727

1728
	int line;
1729

1730 1731 1732 1733 1734 1735
	for (line = 0; line < hpp_list->nr_header_lines; line++) {
		char headers[1024];

		hists_browser__scnprintf_headers(browser, headers,
						 sizeof(headers), line);

1736
		ui_browser__gotorc_title(&browser->b, line, 0);
1737 1738 1739
		ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
		ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
	}
1740 1741 1742 1743 1744 1745 1746 1747 1748 1749
}

static void hist_browser__show_headers(struct hist_browser *browser)
{
	if (symbol_conf.report_hierarchy)
		hists_browser__hierarchy_headers(browser);
	else
		hists_browser__headers(browser);
}

1750 1751 1752 1753 1754 1755
static void ui_browser__hists_init_top(struct ui_browser *browser)
{
	if (browser->top == NULL) {
		struct hist_browser *hb;

		hb = container_of(browser, struct hist_browser, b);
1756
		browser->top = rb_first_cached(&hb->hists->entries);
1757 1758 1759
	}
}

1760
static unsigned int hist_browser__refresh(struct ui_browser *browser)
1761 1762 1763
{
	unsigned row = 0;
	struct rb_node *nd;
1764
	struct hist_browser *hb = container_of(browser, struct hist_browser, b);
1765

1766
	if (hb->show_headers)
1767 1768
		hist_browser__show_headers(hb);

1769
	ui_browser__hists_init_top(browser);
1770 1771
	hb->he_selection = NULL;
	hb->selection = NULL;
1772

1773
	for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) {
1774
		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1775
		float percent;
1776

1777 1778 1779
		if (h->filtered) {
			/* let it move to sibling */
			h->unfolded = false;
1780
			continue;
1781
		}
1782

1783
		percent = hist_entry__get_percent_limit(h);
1784 1785 1786
		if (percent < hb->min_pcnt)
			continue;

1787 1788
		if (symbol_conf.report_hierarchy) {
			row += hist_browser__show_hierarchy_entry(hb, h, row,
1789
								  h->depth);
1790 1791 1792 1793
			if (row == browser->rows)
				break;

			if (h->has_no_entry) {
1794
				hist_browser__show_no_entry(hb, row, h->depth + 1);
1795 1796
				row++;
			}
1797 1798 1799 1800
		} else {
			row += hist_browser__show_entry(hb, h, row);
		}

1801
		if (row == browser->rows)
1802 1803 1804
			break;
	}

1805
	return row;
1806 1807
}

1808 1809
static struct rb_node *hists__filter_entries(struct rb_node *nd,
					     float min_pcnt)
1810 1811 1812
{
	while (nd != NULL) {
		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1813
		float percent = hist_entry__get_percent_limit(h);
1814

1815
		if (!h->filtered && percent >= min_pcnt)
1816 1817
			return nd;

1818 1819 1820 1821 1822 1823 1824 1825
		/*
		 * If it's filtered, its all children also were filtered.
		 * So move to sibling node.
		 */
		if (rb_next(nd))
			nd = rb_next(nd);
		else
			nd = rb_hierarchy_next(nd);
1826 1827 1828 1829 1830
	}

	return NULL;
}

1831 1832
static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
						  float min_pcnt)
1833 1834 1835
{
	while (nd != NULL) {
		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1836
		float percent = hist_entry__get_percent_limit(h);
1837 1838

		if (!h->filtered && percent >= min_pcnt)
1839 1840
			return nd;

1841
		nd = rb_hierarchy_prev(nd);
1842 1843 1844 1845 1846
	}

	return NULL;
}

1847
static void ui_browser__hists_seek(struct ui_browser *browser,
1848 1849 1850 1851 1852
				   off_t offset, int whence)
{
	struct hist_entry *h;
	struct rb_node *nd;
	bool first = true;
1853 1854 1855
	struct hist_browser *hb;

	hb = container_of(browser, struct hist_browser, b);
1856

1857
	if (browser->nr_entries == 0)
1858 1859
		return;

1860
	ui_browser__hists_init_top(browser);
1861

1862 1863
	switch (whence) {
	case SEEK_SET:
1864
		nd = hists__filter_entries(rb_first(browser->entries),
1865
					   hb->min_pcnt);
1866 1867
		break;
	case SEEK_CUR:
1868
		nd = browser->top;
1869 1870
		goto do_offset;
	case SEEK_END:
1871 1872
		nd = rb_hierarchy_last(rb_last(browser->entries));
		nd = hists__filter_prev_entries(nd, hb->min_pcnt);
1873 1874 1875 1876 1877 1878 1879 1880 1881 1882
		first = false;
		break;
	default:
		return;
	}

	/*
	 * Moves not relative to the first visible entry invalidates its
	 * row_offset:
	 */
1883
	h = rb_entry(browser->top, struct hist_entry, rb_node);
1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899
	h->row_offset = 0;

	/*
	 * Here we have to check if nd is expanded (+), if it is we can't go
	 * the next top level hist_entry, instead we must compute an offset of
	 * what _not_ to show and not change the first visible entry.
	 *
	 * This offset increments when we are going from top to bottom and
	 * decreases when we're going from bottom to top.
	 *
	 * As we don't have backpointers to the top level in the callchains
	 * structure, we need to always print the whole hist_entry callchain,
	 * skipping the first ones that are before the first visible entry
	 * and stop when we printed enough lines to fill the screen.
	 */
do_offset:
1900 1901 1902
	if (!nd)
		return;

1903 1904 1905
	if (offset > 0) {
		do {
			h = rb_entry(nd, struct hist_entry, rb_node);
1906
			if (h->unfolded && h->leaf) {
1907 1908 1909 1910 1911 1912 1913
				u16 remaining = h->nr_rows - h->row_offset;
				if (offset > remaining) {
					offset -= remaining;
					h->row_offset = 0;
				} else {
					h->row_offset += offset;
					offset = 0;
1914
					browser->top = nd;
1915 1916 1917
					break;
				}
			}
1918 1919
			nd = hists__filter_entries(rb_hierarchy_next(nd),
						   hb->min_pcnt);
1920 1921 1922
			if (nd == NULL)
				break;
			--offset;
1923
			browser->top = nd;
1924 1925 1926 1927
		} while (offset != 0);
	} else if (offset < 0) {
		while (1) {
			h = rb_entry(nd, struct hist_entry, rb_node);
1928
			if (h->unfolded && h->leaf) {
1929 1930 1931 1932 1933 1934 1935
				if (first) {
					if (-offset > h->row_offset) {
						offset += h->row_offset;
						h->row_offset = 0;
					} else {
						h->row_offset += offset;
						offset = 0;
1936
						browser->top = nd;
1937 1938 1939 1940 1941 1942 1943 1944 1945
						break;
					}
				} else {
					if (-offset > h->nr_rows) {
						offset += h->nr_rows;
						h->row_offset = 0;
					} else {
						h->row_offset = h->nr_rows + offset;
						offset = 0;
1946
						browser->top = nd;
1947 1948 1949 1950 1951
						break;
					}
				}
			}

1952
			nd = hists__filter_prev_entries(rb_hierarchy_prev(nd),
1953
							hb->min_pcnt);
1954 1955 1956
			if (nd == NULL)
				break;
			++offset;
1957
			browser->top = nd;
1958 1959 1960 1961 1962 1963 1964
			if (offset == 0) {
				/*
				 * Last unfiltered hist_entry, check if it is
				 * unfolded, if it is then we should have
				 * row_offset at its last entry.
				 */
				h = rb_entry(nd, struct hist_entry, rb_node);
1965
				if (h->unfolded && h->leaf)
1966 1967 1968 1969 1970 1971
					h->row_offset = h->nr_rows;
				break;
			}
			first = false;
		}
	} else {
1972
		browser->top = nd;
1973 1974 1975 1976 1977
		h = rb_entry(nd, struct hist_entry, rb_node);
		h->row_offset = 0;
	}
}

1978
static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1979 1980
					   struct hist_entry *he, FILE *fp,
					   int level)
1981
{
1982 1983 1984
	struct callchain_print_arg arg  = {
		.fp = fp,
	};
1985

1986
	hist_browser__show_callchain(browser, he, level, 0,
1987 1988 1989
				     hist_browser__fprintf_callchain_entry, &arg,
				     hist_browser__check_dump_full);
	return arg.printed;
1990 1991 1992 1993 1994 1995 1996 1997
}

static int hist_browser__fprintf_entry(struct hist_browser *browser,
				       struct hist_entry *he, FILE *fp)
{
	char s[8192];
	int printed = 0;
	char folded_sign = ' ';
1998 1999 2000 2001 2002 2003 2004
	struct perf_hpp hpp = {
		.buf = s,
		.size = sizeof(s),
	};
	struct perf_hpp_fmt *fmt;
	bool first = true;
	int ret;
2005

2006
	if (hist_entry__has_callchains(he) && symbol_conf.use_callchain) {
2007 2008
		folded_sign = hist_entry__folded(he);
		printed += fprintf(fp, "%c ", folded_sign);
2009
	}
2010

2011
	hists__for_each_format(browser->hists, fmt) {
2012
		if (perf_hpp__should_skip(fmt, he->hists))
2013 2014
			continue;

2015 2016 2017 2018 2019
		if (!first) {
			ret = scnprintf(hpp.buf, hpp.size, "  ");
			advance_hpp(&hpp, ret);
		} else
			first = false;
2020

2021
		ret = fmt->entry(fmt, &hpp, he);
2022
		ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret);
2023 2024
		advance_hpp(&hpp, ret);
	}
2025
	printed += fprintf(fp, "%s\n", s);
2026 2027

	if (folded_sign == '-')
2028 2029 2030 2031 2032 2033 2034 2035
		printed += hist_browser__fprintf_callchain(browser, he, fp, 1);

	return printed;
}


static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
						 struct hist_entry *he,
2036
						 FILE *fp, int level)
2037 2038 2039 2040 2041 2042 2043 2044 2045
{
	char s[8192];
	int printed = 0;
	char folded_sign = ' ';
	struct perf_hpp hpp = {
		.buf = s,
		.size = sizeof(s),
	};
	struct perf_hpp_fmt *fmt;
2046
	struct perf_hpp_list_node *fmt_node;
2047 2048
	bool first = true;
	int ret;
2049
	int hierarchy_indent = (he->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
2050 2051 2052 2053 2054 2055

	printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, "");

	folded_sign = hist_entry__folded(he);
	printed += fprintf(fp, "%c", folded_sign);

2056 2057 2058 2059
	/* the first hpp_list_node is for overhead columns */
	fmt_node = list_first_entry(&he->hists->hpp_formats,
				    struct perf_hpp_list_node, list);
	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072
		if (!first) {
			ret = scnprintf(hpp.buf, hpp.size, "  ");
			advance_hpp(&hpp, ret);
		} else
			first = false;

		ret = fmt->entry(fmt, &hpp, he);
		advance_hpp(&hpp, ret);
	}

	ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, "");
	advance_hpp(&hpp, ret);

2073 2074 2075 2076 2077 2078 2079
	perf_hpp_list__for_each_format(he->hpp_list, fmt) {
		ret = scnprintf(hpp.buf, hpp.size, "  ");
		advance_hpp(&hpp, ret);

		ret = fmt->entry(fmt, &hpp, he);
		advance_hpp(&hpp, ret);
	}
2080

2081 2082
	strim(s);
	printed += fprintf(fp, "%s\n", s);
2083 2084 2085 2086 2087

	if (he->leaf && folded_sign == '-') {
		printed += hist_browser__fprintf_callchain(browser, he, fp,
							   he->depth + 1);
	}
2088 2089 2090 2091 2092 2093

	return printed;
}

static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
{
2094 2095
	struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
						   browser->min_pcnt);
2096 2097 2098 2099 2100
	int printed = 0;

	while (nd) {
		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);

2101 2102 2103
		if (symbol_conf.report_hierarchy) {
			printed += hist_browser__fprintf_hierarchy_entry(browser,
									 h, fp,
2104
									 h->depth);
2105 2106 2107 2108 2109 2110
		} else {
			printed += hist_browser__fprintf_entry(browser, h, fp);
		}

		nd = hists__filter_entries(rb_hierarchy_next(nd),
					   browser->min_pcnt);
2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136
	}

	return printed;
}

static int hist_browser__dump(struct hist_browser *browser)
{
	char filename[64];
	FILE *fp;

	while (1) {
		scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
		if (access(filename, F_OK))
			break;
		/*
 		 * XXX: Just an arbitrary lazy upper limit
 		 */
		if (++browser->print_seq == 8192) {
			ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
			return -1;
		}
	}

	fp = fopen(filename, "w");
	if (fp == NULL) {
		char bf[64];
2137
		const char *err = str_error_r(errno, bf, sizeof(bf));
2138
		ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149
		return -1;
	}

	++browser->print_seq;
	hist_browser__fprintf(browser, fp);
	fclose(fp);
	ui_helpline__fpush("%s written!", filename);

	return 0;
}

2150 2151
void hist_browser__init(struct hist_browser *browser,
			struct hists *hists)
2152
{
2153
	struct perf_hpp_fmt *fmt;
2154

2155 2156 2157 2158 2159 2160
	browser->hists			= hists;
	browser->b.refresh		= hist_browser__refresh;
	browser->b.refresh_dimensions	= hist_browser__refresh_dimensions;
	browser->b.seek			= ui_browser__hists_seek;
	browser->b.use_navkeypressed	= true;
	browser->show_headers		= symbol_conf.show_hist_headers;
2161
	hist_browser__set_title_space(browser);
2162

2163 2164 2165 2166 2167 2168 2169 2170 2171 2172
	if (symbol_conf.report_hierarchy) {
		struct perf_hpp_list_node *fmt_node;

		/* count overhead columns (in the first node) */
		fmt_node = list_first_entry(&hists->hpp_formats,
					    struct perf_hpp_list_node, list);
		perf_hpp_list__for_each_format(&fmt_node->hpp, fmt)
			++browser->b.columns;

		/* add a single column for whole hierarchy sort keys*/
2173
		++browser->b.columns;
2174 2175 2176 2177
	} else {
		hists__for_each_format(hists, fmt)
			++browser->b.columns;
	}
2178 2179

	hists__reset_column_width(hists);
2180 2181 2182 2183 2184 2185 2186 2187
}

struct hist_browser *hist_browser__new(struct hists *hists)
{
	struct hist_browser *browser = zalloc(sizeof(*browser));

	if (browser)
		hist_browser__init(browser, hists);
2188

2189
	return browser;
2190 2191
}

2192
static struct hist_browser *
2193
perf_evsel_browser__new(struct evsel *evsel,
2194
			struct hist_browser_timer *hbt,
2195 2196
			struct perf_env *env,
			struct annotation_options *annotation_opts)
2197 2198 2199 2200 2201 2202
{
	struct hist_browser *browser = hist_browser__new(evsel__hists(evsel));

	if (browser) {
		browser->hbt   = hbt;
		browser->env   = env;
2203
		browser->title = hists_browser__scnprintf_title;
2204
		browser->annotation_opts = annotation_opts;
2205 2206 2207 2208
	}
	return browser;
}

2209
void hist_browser__delete(struct hist_browser *browser)
2210
{
2211
	free(browser);
2212 2213
}

2214
static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
2215
{
2216
	return browser->he_selection;
2217 2218
}

2219
static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
2220
{
2221
	return browser->he_selection->thread;
2222 2223
}

2224 2225 2226 2227 2228 2229
/* Check whether the browser is for 'top' or 'report' */
static inline bool is_report_browser(void *timer)
{
	return timer == NULL;
}

2230 2231 2232 2233 2234
static int hists_browser__scnprintf_title(struct hist_browser *browser, char *bf, size_t size)
{
	struct hist_browser_timer *hbt = browser->hbt;
	int printed = __hists__scnprintf_title(browser->hists, bf, size, !is_report_browser(hbt));

2235 2236 2237
	if (!is_report_browser(hbt)) {
		struct perf_top *top = hbt->arg;

2238 2239 2240 2241
		printed += scnprintf(bf + printed, size - printed,
				     " lost: %" PRIu64 "/%" PRIu64,
				     top->lost, top->lost_total);

2242 2243 2244 2245
		printed += scnprintf(bf + printed, size - printed,
				     " drop: %" PRIu64 "/%" PRIu64,
				     top->drop, top->drop_total);

2246 2247
		if (top->zero)
			printed += scnprintf(bf + printed, size - printed, " [z]");
2248 2249

		perf_top__reset_sample_counters(top);
2250 2251
	}

2252

2253
	return printed;
2254 2255
}

2256 2257 2258 2259
static inline void free_popup_options(char **options, int n)
{
	int i;

2260 2261
	for (i = 0; i < n; ++i)
		zfree(&options[i]);
2262 2263
}

2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286
/*
 * Only runtime switching of perf data file will make "input_name" point
 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
 * whether we need to call free() for current "input_name" during the switch.
 */
static bool is_input_name_malloced = false;

static int switch_data_file(void)
{
	char *pwd, *options[32], *abs_path[32], *tmp;
	DIR *pwd_dir;
	int nr_options = 0, choice = -1, ret = -1;
	struct dirent *dent;

	pwd = getenv("PWD");
	if (!pwd)
		return ret;

	pwd_dir = opendir(pwd);
	if (!pwd_dir)
		return ret;

	memset(options, 0, sizeof(options));
2287
	memset(abs_path, 0, sizeof(abs_path));
2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313

	while ((dent = readdir(pwd_dir))) {
		char path[PATH_MAX];
		u64 magic;
		char *name = dent->d_name;
		FILE *file;

		if (!(dent->d_type == DT_REG))
			continue;

		snprintf(path, sizeof(path), "%s/%s", pwd, name);

		file = fopen(path, "r");
		if (!file)
			continue;

		if (fread(&magic, 1, 8, file) < 8)
			goto close_file_and_continue;

		if (is_perf_magic(magic)) {
			options[nr_options] = strdup(name);
			if (!options[nr_options])
				goto close_file_and_continue;

			abs_path[nr_options] = strdup(path);
			if (!abs_path[nr_options]) {
2314
				zfree(&options[nr_options]);
2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352
				ui__warning("Can't search all data files due to memory shortage.\n");
				fclose(file);
				break;
			}

			nr_options++;
		}

close_file_and_continue:
		fclose(file);
		if (nr_options >= 32) {
			ui__warning("Too many perf data files in PWD!\n"
				    "Only the first 32 files will be listed.\n");
			break;
		}
	}
	closedir(pwd_dir);

	if (nr_options) {
		choice = ui__popup_menu(nr_options, options);
		if (choice < nr_options && choice >= 0) {
			tmp = strdup(abs_path[choice]);
			if (tmp) {
				if (is_input_name_malloced)
					free((void *)input_name);
				input_name = tmp;
				is_input_name_malloced = true;
				ret = 0;
			} else
				ui__warning("Data switch failed due to memory shortage!\n");
		}
	}

	free_popup_options(options, nr_options);
	free_popup_options(abs_path, nr_options);
	return ret;
}

2353
struct popup_action {
2354
	unsigned long		time;
2355 2356
	struct thread 		*thread;
	struct map_symbol 	ms;
2357
	int			socket;
2358
	struct evsel	*evsel;
2359
	enum rstype		rstype;
2360 2361 2362 2363

	int (*fn)(struct hist_browser *browser, struct popup_action *act);
};

2364
static int
2365
do_annotate(struct hist_browser *browser, struct popup_action *act)
2366
{
2367
	struct evsel *evsel;
2368 2369 2370 2371
	struct annotation *notes;
	struct hist_entry *he;
	int err;

2372 2373
	if (!browser->annotation_opts->objdump_path &&
	    perf_env__lookup_objdump(browser->env, &browser->annotation_opts->objdump_path))
2374 2375
		return 0;

2376
	notes = symbol__annotation(act->ms.sym);
2377 2378 2379 2380
	if (!notes->src)
		return 0;

	evsel = hists_to_evsel(browser->hists);
2381 2382
	err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt,
				       browser->annotation_opts);
2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397
	he = hist_browser__selected_entry(browser);
	/*
	 * offer option to annotate the other branch source or target
	 * (if they exists) when returning from annotate
	 */
	if ((err == 'q' || err == CTRL('c')) && he->branch_info)
		return 1;

	ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
	if (err)
		ui_browser__handle_resize(&browser->b);
	return 0;
}

static int
2398 2399 2400
add_annotate_opt(struct hist_browser *browser __maybe_unused,
		 struct popup_action *act, char **optstr,
		 struct map *map, struct symbol *sym)
2401
{
2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418
	if (sym == NULL || map->dso->annotate_warned)
		return 0;

	if (asprintf(optstr, "Annotate %s", sym->name) < 0)
		return 0;

	act->ms.map = map;
	act->ms.sym = sym;
	act->fn = do_annotate;
	return 1;
}

static int
do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
{
	struct thread *thread = act->thread;

2419 2420
	if ((!hists__has(browser->hists, thread) &&
	     !hists__has(browser->hists, comm)) || thread == NULL)
2421 2422
		return 0;

2423 2424 2425 2426 2427 2428
	if (browser->hists->thread_filter) {
		pstack__remove(browser->pstack, &browser->hists->thread_filter);
		perf_hpp__set_elide(HISTC_THREAD, false);
		thread__zput(browser->hists->thread_filter);
		ui_helpline__pop();
	} else {
2429
		if (hists__has(browser->hists, thread)) {
2430 2431 2432 2433 2434 2435 2436 2437
			ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
					   thread->comm_set ? thread__comm_str(thread) : "",
					   thread->tid);
		} else {
			ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
					   thread->comm_set ? thread__comm_str(thread) : "");
		}

2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448
		browser->hists->thread_filter = thread__get(thread);
		perf_hpp__set_elide(HISTC_THREAD, false);
		pstack__push(browser->pstack, &browser->hists->thread_filter);
	}

	hists__filter_by_thread(browser->hists);
	hist_browser__reset(browser);
	return 0;
}

static int
2449 2450 2451
add_thread_opt(struct hist_browser *browser, struct popup_action *act,
	       char **optstr, struct thread *thread)
{
2452 2453
	int ret;

2454 2455
	if ((!hists__has(browser->hists, thread) &&
	     !hists__has(browser->hists, comm)) || thread == NULL)
2456 2457
		return 0;

2458
	if (hists__has(browser->hists, thread)) {
2459 2460 2461 2462 2463 2464 2465 2466 2467 2468
		ret = asprintf(optstr, "Zoom %s %s(%d) thread",
			       browser->hists->thread_filter ? "out of" : "into",
			       thread->comm_set ? thread__comm_str(thread) : "",
			       thread->tid);
	} else {
		ret = asprintf(optstr, "Zoom %s %s thread",
			       browser->hists->thread_filter ? "out of" : "into",
			       thread->comm_set ? thread__comm_str(thread) : "");
	}
	if (ret < 0)
2469 2470 2471 2472 2473 2474 2475 2476 2477
		return 0;

	act->thread = thread;
	act->fn = do_zoom_thread;
	return 1;
}

static int
do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2478
{
2479
	struct map *map = act->ms.map;
2480

2481
	if (!hists__has(browser->hists, dso) || map == NULL)
2482 2483
		return 0;

2484 2485 2486 2487 2488 2489
	if (browser->hists->dso_filter) {
		pstack__remove(browser->pstack, &browser->hists->dso_filter);
		perf_hpp__set_elide(HISTC_DSO, false);
		browser->hists->dso_filter = NULL;
		ui_helpline__pop();
	} else {
2490
		ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
2491 2492
				   __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
		browser->hists->dso_filter = map->dso;
2493 2494 2495 2496 2497 2498 2499 2500 2501 2502
		perf_hpp__set_elide(HISTC_DSO, true);
		pstack__push(browser->pstack, &browser->hists->dso_filter);
	}

	hists__filter_by_dso(browser->hists);
	hist_browser__reset(browser);
	return 0;
}

static int
2503
add_dso_opt(struct hist_browser *browser, struct popup_action *act,
2504
	    char **optstr, struct map *map)
2505
{
2506
	if (!hists__has(browser->hists, dso) || map == NULL)
2507 2508 2509 2510
		return 0;

	if (asprintf(optstr, "Zoom %s %s DSO",
		     browser->hists->dso_filter ? "out of" : "into",
2511
		     __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
2512 2513
		return 0;

2514
	act->ms.map = map;
2515 2516 2517 2518 2519 2520 2521 2522 2523
	act->fn = do_zoom_dso;
	return 1;
}

static int
do_browse_map(struct hist_browser *browser __maybe_unused,
	      struct popup_action *act)
{
	map__browse(act->ms.map);
2524 2525 2526
	return 0;
}

2527
static int
2528
add_map_opt(struct hist_browser *browser,
2529 2530
	    struct popup_action *act, char **optstr, struct map *map)
{
2531
	if (!hists__has(browser->hists, dso) || map == NULL)
2532 2533 2534 2535 2536 2537 2538 2539 2540 2541
		return 0;

	if (asprintf(optstr, "Browse map details") < 0)
		return 0;

	act->ms.map = map;
	act->fn = do_browse_map;
	return 1;
}

2542 2543
static int
do_run_script(struct hist_browser *browser __maybe_unused,
2544
	      struct popup_action *act)
2545
{
2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557
	char *script_opt;
	int len;
	int n = 0;

	len = 100;
	if (act->thread)
		len += strlen(thread__comm_str(act->thread));
	else if (act->ms.sym)
		len += strlen(act->ms.sym->name);
	script_opt = malloc(len);
	if (!script_opt)
		return -1;
2558

2559
	script_opt[0] = 0;
2560
	if (act->thread) {
2561
		n = scnprintf(script_opt, len, " -c %s ",
2562 2563
			  thread__comm_str(act->thread));
	} else if (act->ms.sym) {
2564
		n = scnprintf(script_opt, len, " -S %s ",
2565
			  act->ms.sym->name);
2566 2567
	}

2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581
	if (act->time) {
		char start[32], end[32];
		unsigned long starttime = act->time;
		unsigned long endtime = act->time + symbol_conf.time_quantum;

		if (starttime == endtime) { /* Display 1ms as fallback */
			starttime -= 1*NSEC_PER_MSEC;
			endtime += 1*NSEC_PER_MSEC;
		}
		timestamp__scnprintf_usec(starttime, start, sizeof start);
		timestamp__scnprintf_usec(endtime, end, sizeof end);
		n += snprintf(script_opt + n, len - n, " --time %s,%s", start, end);
	}

2582
	script_browse(script_opt, act->evsel);
2583
	free(script_opt);
2584 2585 2586
	return 0;
}

2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597
static int
do_res_sample_script(struct hist_browser *browser __maybe_unused,
		     struct popup_action *act)
{
	struct hist_entry *he;

	he = hist_browser__selected_entry(browser);
	res_sample_browse(he->res_samples, he->num_res, act->evsel, act->rstype);
	return 0;
}

2598
static int
2599
add_script_opt_2(struct hist_browser *browser __maybe_unused,
2600
	       struct popup_action *act, char **optstr,
2601
	       struct thread *thread, struct symbol *sym,
2602
	       struct evsel *evsel, const char *tstr)
2603
{
2604

2605
	if (thread) {
2606 2607
		if (asprintf(optstr, "Run scripts for samples of thread [%s]%s",
			     thread__comm_str(thread), tstr) < 0)
2608 2609
			return 0;
	} else if (sym) {
2610 2611
		if (asprintf(optstr, "Run scripts for samples of symbol [%s]%s",
			     sym->name, tstr) < 0)
2612 2613
			return 0;
	} else {
2614
		if (asprintf(optstr, "Run scripts for all samples%s", tstr) < 0)
2615 2616 2617 2618 2619
			return 0;
	}

	act->thread = thread;
	act->ms.sym = sym;
2620
	act->evsel = evsel;
2621 2622 2623 2624
	act->fn = do_run_script;
	return 1;
}

2625 2626 2627
static int
add_script_opt(struct hist_browser *browser,
	       struct popup_action *act, char **optstr,
2628
	       struct thread *thread, struct symbol *sym,
2629
	       struct evsel *evsel)
2630 2631 2632 2633
{
	int n, j;
	struct hist_entry *he;

2634
	n = add_script_opt_2(browser, act, optstr, thread, sym, evsel, "");
2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646

	he = hist_browser__selected_entry(browser);
	if (sort_order && strstr(sort_order, "time")) {
		char tstr[128];

		optstr++;
		act++;
		j = sprintf(tstr, " in ");
		j += timestamp__scnprintf_usec(he->time, tstr + j,
					       sizeof tstr - j);
		j += sprintf(tstr + j, "-");
		timestamp__scnprintf_usec(he->time + symbol_conf.time_quantum,
2647
				          tstr + j, sizeof tstr - j);
2648
		n += add_script_opt_2(browser, act, optstr, thread, sym,
2649
					  evsel, tstr);
2650 2651 2652 2653 2654
		act->time = he->time;
	}
	return n;
}

2655 2656 2657 2658
static int
add_res_sample_opt(struct hist_browser *browser __maybe_unused,
		   struct popup_action *act, char **optstr,
		   struct res_sample *res_sample,
2659
		   struct evsel *evsel,
2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675
		   enum rstype type)
{
	if (!res_sample)
		return 0;

	if (asprintf(optstr, "Show context for individual samples %s",
		type == A_ASM ? "with assembler" :
		type == A_SOURCE ? "with source" : "") < 0)
		return 0;

	act->fn = do_res_sample_script;
	act->evsel = evsel;
	act->rstype = type;
	return 1;
}

2676 2677 2678
static int
do_switch_data(struct hist_browser *browser __maybe_unused,
	       struct popup_action *act __maybe_unused)
2679 2680 2681 2682
{
	if (switch_data_file()) {
		ui__warning("Won't switch the data files due to\n"
			    "no valid data file get selected!\n");
2683
		return 0;
2684 2685 2686 2687 2688
	}

	return K_SWITCH_INPUT_DATA;
}

2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720
static int
add_switch_opt(struct hist_browser *browser,
	       struct popup_action *act, char **optstr)
{
	if (!is_report_browser(browser->hbt))
		return 0;

	if (asprintf(optstr, "Switch to another data file in PWD") < 0)
		return 0;

	act->fn = do_switch_data;
	return 1;
}

static int
do_exit_browser(struct hist_browser *browser __maybe_unused,
		struct popup_action *act __maybe_unused)
{
	return 0;
}

static int
add_exit_opt(struct hist_browser *browser __maybe_unused,
	     struct popup_action *act, char **optstr)
{
	if (asprintf(optstr, "Exit") < 0)
		return 0;

	act->fn = do_exit_browser;
	return 1;
}

2721 2722 2723
static int
do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
{
2724
	if (!hists__has(browser->hists, socket) || act->socket < 0)
2725 2726
		return 0;

2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745
	if (browser->hists->socket_filter > -1) {
		pstack__remove(browser->pstack, &browser->hists->socket_filter);
		browser->hists->socket_filter = -1;
		perf_hpp__set_elide(HISTC_SOCKET, false);
	} else {
		browser->hists->socket_filter = act->socket;
		perf_hpp__set_elide(HISTC_SOCKET, true);
		pstack__push(browser->pstack, &browser->hists->socket_filter);
	}

	hists__filter_by_socket(browser->hists);
	hist_browser__reset(browser);
	return 0;
}

static int
add_socket_opt(struct hist_browser *browser, struct popup_action *act,
	       char **optstr, int socket_id)
{
2746
	if (!hists__has(browser->hists, socket) || socket_id < 0)
2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758
		return 0;

	if (asprintf(optstr, "Zoom %s Processor Socket %d",
		     (browser->hists->socket_filter > -1) ? "out of" : "into",
		     socket_id) < 0)
		return 0;

	act->socket = socket_id;
	act->fn = do_zoom_socket;
	return 1;
}

2759
static void hist_browser__update_nr_entries(struct hist_browser *hb)
2760 2761
{
	u64 nr_entries = 0;
2762
	struct rb_node *nd = rb_first_cached(&hb->hists->entries);
2763

2764
	if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
2765 2766 2767 2768
		hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
		return;
	}

2769
	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2770
		nr_entries++;
2771
		nd = rb_hierarchy_next(nd);
2772 2773
	}

2774
	hb->nr_non_filtered_entries = nr_entries;
2775
	hb->nr_hierarchy_entries = nr_entries;
2776
}
2777

2778 2779 2780 2781
static void hist_browser__update_percent_limit(struct hist_browser *hb,
					       double percent)
{
	struct hist_entry *he;
2782
	struct rb_node *nd = rb_first_cached(&hb->hists->entries);
2783 2784 2785 2786 2787 2788 2789 2790
	u64 total = hists__total_period(hb->hists);
	u64 min_callchain_hits = total * (percent / 100);

	hb->min_pcnt = callchain_param.min_percent = percent;

	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
		he = rb_entry(nd, struct hist_entry, rb_node);

2791 2792 2793 2794 2795
		if (he->has_no_entry) {
			he->has_no_entry = false;
			he->nr_rows = 0;
		}

2796
		if (!he->leaf || !hist_entry__has_callchains(he) || !symbol_conf.use_callchain)
2797 2798
			goto next;

2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810
		if (callchain_param.mode == CHAIN_GRAPH_REL) {
			total = he->stat.period;

			if (symbol_conf.cumulate_callchain)
				total = he->stat_acc->period;

			min_callchain_hits = total * (percent / 100);
		}

		callchain_param.sort(&he->sorted_chain, he->callchain,
				     min_callchain_hits, &callchain_param);

2811
next:
2812
		nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
2813

2814 2815
		/* force to re-evaluate folding state of callchains */
		he->init_have_children = false;
2816
		hist_entry__set_folding(he, hb, false);
2817 2818 2819
	}
}

2820
static int perf_evsel__hists_browse(struct evsel *evsel, int nr_events,
2821
				    const char *helpline,
2822
				    bool left_exits,
2823
				    struct hist_browser_timer *hbt,
2824
				    float min_pcnt,
2825
				    struct perf_env *env,
2826 2827
				    bool warn_lost_event,
				    struct annotation_options *annotation_opts)
2828
{
2829
	struct hists *hists = evsel__hists(evsel);
2830
	struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env, annotation_opts);
2831
	struct branch_info *bi = NULL;
2832 2833
#define MAX_OPTIONS  16
	char *options[MAX_OPTIONS];
2834
	struct popup_action actions[MAX_OPTIONS];
2835
	int nr_options = 0;
2836
	int key = -1;
2837
	char buf[64];
2838
	int delay_secs = hbt ? hbt->refresh : 0;
2839

2840 2841 2842 2843
#define HIST_BROWSER_HELP_COMMON					\
	"h/?/F1        Show this window\n"				\
	"UP/DOWN/PGUP\n"						\
	"PGDN/SPACE    Navigate\n"					\
2844
	"q/ESC/CTRL+C  Exit browser or go back to previous screen\n\n"	\
2845 2846 2847
	"For multiple event sessions:\n\n"				\
	"TAB/UNTAB     Switch events\n\n"				\
	"For symbolic views (--sort has sym):\n\n"			\
2848 2849
	"ENTER         Zoom into DSO/Threads & Annotate current symbol\n" \
	"ESC           Zoom out\n"					\
2850 2851 2852 2853
	"a             Annotate current symbol\n"			\
	"C             Collapse all callchains\n"			\
	"d             Zoom into current DSO\n"				\
	"E             Expand all callchains\n"				\
2854
	"F             Toggle percentage of filtered entries\n"		\
2855
	"H             Display column headers\n"			\
2856
	"L             Change percent limit\n"				\
2857
	"m             Display context menu\n"				\
2858
	"S             Zoom into current Processor Socket\n"		\
2859 2860

	/* help messages are sorted by lexical order of the hotkey */
2861
	static const char report_help[] = HIST_BROWSER_HELP_COMMON
2862
	"i             Show header information\n"
2863 2864 2865 2866 2867 2868
	"P             Print histograms to perf.hist.N\n"
	"r             Run available scripts\n"
	"s             Switch to another data file in PWD\n"
	"t             Zoom into current Thread\n"
	"V             Verbose (DSO names in callchains, etc)\n"
	"/             Filter symbol by name";
2869
	static const char top_help[] = HIST_BROWSER_HELP_COMMON
2870 2871 2872
	"P             Print histograms to perf.hist.N\n"
	"t             Zoom into current Thread\n"
	"V             Verbose (DSO names in callchains, etc)\n"
2873
	"z             Toggle zeroing of samples\n"
2874
	"f             Enable/Disable events\n"
2875 2876
	"/             Filter symbol by name";

2877 2878 2879
	if (browser == NULL)
		return -1;

2880 2881 2882 2883
	/* reset abort key so that it can get Ctrl-C as a key */
	SLang_reset_tty();
	SLang_init_tty(0, 0, 0);

2884
	if (min_pcnt)
2885
		browser->min_pcnt = min_pcnt;
2886
	hist_browser__update_nr_entries(browser);
2887

2888
	browser->pstack = pstack__new(3);
2889
	if (browser->pstack == NULL)
2890 2891 2892 2893
		goto out;

	ui_helpline__push(helpline);

2894
	memset(options, 0, sizeof(options));
2895
	memset(actions, 0, sizeof(actions));
2896

2897 2898 2899
	if (symbol_conf.col_width_list_str)
		perf_hpp__set_user_width(symbol_conf.col_width_list_str);

2900 2901 2902
	if (!is_report_browser(hbt))
		browser->b.no_samples_msg = "Collecting samples...";

2903
	while (1) {
2904
		struct thread *thread = NULL;
2905
		struct map *map = NULL;
2906
		int choice = 0;
2907
		int socked_id = -1;
2908

2909 2910
		nr_options = 0;

2911 2912
		key = hist_browser__run(browser, helpline,
					warn_lost_event);
2913

2914 2915
		if (browser->he_selection != NULL) {
			thread = hist_browser__selected_thread(browser);
2916
			map = browser->selection->map;
2917
			socked_id = browser->he_selection->socket;
2918
		}
2919
		switch (key) {
2920 2921
		case K_TAB:
		case K_UNTAB:
2922 2923
			if (nr_events == 1)
				continue;
2924 2925 2926 2927 2928 2929
			/*
			 * Exit the browser, let hists__browser_tree
			 * go to the next or previous
			 */
			goto out_free_stack;
		case 'a':
2930
			if (!hists__has(hists, sym)) {
2931
				ui_browser__warning(&browser->b, delay_secs * 2,
2932
			"Annotation is only available for symbolic views, "
2933
			"include \"sym*\" in --sort to use it.");
2934 2935 2936
				continue;
			}

2937
			if (browser->selection == NULL ||
2938
			    browser->selection->sym == NULL ||
2939
			    browser->selection->map->dso->annotate_warned)
2940
				continue;
2941

2942 2943 2944
			actions->ms.map = browser->selection->map;
			actions->ms.sym = browser->selection->sym;
			do_annotate(browser, actions);
2945
			continue;
2946 2947 2948
		case 'P':
			hist_browser__dump(browser);
			continue;
2949
		case 'd':
2950
			actions->ms.map = map;
2951
			do_zoom_dso(browser, actions);
2952
			continue;
2953
		case 'V':
2954 2955 2956 2957
			verbose = (verbose + 1) % 4;
			browser->show_dso = verbose > 0;
			ui_helpline__fpush("Verbosity level set to %d\n",
					   verbose);
2958
			continue;
2959
		case 't':
2960 2961
			actions->thread = thread;
			do_zoom_thread(browser, actions);
2962
			continue;
2963 2964 2965 2966
		case 'S':
			actions->socket = socked_id;
			do_zoom_socket(browser, actions);
			continue;
2967
		case '/':
2968
			if (ui_browser__input_window("Symbol to show",
2969 2970
					"Please enter the name of symbol you want to see.\n"
					"To remove the filter later, press / + ENTER.",
2971 2972
					buf, "ENTER: OK, ESC: Cancel",
					delay_secs * 2) == K_ENTER) {
2973 2974
				hists->symbol_filter_str = *buf ? buf : NULL;
				hists__filter_by_symbol(hists);
2975 2976 2977
				hist_browser__reset(browser);
			}
			continue;
2978
		case 'r':
2979 2980 2981 2982 2983
			if (is_report_browser(hbt)) {
				actions->thread = NULL;
				actions->ms.sym = NULL;
				do_run_script(browser, actions);
			}
2984
			continue;
2985
		case 's':
2986
			if (is_report_browser(hbt)) {
2987
				key = do_switch_data(browser, actions);
2988 2989 2990
				if (key == K_SWITCH_INPUT_DATA)
					goto out_free_stack;
			}
2991
			continue;
2992 2993 2994 2995 2996
		case 'i':
			/* env->arch is NULL for live-mode (i.e. perf top) */
			if (env->arch)
				tui__header_window(env);
			continue;
2997 2998 2999
		case 'F':
			symbol_conf.filter_relative ^= 1;
			continue;
3000 3001 3002 3003 3004 3005 3006
		case 'z':
			if (!is_report_browser(hbt)) {
				struct perf_top *top = hbt->arg;

				top->zero = !top->zero;
			}
			continue;
3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024
		case 'L':
			if (ui_browser__input_window("Percent Limit",
					"Please enter the value you want to hide entries under that percent.",
					buf, "ENTER: OK, ESC: Cancel",
					delay_secs * 2) == K_ENTER) {
				char *end;
				double new_percent = strtod(buf, &end);

				if (new_percent < 0 || new_percent > 100) {
					ui_browser__warning(&browser->b, delay_secs * 2,
						"Invalid percent: %.2f", new_percent);
					continue;
				}

				hist_browser__update_percent_limit(browser, new_percent);
				hist_browser__reset(browser);
			}
			continue;
3025
		case K_F1:
3026 3027
		case 'h':
		case '?':
3028
			ui_browser__help_window(&browser->b,
3029
				is_report_browser(hbt) ? report_help : top_help);
3030
			continue;
3031 3032
		case K_ENTER:
		case K_RIGHT:
3033
		case 'm':
3034 3035
			/* menu */
			break;
3036
		case K_ESC:
3037
		case K_LEFT: {
3038
			const void *top;
3039

3040
			if (pstack__empty(browser->pstack)) {
3041 3042 3043 3044 3045
				/*
				 * Go back to the perf_evsel_menu__run or other user
				 */
				if (left_exits)
					goto out_free_stack;
3046 3047 3048 3049 3050 3051

				if (key == K_ESC &&
				    ui_browser__dialog_yesno(&browser->b,
							     "Do you really want to exit?"))
					goto out_free_stack;

3052
				continue;
3053
			}
3054
			top = pstack__peek(browser->pstack);
3055
			if (top == &browser->hists->dso_filter) {
3056 3057 3058 3059 3060 3061
				/*
				 * No need to set actions->dso here since
				 * it's just to remove the current filter.
				 * Ditto for thread below.
				 */
				do_zoom_dso(browser, actions);
3062
			} else if (top == &browser->hists->thread_filter) {
3063
				do_zoom_thread(browser, actions);
3064 3065 3066
			} else if (top == &browser->hists->socket_filter) {
				do_zoom_socket(browser, actions);
			}
3067 3068
			continue;
		}
3069 3070
		case 'q':
		case CTRL('c'):
3071
			goto out_free_stack;
3072
		case 'f':
3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089
			if (!is_report_browser(hbt)) {
				struct perf_top *top = hbt->arg;

				perf_evlist__toggle_enable(top->evlist);
				/*
				 * No need to refresh, resort/decay histogram
				 * entries if we are not collecting samples:
				 */
				if (top->evlist->enabled) {
					helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
					hbt->refresh = delay_secs;
				} else {
					helpline = "Press 'f' again to re-enable the events";
					hbt->refresh = 0;
				}
				continue;
			}
3090
			/* Fall thru */
3091
		default:
3092
			helpline = "Press '?' for help on key bindings";
3093
			continue;
3094 3095
		}

3096
		if (!hists__has(hists, sym) || browser->selection == NULL)
3097 3098
			goto skip_annotation;

3099
		if (sort__mode == SORT_MODE__BRANCH) {
3100 3101 3102

			if (browser->he_selection)
				bi = browser->he_selection->branch_info;
3103 3104 3105 3106

			if (bi == NULL)
				goto skip_annotation;

3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117
			nr_options += add_annotate_opt(browser,
						       &actions[nr_options],
						       &options[nr_options],
						       bi->from.map,
						       bi->from.sym);
			if (bi->to.sym != bi->from.sym)
				nr_options += add_annotate_opt(browser,
							&actions[nr_options],
							&options[nr_options],
							bi->to.map,
							bi->to.sym);
3118
		} else {
3119 3120 3121 3122 3123
			nr_options += add_annotate_opt(browser,
						       &actions[nr_options],
						       &options[nr_options],
						       browser->selection->map,
						       browser->selection->sym);
3124
		}
3125
skip_annotation:
3126 3127 3128
		nr_options += add_thread_opt(browser, &actions[nr_options],
					     &options[nr_options], thread);
		nr_options += add_dso_opt(browser, &actions[nr_options],
3129
					  &options[nr_options], map);
3130 3131
		nr_options += add_map_opt(browser, &actions[nr_options],
					  &options[nr_options],
3132 3133
					  browser->selection ?
						browser->selection->map : NULL);
3134 3135 3136
		nr_options += add_socket_opt(browser, &actions[nr_options],
					     &options[nr_options],
					     socked_id);
3137
		/* perf script support */
3138 3139 3140
		if (!is_report_browser(hbt))
			goto skip_scripting;

3141
		if (browser->he_selection) {
3142
			if (hists__has(hists, thread) && thread) {
3143 3144 3145
				nr_options += add_script_opt(browser,
							     &actions[nr_options],
							     &options[nr_options],
3146
							     thread, NULL, evsel);
3147
			}
3148 3149 3150 3151 3152 3153 3154 3155 3156
			/*
			 * Note that browser->selection != NULL
			 * when browser->he_selection is not NULL,
			 * so we don't need to check browser->selection
			 * before fetching browser->selection->sym like what
			 * we do before fetching browser->selection->map.
			 *
			 * See hist_browser__show_entry.
			 */
3157
			if (hists__has(hists, sym) && browser->selection->sym) {
3158 3159 3160
				nr_options += add_script_opt(browser,
							     &actions[nr_options],
							     &options[nr_options],
3161 3162
							     NULL, browser->selection->sym,
							     evsel);
3163
			}
3164
		}
3165
		nr_options += add_script_opt(browser, &actions[nr_options],
3166
					     &options[nr_options], NULL, NULL, evsel);
3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178
		nr_options += add_res_sample_opt(browser, &actions[nr_options],
						 &options[nr_options],
				 hist_browser__selected_entry(browser)->res_samples,
				 evsel, A_NORMAL);
		nr_options += add_res_sample_opt(browser, &actions[nr_options],
						 &options[nr_options],
				 hist_browser__selected_entry(browser)->res_samples,
				 evsel, A_ASM);
		nr_options += add_res_sample_opt(browser, &actions[nr_options],
						 &options[nr_options],
				 hist_browser__selected_entry(browser)->res_samples,
				 evsel, A_SOURCE);
3179 3180
		nr_options += add_switch_opt(browser, &actions[nr_options],
					     &options[nr_options]);
3181
skip_scripting:
3182 3183
		nr_options += add_exit_opt(browser, &actions[nr_options],
					   &options[nr_options]);
3184

3185 3186
		do {
			struct popup_action *act;
3187

3188 3189 3190
			choice = ui__popup_menu(nr_options, options);
			if (choice == -1 || choice >= nr_options)
				break;
3191

3192 3193 3194
			act = &actions[choice];
			key = act->fn(browser, act);
		} while (key == 1);
3195

3196 3197
		if (key == K_SWITCH_INPUT_DATA)
			break;
3198 3199
	}
out_free_stack:
3200
	pstack__delete(browser->pstack);
3201 3202
out:
	hist_browser__delete(browser);
3203
	free_popup_options(options, MAX_OPTIONS);
3204 3205 3206
	return key;
}

3207
struct evsel_menu {
3208
	struct ui_browser b;
3209
	struct evsel *selection;
3210
	struct annotation_options *annotation_opts;
3211
	bool lost_events, lost_events_warned;
3212
	float min_pcnt;
3213
	struct perf_env *env;
3214 3215 3216 3217 3218
};

static void perf_evsel_menu__write(struct ui_browser *browser,
				   void *entry, int row)
{
3219 3220
	struct evsel_menu *menu = container_of(browser,
						    struct evsel_menu, b);
3221
	struct evsel *evsel = list_entry(entry, struct evsel, core.node);
3222
	struct hists *hists = evsel__hists(evsel);
3223
	bool current_entry = ui_browser__is_current_entry(browser, row);
3224
	unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
3225
	const char *ev_name = perf_evsel__name(evsel);
3226
	char bf[256], unit;
3227 3228
	const char *warn = " ";
	size_t printed;
3229 3230 3231 3232

	ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
						       HE_COLORSET_NORMAL);

3233
	if (perf_evsel__is_group_event(evsel)) {
3234
		struct evsel *pos;
3235 3236 3237 3238

		ev_name = perf_evsel__group_name(evsel);

		for_each_group_member(pos, evsel) {
3239 3240
			struct hists *pos_hists = evsel__hists(pos);
			nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
3241 3242 3243
		}
	}

3244
	nr_events = convert_unit(nr_events, &unit);
3245
	printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
3246
			   unit, unit == ' ' ? "" : " ", ev_name);
3247
	ui_browser__printf(browser, "%s", bf);
3248

3249
	nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
3250 3251 3252 3253 3254
	if (nr_events != 0) {
		menu->lost_events = true;
		if (!current_entry)
			ui_browser__set_color(browser, HE_COLORSET_TOP);
		nr_events = convert_unit(nr_events, &unit);
3255 3256
		printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
				     nr_events, unit, unit == ' ' ? "" : " ");
3257 3258 3259
		warn = bf;
	}

3260
	ui_browser__write_nstring(browser, warn, browser->width - printed);
3261 3262 3263 3264 3265

	if (current_entry)
		menu->selection = evsel;
}

3266
static int perf_evsel_menu__run(struct evsel_menu *menu,
3267
				int nr_events, const char *help,
3268 3269
				struct hist_browser_timer *hbt,
				bool warn_lost_event)
3270
{
3271
	struct evlist *evlist = menu->b.priv;
3272
	struct evsel *pos;
3273
	const char *title = "Available samples";
3274
	int delay_secs = hbt ? hbt->refresh : 0;
3275
	int key;
3276

3277 3278 3279 3280 3281
	if (ui_browser__show(&menu->b, title,
			     "ESC: exit, ENTER|->: Browse histograms") < 0)
		return -1;

	while (1) {
3282
		key = ui_browser__run(&menu->b, delay_secs);
3283 3284

		switch (key) {
3285
		case K_TIMER:
3286 3287
			if (hbt)
				hbt->timer(hbt->arg);
3288

3289 3290 3291
			if (!menu->lost_events_warned &&
			    menu->lost_events &&
			    warn_lost_event) {
3292 3293 3294
				ui_browser__warn_lost_events(&menu->b);
				menu->lost_events_warned = true;
			}
3295
			continue;
3296 3297
		case K_RIGHT:
		case K_ENTER:
3298 3299 3300 3301
			if (!menu->selection)
				continue;
			pos = menu->selection;
browse_hists:
3302 3303 3304 3305 3306
			perf_evlist__set_selected(evlist, pos);
			/*
			 * Give the calling tool a chance to populate the non
			 * default evsel resorted hists tree.
			 */
3307 3308
			if (hbt)
				hbt->timer(hbt->arg);
3309
			key = perf_evsel__hists_browse(pos, nr_events, help,
3310
						       true, hbt,
3311
						       menu->min_pcnt,
3312
						       menu->env,
3313 3314
						       warn_lost_event,
						       menu->annotation_opts);
3315
			ui_browser__show_title(&menu->b, title);
3316
			switch (key) {
3317
			case K_TAB:
3318
				if (pos->core.node.next == &evlist->core.entries)
3319
					pos = perf_evlist__first(evlist);
3320
				else
3321
					pos = perf_evsel__next(pos);
3322
				goto browse_hists;
3323
			case K_UNTAB:
3324
				if (pos->core.node.prev == &evlist->core.entries)
3325
					pos = perf_evlist__last(evlist);
3326
				else
3327
					pos = perf_evsel__prev(pos);
3328
				goto browse_hists;
3329
			case K_SWITCH_INPUT_DATA:
3330 3331 3332
			case 'q':
			case CTRL('c'):
				goto out;
3333
			case K_ESC:
3334 3335 3336
			default:
				continue;
			}
3337
		case K_LEFT:
3338
			continue;
3339
		case K_ESC:
3340 3341
			if (!ui_browser__dialog_yesno(&menu->b,
					       "Do you really want to exit?"))
3342 3343
				continue;
			/* Fall thru */
3344 3345 3346
		case 'q':
		case CTRL('c'):
			goto out;
3347
		default:
3348
			continue;
3349 3350 3351
		}
	}

3352 3353 3354 3355 3356
out:
	ui_browser__hide(&menu->b);
	return key;
}

3357
static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
3358 3359
				 void *entry)
{
3360
	struct evsel *evsel = list_entry(entry, struct evsel, core.node);
3361 3362 3363 3364 3365 3366 3367

	if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
		return true;

	return false;
}

3368
static int __perf_evlist__tui_browse_hists(struct evlist *evlist,
3369
					   int nr_entries, const char *help,
3370
					   struct hist_browser_timer *hbt,
3371
					   float min_pcnt,
3372
					   struct perf_env *env,
3373 3374
					   bool warn_lost_event,
					   struct annotation_options *annotation_opts)
3375
{
3376 3377
	struct evsel *pos;
	struct evsel_menu menu = {
3378
		.b = {
3379
			.entries    = &evlist->core.entries,
3380 3381 3382
			.refresh    = ui_browser__list_head_refresh,
			.seek	    = ui_browser__list_head_seek,
			.write	    = perf_evsel_menu__write,
3383 3384
			.filter	    = filter_group_entries,
			.nr_entries = nr_entries,
3385 3386
			.priv	    = evlist,
		},
3387
		.min_pcnt = min_pcnt,
3388
		.env = env,
3389
		.annotation_opts = annotation_opts,
3390 3391 3392 3393
	};

	ui_helpline__push("Press ESC to exit");

3394
	evlist__for_each_entry(evlist, pos) {
3395
		const char *ev_name = perf_evsel__name(pos);
3396 3397 3398 3399 3400 3401
		size_t line_len = strlen(ev_name) + 7;

		if (menu.b.width < line_len)
			menu.b.width = line_len;
	}

3402 3403
	return perf_evsel_menu__run(&menu, nr_entries, help,
				    hbt, warn_lost_event);
3404 3405
}

3406
int perf_evlist__tui_browse_hists(struct evlist *evlist, const char *help,
3407
				  struct hist_browser_timer *hbt,
3408
				  float min_pcnt,
3409
				  struct perf_env *env,
3410 3411
				  bool warn_lost_event,
				  struct annotation_options *annotation_opts)
3412
{
3413
	int nr_entries = evlist->core.nr_entries;
3414 3415 3416

single_entry:
	if (nr_entries == 1) {
3417
		struct evsel *first = perf_evlist__first(evlist);
3418 3419

		return perf_evsel__hists_browse(first, nr_entries, help,
3420
						false, hbt, min_pcnt,
3421 3422
						env, warn_lost_event,
						annotation_opts);
3423 3424
	}

3425
	if (symbol_conf.event_group) {
3426
		struct evsel *pos;
3427 3428

		nr_entries = 0;
3429
		evlist__for_each_entry(evlist, pos) {
3430 3431
			if (perf_evsel__is_group_leader(pos))
				nr_entries++;
3432
		}
3433 3434 3435 3436 3437 3438

		if (nr_entries == 1)
			goto single_entry;
	}

	return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
3439
					       hbt, min_pcnt, env,
3440 3441
					       warn_lost_event,
					       annotation_opts);
3442
}