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