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