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