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