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