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