Linux Audio

Check our new training course

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