Linux Audio

Check our new training course

Open-source upstreaming

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