Linux Audio

Check our new training course

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