Linux Audio

Check our new training course

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