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