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