Linux Audio

Check our new training course

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