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