xref: /openbmc/linux/tools/perf/ui/browsers/annotate.c (revision e23feb16)
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 #define SYM_TITLE_MAX_SIZE (PATH_MAX + 64)
432 
433 static int sym_title(struct symbol *sym, struct map *map, char *title,
434 		     size_t sz)
435 {
436 	return snprintf(title, sz, "%s  %s", sym->name, map->dso->long_name);
437 }
438 
439 static bool annotate_browser__callq(struct annotate_browser *browser,
440 				    struct perf_evsel *evsel,
441 				    struct hist_browser_timer *hbt)
442 {
443 	struct map_symbol *ms = browser->b.priv;
444 	struct disasm_line *dl = browser->selection;
445 	struct symbol *sym = ms->sym;
446 	struct annotation *notes;
447 	struct symbol *target;
448 	u64 ip;
449 	char title[SYM_TITLE_MAX_SIZE];
450 
451 	if (!ins__is_call(dl->ins))
452 		return false;
453 
454 	ip = ms->map->map_ip(ms->map, dl->ops.target.addr);
455 	target = map__find_symbol(ms->map, ip, NULL);
456 	if (target == NULL) {
457 		ui_helpline__puts("The called function was not found.");
458 		return true;
459 	}
460 
461 	notes = symbol__annotation(target);
462 	pthread_mutex_lock(&notes->lock);
463 
464 	if (notes->src == NULL && symbol__alloc_hist(target) < 0) {
465 		pthread_mutex_unlock(&notes->lock);
466 		ui__warning("Not enough memory for annotating '%s' symbol!\n",
467 			    target->name);
468 		return true;
469 	}
470 
471 	pthread_mutex_unlock(&notes->lock);
472 	symbol__tui_annotate(target, ms->map, evsel, hbt);
473 	sym_title(sym, ms->map, title, sizeof(title));
474 	ui_browser__show_title(&browser->b, title);
475 	return true;
476 }
477 
478 static
479 struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
480 					  s64 offset, s64 *idx)
481 {
482 	struct map_symbol *ms = browser->b.priv;
483 	struct symbol *sym = ms->sym;
484 	struct annotation *notes = symbol__annotation(sym);
485 	struct disasm_line *pos;
486 
487 	*idx = 0;
488 	list_for_each_entry(pos, &notes->src->source, node) {
489 		if (pos->offset == offset)
490 			return pos;
491 		if (!disasm_line__filter(&browser->b, &pos->node))
492 			++*idx;
493 	}
494 
495 	return NULL;
496 }
497 
498 static bool annotate_browser__jump(struct annotate_browser *browser)
499 {
500 	struct disasm_line *dl = browser->selection;
501 	s64 idx;
502 
503 	if (!ins__is_jump(dl->ins))
504 		return false;
505 
506 	dl = annotate_browser__find_offset(browser, dl->ops.target.offset, &idx);
507 	if (dl == NULL) {
508 		ui_helpline__puts("Invalid jump offset");
509 		return true;
510 	}
511 
512 	annotate_browser__set_top(browser, dl, idx);
513 
514 	return true;
515 }
516 
517 static
518 struct disasm_line *annotate_browser__find_string(struct annotate_browser *browser,
519 					  char *s, s64 *idx)
520 {
521 	struct map_symbol *ms = browser->b.priv;
522 	struct symbol *sym = ms->sym;
523 	struct annotation *notes = symbol__annotation(sym);
524 	struct disasm_line *pos = browser->selection;
525 
526 	*idx = browser->b.index;
527 	list_for_each_entry_continue(pos, &notes->src->source, node) {
528 		if (disasm_line__filter(&browser->b, &pos->node))
529 			continue;
530 
531 		++*idx;
532 
533 		if (pos->line && strstr(pos->line, s) != NULL)
534 			return pos;
535 	}
536 
537 	return NULL;
538 }
539 
540 static bool __annotate_browser__search(struct annotate_browser *browser)
541 {
542 	struct disasm_line *dl;
543 	s64 idx;
544 
545 	dl = annotate_browser__find_string(browser, browser->search_bf, &idx);
546 	if (dl == NULL) {
547 		ui_helpline__puts("String not found!");
548 		return false;
549 	}
550 
551 	annotate_browser__set_top(browser, dl, idx);
552 	browser->searching_backwards = false;
553 	return true;
554 }
555 
556 static
557 struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
558 						  char *s, s64 *idx)
559 {
560 	struct map_symbol *ms = browser->b.priv;
561 	struct symbol *sym = ms->sym;
562 	struct annotation *notes = symbol__annotation(sym);
563 	struct disasm_line *pos = browser->selection;
564 
565 	*idx = browser->b.index;
566 	list_for_each_entry_continue_reverse(pos, &notes->src->source, node) {
567 		if (disasm_line__filter(&browser->b, &pos->node))
568 			continue;
569 
570 		--*idx;
571 
572 		if (pos->line && strstr(pos->line, s) != NULL)
573 			return pos;
574 	}
575 
576 	return NULL;
577 }
578 
579 static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
580 {
581 	struct disasm_line *dl;
582 	s64 idx;
583 
584 	dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
585 	if (dl == NULL) {
586 		ui_helpline__puts("String not found!");
587 		return false;
588 	}
589 
590 	annotate_browser__set_top(browser, dl, idx);
591 	browser->searching_backwards = true;
592 	return true;
593 }
594 
595 static bool annotate_browser__search_window(struct annotate_browser *browser,
596 					    int delay_secs)
597 {
598 	if (ui_browser__input_window("Search", "String: ", browser->search_bf,
599 				     "ENTER: OK, ESC: Cancel",
600 				     delay_secs * 2) != K_ENTER ||
601 	    !*browser->search_bf)
602 		return false;
603 
604 	return true;
605 }
606 
607 static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs)
608 {
609 	if (annotate_browser__search_window(browser, delay_secs))
610 		return __annotate_browser__search(browser);
611 
612 	return false;
613 }
614 
615 static bool annotate_browser__continue_search(struct annotate_browser *browser,
616 					      int delay_secs)
617 {
618 	if (!*browser->search_bf)
619 		return annotate_browser__search(browser, delay_secs);
620 
621 	return __annotate_browser__search(browser);
622 }
623 
624 static bool annotate_browser__search_reverse(struct annotate_browser *browser,
625 					   int delay_secs)
626 {
627 	if (annotate_browser__search_window(browser, delay_secs))
628 		return __annotate_browser__search_reverse(browser);
629 
630 	return false;
631 }
632 
633 static
634 bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
635 					       int delay_secs)
636 {
637 	if (!*browser->search_bf)
638 		return annotate_browser__search_reverse(browser, delay_secs);
639 
640 	return __annotate_browser__search_reverse(browser);
641 }
642 
643 static void annotate_browser__update_addr_width(struct annotate_browser *browser)
644 {
645 	if (annotate_browser__opts.use_offset)
646 		browser->target_width = browser->min_addr_width;
647 	else
648 		browser->target_width = browser->max_addr_width;
649 
650 	browser->addr_width = browser->target_width;
651 
652 	if (annotate_browser__opts.show_nr_jumps)
653 		browser->addr_width += browser->jumps_width + 1;
654 }
655 
656 static int annotate_browser__run(struct annotate_browser *browser,
657 				 struct perf_evsel *evsel,
658 				 struct hist_browser_timer *hbt)
659 {
660 	struct rb_node *nd = NULL;
661 	struct map_symbol *ms = browser->b.priv;
662 	struct symbol *sym = ms->sym;
663 	const char *help = "Press 'h' for help on key bindings";
664 	int delay_secs = hbt ? hbt->refresh : 0;
665 	int key;
666 	char title[SYM_TITLE_MAX_SIZE];
667 
668 	sym_title(sym, ms->map, title, sizeof(title));
669 	if (ui_browser__show(&browser->b, title, help) < 0)
670 		return -1;
671 
672 	annotate_browser__calc_percent(browser, evsel);
673 
674 	if (browser->curr_hot) {
675 		annotate_browser__set_rb_top(browser, browser->curr_hot);
676 		browser->b.navkeypressed = false;
677 	}
678 
679 	nd = browser->curr_hot;
680 
681 	while (1) {
682 		key = ui_browser__run(&browser->b, delay_secs);
683 
684 		if (delay_secs != 0) {
685 			annotate_browser__calc_percent(browser, evsel);
686 			/*
687 			 * Current line focus got out of the list of most active
688 			 * lines, NULL it so that if TAB|UNTAB is pressed, we
689 			 * move to curr_hot (current hottest line).
690 			 */
691 			if (nd != NULL && RB_EMPTY_NODE(nd))
692 				nd = NULL;
693 		}
694 
695 		switch (key) {
696 		case K_TIMER:
697 			if (hbt)
698 				hbt->timer(hbt->arg);
699 
700 			if (delay_secs != 0)
701 				symbol__annotate_decay_histogram(sym, evsel->idx);
702 			continue;
703 		case K_TAB:
704 			if (nd != NULL) {
705 				nd = rb_prev(nd);
706 				if (nd == NULL)
707 					nd = rb_last(&browser->entries);
708 			} else
709 				nd = browser->curr_hot;
710 			break;
711 		case K_UNTAB:
712 			if (nd != NULL)
713 				nd = rb_next(nd);
714 				if (nd == NULL)
715 					nd = rb_first(&browser->entries);
716 			else
717 				nd = browser->curr_hot;
718 			break;
719 		case K_F1:
720 		case 'h':
721 			ui_browser__help_window(&browser->b,
722 		"UP/DOWN/PGUP\n"
723 		"PGDN/SPACE    Navigate\n"
724 		"q/ESC/CTRL+C  Exit\n\n"
725 		"->            Go to target\n"
726 		"<-            Exit\n"
727 		"H             Cycle thru hottest instructions\n"
728 		"j             Toggle showing jump to target arrows\n"
729 		"J             Toggle showing number of jump sources on targets\n"
730 		"n             Search next string\n"
731 		"o             Toggle disassembler output/simplified view\n"
732 		"s             Toggle source code view\n"
733 		"/             Search string\n"
734 		"r             Run available scripts\n"
735 		"?             Search string backwards\n");
736 			continue;
737 		case 'r':
738 			{
739 				script_browse(NULL);
740 				continue;
741 			}
742 		case 'H':
743 			nd = browser->curr_hot;
744 			break;
745 		case 's':
746 			if (annotate_browser__toggle_source(browser))
747 				ui_helpline__puts(help);
748 			continue;
749 		case 'o':
750 			annotate_browser__opts.use_offset = !annotate_browser__opts.use_offset;
751 			annotate_browser__update_addr_width(browser);
752 			continue;
753 		case 'j':
754 			annotate_browser__opts.jump_arrows = !annotate_browser__opts.jump_arrows;
755 			continue;
756 		case 'J':
757 			annotate_browser__opts.show_nr_jumps = !annotate_browser__opts.show_nr_jumps;
758 			annotate_browser__update_addr_width(browser);
759 			continue;
760 		case '/':
761 			if (annotate_browser__search(browser, delay_secs)) {
762 show_help:
763 				ui_helpline__puts(help);
764 			}
765 			continue;
766 		case 'n':
767 			if (browser->searching_backwards ?
768 			    annotate_browser__continue_search_reverse(browser, delay_secs) :
769 			    annotate_browser__continue_search(browser, delay_secs))
770 				goto show_help;
771 			continue;
772 		case '?':
773 			if (annotate_browser__search_reverse(browser, delay_secs))
774 				goto show_help;
775 			continue;
776 		case 'D': {
777 			static int seq;
778 			ui_helpline__pop();
779 			ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d",
780 					   seq++, browser->b.nr_entries,
781 					   browser->b.height,
782 					   browser->b.index,
783 					   browser->b.top_idx,
784 					   browser->nr_asm_entries);
785 		}
786 			continue;
787 		case K_ENTER:
788 		case K_RIGHT:
789 			if (browser->selection == NULL)
790 				ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
791 			else if (browser->selection->offset == -1)
792 				ui_helpline__puts("Actions are only available for assembly lines.");
793 			else if (!browser->selection->ins) {
794 				if (strcmp(browser->selection->name, "retq"))
795 					goto show_sup_ins;
796 				goto out;
797 			} else if (!(annotate_browser__jump(browser) ||
798 				     annotate_browser__callq(browser, evsel, hbt))) {
799 show_sup_ins:
800 				ui_helpline__puts("Actions are only available for 'callq', 'retq' & jump instructions.");
801 			}
802 			continue;
803 		case K_LEFT:
804 		case K_ESC:
805 		case 'q':
806 		case CTRL('c'):
807 			goto out;
808 		default:
809 			continue;
810 		}
811 
812 		if (nd != NULL)
813 			annotate_browser__set_rb_top(browser, nd);
814 	}
815 out:
816 	ui_browser__hide(&browser->b);
817 	return key;
818 }
819 
820 int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
821 			     struct hist_browser_timer *hbt)
822 {
823 	return symbol__tui_annotate(he->ms.sym, he->ms.map, evsel, hbt);
824 }
825 
826 static void annotate_browser__mark_jump_targets(struct annotate_browser *browser,
827 						size_t size)
828 {
829 	u64 offset;
830 	struct map_symbol *ms = browser->b.priv;
831 	struct symbol *sym = ms->sym;
832 
833 	/* PLT symbols contain external offsets */
834 	if (strstr(sym->name, "@plt"))
835 		return;
836 
837 	for (offset = 0; offset < size; ++offset) {
838 		struct disasm_line *dl = browser->offsets[offset], *dlt;
839 		struct browser_disasm_line *bdlt;
840 
841 		if (!disasm_line__is_valid_jump(dl, sym))
842 			continue;
843 
844 		dlt = browser->offsets[dl->ops.target.offset];
845 		/*
846  		 * FIXME: Oops, no jump target? Buggy disassembler? Or do we
847  		 * have to adjust to the previous offset?
848  		 */
849 		if (dlt == NULL)
850 			continue;
851 
852 		bdlt = disasm_line__browser(dlt);
853 		if (++bdlt->jump_sources > browser->max_jump_sources)
854 			browser->max_jump_sources = bdlt->jump_sources;
855 
856 		++browser->nr_jumps;
857 	}
858 
859 }
860 
861 static inline int width_jumps(int n)
862 {
863 	if (n >= 100)
864 		return 5;
865 	if (n / 10)
866 		return 2;
867 	return 1;
868 }
869 
870 int symbol__tui_annotate(struct symbol *sym, struct map *map,
871 			 struct perf_evsel *evsel,
872 			 struct hist_browser_timer *hbt)
873 {
874 	struct disasm_line *pos, *n;
875 	struct annotation *notes;
876 	size_t size;
877 	struct map_symbol ms = {
878 		.map = map,
879 		.sym = sym,
880 	};
881 	struct annotate_browser browser = {
882 		.b = {
883 			.refresh = annotate_browser__refresh,
884 			.seek	 = ui_browser__list_head_seek,
885 			.write	 = annotate_browser__write,
886 			.filter  = disasm_line__filter,
887 			.priv	 = &ms,
888 			.use_navkeypressed = true,
889 		},
890 	};
891 	int ret = -1;
892 	int nr_pcnt = 1;
893 	size_t sizeof_bdl = sizeof(struct browser_disasm_line);
894 
895 	if (sym == NULL)
896 		return -1;
897 
898 	size = symbol__size(sym);
899 
900 	if (map->dso->annotate_warned)
901 		return -1;
902 
903 	browser.offsets = zalloc(size * sizeof(struct disasm_line *));
904 	if (browser.offsets == NULL) {
905 		ui__error("Not enough memory!");
906 		return -1;
907 	}
908 
909 	if (perf_evsel__is_group_event(evsel)) {
910 		nr_pcnt = evsel->nr_members;
911 		sizeof_bdl += sizeof(double) * (nr_pcnt - 1);
912 	}
913 
914 	if (symbol__annotate(sym, map, sizeof_bdl) < 0) {
915 		ui__error("%s", ui_helpline__last_msg);
916 		goto out_free_offsets;
917 	}
918 
919 	ui_helpline__push("Press <- or ESC to exit");
920 
921 	notes = symbol__annotation(sym);
922 	browser.start = map__rip_2objdump(map, sym->start);
923 
924 	list_for_each_entry(pos, &notes->src->source, node) {
925 		struct browser_disasm_line *bpos;
926 		size_t line_len = strlen(pos->line);
927 
928 		if (browser.b.width < line_len)
929 			browser.b.width = line_len;
930 		bpos = disasm_line__browser(pos);
931 		bpos->idx = browser.nr_entries++;
932 		if (pos->offset != -1) {
933 			bpos->idx_asm = browser.nr_asm_entries++;
934 			/*
935 			 * FIXME: short term bandaid to cope with assembly
936 			 * routines that comes with labels in the same column
937 			 * as the address in objdump, sigh.
938 			 *
939 			 * E.g. copy_user_generic_unrolled
940  			 */
941 			if (pos->offset < (s64)size)
942 				browser.offsets[pos->offset] = pos;
943 		} else
944 			bpos->idx_asm = -1;
945 	}
946 
947 	annotate_browser__mark_jump_targets(&browser, size);
948 
949 	browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size);
950 	browser.max_addr_width = hex_width(sym->end);
951 	browser.jumps_width = width_jumps(browser.max_jump_sources);
952 	browser.nr_events = nr_pcnt;
953 	browser.b.nr_entries = browser.nr_entries;
954 	browser.b.entries = &notes->src->source,
955 	browser.b.width += 18; /* Percentage */
956 
957 	if (annotate_browser__opts.hide_src_code)
958 		annotate_browser__init_asm_mode(&browser);
959 
960 	annotate_browser__update_addr_width(&browser);
961 
962 	ret = annotate_browser__run(&browser, evsel, hbt);
963 	list_for_each_entry_safe(pos, n, &notes->src->source, node) {
964 		list_del(&pos->node);
965 		disasm_line__free(pos);
966 	}
967 
968 out_free_offsets:
969 	free(browser.offsets);
970 	return ret;
971 }
972 
973 #define ANNOTATE_CFG(n) \
974 	{ .name = #n, .value = &annotate_browser__opts.n, }
975 
976 /*
977  * Keep the entries sorted, they are bsearch'ed
978  */
979 static struct annotate_config {
980 	const char *name;
981 	bool *value;
982 } annotate__configs[] = {
983 	ANNOTATE_CFG(hide_src_code),
984 	ANNOTATE_CFG(jump_arrows),
985 	ANNOTATE_CFG(show_nr_jumps),
986 	ANNOTATE_CFG(use_offset),
987 };
988 
989 #undef ANNOTATE_CFG
990 
991 static int annotate_config__cmp(const void *name, const void *cfgp)
992 {
993 	const struct annotate_config *cfg = cfgp;
994 
995 	return strcmp(name, cfg->name);
996 }
997 
998 static int annotate__config(const char *var, const char *value,
999 			    void *data __maybe_unused)
1000 {
1001 	struct annotate_config *cfg;
1002 	const char *name;
1003 
1004 	if (prefixcmp(var, "annotate.") != 0)
1005 		return 0;
1006 
1007 	name = var + 9;
1008 	cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
1009 		      sizeof(struct annotate_config), annotate_config__cmp);
1010 
1011 	if (cfg == NULL)
1012 		return -1;
1013 
1014 	*cfg->value = perf_config_bool(name, value);
1015 	return 0;
1016 }
1017 
1018 void annotate_browser__init(void)
1019 {
1020 	perf_config(annotate__config, NULL);
1021 }
1022