1#include "../../util/util.h"
2#include "../browser.h"
3#include "../helpline.h"
4#include "../libslang.h"
5#include "../ui.h"
6#include "../util.h"
7#include "../../util/annotate.h"
8#include "../../util/hist.h"
9#include "../../util/sort.h"
10#include "../../util/symbol.h"
11#include "../../util/evsel.h"
12#include <pthread.h>
13
14struct browser_disasm_line {
15	struct rb_node	rb_node;
16	u32		idx;
17	int		idx_asm;
18	int		jump_sources;
19	/*
20	 * actual length of this array is saved on the nr_events field
21	 * of the struct annotate_browser
22	 */
23	double		percent[1];
24};
25
26static struct annotate_browser_opt {
27	bool hide_src_code,
28	     use_offset,
29	     jump_arrows,
30	     show_linenr,
31	     show_nr_jumps;
32} annotate_browser__opts = {
33	.use_offset	= true,
34	.jump_arrows	= true,
35};
36
37struct annotate_browser {
38	struct ui_browser b;
39	struct rb_root	  entries;
40	struct rb_node	  *curr_hot;
41	struct disasm_line  *selection;
42	struct disasm_line  **offsets;
43	int		    nr_events;
44	u64		    start;
45	int		    nr_asm_entries;
46	int		    nr_entries;
47	int		    max_jump_sources;
48	int		    nr_jumps;
49	bool		    searching_backwards;
50	u8		    addr_width;
51	u8		    jumps_width;
52	u8		    target_width;
53	u8		    min_addr_width;
54	u8		    max_addr_width;
55	char		    search_bf[128];
56};
57
58static inline struct browser_disasm_line *disasm_line__browser(struct disasm_line *dl)
59{
60	return (struct browser_disasm_line *)(dl + 1);
61}
62
63static bool disasm_line__filter(struct ui_browser *browser __maybe_unused,
64				void *entry)
65{
66	if (annotate_browser__opts.hide_src_code) {
67		struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
68		return dl->offset == -1;
69	}
70
71	return false;
72}
73
74static int annotate_browser__jumps_percent_color(struct annotate_browser *browser,
75						 int nr, bool current)
76{
77	if (current && (!browser->b.use_navkeypressed || browser->b.navkeypressed))
78		return HE_COLORSET_SELECTED;
79	if (nr == browser->max_jump_sources)
80		return HE_COLORSET_TOP;
81	if (nr > 1)
82		return HE_COLORSET_MEDIUM;
83	return HE_COLORSET_NORMAL;
84}
85
86static int annotate_browser__set_jumps_percent_color(struct annotate_browser *browser,
87						     int nr, bool current)
88{
89	 int color = annotate_browser__jumps_percent_color(browser, nr, current);
90	 return ui_browser__set_color(&browser->b, color);
91}
92
93static void annotate_browser__write(struct ui_browser *browser, void *entry, int row)
94{
95	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
96	struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
97	struct browser_disasm_line *bdl = disasm_line__browser(dl);
98	bool current_entry = ui_browser__is_current_entry(browser, row);
99	bool change_color = (!annotate_browser__opts.hide_src_code &&
100			     (!current_entry || (browser->use_navkeypressed &&
101					         !browser->navkeypressed)));
102	int width = browser->width, printed;
103	int i, pcnt_width = 7 * ab->nr_events;
104	double percent_max = 0.0;
105	char bf[256];
106
107	for (i = 0; i < ab->nr_events; i++) {
108		if (bdl->percent[i] > percent_max)
109			percent_max = bdl->percent[i];
110	}
111
112	if (dl->offset != -1 && percent_max != 0.0) {
113		for (i = 0; i < ab->nr_events; i++) {
114			ui_browser__set_percent_color(browser, bdl->percent[i],
115						      current_entry);
116			slsmg_printf("%6.2f ", bdl->percent[i]);
117		}
118	} else {
119		ui_browser__set_percent_color(browser, 0, current_entry);
120		slsmg_write_nstring(" ", pcnt_width);
121	}
122
123	SLsmg_write_char(' ');
124
125	/* The scroll bar isn't being used */
126	if (!browser->navkeypressed)
127		width += 1;
128
129	if (!*dl->line)
130		slsmg_write_nstring(" ", width - pcnt_width);
131	else if (dl->offset == -1) {
132		if (dl->line_nr && annotate_browser__opts.show_linenr)
133			printed = scnprintf(bf, sizeof(bf), "%-*d ",
134					ab->addr_width + 1, dl->line_nr);
135		else
136			printed = scnprintf(bf, sizeof(bf), "%*s  ",
137				    ab->addr_width, " ");
138		slsmg_write_nstring(bf, printed);
139		slsmg_write_nstring(dl->line, width - printed - pcnt_width + 1);
140	} else {
141		u64 addr = dl->offset;
142		int color = -1;
143
144		if (!annotate_browser__opts.use_offset)
145			addr += ab->start;
146
147		if (!annotate_browser__opts.use_offset) {
148			printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr);
149		} else {
150			if (bdl->jump_sources) {
151				if (annotate_browser__opts.show_nr_jumps) {
152					int prev;
153					printed = scnprintf(bf, sizeof(bf), "%*d ",
154							    ab->jumps_width,
155							    bdl->jump_sources);
156					prev = annotate_browser__set_jumps_percent_color(ab, bdl->jump_sources,
157											 current_entry);
158					slsmg_write_nstring(bf, printed);
159					ui_browser__set_color(browser, prev);
160				}
161
162				printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ": ",
163						    ab->target_width, addr);
164			} else {
165				printed = scnprintf(bf, sizeof(bf), "%*s  ",
166						    ab->addr_width, " ");
167			}
168		}
169
170		if (change_color)
171			color = ui_browser__set_color(browser, HE_COLORSET_ADDR);
172		slsmg_write_nstring(bf, printed);
173		if (change_color)
174			ui_browser__set_color(browser, color);
175		if (dl->ins && dl->ins->ops->scnprintf) {
176			if (ins__is_jump(dl->ins)) {
177				bool fwd = dl->ops.target.offset > (u64)dl->offset;
178
179				ui_browser__write_graph(browser, fwd ? SLSMG_DARROW_CHAR :
180								    SLSMG_UARROW_CHAR);
181				SLsmg_write_char(' ');
182			} else if (ins__is_call(dl->ins)) {
183				ui_browser__write_graph(browser, SLSMG_RARROW_CHAR);
184				SLsmg_write_char(' ');
185			} else {
186				slsmg_write_nstring(" ", 2);
187			}
188		} else {
189			if (strcmp(dl->name, "retq")) {
190				slsmg_write_nstring(" ", 2);
191			} else {
192				ui_browser__write_graph(browser, SLSMG_LARROW_CHAR);
193				SLsmg_write_char(' ');
194			}
195		}
196
197		disasm_line__scnprintf(dl, bf, sizeof(bf), !annotate_browser__opts.use_offset);
198		slsmg_write_nstring(bf, width - pcnt_width - 3 - printed);
199	}
200
201	if (current_entry)
202		ab->selection = dl;
203}
204
205static bool disasm_line__is_valid_jump(struct disasm_line *dl, struct symbol *sym)
206{
207	if (!dl || !dl->ins || !ins__is_jump(dl->ins)
208	    || !disasm_line__has_offset(dl)
209	    || dl->ops.target.offset >= symbol__size(sym))
210		return false;
211
212	return true;
213}
214
215static void annotate_browser__draw_current_jump(struct ui_browser *browser)
216{
217	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
218	struct disasm_line *cursor = ab->selection, *target;
219	struct browser_disasm_line *btarget, *bcursor;
220	unsigned int from, to;
221	struct map_symbol *ms = ab->b.priv;
222	struct symbol *sym = ms->sym;
223	u8 pcnt_width = 7;
224
225	/* PLT symbols contain external offsets */
226	if (strstr(sym->name, "@plt"))
227		return;
228
229	if (!disasm_line__is_valid_jump(cursor, sym))
230		return;
231
232	target = ab->offsets[cursor->ops.target.offset];
233	if (!target)
234		return;
235
236	bcursor = disasm_line__browser(cursor);
237	btarget = disasm_line__browser(target);
238
239	if (annotate_browser__opts.hide_src_code) {
240		from = bcursor->idx_asm;
241		to = btarget->idx_asm;
242	} else {
243		from = (u64)bcursor->idx;
244		to = (u64)btarget->idx;
245	}
246
247	pcnt_width *= ab->nr_events;
248
249	ui_browser__set_color(browser, HE_COLORSET_CODE);
250	__ui_browser__line_arrow(browser, pcnt_width + 2 + ab->addr_width,
251				 from, to);
252}
253
254static unsigned int annotate_browser__refresh(struct ui_browser *browser)
255{
256	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
257	int ret = ui_browser__list_head_refresh(browser);
258	int pcnt_width;
259
260	pcnt_width = 7 * ab->nr_events;
261
262	if (annotate_browser__opts.jump_arrows)
263		annotate_browser__draw_current_jump(browser);
264
265	ui_browser__set_color(browser, HE_COLORSET_NORMAL);
266	__ui_browser__vline(browser, pcnt_width, 0, browser->height - 1);
267	return ret;
268}
269
270static int disasm__cmp(struct browser_disasm_line *a,
271		       struct browser_disasm_line *b, int nr_pcnt)
272{
273	int i;
274
275	for (i = 0; i < nr_pcnt; i++) {
276		if (a->percent[i] == b->percent[i])
277			continue;
278		return a->percent[i] < b->percent[i];
279	}
280	return 0;
281}
282
283static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_line *bdl,
284				   int nr_events)
285{
286	struct rb_node **p = &root->rb_node;
287	struct rb_node *parent = NULL;
288	struct browser_disasm_line *l;
289
290	while (*p != NULL) {
291		parent = *p;
292		l = rb_entry(parent, struct browser_disasm_line, rb_node);
293
294		if (disasm__cmp(bdl, l, nr_events))
295			p = &(*p)->rb_left;
296		else
297			p = &(*p)->rb_right;
298	}
299	rb_link_node(&bdl->rb_node, parent, p);
300	rb_insert_color(&bdl->rb_node, root);
301}
302
303static void annotate_browser__set_top(struct annotate_browser *browser,
304				      struct disasm_line *pos, u32 idx)
305{
306	unsigned back;
307
308	ui_browser__refresh_dimensions(&browser->b);
309	back = browser->b.height / 2;
310	browser->b.top_idx = browser->b.index = idx;
311
312	while (browser->b.top_idx != 0 && back != 0) {
313		pos = list_entry(pos->node.prev, struct disasm_line, node);
314
315		if (disasm_line__filter(&browser->b, &pos->node))
316			continue;
317
318		--browser->b.top_idx;
319		--back;
320	}
321
322	browser->b.top = pos;
323	browser->b.navkeypressed = true;
324}
325
326static void annotate_browser__set_rb_top(struct annotate_browser *browser,
327					 struct rb_node *nd)
328{
329	struct browser_disasm_line *bpos;
330	struct disasm_line *pos;
331	u32 idx;
332
333	bpos = rb_entry(nd, struct browser_disasm_line, rb_node);
334	pos = ((struct disasm_line *)bpos) - 1;
335	idx = bpos->idx;
336	if (annotate_browser__opts.hide_src_code)
337		idx = bpos->idx_asm;
338	annotate_browser__set_top(browser, pos, idx);
339	browser->curr_hot = nd;
340}
341
342static void annotate_browser__calc_percent(struct annotate_browser *browser,
343					   struct perf_evsel *evsel)
344{
345	struct map_symbol *ms = browser->b.priv;
346	struct symbol *sym = ms->sym;
347	struct annotation *notes = symbol__annotation(sym);
348	struct disasm_line *pos, *next;
349	s64 len = symbol__size(sym);
350
351	browser->entries = RB_ROOT;
352
353	pthread_mutex_lock(&notes->lock);
354
355	list_for_each_entry(pos, &notes->src->source, node) {
356		struct browser_disasm_line *bpos = disasm_line__browser(pos);
357		const char *path = NULL;
358		double max_percent = 0.0;
359		int i;
360
361		if (pos->offset == -1) {
362			RB_CLEAR_NODE(&bpos->rb_node);
363			continue;
364		}
365
366		next = disasm__get_next_ip_line(&notes->src->source, pos);
367
368		for (i = 0; i < browser->nr_events; i++) {
369			bpos->percent[i] = disasm__calc_percent(notes,
370						evsel->idx + i,
371						pos->offset,
372						next ? next->offset : len,
373					        &path);
374
375			if (max_percent < bpos->percent[i])
376				max_percent = bpos->percent[i];
377		}
378
379		if (max_percent < 0.01) {
380			RB_CLEAR_NODE(&bpos->rb_node);
381			continue;
382		}
383		disasm_rb_tree__insert(&browser->entries, bpos,
384				       browser->nr_events);
385	}
386	pthread_mutex_unlock(&notes->lock);
387
388	browser->curr_hot = rb_last(&browser->entries);
389}
390
391static bool annotate_browser__toggle_source(struct annotate_browser *browser)
392{
393	struct disasm_line *dl;
394	struct browser_disasm_line *bdl;
395	off_t offset = browser->b.index - browser->b.top_idx;
396
397	browser->b.seek(&browser->b, offset, SEEK_CUR);
398	dl = list_entry(browser->b.top, struct disasm_line, node);
399	bdl = disasm_line__browser(dl);
400
401	if (annotate_browser__opts.hide_src_code) {
402		if (bdl->idx_asm < offset)
403			offset = bdl->idx;
404
405		browser->b.nr_entries = browser->nr_entries;
406		annotate_browser__opts.hide_src_code = false;
407		browser->b.seek(&browser->b, -offset, SEEK_CUR);
408		browser->b.top_idx = bdl->idx - offset;
409		browser->b.index = bdl->idx;
410	} else {
411		if (bdl->idx_asm < 0) {
412			ui_helpline__puts("Only available for assembly lines.");
413			browser->b.seek(&browser->b, -offset, SEEK_CUR);
414			return false;
415		}
416
417		if (bdl->idx_asm < offset)
418			offset = bdl->idx_asm;
419
420		browser->b.nr_entries = browser->nr_asm_entries;
421		annotate_browser__opts.hide_src_code = true;
422		browser->b.seek(&browser->b, -offset, SEEK_CUR);
423		browser->b.top_idx = bdl->idx_asm - offset;
424		browser->b.index = bdl->idx_asm;
425	}
426
427	return true;
428}
429
430static void annotate_browser__init_asm_mode(struct annotate_browser *browser)
431{
432	ui_browser__reset_index(&browser->b);
433	browser->b.nr_entries = browser->nr_asm_entries;
434}
435
436#define SYM_TITLE_MAX_SIZE (PATH_MAX + 64)
437
438static int sym_title(struct symbol *sym, struct map *map, char *title,
439		     size_t sz)
440{
441	return snprintf(title, sz, "%s  %s", sym->name, map->dso->long_name);
442}
443
444static bool annotate_browser__callq(struct annotate_browser *browser,
445				    struct perf_evsel *evsel,
446				    struct hist_browser_timer *hbt)
447{
448	struct map_symbol *ms = browser->b.priv;
449	struct disasm_line *dl = browser->selection;
450	struct annotation *notes;
451	struct addr_map_symbol target = {
452		.map = ms->map,
453		.addr = map__objdump_2mem(ms->map, dl->ops.target.addr),
454	};
455	char title[SYM_TITLE_MAX_SIZE];
456
457	if (!ins__is_call(dl->ins))
458		return false;
459
460	if (map_groups__find_ams(&target, NULL) ||
461	    map__rip_2objdump(target.map, target.map->map_ip(target.map,
462							     target.addr)) !=
463	    dl->ops.target.addr) {
464		ui_helpline__puts("The called function was not found.");
465		return true;
466	}
467
468	notes = symbol__annotation(target.sym);
469	pthread_mutex_lock(&notes->lock);
470
471	if (notes->src == NULL && symbol__alloc_hist(target.sym) < 0) {
472		pthread_mutex_unlock(&notes->lock);
473		ui__warning("Not enough memory for annotating '%s' symbol!\n",
474			    target.sym->name);
475		return true;
476	}
477
478	pthread_mutex_unlock(&notes->lock);
479	symbol__tui_annotate(target.sym, target.map, evsel, hbt);
480	sym_title(ms->sym, ms->map, title, sizeof(title));
481	ui_browser__show_title(&browser->b, title);
482	return true;
483}
484
485static
486struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
487					  s64 offset, s64 *idx)
488{
489	struct map_symbol *ms = browser->b.priv;
490	struct symbol *sym = ms->sym;
491	struct annotation *notes = symbol__annotation(sym);
492	struct disasm_line *pos;
493
494	*idx = 0;
495	list_for_each_entry(pos, &notes->src->source, node) {
496		if (pos->offset == offset)
497			return pos;
498		if (!disasm_line__filter(&browser->b, &pos->node))
499			++*idx;
500	}
501
502	return NULL;
503}
504
505static bool annotate_browser__jump(struct annotate_browser *browser)
506{
507	struct disasm_line *dl = browser->selection;
508	s64 idx;
509
510	if (!ins__is_jump(dl->ins))
511		return false;
512
513	dl = annotate_browser__find_offset(browser, dl->ops.target.offset, &idx);
514	if (dl == NULL) {
515		ui_helpline__puts("Invalid jump offset");
516		return true;
517	}
518
519	annotate_browser__set_top(browser, dl, idx);
520
521	return true;
522}
523
524static
525struct disasm_line *annotate_browser__find_string(struct annotate_browser *browser,
526					  char *s, s64 *idx)
527{
528	struct map_symbol *ms = browser->b.priv;
529	struct symbol *sym = ms->sym;
530	struct annotation *notes = symbol__annotation(sym);
531	struct disasm_line *pos = browser->selection;
532
533	*idx = browser->b.index;
534	list_for_each_entry_continue(pos, &notes->src->source, node) {
535		if (disasm_line__filter(&browser->b, &pos->node))
536			continue;
537
538		++*idx;
539
540		if (pos->line && strstr(pos->line, s) != NULL)
541			return pos;
542	}
543
544	return NULL;
545}
546
547static bool __annotate_browser__search(struct annotate_browser *browser)
548{
549	struct disasm_line *dl;
550	s64 idx;
551
552	dl = annotate_browser__find_string(browser, browser->search_bf, &idx);
553	if (dl == NULL) {
554		ui_helpline__puts("String not found!");
555		return false;
556	}
557
558	annotate_browser__set_top(browser, dl, idx);
559	browser->searching_backwards = false;
560	return true;
561}
562
563static
564struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
565						  char *s, s64 *idx)
566{
567	struct map_symbol *ms = browser->b.priv;
568	struct symbol *sym = ms->sym;
569	struct annotation *notes = symbol__annotation(sym);
570	struct disasm_line *pos = browser->selection;
571
572	*idx = browser->b.index;
573	list_for_each_entry_continue_reverse(pos, &notes->src->source, node) {
574		if (disasm_line__filter(&browser->b, &pos->node))
575			continue;
576
577		--*idx;
578
579		if (pos->line && strstr(pos->line, s) != NULL)
580			return pos;
581	}
582
583	return NULL;
584}
585
586static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
587{
588	struct disasm_line *dl;
589	s64 idx;
590
591	dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
592	if (dl == NULL) {
593		ui_helpline__puts("String not found!");
594		return false;
595	}
596
597	annotate_browser__set_top(browser, dl, idx);
598	browser->searching_backwards = true;
599	return true;
600}
601
602static bool annotate_browser__search_window(struct annotate_browser *browser,
603					    int delay_secs)
604{
605	if (ui_browser__input_window("Search", "String: ", browser->search_bf,
606				     "ENTER: OK, ESC: Cancel",
607				     delay_secs * 2) != K_ENTER ||
608	    !*browser->search_bf)
609		return false;
610
611	return true;
612}
613
614static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs)
615{
616	if (annotate_browser__search_window(browser, delay_secs))
617		return __annotate_browser__search(browser);
618
619	return false;
620}
621
622static bool annotate_browser__continue_search(struct annotate_browser *browser,
623					      int delay_secs)
624{
625	if (!*browser->search_bf)
626		return annotate_browser__search(browser, delay_secs);
627
628	return __annotate_browser__search(browser);
629}
630
631static bool annotate_browser__search_reverse(struct annotate_browser *browser,
632					   int delay_secs)
633{
634	if (annotate_browser__search_window(browser, delay_secs))
635		return __annotate_browser__search_reverse(browser);
636
637	return false;
638}
639
640static
641bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
642					       int delay_secs)
643{
644	if (!*browser->search_bf)
645		return annotate_browser__search_reverse(browser, delay_secs);
646
647	return __annotate_browser__search_reverse(browser);
648}
649
650static void annotate_browser__update_addr_width(struct annotate_browser *browser)
651{
652	if (annotate_browser__opts.use_offset)
653		browser->target_width = browser->min_addr_width;
654	else
655		browser->target_width = browser->max_addr_width;
656
657	browser->addr_width = browser->target_width;
658
659	if (annotate_browser__opts.show_nr_jumps)
660		browser->addr_width += browser->jumps_width + 1;
661}
662
663static int annotate_browser__run(struct annotate_browser *browser,
664				 struct perf_evsel *evsel,
665				 struct hist_browser_timer *hbt)
666{
667	struct rb_node *nd = NULL;
668	struct map_symbol *ms = browser->b.priv;
669	struct symbol *sym = ms->sym;
670	const char *help = "Press 'h' for help on key bindings";
671	int delay_secs = hbt ? hbt->refresh : 0;
672	int key;
673	char title[SYM_TITLE_MAX_SIZE];
674
675	sym_title(sym, ms->map, title, sizeof(title));
676	if (ui_browser__show(&browser->b, title, help) < 0)
677		return -1;
678
679	annotate_browser__calc_percent(browser, evsel);
680
681	if (browser->curr_hot) {
682		annotate_browser__set_rb_top(browser, browser->curr_hot);
683		browser->b.navkeypressed = false;
684	}
685
686	nd = browser->curr_hot;
687
688	while (1) {
689		key = ui_browser__run(&browser->b, delay_secs);
690
691		if (delay_secs != 0) {
692			annotate_browser__calc_percent(browser, evsel);
693			/*
694			 * Current line focus got out of the list of most active
695			 * lines, NULL it so that if TAB|UNTAB is pressed, we
696			 * move to curr_hot (current hottest line).
697			 */
698			if (nd != NULL && RB_EMPTY_NODE(nd))
699				nd = NULL;
700		}
701
702		switch (key) {
703		case K_TIMER:
704			if (hbt)
705				hbt->timer(hbt->arg);
706
707			if (delay_secs != 0)
708				symbol__annotate_decay_histogram(sym, evsel->idx);
709			continue;
710		case K_TAB:
711			if (nd != NULL) {
712				nd = rb_prev(nd);
713				if (nd == NULL)
714					nd = rb_last(&browser->entries);
715			} else
716				nd = browser->curr_hot;
717			break;
718		case K_UNTAB:
719			if (nd != NULL)
720				nd = rb_next(nd);
721				if (nd == NULL)
722					nd = rb_first(&browser->entries);
723			else
724				nd = browser->curr_hot;
725			break;
726		case K_F1:
727		case 'h':
728			ui_browser__help_window(&browser->b,
729		"UP/DOWN/PGUP\n"
730		"PGDN/SPACE    Navigate\n"
731		"q/ESC/CTRL+C  Exit\n\n"
732		"->            Go to target\n"
733		"<-            Exit\n"
734		"H             Cycle thru hottest instructions\n"
735		"j             Toggle showing jump to target arrows\n"
736		"J             Toggle showing number of jump sources on targets\n"
737		"n             Search next string\n"
738		"o             Toggle disassembler output/simplified view\n"
739		"s             Toggle source code view\n"
740		"/             Search string\n"
741		"k             Toggle line numbers\n"
742		"r             Run available scripts\n"
743		"?             Search string backwards\n");
744			continue;
745		case 'r':
746			{
747				script_browse(NULL);
748				continue;
749			}
750		case 'k':
751			annotate_browser__opts.show_linenr =
752				!annotate_browser__opts.show_linenr;
753			break;
754		case 'H':
755			nd = browser->curr_hot;
756			break;
757		case 's':
758			if (annotate_browser__toggle_source(browser))
759				ui_helpline__puts(help);
760			continue;
761		case 'o':
762			annotate_browser__opts.use_offset = !annotate_browser__opts.use_offset;
763			annotate_browser__update_addr_width(browser);
764			continue;
765		case 'j':
766			annotate_browser__opts.jump_arrows = !annotate_browser__opts.jump_arrows;
767			continue;
768		case 'J':
769			annotate_browser__opts.show_nr_jumps = !annotate_browser__opts.show_nr_jumps;
770			annotate_browser__update_addr_width(browser);
771			continue;
772		case '/':
773			if (annotate_browser__search(browser, delay_secs)) {
774show_help:
775				ui_helpline__puts(help);
776			}
777			continue;
778		case 'n':
779			if (browser->searching_backwards ?
780			    annotate_browser__continue_search_reverse(browser, delay_secs) :
781			    annotate_browser__continue_search(browser, delay_secs))
782				goto show_help;
783			continue;
784		case '?':
785			if (annotate_browser__search_reverse(browser, delay_secs))
786				goto show_help;
787			continue;
788		case 'D': {
789			static int seq;
790			ui_helpline__pop();
791			ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d",
792					   seq++, browser->b.nr_entries,
793					   browser->b.height,
794					   browser->b.index,
795					   browser->b.top_idx,
796					   browser->nr_asm_entries);
797		}
798			continue;
799		case K_ENTER:
800		case K_RIGHT:
801			if (browser->selection == NULL)
802				ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
803			else if (browser->selection->offset == -1)
804				ui_helpline__puts("Actions are only available for assembly lines.");
805			else if (!browser->selection->ins) {
806				if (strcmp(browser->selection->name, "retq"))
807					goto show_sup_ins;
808				goto out;
809			} else if (!(annotate_browser__jump(browser) ||
810				     annotate_browser__callq(browser, evsel, hbt))) {
811show_sup_ins:
812				ui_helpline__puts("Actions are only available for 'callq', 'retq' & jump instructions.");
813			}
814			continue;
815		case K_LEFT:
816		case K_ESC:
817		case 'q':
818		case CTRL('c'):
819			goto out;
820		default:
821			continue;
822		}
823
824		if (nd != NULL)
825			annotate_browser__set_rb_top(browser, nd);
826	}
827out:
828	ui_browser__hide(&browser->b);
829	return key;
830}
831
832int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel,
833			     struct hist_browser_timer *hbt)
834{
835	return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt);
836}
837
838int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
839			     struct hist_browser_timer *hbt)
840{
841	return map_symbol__tui_annotate(&he->ms, evsel, hbt);
842}
843
844static void annotate_browser__mark_jump_targets(struct annotate_browser *browser,
845						size_t size)
846{
847	u64 offset;
848	struct map_symbol *ms = browser->b.priv;
849	struct symbol *sym = ms->sym;
850
851	/* PLT symbols contain external offsets */
852	if (strstr(sym->name, "@plt"))
853		return;
854
855	for (offset = 0; offset < size; ++offset) {
856		struct disasm_line *dl = browser->offsets[offset], *dlt;
857		struct browser_disasm_line *bdlt;
858
859		if (!disasm_line__is_valid_jump(dl, sym))
860			continue;
861
862		dlt = browser->offsets[dl->ops.target.offset];
863		/*
864 		 * FIXME: Oops, no jump target? Buggy disassembler? Or do we
865 		 * have to adjust to the previous offset?
866 		 */
867		if (dlt == NULL)
868			continue;
869
870		bdlt = disasm_line__browser(dlt);
871		if (++bdlt->jump_sources > browser->max_jump_sources)
872			browser->max_jump_sources = bdlt->jump_sources;
873
874		++browser->nr_jumps;
875	}
876}
877
878static inline int width_jumps(int n)
879{
880	if (n >= 100)
881		return 5;
882	if (n / 10)
883		return 2;
884	return 1;
885}
886
887int symbol__tui_annotate(struct symbol *sym, struct map *map,
888			 struct perf_evsel *evsel,
889			 struct hist_browser_timer *hbt)
890{
891	struct disasm_line *pos, *n;
892	struct annotation *notes;
893	size_t size;
894	struct map_symbol ms = {
895		.map = map,
896		.sym = sym,
897	};
898	struct annotate_browser browser = {
899		.b = {
900			.refresh = annotate_browser__refresh,
901			.seek	 = ui_browser__list_head_seek,
902			.write	 = annotate_browser__write,
903			.filter  = disasm_line__filter,
904			.priv	 = &ms,
905			.use_navkeypressed = true,
906		},
907	};
908	int ret = -1;
909	int nr_pcnt = 1;
910	size_t sizeof_bdl = sizeof(struct browser_disasm_line);
911
912	if (sym == NULL)
913		return -1;
914
915	size = symbol__size(sym);
916
917	if (map->dso->annotate_warned)
918		return -1;
919
920	browser.offsets = zalloc(size * sizeof(struct disasm_line *));
921	if (browser.offsets == NULL) {
922		ui__error("Not enough memory!");
923		return -1;
924	}
925
926	if (perf_evsel__is_group_event(evsel)) {
927		nr_pcnt = evsel->nr_members;
928		sizeof_bdl += sizeof(double) * (nr_pcnt - 1);
929	}
930
931	if (symbol__annotate(sym, map, sizeof_bdl) < 0) {
932		ui__error("%s", ui_helpline__last_msg);
933		goto out_free_offsets;
934	}
935
936	ui_helpline__push("Press <- or ESC to exit");
937
938	notes = symbol__annotation(sym);
939	browser.start = map__rip_2objdump(map, sym->start);
940
941	list_for_each_entry(pos, &notes->src->source, node) {
942		struct browser_disasm_line *bpos;
943		size_t line_len = strlen(pos->line);
944
945		if (browser.b.width < line_len)
946			browser.b.width = line_len;
947		bpos = disasm_line__browser(pos);
948		bpos->idx = browser.nr_entries++;
949		if (pos->offset != -1) {
950			bpos->idx_asm = browser.nr_asm_entries++;
951			/*
952			 * FIXME: short term bandaid to cope with assembly
953			 * routines that comes with labels in the same column
954			 * as the address in objdump, sigh.
955			 *
956			 * E.g. copy_user_generic_unrolled
957 			 */
958			if (pos->offset < (s64)size)
959				browser.offsets[pos->offset] = pos;
960		} else
961			bpos->idx_asm = -1;
962	}
963
964	annotate_browser__mark_jump_targets(&browser, size);
965
966	browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size);
967	browser.max_addr_width = hex_width(sym->end);
968	browser.jumps_width = width_jumps(browser.max_jump_sources);
969	browser.nr_events = nr_pcnt;
970	browser.b.nr_entries = browser.nr_entries;
971	browser.b.entries = &notes->src->source,
972	browser.b.width += 18; /* Percentage */
973
974	if (annotate_browser__opts.hide_src_code)
975		annotate_browser__init_asm_mode(&browser);
976
977	annotate_browser__update_addr_width(&browser);
978
979	ret = annotate_browser__run(&browser, evsel, hbt);
980	list_for_each_entry_safe(pos, n, &notes->src->source, node) {
981		list_del(&pos->node);
982		disasm_line__free(pos);
983	}
984
985out_free_offsets:
986	free(browser.offsets);
987	return ret;
988}
989
990#define ANNOTATE_CFG(n) \
991	{ .name = #n, .value = &annotate_browser__opts.n, }
992
993/*
994 * Keep the entries sorted, they are bsearch'ed
995 */
996static struct annotate_config {
997	const char *name;
998	bool *value;
999} annotate__configs[] = {
1000	ANNOTATE_CFG(hide_src_code),
1001	ANNOTATE_CFG(jump_arrows),
1002	ANNOTATE_CFG(show_linenr),
1003	ANNOTATE_CFG(show_nr_jumps),
1004	ANNOTATE_CFG(use_offset),
1005};
1006
1007#undef ANNOTATE_CFG
1008
1009static int annotate_config__cmp(const void *name, const void *cfgp)
1010{
1011	const struct annotate_config *cfg = cfgp;
1012
1013	return strcmp(name, cfg->name);
1014}
1015
1016static int annotate__config(const char *var, const char *value,
1017			    void *data __maybe_unused)
1018{
1019	struct annotate_config *cfg;
1020	const char *name;
1021
1022	if (prefixcmp(var, "annotate.") != 0)
1023		return 0;
1024
1025	name = var + 9;
1026	cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
1027		      sizeof(struct annotate_config), annotate_config__cmp);
1028
1029	if (cfg == NULL)
1030		return -1;
1031
1032	*cfg->value = perf_config_bool(name, value);
1033	return 0;
1034}
1035
1036void annotate_browser__init(void)
1037{
1038	perf_config(annotate__config, NULL);
1039}
1040