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