xref: /openbmc/linux/tools/perf/ui/browsers/annotate.c (revision 82003e04)
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 && 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 || !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 	s64 idx;
547 
548 	if (!ins__is_jump(dl->ins))
549 		return false;
550 
551 	dl = annotate_browser__find_offset(browser, dl->ops.target.offset, &idx);
552 	if (dl == NULL) {
553 		ui_helpline__puts("Invalid jump offset");
554 		return true;
555 	}
556 
557 	annotate_browser__set_top(browser, dl, idx);
558 
559 	return true;
560 }
561 
562 static
563 struct disasm_line *annotate_browser__find_string(struct annotate_browser *browser,
564 					  char *s, s64 *idx)
565 {
566 	struct map_symbol *ms = browser->b.priv;
567 	struct symbol *sym = ms->sym;
568 	struct annotation *notes = symbol__annotation(sym);
569 	struct disasm_line *pos = browser->selection;
570 
571 	*idx = browser->b.index;
572 	list_for_each_entry_continue(pos, &notes->src->source, node) {
573 		if (disasm_line__filter(&browser->b, &pos->node))
574 			continue;
575 
576 		++*idx;
577 
578 		if (pos->line && strstr(pos->line, s) != NULL)
579 			return pos;
580 	}
581 
582 	return NULL;
583 }
584 
585 static bool __annotate_browser__search(struct annotate_browser *browser)
586 {
587 	struct disasm_line *dl;
588 	s64 idx;
589 
590 	dl = annotate_browser__find_string(browser, browser->search_bf, &idx);
591 	if (dl == NULL) {
592 		ui_helpline__puts("String not found!");
593 		return false;
594 	}
595 
596 	annotate_browser__set_top(browser, dl, idx);
597 	browser->searching_backwards = false;
598 	return true;
599 }
600 
601 static
602 struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
603 						  char *s, s64 *idx)
604 {
605 	struct map_symbol *ms = browser->b.priv;
606 	struct symbol *sym = ms->sym;
607 	struct annotation *notes = symbol__annotation(sym);
608 	struct disasm_line *pos = browser->selection;
609 
610 	*idx = browser->b.index;
611 	list_for_each_entry_continue_reverse(pos, &notes->src->source, node) {
612 		if (disasm_line__filter(&browser->b, &pos->node))
613 			continue;
614 
615 		--*idx;
616 
617 		if (pos->line && strstr(pos->line, s) != NULL)
618 			return pos;
619 	}
620 
621 	return NULL;
622 }
623 
624 static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
625 {
626 	struct disasm_line *dl;
627 	s64 idx;
628 
629 	dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
630 	if (dl == NULL) {
631 		ui_helpline__puts("String not found!");
632 		return false;
633 	}
634 
635 	annotate_browser__set_top(browser, dl, idx);
636 	browser->searching_backwards = true;
637 	return true;
638 }
639 
640 static bool annotate_browser__search_window(struct annotate_browser *browser,
641 					    int delay_secs)
642 {
643 	if (ui_browser__input_window("Search", "String: ", browser->search_bf,
644 				     "ENTER: OK, ESC: Cancel",
645 				     delay_secs * 2) != K_ENTER ||
646 	    !*browser->search_bf)
647 		return false;
648 
649 	return true;
650 }
651 
652 static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs)
653 {
654 	if (annotate_browser__search_window(browser, delay_secs))
655 		return __annotate_browser__search(browser);
656 
657 	return false;
658 }
659 
660 static bool annotate_browser__continue_search(struct annotate_browser *browser,
661 					      int delay_secs)
662 {
663 	if (!*browser->search_bf)
664 		return annotate_browser__search(browser, delay_secs);
665 
666 	return __annotate_browser__search(browser);
667 }
668 
669 static bool annotate_browser__search_reverse(struct annotate_browser *browser,
670 					   int delay_secs)
671 {
672 	if (annotate_browser__search_window(browser, delay_secs))
673 		return __annotate_browser__search_reverse(browser);
674 
675 	return false;
676 }
677 
678 static
679 bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
680 					       int delay_secs)
681 {
682 	if (!*browser->search_bf)
683 		return annotate_browser__search_reverse(browser, delay_secs);
684 
685 	return __annotate_browser__search_reverse(browser);
686 }
687 
688 static void annotate_browser__update_addr_width(struct annotate_browser *browser)
689 {
690 	if (annotate_browser__opts.use_offset)
691 		browser->target_width = browser->min_addr_width;
692 	else
693 		browser->target_width = browser->max_addr_width;
694 
695 	browser->addr_width = browser->target_width;
696 
697 	if (annotate_browser__opts.show_nr_jumps)
698 		browser->addr_width += browser->jumps_width + 1;
699 }
700 
701 static int annotate_browser__run(struct annotate_browser *browser,
702 				 struct perf_evsel *evsel,
703 				 struct hist_browser_timer *hbt)
704 {
705 	struct rb_node *nd = NULL;
706 	struct map_symbol *ms = browser->b.priv;
707 	struct symbol *sym = ms->sym;
708 	const char *help = "Press 'h' for help on key bindings";
709 	int delay_secs = hbt ? hbt->refresh : 0;
710 	int key;
711 	char title[SYM_TITLE_MAX_SIZE];
712 
713 	sym_title(sym, ms->map, title, sizeof(title));
714 	if (ui_browser__show(&browser->b, title, help) < 0)
715 		return -1;
716 
717 	annotate_browser__calc_percent(browser, evsel);
718 
719 	if (browser->curr_hot) {
720 		annotate_browser__set_rb_top(browser, browser->curr_hot);
721 		browser->b.navkeypressed = false;
722 	}
723 
724 	nd = browser->curr_hot;
725 
726 	while (1) {
727 		key = ui_browser__run(&browser->b, delay_secs);
728 
729 		if (delay_secs != 0) {
730 			annotate_browser__calc_percent(browser, evsel);
731 			/*
732 			 * Current line focus got out of the list of most active
733 			 * lines, NULL it so that if TAB|UNTAB is pressed, we
734 			 * move to curr_hot (current hottest line).
735 			 */
736 			if (nd != NULL && RB_EMPTY_NODE(nd))
737 				nd = NULL;
738 		}
739 
740 		switch (key) {
741 		case K_TIMER:
742 			if (hbt)
743 				hbt->timer(hbt->arg);
744 
745 			if (delay_secs != 0)
746 				symbol__annotate_decay_histogram(sym, evsel->idx);
747 			continue;
748 		case K_TAB:
749 			if (nd != NULL) {
750 				nd = rb_prev(nd);
751 				if (nd == NULL)
752 					nd = rb_last(&browser->entries);
753 			} else
754 				nd = browser->curr_hot;
755 			break;
756 		case K_UNTAB:
757 			if (nd != NULL) {
758 				nd = rb_next(nd);
759 				if (nd == NULL)
760 					nd = rb_first(&browser->entries);
761 			} else
762 				nd = browser->curr_hot;
763 			break;
764 		case K_F1:
765 		case 'h':
766 			ui_browser__help_window(&browser->b,
767 		"UP/DOWN/PGUP\n"
768 		"PGDN/SPACE    Navigate\n"
769 		"q/ESC/CTRL+C  Exit\n\n"
770 		"ENTER         Go to target\n"
771 		"ESC           Exit\n"
772 		"H             Cycle thru hottest instructions\n"
773 		"j             Toggle showing jump to target arrows\n"
774 		"J             Toggle showing number of jump sources on targets\n"
775 		"n             Search next string\n"
776 		"o             Toggle disassembler output/simplified view\n"
777 		"s             Toggle source code view\n"
778 		"t             Toggle total period view\n"
779 		"/             Search string\n"
780 		"k             Toggle line numbers\n"
781 		"r             Run available scripts\n"
782 		"?             Search string backwards\n");
783 			continue;
784 		case 'r':
785 			{
786 				script_browse(NULL);
787 				continue;
788 			}
789 		case 'k':
790 			annotate_browser__opts.show_linenr =
791 				!annotate_browser__opts.show_linenr;
792 			break;
793 		case 'H':
794 			nd = browser->curr_hot;
795 			break;
796 		case 's':
797 			if (annotate_browser__toggle_source(browser))
798 				ui_helpline__puts(help);
799 			continue;
800 		case 'o':
801 			annotate_browser__opts.use_offset = !annotate_browser__opts.use_offset;
802 			annotate_browser__update_addr_width(browser);
803 			continue;
804 		case 'j':
805 			annotate_browser__opts.jump_arrows = !annotate_browser__opts.jump_arrows;
806 			continue;
807 		case 'J':
808 			annotate_browser__opts.show_nr_jumps = !annotate_browser__opts.show_nr_jumps;
809 			annotate_browser__update_addr_width(browser);
810 			continue;
811 		case '/':
812 			if (annotate_browser__search(browser, delay_secs)) {
813 show_help:
814 				ui_helpline__puts(help);
815 			}
816 			continue;
817 		case 'n':
818 			if (browser->searching_backwards ?
819 			    annotate_browser__continue_search_reverse(browser, delay_secs) :
820 			    annotate_browser__continue_search(browser, delay_secs))
821 				goto show_help;
822 			continue;
823 		case '?':
824 			if (annotate_browser__search_reverse(browser, delay_secs))
825 				goto show_help;
826 			continue;
827 		case 'D': {
828 			static int seq;
829 			ui_helpline__pop();
830 			ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d",
831 					   seq++, browser->b.nr_entries,
832 					   browser->b.height,
833 					   browser->b.index,
834 					   browser->b.top_idx,
835 					   browser->nr_asm_entries);
836 		}
837 			continue;
838 		case K_ENTER:
839 		case K_RIGHT:
840 			if (browser->selection == NULL)
841 				ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
842 			else if (browser->selection->offset == -1)
843 				ui_helpline__puts("Actions are only available for assembly lines.");
844 			else if (!browser->selection->ins)
845 				goto show_sup_ins;
846 			else if (ins__is_ret(browser->selection->ins))
847 				goto out;
848 			else if (!(annotate_browser__jump(browser) ||
849 				     annotate_browser__callq(browser, evsel, hbt))) {
850 show_sup_ins:
851 				ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
852 			}
853 			continue;
854 		case 't':
855 			annotate_browser__opts.show_total_period =
856 			  !annotate_browser__opts.show_total_period;
857 			annotate_browser__update_addr_width(browser);
858 			continue;
859 		case K_LEFT:
860 		case K_ESC:
861 		case 'q':
862 		case CTRL('c'):
863 			goto out;
864 		default:
865 			continue;
866 		}
867 
868 		if (nd != NULL)
869 			annotate_browser__set_rb_top(browser, nd);
870 	}
871 out:
872 	ui_browser__hide(&browser->b);
873 	return key;
874 }
875 
876 int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel,
877 			     struct hist_browser_timer *hbt)
878 {
879 	/* Set default value for show_total_period.  */
880 	annotate_browser__opts.show_total_period =
881 	  symbol_conf.show_total_period;
882 
883 	return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt);
884 }
885 
886 int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
887 			     struct hist_browser_timer *hbt)
888 {
889 	/* reset abort key so that it can get Ctrl-C as a key */
890 	SLang_reset_tty();
891 	SLang_init_tty(0, 0, 0);
892 
893 	return map_symbol__tui_annotate(&he->ms, evsel, hbt);
894 }
895 
896 
897 static unsigned count_insn(struct annotate_browser *browser, u64 start, u64 end)
898 {
899 	unsigned n_insn = 0;
900 	u64 offset;
901 
902 	for (offset = start; offset <= end; offset++) {
903 		if (browser->offsets[offset])
904 			n_insn++;
905 	}
906 	return n_insn;
907 }
908 
909 static void count_and_fill(struct annotate_browser *browser, u64 start, u64 end,
910 			   struct cyc_hist *ch)
911 {
912 	unsigned n_insn;
913 	u64 offset;
914 
915 	n_insn = count_insn(browser, start, end);
916 	if (n_insn && ch->num && ch->cycles) {
917 		float ipc = n_insn / ((double)ch->cycles / (double)ch->num);
918 
919 		/* Hide data when there are too many overlaps. */
920 		if (ch->reset >= 0x7fff || ch->reset >= ch->num / 2)
921 			return;
922 
923 		for (offset = start; offset <= end; offset++) {
924 			struct disasm_line *dl = browser->offsets[offset];
925 
926 			if (dl)
927 				dl->ipc = ipc;
928 		}
929 	}
930 }
931 
932 /*
933  * This should probably be in util/annotate.c to share with the tty
934  * annotate, but right now we need the per byte offsets arrays,
935  * which are only here.
936  */
937 static void annotate__compute_ipc(struct annotate_browser *browser, size_t size,
938 			   struct symbol *sym)
939 {
940 	u64 offset;
941 	struct annotation *notes = symbol__annotation(sym);
942 
943 	if (!notes->src || !notes->src->cycles_hist)
944 		return;
945 
946 	pthread_mutex_lock(&notes->lock);
947 	for (offset = 0; offset < size; ++offset) {
948 		struct cyc_hist *ch;
949 
950 		ch = &notes->src->cycles_hist[offset];
951 		if (ch && ch->cycles) {
952 			struct disasm_line *dl;
953 
954 			if (ch->have_start)
955 				count_and_fill(browser, ch->start, offset, ch);
956 			dl = browser->offsets[offset];
957 			if (dl && ch->num_aggr)
958 				dl->cycles = ch->cycles_aggr / ch->num_aggr;
959 			browser->have_cycles = true;
960 		}
961 	}
962 	pthread_mutex_unlock(&notes->lock);
963 }
964 
965 static void annotate_browser__mark_jump_targets(struct annotate_browser *browser,
966 						size_t size)
967 {
968 	u64 offset;
969 	struct map_symbol *ms = browser->b.priv;
970 	struct symbol *sym = ms->sym;
971 
972 	/* PLT symbols contain external offsets */
973 	if (strstr(sym->name, "@plt"))
974 		return;
975 
976 	for (offset = 0; offset < size; ++offset) {
977 		struct disasm_line *dl = browser->offsets[offset], *dlt;
978 		struct browser_disasm_line *bdlt;
979 
980 		if (!disasm_line__is_valid_jump(dl, sym))
981 			continue;
982 
983 		dlt = browser->offsets[dl->ops.target.offset];
984 		/*
985  		 * FIXME: Oops, no jump target? Buggy disassembler? Or do we
986  		 * have to adjust to the previous offset?
987  		 */
988 		if (dlt == NULL)
989 			continue;
990 
991 		bdlt = disasm_line__browser(dlt);
992 		if (++bdlt->jump_sources > browser->max_jump_sources)
993 			browser->max_jump_sources = bdlt->jump_sources;
994 
995 		++browser->nr_jumps;
996 	}
997 }
998 
999 static inline int width_jumps(int n)
1000 {
1001 	if (n >= 100)
1002 		return 5;
1003 	if (n / 10)
1004 		return 2;
1005 	return 1;
1006 }
1007 
1008 int symbol__tui_annotate(struct symbol *sym, struct map *map,
1009 			 struct perf_evsel *evsel,
1010 			 struct hist_browser_timer *hbt)
1011 {
1012 	struct disasm_line *pos, *n;
1013 	struct annotation *notes;
1014 	size_t size;
1015 	struct map_symbol ms = {
1016 		.map = map,
1017 		.sym = sym,
1018 	};
1019 	struct annotate_browser browser = {
1020 		.b = {
1021 			.refresh = annotate_browser__refresh,
1022 			.seek	 = ui_browser__list_head_seek,
1023 			.write	 = annotate_browser__write,
1024 			.filter  = disasm_line__filter,
1025 			.priv	 = &ms,
1026 			.use_navkeypressed = true,
1027 		},
1028 	};
1029 	int ret = -1, err;
1030 	int nr_pcnt = 1;
1031 	size_t sizeof_bdl = sizeof(struct browser_disasm_line);
1032 
1033 	if (sym == NULL)
1034 		return -1;
1035 
1036 	size = symbol__size(sym);
1037 
1038 	if (map->dso->annotate_warned)
1039 		return -1;
1040 
1041 	browser.offsets = zalloc(size * sizeof(struct disasm_line *));
1042 	if (browser.offsets == NULL) {
1043 		ui__error("Not enough memory!");
1044 		return -1;
1045 	}
1046 
1047 	if (perf_evsel__is_group_event(evsel)) {
1048 		nr_pcnt = evsel->nr_members;
1049 		sizeof_bdl += sizeof(struct disasm_line_samples) *
1050 		  (nr_pcnt - 1);
1051 	}
1052 
1053 	err = symbol__disassemble(sym, map, sizeof_bdl);
1054 	if (err) {
1055 		char msg[BUFSIZ];
1056 		symbol__strerror_disassemble(sym, map, err, msg, sizeof(msg));
1057 		ui__error("Couldn't annotate %s:\n%s", sym->name, msg);
1058 		goto out_free_offsets;
1059 	}
1060 
1061 	ui_helpline__push("Press ESC to exit");
1062 
1063 	notes = symbol__annotation(sym);
1064 	browser.start = map__rip_2objdump(map, sym->start);
1065 
1066 	list_for_each_entry(pos, &notes->src->source, node) {
1067 		struct browser_disasm_line *bpos;
1068 		size_t line_len = strlen(pos->line);
1069 
1070 		if (browser.b.width < line_len)
1071 			browser.b.width = line_len;
1072 		bpos = disasm_line__browser(pos);
1073 		bpos->idx = browser.nr_entries++;
1074 		if (pos->offset != -1) {
1075 			bpos->idx_asm = browser.nr_asm_entries++;
1076 			/*
1077 			 * FIXME: short term bandaid to cope with assembly
1078 			 * routines that comes with labels in the same column
1079 			 * as the address in objdump, sigh.
1080 			 *
1081 			 * E.g. copy_user_generic_unrolled
1082  			 */
1083 			if (pos->offset < (s64)size)
1084 				browser.offsets[pos->offset] = pos;
1085 		} else
1086 			bpos->idx_asm = -1;
1087 	}
1088 
1089 	annotate_browser__mark_jump_targets(&browser, size);
1090 	annotate__compute_ipc(&browser, size, sym);
1091 
1092 	browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size);
1093 	browser.max_addr_width = hex_width(sym->end);
1094 	browser.jumps_width = width_jumps(browser.max_jump_sources);
1095 	browser.nr_events = nr_pcnt;
1096 	browser.b.nr_entries = browser.nr_entries;
1097 	browser.b.entries = &notes->src->source,
1098 	browser.b.width += 18; /* Percentage */
1099 
1100 	if (annotate_browser__opts.hide_src_code)
1101 		annotate_browser__init_asm_mode(&browser);
1102 
1103 	annotate_browser__update_addr_width(&browser);
1104 
1105 	ret = annotate_browser__run(&browser, evsel, hbt);
1106 	list_for_each_entry_safe(pos, n, &notes->src->source, node) {
1107 		list_del(&pos->node);
1108 		disasm_line__free(pos);
1109 	}
1110 
1111 out_free_offsets:
1112 	free(browser.offsets);
1113 	return ret;
1114 }
1115 
1116 #define ANNOTATE_CFG(n) \
1117 	{ .name = #n, .value = &annotate_browser__opts.n, }
1118 
1119 /*
1120  * Keep the entries sorted, they are bsearch'ed
1121  */
1122 static struct annotate_config {
1123 	const char *name;
1124 	bool *value;
1125 } annotate__configs[] = {
1126 	ANNOTATE_CFG(hide_src_code),
1127 	ANNOTATE_CFG(jump_arrows),
1128 	ANNOTATE_CFG(show_linenr),
1129 	ANNOTATE_CFG(show_nr_jumps),
1130 	ANNOTATE_CFG(show_total_period),
1131 	ANNOTATE_CFG(use_offset),
1132 };
1133 
1134 #undef ANNOTATE_CFG
1135 
1136 static int annotate_config__cmp(const void *name, const void *cfgp)
1137 {
1138 	const struct annotate_config *cfg = cfgp;
1139 
1140 	return strcmp(name, cfg->name);
1141 }
1142 
1143 static int annotate__config(const char *var, const char *value,
1144 			    void *data __maybe_unused)
1145 {
1146 	struct annotate_config *cfg;
1147 	const char *name;
1148 
1149 	if (prefixcmp(var, "annotate.") != 0)
1150 		return 0;
1151 
1152 	name = var + 9;
1153 	cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
1154 		      sizeof(struct annotate_config), annotate_config__cmp);
1155 
1156 	if (cfg == NULL)
1157 		ui__warning("%s variable unknown, ignoring...", var);
1158 	else
1159 		*cfg->value = perf_config_bool(name, value);
1160 	return 0;
1161 }
1162 
1163 void annotate_browser__init(void)
1164 {
1165 	perf_config(annotate__config, NULL);
1166 }
1167