Linux Audio

Check our new training course

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