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