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