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