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