1// SPDX-License-Identifier: GPL-2.0
2#include "../browser.h"
3#include "../helpline.h"
4#include "../ui.h"
5#include "../../util/annotate.h"
6#include "../../util/debug.h"
7#include "../../util/dso.h"
8#include "../../util/hist.h"
9#include "../../util/sort.h"
10#include "../../util/map.h"
11#include "../../util/mutex.h"
12#include "../../util/symbol.h"
13#include "../../util/evsel.h"
14#include "../../util/evlist.h"
15#include <inttypes.h>
16#include <linux/kernel.h>
17#include <linux/string.h>
18#include <linux/zalloc.h>
19#include <sys/ttydefaults.h>
20#include <asm/bug.h>
21
22struct arch;
23
24struct annotate_browser {
25	struct ui_browser	    b;
26	struct rb_root		    entries;
27	struct rb_node		   *curr_hot;
28	struct annotation_line	   *selection;
29	struct arch		   *arch;
30	struct annotation_options  *opts;
31	bool			    searching_backwards;
32	char			    search_bf[128];
33};
34
35static inline struct annotation *browser__annotation(struct ui_browser *browser)
36{
37	struct map_symbol *ms = browser->priv;
38	return symbol__annotation(ms->sym);
39}
40
41static bool disasm_line__filter(struct ui_browser *browser, void *entry)
42{
43	struct annotation *notes = browser__annotation(browser);
44	struct annotation_line *al = list_entry(entry, struct annotation_line, node);
45	return annotation_line__filter(al, notes);
46}
47
48static int ui_browser__jumps_percent_color(struct ui_browser *browser, int nr, bool current)
49{
50	struct annotation *notes = browser__annotation(browser);
51
52	if (current && (!browser->use_navkeypressed || browser->navkeypressed))
53		return HE_COLORSET_SELECTED;
54	if (nr == notes->max_jump_sources)
55		return HE_COLORSET_TOP;
56	if (nr > 1)
57		return HE_COLORSET_MEDIUM;
58	return HE_COLORSET_NORMAL;
59}
60
61static int ui_browser__set_jumps_percent_color(void *browser, int nr, bool current)
62{
63	 int color = ui_browser__jumps_percent_color(browser, nr, current);
64	 return ui_browser__set_color(browser, color);
65}
66
67static int annotate_browser__set_color(void *browser, int color)
68{
69	return ui_browser__set_color(browser, color);
70}
71
72static void annotate_browser__write_graph(void *browser, int graph)
73{
74	ui_browser__write_graph(browser, graph);
75}
76
77static void annotate_browser__set_percent_color(void *browser, double percent, bool current)
78{
79	ui_browser__set_percent_color(browser, percent, current);
80}
81
82static void annotate_browser__printf(void *browser, const char *fmt, ...)
83{
84	va_list args;
85
86	va_start(args, fmt);
87	ui_browser__vprintf(browser, fmt, args);
88	va_end(args);
89}
90
91static void annotate_browser__write(struct ui_browser *browser, void *entry, int row)
92{
93	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
94	struct annotation *notes = browser__annotation(browser);
95	struct annotation_line *al = list_entry(entry, struct annotation_line, node);
96	const bool is_current_entry = ui_browser__is_current_entry(browser, row);
97	struct annotation_write_ops ops = {
98		.first_line		 = row == 0,
99		.current_entry		 = is_current_entry,
100		.change_color		 = (!notes->options->hide_src_code &&
101					    (!is_current_entry ||
102					     (browser->use_navkeypressed &&
103					      !browser->navkeypressed))),
104		.width			 = browser->width,
105		.obj			 = browser,
106		.set_color		 = annotate_browser__set_color,
107		.set_percent_color	 = annotate_browser__set_percent_color,
108		.set_jumps_percent_color = ui_browser__set_jumps_percent_color,
109		.printf			 = annotate_browser__printf,
110		.write_graph		 = annotate_browser__write_graph,
111	};
112
113	/* The scroll bar isn't being used */
114	if (!browser->navkeypressed)
115		ops.width += 1;
116
117	annotation_line__write(al, notes, &ops, ab->opts);
118
119	if (ops.current_entry)
120		ab->selection = al;
121}
122
123static int is_fused(struct annotate_browser *ab, struct disasm_line *cursor)
124{
125	struct disasm_line *pos = list_prev_entry(cursor, al.node);
126	const char *name;
127	int diff = 1;
128
129	while (pos && pos->al.offset == -1) {
130		pos = list_prev_entry(pos, al.node);
131		if (!ab->opts->hide_src_code)
132			diff++;
133	}
134
135	if (!pos)
136		return 0;
137
138	if (ins__is_lock(&pos->ins))
139		name = pos->ops.locked.ins.name;
140	else
141		name = pos->ins.name;
142
143	if (!name || !cursor->ins.name)
144		return 0;
145
146	if (ins__is_fused(ab->arch, name, cursor->ins.name))
147		return diff;
148	return 0;
149}
150
151static void annotate_browser__draw_current_jump(struct ui_browser *browser)
152{
153	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
154	struct disasm_line *cursor = disasm_line(ab->selection);
155	struct annotation_line *target;
156	unsigned int from, to;
157	struct map_symbol *ms = ab->b.priv;
158	struct symbol *sym = ms->sym;
159	struct annotation *notes = symbol__annotation(sym);
160	u8 pcnt_width = annotation__pcnt_width(notes);
161	int width;
162	int diff = 0;
163
164	/* PLT symbols contain external offsets */
165	if (strstr(sym->name, "@plt"))
166		return;
167
168	if (!disasm_line__is_valid_local_jump(cursor, sym))
169		return;
170
171	/*
172	 * This first was seen with a gcc function, _cpp_lex_token, that
173	 * has the usual jumps:
174	 *
175	 *  │1159e6c: ↓ jne    115aa32 <_cpp_lex_token@@Base+0xf92>
176	 *
177	 * I.e. jumps to a label inside that function (_cpp_lex_token), and
178	 * those works, but also this kind:
179	 *
180	 *  │1159e8b: ↓ jne    c469be <cpp_named_operator2name@@Base+0xa72>
181	 *
182	 *  I.e. jumps to another function, outside _cpp_lex_token, which
183	 *  are not being correctly handled generating as a side effect references
184	 *  to ab->offset[] entries that are set to NULL, so to make this code
185	 *  more robust, check that here.
186	 *
187	 *  A proper fix for will be put in place, looking at the function
188	 *  name right after the '<' token and probably treating this like a
189	 *  'call' instruction.
190	 */
191	target = notes->offsets[cursor->ops.target.offset];
192	if (target == NULL) {
193		ui_helpline__printf("WARN: jump target inconsistency, press 'o', notes->offsets[%#x] = NULL\n",
194				    cursor->ops.target.offset);
195		return;
196	}
197
198	if (notes->options->hide_src_code) {
199		from = cursor->al.idx_asm;
200		to = target->idx_asm;
201	} else {
202		from = (u64)cursor->al.idx;
203		to = (u64)target->idx;
204	}
205
206	width = annotation__cycles_width(notes);
207
208	ui_browser__set_color(browser, HE_COLORSET_JUMP_ARROWS);
209	__ui_browser__line_arrow(browser,
210				 pcnt_width + 2 + notes->widths.addr + width,
211				 from, to);
212
213	diff = is_fused(ab, cursor);
214	if (diff > 0) {
215		ui_browser__mark_fused(browser,
216				       pcnt_width + 3 + notes->widths.addr + width,
217				       from - diff, diff, to > from);
218	}
219}
220
221static unsigned int annotate_browser__refresh(struct ui_browser *browser)
222{
223	struct annotation *notes = browser__annotation(browser);
224	int ret = ui_browser__list_head_refresh(browser);
225	int pcnt_width = annotation__pcnt_width(notes);
226
227	if (notes->options->jump_arrows)
228		annotate_browser__draw_current_jump(browser);
229
230	ui_browser__set_color(browser, HE_COLORSET_NORMAL);
231	__ui_browser__vline(browser, pcnt_width, 0, browser->rows - 1);
232	return ret;
233}
234
235static double disasm__cmp(struct annotation_line *a, struct annotation_line *b,
236						  int percent_type)
237{
238	int i;
239
240	for (i = 0; i < a->data_nr; i++) {
241		if (a->data[i].percent[percent_type] == b->data[i].percent[percent_type])
242			continue;
243		return a->data[i].percent[percent_type] -
244			   b->data[i].percent[percent_type];
245	}
246	return 0;
247}
248
249static void disasm_rb_tree__insert(struct annotate_browser *browser,
250				struct annotation_line *al)
251{
252	struct rb_root *root = &browser->entries;
253	struct rb_node **p = &root->rb_node;
254	struct rb_node *parent = NULL;
255	struct annotation_line *l;
256
257	while (*p != NULL) {
258		parent = *p;
259		l = rb_entry(parent, struct annotation_line, rb_node);
260
261		if (disasm__cmp(al, l, browser->opts->percent_type) < 0)
262			p = &(*p)->rb_left;
263		else
264			p = &(*p)->rb_right;
265	}
266	rb_link_node(&al->rb_node, parent, p);
267	rb_insert_color(&al->rb_node, root);
268}
269
270static void annotate_browser__set_top(struct annotate_browser *browser,
271				      struct annotation_line *pos, u32 idx)
272{
273	struct annotation *notes = browser__annotation(&browser->b);
274	unsigned back;
275
276	ui_browser__refresh_dimensions(&browser->b);
277	back = browser->b.height / 2;
278	browser->b.top_idx = browser->b.index = idx;
279
280	while (browser->b.top_idx != 0 && back != 0) {
281		pos = list_entry(pos->node.prev, struct annotation_line, node);
282
283		if (annotation_line__filter(pos, notes))
284			continue;
285
286		--browser->b.top_idx;
287		--back;
288	}
289
290	browser->b.top = pos;
291	browser->b.navkeypressed = true;
292}
293
294static void annotate_browser__set_rb_top(struct annotate_browser *browser,
295					 struct rb_node *nd)
296{
297	struct annotation *notes = browser__annotation(&browser->b);
298	struct annotation_line * pos = rb_entry(nd, struct annotation_line, rb_node);
299	u32 idx = pos->idx;
300
301	if (notes->options->hide_src_code)
302		idx = pos->idx_asm;
303	annotate_browser__set_top(browser, pos, idx);
304	browser->curr_hot = nd;
305}
306
307static void annotate_browser__calc_percent(struct annotate_browser *browser,
308					   struct evsel *evsel)
309{
310	struct map_symbol *ms = browser->b.priv;
311	struct symbol *sym = ms->sym;
312	struct annotation *notes = symbol__annotation(sym);
313	struct disasm_line *pos;
314
315	browser->entries = RB_ROOT;
316
317	annotation__lock(notes);
318
319	symbol__calc_percent(sym, evsel);
320
321	list_for_each_entry(pos, &notes->src->source, al.node) {
322		double max_percent = 0.0;
323		int i;
324
325		if (pos->al.offset == -1) {
326			RB_CLEAR_NODE(&pos->al.rb_node);
327			continue;
328		}
329
330		for (i = 0; i < pos->al.data_nr; i++) {
331			double percent;
332
333			percent = annotation_data__percent(&pos->al.data[i],
334							   browser->opts->percent_type);
335
336			if (max_percent < percent)
337				max_percent = percent;
338		}
339
340		if (max_percent < 0.01 && pos->al.ipc == 0) {
341			RB_CLEAR_NODE(&pos->al.rb_node);
342			continue;
343		}
344		disasm_rb_tree__insert(browser, &pos->al);
345	}
346	annotation__unlock(notes);
347
348	browser->curr_hot = rb_last(&browser->entries);
349}
350
351static struct annotation_line *annotate_browser__find_next_asm_line(
352					struct annotate_browser *browser,
353					struct annotation_line *al)
354{
355	struct annotation_line *it = al;
356
357	/* find next asm line */
358	list_for_each_entry_continue(it, browser->b.entries, node) {
359		if (it->idx_asm >= 0)
360			return it;
361	}
362
363	/* no asm line found forwards, try backwards */
364	it = al;
365	list_for_each_entry_continue_reverse(it, browser->b.entries, node) {
366		if (it->idx_asm >= 0)
367			return it;
368	}
369
370	/* There are no asm lines */
371	return NULL;
372}
373
374static bool annotate_browser__toggle_source(struct annotate_browser *browser)
375{
376	struct annotation *notes = browser__annotation(&browser->b);
377	struct annotation_line *al;
378	off_t offset = browser->b.index - browser->b.top_idx;
379
380	browser->b.seek(&browser->b, offset, SEEK_CUR);
381	al = list_entry(browser->b.top, struct annotation_line, node);
382
383	if (notes->options->hide_src_code) {
384		if (al->idx_asm < offset)
385			offset = al->idx;
386
387		browser->b.nr_entries = notes->nr_entries;
388		notes->options->hide_src_code = false;
389		browser->b.seek(&browser->b, -offset, SEEK_CUR);
390		browser->b.top_idx = al->idx - offset;
391		browser->b.index = al->idx;
392	} else {
393		if (al->idx_asm < 0) {
394			/* move cursor to next asm line */
395			al = annotate_browser__find_next_asm_line(browser, al);
396			if (!al) {
397				browser->b.seek(&browser->b, -offset, SEEK_CUR);
398				return false;
399			}
400		}
401
402		if (al->idx_asm < offset)
403			offset = al->idx_asm;
404
405		browser->b.nr_entries = notes->nr_asm_entries;
406		notes->options->hide_src_code = true;
407		browser->b.seek(&browser->b, -offset, SEEK_CUR);
408		browser->b.top_idx = al->idx_asm - offset;
409		browser->b.index = al->idx_asm;
410	}
411
412	return true;
413}
414
415#define SYM_TITLE_MAX_SIZE (PATH_MAX + 64)
416
417static void annotate_browser__show_full_location(struct ui_browser *browser)
418{
419	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
420	struct disasm_line *cursor = disasm_line(ab->selection);
421	struct annotation_line *al = &cursor->al;
422
423	if (al->offset != -1)
424		ui_helpline__puts("Only available for source code lines.");
425	else if (al->fileloc == NULL)
426		ui_helpline__puts("No source file location.");
427	else {
428		char help_line[SYM_TITLE_MAX_SIZE];
429		sprintf (help_line, "Source file location: %s", al->fileloc);
430		ui_helpline__puts(help_line);
431	}
432}
433
434static void ui_browser__init_asm_mode(struct ui_browser *browser)
435{
436	struct annotation *notes = browser__annotation(browser);
437	ui_browser__reset_index(browser);
438	browser->nr_entries = notes->nr_asm_entries;
439}
440
441static int sym_title(struct symbol *sym, struct map *map, char *title,
442		     size_t sz, int percent_type)
443{
444	return snprintf(title, sz, "%s  %s [Percent: %s]", sym->name,
445			map__dso(map)->long_name,
446			percent_type_str(percent_type));
447}
448
449/*
450 * This can be called from external jumps, i.e. jumps from one function
451 * to another, like from the kernel's entry_SYSCALL_64 function to the
452 * swapgs_restore_regs_and_return_to_usermode() function.
453 *
454 * So all we check here is that dl->ops.target.sym is set, if it is, just
455 * go to that function and when exiting from its disassembly, come back
456 * to the calling function.
457 */
458static bool annotate_browser__callq(struct annotate_browser *browser,
459				    struct evsel *evsel,
460				    struct hist_browser_timer *hbt)
461{
462	struct map_symbol *ms = browser->b.priv, target_ms;
463	struct disasm_line *dl = disasm_line(browser->selection);
464	struct annotation *notes;
465	char title[SYM_TITLE_MAX_SIZE];
466
467	if (!dl->ops.target.sym) {
468		ui_helpline__puts("The called function was not found.");
469		return true;
470	}
471
472	notes = symbol__annotation(dl->ops.target.sym);
473	annotation__lock(notes);
474
475	if (!symbol__hists(dl->ops.target.sym, evsel->evlist->core.nr_entries)) {
476		annotation__unlock(notes);
477		ui__warning("Not enough memory for annotating '%s' symbol!\n",
478			    dl->ops.target.sym->name);
479		return true;
480	}
481
482	target_ms.maps = ms->maps;
483	target_ms.map = ms->map;
484	target_ms.sym = dl->ops.target.sym;
485	annotation__unlock(notes);
486	symbol__tui_annotate(&target_ms, evsel, hbt, browser->opts);
487	sym_title(ms->sym, ms->map, title, sizeof(title), browser->opts->percent_type);
488	ui_browser__show_title(&browser->b, title);
489	return true;
490}
491
492static
493struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
494					  s64 offset, s64 *idx)
495{
496	struct annotation *notes = browser__annotation(&browser->b);
497	struct disasm_line *pos;
498
499	*idx = 0;
500	list_for_each_entry(pos, &notes->src->source, al.node) {
501		if (pos->al.offset == offset)
502			return pos;
503		if (!annotation_line__filter(&pos->al, notes))
504			++*idx;
505	}
506
507	return NULL;
508}
509
510static bool annotate_browser__jump(struct annotate_browser *browser,
511				   struct evsel *evsel,
512				   struct hist_browser_timer *hbt)
513{
514	struct disasm_line *dl = disasm_line(browser->selection);
515	u64 offset;
516	s64 idx;
517
518	if (!ins__is_jump(&dl->ins))
519		return false;
520
521	if (dl->ops.target.outside) {
522		annotate_browser__callq(browser, evsel, hbt);
523		return true;
524	}
525
526	offset = dl->ops.target.offset;
527	dl = annotate_browser__find_offset(browser, offset, &idx);
528	if (dl == NULL) {
529		ui_helpline__printf("Invalid jump offset: %" PRIx64, offset);
530		return true;
531	}
532
533	annotate_browser__set_top(browser, &dl->al, idx);
534
535	return true;
536}
537
538static
539struct annotation_line *annotate_browser__find_string(struct annotate_browser *browser,
540					  char *s, s64 *idx)
541{
542	struct annotation *notes = browser__annotation(&browser->b);
543	struct annotation_line *al = browser->selection;
544
545	*idx = browser->b.index;
546	list_for_each_entry_continue(al, &notes->src->source, node) {
547		if (annotation_line__filter(al, notes))
548			continue;
549
550		++*idx;
551
552		if (al->line && strstr(al->line, s) != NULL)
553			return al;
554	}
555
556	return NULL;
557}
558
559static bool __annotate_browser__search(struct annotate_browser *browser)
560{
561	struct annotation_line *al;
562	s64 idx;
563
564	al = annotate_browser__find_string(browser, browser->search_bf, &idx);
565	if (al == NULL) {
566		ui_helpline__puts("String not found!");
567		return false;
568	}
569
570	annotate_browser__set_top(browser, al, idx);
571	browser->searching_backwards = false;
572	return true;
573}
574
575static
576struct annotation_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
577						  char *s, s64 *idx)
578{
579	struct annotation *notes = browser__annotation(&browser->b);
580	struct annotation_line *al = browser->selection;
581
582	*idx = browser->b.index;
583	list_for_each_entry_continue_reverse(al, &notes->src->source, node) {
584		if (annotation_line__filter(al, notes))
585			continue;
586
587		--*idx;
588
589		if (al->line && strstr(al->line, s) != NULL)
590			return al;
591	}
592
593	return NULL;
594}
595
596static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
597{
598	struct annotation_line *al;
599	s64 idx;
600
601	al = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
602	if (al == NULL) {
603		ui_helpline__puts("String not found!");
604		return false;
605	}
606
607	annotate_browser__set_top(browser, al, idx);
608	browser->searching_backwards = true;
609	return true;
610}
611
612static bool annotate_browser__search_window(struct annotate_browser *browser,
613					    int delay_secs)
614{
615	if (ui_browser__input_window("Search", "String: ", browser->search_bf,
616				     "ENTER: OK, ESC: Cancel",
617				     delay_secs * 2) != K_ENTER ||
618	    !*browser->search_bf)
619		return false;
620
621	return true;
622}
623
624static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs)
625{
626	if (annotate_browser__search_window(browser, delay_secs))
627		return __annotate_browser__search(browser);
628
629	return false;
630}
631
632static bool annotate_browser__continue_search(struct annotate_browser *browser,
633					      int delay_secs)
634{
635	if (!*browser->search_bf)
636		return annotate_browser__search(browser, delay_secs);
637
638	return __annotate_browser__search(browser);
639}
640
641static bool annotate_browser__search_reverse(struct annotate_browser *browser,
642					   int delay_secs)
643{
644	if (annotate_browser__search_window(browser, delay_secs))
645		return __annotate_browser__search_reverse(browser);
646
647	return false;
648}
649
650static
651bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
652					       int delay_secs)
653{
654	if (!*browser->search_bf)
655		return annotate_browser__search_reverse(browser, delay_secs);
656
657	return __annotate_browser__search_reverse(browser);
658}
659
660static int annotate_browser__show(struct ui_browser *browser, char *title, const char *help)
661{
662	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
663	struct map_symbol *ms = browser->priv;
664	struct symbol *sym = ms->sym;
665	char symbol_dso[SYM_TITLE_MAX_SIZE];
666
667	if (ui_browser__show(browser, title, help) < 0)
668		return -1;
669
670	sym_title(sym, ms->map, symbol_dso, sizeof(symbol_dso), ab->opts->percent_type);
671
672	ui_browser__gotorc_title(browser, 0, 0);
673	ui_browser__set_color(browser, HE_COLORSET_ROOT);
674	ui_browser__write_nstring(browser, symbol_dso, browser->width + 1);
675	return 0;
676}
677
678static void
679switch_percent_type(struct annotation_options *opts, bool base)
680{
681	switch (opts->percent_type) {
682	case PERCENT_HITS_LOCAL:
683		if (base)
684			opts->percent_type = PERCENT_PERIOD_LOCAL;
685		else
686			opts->percent_type = PERCENT_HITS_GLOBAL;
687		break;
688	case PERCENT_HITS_GLOBAL:
689		if (base)
690			opts->percent_type = PERCENT_PERIOD_GLOBAL;
691		else
692			opts->percent_type = PERCENT_HITS_LOCAL;
693		break;
694	case PERCENT_PERIOD_LOCAL:
695		if (base)
696			opts->percent_type = PERCENT_HITS_LOCAL;
697		else
698			opts->percent_type = PERCENT_PERIOD_GLOBAL;
699		break;
700	case PERCENT_PERIOD_GLOBAL:
701		if (base)
702			opts->percent_type = PERCENT_HITS_GLOBAL;
703		else
704			opts->percent_type = PERCENT_PERIOD_LOCAL;
705		break;
706	default:
707		WARN_ON(1);
708	}
709}
710
711static int annotate_browser__run(struct annotate_browser *browser,
712				 struct evsel *evsel,
713				 struct hist_browser_timer *hbt)
714{
715	struct rb_node *nd = NULL;
716	struct hists *hists = evsel__hists(evsel);
717	struct map_symbol *ms = browser->b.priv;
718	struct symbol *sym = ms->sym;
719	struct annotation *notes = symbol__annotation(ms->sym);
720	const char *help = "Press 'h' for help on key bindings";
721	int delay_secs = hbt ? hbt->refresh : 0;
722	char title[256];
723	int key;
724
725	hists__scnprintf_title(hists, title, sizeof(title));
726	if (annotate_browser__show(&browser->b, title, help) < 0)
727		return -1;
728
729	annotate_browser__calc_percent(browser, evsel);
730
731	if (browser->curr_hot) {
732		annotate_browser__set_rb_top(browser, browser->curr_hot);
733		browser->b.navkeypressed = false;
734	}
735
736	nd = browser->curr_hot;
737
738	while (1) {
739		key = ui_browser__run(&browser->b, delay_secs);
740
741		if (delay_secs != 0) {
742			annotate_browser__calc_percent(browser, evsel);
743			/*
744			 * Current line focus got out of the list of most active
745			 * lines, NULL it so that if TAB|UNTAB is pressed, we
746			 * move to curr_hot (current hottest line).
747			 */
748			if (nd != NULL && RB_EMPTY_NODE(nd))
749				nd = NULL;
750		}
751
752		switch (key) {
753		case K_TIMER:
754			if (hbt)
755				hbt->timer(hbt->arg);
756
757			if (delay_secs != 0) {
758				symbol__annotate_decay_histogram(sym, evsel->core.idx);
759				hists__scnprintf_title(hists, title, sizeof(title));
760				annotate_browser__show(&browser->b, title, help);
761			}
762			continue;
763		case K_TAB:
764			if (nd != NULL) {
765				nd = rb_prev(nd);
766				if (nd == NULL)
767					nd = rb_last(&browser->entries);
768			} else
769				nd = browser->curr_hot;
770			break;
771		case K_UNTAB:
772			if (nd != NULL) {
773				nd = rb_next(nd);
774				if (nd == NULL)
775					nd = rb_first(&browser->entries);
776			} else
777				nd = browser->curr_hot;
778			break;
779		case K_F1:
780		case 'h':
781			ui_browser__help_window(&browser->b,
782		"UP/DOWN/PGUP\n"
783		"PGDN/SPACE    Navigate\n"
784		"</>           Move to prev/next symbol\n"
785		"q/ESC/CTRL+C  Exit\n\n"
786		"ENTER         Go to target\n"
787		"H             Go to hottest instruction\n"
788		"TAB/shift+TAB Cycle thru hottest instructions\n"
789		"j             Toggle showing jump to target arrows\n"
790		"J             Toggle showing number of jump sources on targets\n"
791		"n             Search next string\n"
792		"o             Toggle disassembler output/simplified view\n"
793		"O             Bump offset level (jump targets -> +call -> all -> cycle thru)\n"
794		"s             Toggle source code view\n"
795		"t             Circulate percent, total period, samples view\n"
796		"c             Show min/max cycle\n"
797		"/             Search string\n"
798		"k             Toggle line numbers\n"
799		"l             Show full source file location\n"
800		"P             Print to [symbol_name].annotation file.\n"
801		"r             Run available scripts\n"
802		"p             Toggle percent type [local/global]\n"
803		"b             Toggle percent base [period/hits]\n"
804		"?             Search string backwards\n"
805		"f             Toggle showing offsets to full address\n");
806			continue;
807		case 'r':
808			script_browse(NULL, NULL);
809			annotate_browser__show(&browser->b, title, help);
810			continue;
811		case 'k':
812			notes->options->show_linenr = !notes->options->show_linenr;
813			continue;
814		case 'l':
815			annotate_browser__show_full_location (&browser->b);
816			continue;
817		case 'H':
818			nd = browser->curr_hot;
819			break;
820		case 's':
821			if (annotate_browser__toggle_source(browser))
822				ui_helpline__puts(help);
823			continue;
824		case 'o':
825			notes->options->use_offset = !notes->options->use_offset;
826			annotation__update_column_widths(notes);
827			continue;
828		case 'O':
829			if (++notes->options->offset_level > ANNOTATION__MAX_OFFSET_LEVEL)
830				notes->options->offset_level = ANNOTATION__MIN_OFFSET_LEVEL;
831			continue;
832		case 'j':
833			notes->options->jump_arrows = !notes->options->jump_arrows;
834			continue;
835		case 'J':
836			notes->options->show_nr_jumps = !notes->options->show_nr_jumps;
837			annotation__update_column_widths(notes);
838			continue;
839		case '/':
840			if (annotate_browser__search(browser, delay_secs)) {
841show_help:
842				ui_helpline__puts(help);
843			}
844			continue;
845		case 'n':
846			if (browser->searching_backwards ?
847			    annotate_browser__continue_search_reverse(browser, delay_secs) :
848			    annotate_browser__continue_search(browser, delay_secs))
849				goto show_help;
850			continue;
851		case '?':
852			if (annotate_browser__search_reverse(browser, delay_secs))
853				goto show_help;
854			continue;
855		case 'D': {
856			static int seq;
857			ui_helpline__pop();
858			ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d",
859					   seq++, browser->b.nr_entries,
860					   browser->b.height,
861					   browser->b.index,
862					   browser->b.top_idx,
863					   notes->nr_asm_entries);
864		}
865			continue;
866		case K_ENTER:
867		case K_RIGHT:
868		{
869			struct disasm_line *dl = disasm_line(browser->selection);
870
871			if (browser->selection == NULL)
872				ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
873			else if (browser->selection->offset == -1)
874				ui_helpline__puts("Actions are only available for assembly lines.");
875			else if (!dl->ins.ops)
876				goto show_sup_ins;
877			else if (ins__is_ret(&dl->ins))
878				goto out;
879			else if (!(annotate_browser__jump(browser, evsel, hbt) ||
880				     annotate_browser__callq(browser, evsel, hbt))) {
881show_sup_ins:
882				ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
883			}
884			continue;
885		}
886		case 'P':
887			map_symbol__annotation_dump(ms, evsel, browser->opts);
888			continue;
889		case 't':
890			if (symbol_conf.show_total_period) {
891				symbol_conf.show_total_period = false;
892				symbol_conf.show_nr_samples = true;
893			} else if (symbol_conf.show_nr_samples)
894				symbol_conf.show_nr_samples = false;
895			else
896				symbol_conf.show_total_period = true;
897			annotation__update_column_widths(notes);
898			continue;
899		case 'c':
900			if (notes->options->show_minmax_cycle)
901				notes->options->show_minmax_cycle = false;
902			else
903				notes->options->show_minmax_cycle = true;
904			annotation__update_column_widths(notes);
905			continue;
906		case 'p':
907		case 'b':
908			switch_percent_type(browser->opts, key == 'b');
909			hists__scnprintf_title(hists, title, sizeof(title));
910			annotate_browser__show(&browser->b, title, help);
911			continue;
912		case 'f':
913			annotation__toggle_full_addr(notes, ms);
914			continue;
915		case K_LEFT:
916		case '<':
917		case '>':
918		case K_ESC:
919		case 'q':
920		case CTRL('c'):
921			goto out;
922		default:
923			continue;
924		}
925
926		if (nd != NULL)
927			annotate_browser__set_rb_top(browser, nd);
928	}
929out:
930	ui_browser__hide(&browser->b);
931	return key;
932}
933
934int map_symbol__tui_annotate(struct map_symbol *ms, struct evsel *evsel,
935			     struct hist_browser_timer *hbt,
936			     struct annotation_options *opts)
937{
938	return symbol__tui_annotate(ms, evsel, hbt, opts);
939}
940
941int hist_entry__tui_annotate(struct hist_entry *he, struct evsel *evsel,
942			     struct hist_browser_timer *hbt,
943			     struct annotation_options *opts)
944{
945	/* reset abort key so that it can get Ctrl-C as a key */
946	SLang_reset_tty();
947	SLang_init_tty(0, 0, 0);
948
949	return map_symbol__tui_annotate(&he->ms, evsel, hbt, opts);
950}
951
952int symbol__tui_annotate(struct map_symbol *ms, struct evsel *evsel,
953			 struct hist_browser_timer *hbt,
954			 struct annotation_options *opts)
955{
956	struct symbol *sym = ms->sym;
957	struct annotation *notes = symbol__annotation(sym);
958	struct annotate_browser browser = {
959		.b = {
960			.refresh = annotate_browser__refresh,
961			.seek	 = ui_browser__list_head_seek,
962			.write	 = annotate_browser__write,
963			.filter  = disasm_line__filter,
964			.extra_title_lines = 1, /* for hists__scnprintf_title() */
965			.priv	 = ms,
966			.use_navkeypressed = true,
967		},
968		.opts = opts,
969	};
970	struct dso *dso;
971	int ret = -1, err;
972	int not_annotated = list_empty(&notes->src->source);
973
974	if (sym == NULL)
975		return -1;
976
977	dso = map__dso(ms->map);
978	if (dso->annotate_warned)
979		return -1;
980
981	if (not_annotated) {
982		err = symbol__annotate2(ms, evsel, opts, &browser.arch);
983		if (err) {
984			char msg[BUFSIZ];
985			dso->annotate_warned = true;
986			symbol__strerror_disassemble(ms, err, msg, sizeof(msg));
987			ui__error("Couldn't annotate %s:\n%s", sym->name, msg);
988			goto out_free_offsets;
989		}
990	}
991
992	ui_helpline__push("Press ESC to exit");
993
994	browser.b.width = notes->max_line_len;
995	browser.b.nr_entries = notes->nr_entries;
996	browser.b.entries = &notes->src->source,
997	browser.b.width += 18; /* Percentage */
998
999	if (notes->options->hide_src_code)
1000		ui_browser__init_asm_mode(&browser.b);
1001
1002	ret = annotate_browser__run(&browser, evsel, hbt);
1003
1004	if(not_annotated)
1005		annotated_source__purge(notes->src);
1006
1007out_free_offsets:
1008	if(not_annotated)
1009		zfree(&notes->offsets);
1010	return ret;
1011}
1012