xref: /openbmc/linux/tools/perf/ui/browsers/annotate.c (revision fa0a497b)
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 <pthread.h>
12 
13 struct disasm_line_samples {
14 	double		percent;
15 	u64		nr;
16 };
17 
18 #define IPC_WIDTH 6
19 #define CYCLES_WIDTH 6
20 
21 struct browser_disasm_line {
22 	struct rb_node			rb_node;
23 	u32				idx;
24 	int				idx_asm;
25 	int				jump_sources;
26 	/*
27 	 * actual length of this array is saved on the nr_events field
28 	 * of the struct annotate_browser
29 	 */
30 	struct disasm_line_samples	samples[1];
31 };
32 
33 static struct annotate_browser_opt {
34 	bool hide_src_code,
35 	     use_offset,
36 	     jump_arrows,
37 	     show_linenr,
38 	     show_nr_jumps,
39 	     show_total_period;
40 } annotate_browser__opts = {
41 	.use_offset	= true,
42 	.jump_arrows	= true,
43 };
44 
45 struct annotate_browser {
46 	struct ui_browser b;
47 	struct rb_root	  entries;
48 	struct rb_node	  *curr_hot;
49 	struct disasm_line  *selection;
50 	struct disasm_line  **offsets;
51 	int		    nr_events;
52 	u64		    start;
53 	int		    nr_asm_entries;
54 	int		    nr_entries;
55 	int		    max_jump_sources;
56 	int		    nr_jumps;
57 	bool		    searching_backwards;
58 	bool		    have_cycles;
59 	u8		    addr_width;
60 	u8		    jumps_width;
61 	u8		    target_width;
62 	u8		    min_addr_width;
63 	u8		    max_addr_width;
64 	char		    search_bf[128];
65 };
66 
67 static inline struct browser_disasm_line *disasm_line__browser(struct disasm_line *dl)
68 {
69 	return (struct browser_disasm_line *)(dl + 1);
70 }
71 
72 static bool disasm_line__filter(struct ui_browser *browser __maybe_unused,
73 				void *entry)
74 {
75 	if (annotate_browser__opts.hide_src_code) {
76 		struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
77 		return dl->offset == -1;
78 	}
79 
80 	return false;
81 }
82 
83 static int annotate_browser__jumps_percent_color(struct annotate_browser *browser,
84 						 int nr, bool current)
85 {
86 	if (current && (!browser->b.use_navkeypressed || browser->b.navkeypressed))
87 		return HE_COLORSET_SELECTED;
88 	if (nr == browser->max_jump_sources)
89 		return HE_COLORSET_TOP;
90 	if (nr > 1)
91 		return HE_COLORSET_MEDIUM;
92 	return HE_COLORSET_NORMAL;
93 }
94 
95 static int annotate_browser__set_jumps_percent_color(struct annotate_browser *browser,
96 						     int nr, bool current)
97 {
98 	 int color = annotate_browser__jumps_percent_color(browser, nr, current);
99 	 return ui_browser__set_color(&browser->b, color);
100 }
101 
102 static int annotate_browser__pcnt_width(struct annotate_browser *ab)
103 {
104 	int w = 7 * ab->nr_events;
105 
106 	if (ab->have_cycles)
107 		w += IPC_WIDTH + CYCLES_WIDTH;
108 	return w;
109 }
110 
111 static void annotate_browser__write(struct ui_browser *browser, void *entry, int row)
112 {
113 	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
114 	struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
115 	struct browser_disasm_line *bdl = disasm_line__browser(dl);
116 	bool current_entry = ui_browser__is_current_entry(browser, row);
117 	bool change_color = (!annotate_browser__opts.hide_src_code &&
118 			     (!current_entry || (browser->use_navkeypressed &&
119 					         !browser->navkeypressed)));
120 	int width = browser->width, printed;
121 	int i, pcnt_width = annotate_browser__pcnt_width(ab);
122 	double percent_max = 0.0;
123 	char bf[256];
124 
125 	for (i = 0; i < ab->nr_events; i++) {
126 		if (bdl->samples[i].percent > percent_max)
127 			percent_max = bdl->samples[i].percent;
128 	}
129 
130 	if (dl->offset != -1 && percent_max != 0.0) {
131 		if (percent_max != 0.0) {
132 			for (i = 0; i < ab->nr_events; i++) {
133 				ui_browser__set_percent_color(browser,
134 							bdl->samples[i].percent,
135 							current_entry);
136 				if (annotate_browser__opts.show_total_period) {
137 					ui_browser__printf(browser, "%6" PRIu64 " ",
138 							   bdl->samples[i].nr);
139 				} else {
140 					ui_browser__printf(browser, "%6.2f ",
141 							   bdl->samples[i].percent);
142 				}
143 			}
144 		} else {
145 			ui_browser__write_nstring(browser, " ", 7 * ab->nr_events);
146 		}
147 	} else {
148 		ui_browser__set_percent_color(browser, 0, current_entry);
149 		ui_browser__write_nstring(browser, " ", 7 * ab->nr_events);
150 	}
151 	if (ab->have_cycles) {
152 		if (dl->ipc)
153 			ui_browser__printf(browser, "%*.2f ", IPC_WIDTH - 1, dl->ipc);
154 		else
155 			ui_browser__write_nstring(browser, " ", IPC_WIDTH);
156 		if (dl->cycles)
157 			ui_browser__printf(browser, "%*" PRIu64 " ",
158 					   CYCLES_WIDTH - 1, dl->cycles);
159 		else
160 			ui_browser__write_nstring(browser, " ", CYCLES_WIDTH);
161 	}
162 
163 	SLsmg_write_char(' ');
164 
165 	/* The scroll bar isn't being used */
166 	if (!browser->navkeypressed)
167 		width += 1;
168 
169 	if (!*dl->line)
170 		ui_browser__write_nstring(browser, " ", width - pcnt_width);
171 	else if (dl->offset == -1) {
172 		if (dl->line_nr && annotate_browser__opts.show_linenr)
173 			printed = scnprintf(bf, sizeof(bf), "%-*d ",
174 					ab->addr_width + 1, dl->line_nr);
175 		else
176 			printed = scnprintf(bf, sizeof(bf), "%*s  ",
177 				    ab->addr_width, " ");
178 		ui_browser__write_nstring(browser, bf, printed);
179 		ui_browser__write_nstring(browser, dl->line, width - printed - pcnt_width + 1);
180 	} else {
181 		u64 addr = dl->offset;
182 		int color = -1;
183 
184 		if (!annotate_browser__opts.use_offset)
185 			addr += ab->start;
186 
187 		if (!annotate_browser__opts.use_offset) {
188 			printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr);
189 		} else {
190 			if (bdl->jump_sources) {
191 				if (annotate_browser__opts.show_nr_jumps) {
192 					int prev;
193 					printed = scnprintf(bf, sizeof(bf), "%*d ",
194 							    ab->jumps_width,
195 							    bdl->jump_sources);
196 					prev = annotate_browser__set_jumps_percent_color(ab, bdl->jump_sources,
197 											 current_entry);
198 					ui_browser__write_nstring(browser, bf, printed);
199 					ui_browser__set_color(browser, prev);
200 				}
201 
202 				printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ": ",
203 						    ab->target_width, addr);
204 			} else {
205 				printed = scnprintf(bf, sizeof(bf), "%*s  ",
206 						    ab->addr_width, " ");
207 			}
208 		}
209 
210 		if (change_color)
211 			color = ui_browser__set_color(browser, HE_COLORSET_ADDR);
212 		ui_browser__write_nstring(browser, bf, printed);
213 		if (change_color)
214 			ui_browser__set_color(browser, color);
215 		if (dl->ins && dl->ins->ops->scnprintf) {
216 			if (ins__is_jump(dl->ins)) {
217 				bool fwd = dl->ops.target.offset > (u64)dl->offset;
218 
219 				ui_browser__write_graph(browser, fwd ? SLSMG_DARROW_CHAR :
220 								    SLSMG_UARROW_CHAR);
221 				SLsmg_write_char(' ');
222 			} else if (ins__is_call(dl->ins)) {
223 				ui_browser__write_graph(browser, SLSMG_RARROW_CHAR);
224 				SLsmg_write_char(' ');
225 			} else {
226 				ui_browser__write_nstring(browser, " ", 2);
227 			}
228 		} else {
229 			if (strcmp(dl->name, "retq")) {
230 				ui_browser__write_nstring(browser, " ", 2);
231 			} else {
232 				ui_browser__write_graph(browser, SLSMG_LARROW_CHAR);
233 				SLsmg_write_char(' ');
234 			}
235 		}
236 
237 		disasm_line__scnprintf(dl, bf, sizeof(bf), !annotate_browser__opts.use_offset);
238 		ui_browser__write_nstring(browser, bf, width - pcnt_width - 3 - printed);
239 	}
240 
241 	if (current_entry)
242 		ab->selection = dl;
243 }
244 
245 static bool disasm_line__is_valid_jump(struct disasm_line *dl, struct symbol *sym)
246 {
247 	if (!dl || !dl->ins || !ins__is_jump(dl->ins)
248 	    || !disasm_line__has_offset(dl)
249 	    || dl->ops.target.offset >= 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, NULL) ||
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 	s64 idx;
548 
549 	if (!ins__is_jump(dl->ins))
550 		return false;
551 
552 	dl = annotate_browser__find_offset(browser, dl->ops.target.offset, &idx);
553 	if (dl == NULL) {
554 		ui_helpline__puts("Invalid jump offset");
555 		return true;
556 	}
557 
558 	annotate_browser__set_top(browser, dl, idx);
559 
560 	return true;
561 }
562 
563 static
564 struct disasm_line *annotate_browser__find_string(struct annotate_browser *browser,
565 					  char *s, s64 *idx)
566 {
567 	struct map_symbol *ms = browser->b.priv;
568 	struct symbol *sym = ms->sym;
569 	struct annotation *notes = symbol__annotation(sym);
570 	struct disasm_line *pos = browser->selection;
571 
572 	*idx = browser->b.index;
573 	list_for_each_entry_continue(pos, &notes->src->source, node) {
574 		if (disasm_line__filter(&browser->b, &pos->node))
575 			continue;
576 
577 		++*idx;
578 
579 		if (pos->line && strstr(pos->line, s) != NULL)
580 			return pos;
581 	}
582 
583 	return NULL;
584 }
585 
586 static bool __annotate_browser__search(struct annotate_browser *browser)
587 {
588 	struct disasm_line *dl;
589 	s64 idx;
590 
591 	dl = annotate_browser__find_string(browser, browser->search_bf, &idx);
592 	if (dl == NULL) {
593 		ui_helpline__puts("String not found!");
594 		return false;
595 	}
596 
597 	annotate_browser__set_top(browser, dl, idx);
598 	browser->searching_backwards = false;
599 	return true;
600 }
601 
602 static
603 struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
604 						  char *s, s64 *idx)
605 {
606 	struct map_symbol *ms = browser->b.priv;
607 	struct symbol *sym = ms->sym;
608 	struct annotation *notes = symbol__annotation(sym);
609 	struct disasm_line *pos = browser->selection;
610 
611 	*idx = browser->b.index;
612 	list_for_each_entry_continue_reverse(pos, &notes->src->source, node) {
613 		if (disasm_line__filter(&browser->b, &pos->node))
614 			continue;
615 
616 		--*idx;
617 
618 		if (pos->line && strstr(pos->line, s) != NULL)
619 			return pos;
620 	}
621 
622 	return NULL;
623 }
624 
625 static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
626 {
627 	struct disasm_line *dl;
628 	s64 idx;
629 
630 	dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
631 	if (dl == NULL) {
632 		ui_helpline__puts("String not found!");
633 		return false;
634 	}
635 
636 	annotate_browser__set_top(browser, dl, idx);
637 	browser->searching_backwards = true;
638 	return true;
639 }
640 
641 static bool annotate_browser__search_window(struct annotate_browser *browser,
642 					    int delay_secs)
643 {
644 	if (ui_browser__input_window("Search", "String: ", browser->search_bf,
645 				     "ENTER: OK, ESC: Cancel",
646 				     delay_secs * 2) != K_ENTER ||
647 	    !*browser->search_bf)
648 		return false;
649 
650 	return true;
651 }
652 
653 static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs)
654 {
655 	if (annotate_browser__search_window(browser, delay_secs))
656 		return __annotate_browser__search(browser);
657 
658 	return false;
659 }
660 
661 static bool annotate_browser__continue_search(struct annotate_browser *browser,
662 					      int delay_secs)
663 {
664 	if (!*browser->search_bf)
665 		return annotate_browser__search(browser, delay_secs);
666 
667 	return __annotate_browser__search(browser);
668 }
669 
670 static bool annotate_browser__search_reverse(struct annotate_browser *browser,
671 					   int delay_secs)
672 {
673 	if (annotate_browser__search_window(browser, delay_secs))
674 		return __annotate_browser__search_reverse(browser);
675 
676 	return false;
677 }
678 
679 static
680 bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
681 					       int delay_secs)
682 {
683 	if (!*browser->search_bf)
684 		return annotate_browser__search_reverse(browser, delay_secs);
685 
686 	return __annotate_browser__search_reverse(browser);
687 }
688 
689 static void annotate_browser__update_addr_width(struct annotate_browser *browser)
690 {
691 	if (annotate_browser__opts.use_offset)
692 		browser->target_width = browser->min_addr_width;
693 	else
694 		browser->target_width = browser->max_addr_width;
695 
696 	browser->addr_width = browser->target_width;
697 
698 	if (annotate_browser__opts.show_nr_jumps)
699 		browser->addr_width += browser->jumps_width + 1;
700 }
701 
702 static int annotate_browser__run(struct annotate_browser *browser,
703 				 struct perf_evsel *evsel,
704 				 struct hist_browser_timer *hbt)
705 {
706 	struct rb_node *nd = NULL;
707 	struct map_symbol *ms = browser->b.priv;
708 	struct symbol *sym = ms->sym;
709 	const char *help = "Press 'h' for help on key bindings";
710 	int delay_secs = hbt ? hbt->refresh : 0;
711 	int key;
712 	char title[SYM_TITLE_MAX_SIZE];
713 
714 	sym_title(sym, ms->map, title, sizeof(title));
715 	if (ui_browser__show(&browser->b, title, help) < 0)
716 		return -1;
717 
718 	annotate_browser__calc_percent(browser, evsel);
719 
720 	if (browser->curr_hot) {
721 		annotate_browser__set_rb_top(browser, browser->curr_hot);
722 		browser->b.navkeypressed = false;
723 	}
724 
725 	nd = browser->curr_hot;
726 
727 	while (1) {
728 		key = ui_browser__run(&browser->b, delay_secs);
729 
730 		if (delay_secs != 0) {
731 			annotate_browser__calc_percent(browser, evsel);
732 			/*
733 			 * Current line focus got out of the list of most active
734 			 * lines, NULL it so that if TAB|UNTAB is pressed, we
735 			 * move to curr_hot (current hottest line).
736 			 */
737 			if (nd != NULL && RB_EMPTY_NODE(nd))
738 				nd = NULL;
739 		}
740 
741 		switch (key) {
742 		case K_TIMER:
743 			if (hbt)
744 				hbt->timer(hbt->arg);
745 
746 			if (delay_secs != 0)
747 				symbol__annotate_decay_histogram(sym, evsel->idx);
748 			continue;
749 		case K_TAB:
750 			if (nd != NULL) {
751 				nd = rb_prev(nd);
752 				if (nd == NULL)
753 					nd = rb_last(&browser->entries);
754 			} else
755 				nd = browser->curr_hot;
756 			break;
757 		case K_UNTAB:
758 			if (nd != NULL) {
759 				nd = rb_next(nd);
760 				if (nd == NULL)
761 					nd = rb_first(&browser->entries);
762 			} else
763 				nd = browser->curr_hot;
764 			break;
765 		case K_F1:
766 		case 'h':
767 			ui_browser__help_window(&browser->b,
768 		"UP/DOWN/PGUP\n"
769 		"PGDN/SPACE    Navigate\n"
770 		"q/ESC/CTRL+C  Exit\n\n"
771 		"ENTER         Go to target\n"
772 		"ESC           Exit\n"
773 		"H             Cycle thru hottest instructions\n"
774 		"j             Toggle showing jump to target arrows\n"
775 		"J             Toggle showing number of jump sources on targets\n"
776 		"n             Search next string\n"
777 		"o             Toggle disassembler output/simplified view\n"
778 		"s             Toggle source code view\n"
779 		"t             Toggle total period view\n"
780 		"/             Search string\n"
781 		"k             Toggle line numbers\n"
782 		"r             Run available scripts\n"
783 		"?             Search string backwards\n");
784 			continue;
785 		case 'r':
786 			{
787 				script_browse(NULL);
788 				continue;
789 			}
790 		case 'k':
791 			annotate_browser__opts.show_linenr =
792 				!annotate_browser__opts.show_linenr;
793 			break;
794 		case 'H':
795 			nd = browser->curr_hot;
796 			break;
797 		case 's':
798 			if (annotate_browser__toggle_source(browser))
799 				ui_helpline__puts(help);
800 			continue;
801 		case 'o':
802 			annotate_browser__opts.use_offset = !annotate_browser__opts.use_offset;
803 			annotate_browser__update_addr_width(browser);
804 			continue;
805 		case 'j':
806 			annotate_browser__opts.jump_arrows = !annotate_browser__opts.jump_arrows;
807 			continue;
808 		case 'J':
809 			annotate_browser__opts.show_nr_jumps = !annotate_browser__opts.show_nr_jumps;
810 			annotate_browser__update_addr_width(browser);
811 			continue;
812 		case '/':
813 			if (annotate_browser__search(browser, delay_secs)) {
814 show_help:
815 				ui_helpline__puts(help);
816 			}
817 			continue;
818 		case 'n':
819 			if (browser->searching_backwards ?
820 			    annotate_browser__continue_search_reverse(browser, delay_secs) :
821 			    annotate_browser__continue_search(browser, delay_secs))
822 				goto show_help;
823 			continue;
824 		case '?':
825 			if (annotate_browser__search_reverse(browser, delay_secs))
826 				goto show_help;
827 			continue;
828 		case 'D': {
829 			static int seq;
830 			ui_helpline__pop();
831 			ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d",
832 					   seq++, browser->b.nr_entries,
833 					   browser->b.height,
834 					   browser->b.index,
835 					   browser->b.top_idx,
836 					   browser->nr_asm_entries);
837 		}
838 			continue;
839 		case K_ENTER:
840 		case K_RIGHT:
841 			if (browser->selection == NULL)
842 				ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
843 			else if (browser->selection->offset == -1)
844 				ui_helpline__puts("Actions are only available for assembly lines.");
845 			else if (!browser->selection->ins) {
846 				if (strcmp(browser->selection->name, "retq"))
847 					goto show_sup_ins;
848 				goto out;
849 			} else if (!(annotate_browser__jump(browser) ||
850 				     annotate_browser__callq(browser, evsel, hbt))) {
851 show_sup_ins:
852 				ui_helpline__puts("Actions are only available for 'callq', 'retq' & jump instructions.");
853 			}
854 			continue;
855 		case 't':
856 			annotate_browser__opts.show_total_period =
857 			  !annotate_browser__opts.show_total_period;
858 			annotate_browser__update_addr_width(browser);
859 			continue;
860 		case K_LEFT:
861 		case K_ESC:
862 		case 'q':
863 		case CTRL('c'):
864 			goto out;
865 		default:
866 			continue;
867 		}
868 
869 		if (nd != NULL)
870 			annotate_browser__set_rb_top(browser, nd);
871 	}
872 out:
873 	ui_browser__hide(&browser->b);
874 	return key;
875 }
876 
877 int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel,
878 			     struct hist_browser_timer *hbt)
879 {
880 	/* Set default value for show_total_period.  */
881 	annotate_browser__opts.show_total_period =
882 	  symbol_conf.show_total_period;
883 
884 	return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt);
885 }
886 
887 int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
888 			     struct hist_browser_timer *hbt)
889 {
890 	/* reset abort key so that it can get Ctrl-C as a key */
891 	SLang_reset_tty();
892 	SLang_init_tty(0, 0, 0);
893 
894 	return map_symbol__tui_annotate(&he->ms, evsel, hbt);
895 }
896 
897 
898 static unsigned count_insn(struct annotate_browser *browser, u64 start, u64 end)
899 {
900 	unsigned n_insn = 0;
901 	u64 offset;
902 
903 	for (offset = start; offset <= end; offset++) {
904 		if (browser->offsets[offset])
905 			n_insn++;
906 	}
907 	return n_insn;
908 }
909 
910 static void count_and_fill(struct annotate_browser *browser, u64 start, u64 end,
911 			   struct cyc_hist *ch)
912 {
913 	unsigned n_insn;
914 	u64 offset;
915 
916 	n_insn = count_insn(browser, start, end);
917 	if (n_insn && ch->num && ch->cycles) {
918 		float ipc = n_insn / ((double)ch->cycles / (double)ch->num);
919 
920 		/* Hide data when there are too many overlaps. */
921 		if (ch->reset >= 0x7fff || ch->reset >= ch->num / 2)
922 			return;
923 
924 		for (offset = start; offset <= end; offset++) {
925 			struct disasm_line *dl = browser->offsets[offset];
926 
927 			if (dl)
928 				dl->ipc = ipc;
929 		}
930 	}
931 }
932 
933 /*
934  * This should probably be in util/annotate.c to share with the tty
935  * annotate, but right now we need the per byte offsets arrays,
936  * which are only here.
937  */
938 static void annotate__compute_ipc(struct annotate_browser *browser, size_t size,
939 			   struct symbol *sym)
940 {
941 	u64 offset;
942 	struct annotation *notes = symbol__annotation(sym);
943 
944 	if (!notes->src || !notes->src->cycles_hist)
945 		return;
946 
947 	pthread_mutex_lock(&notes->lock);
948 	for (offset = 0; offset < size; ++offset) {
949 		struct cyc_hist *ch;
950 
951 		ch = &notes->src->cycles_hist[offset];
952 		if (ch && ch->cycles) {
953 			struct disasm_line *dl;
954 
955 			if (ch->have_start)
956 				count_and_fill(browser, ch->start, offset, ch);
957 			dl = browser->offsets[offset];
958 			if (dl && ch->num_aggr)
959 				dl->cycles = ch->cycles_aggr / ch->num_aggr;
960 			browser->have_cycles = true;
961 		}
962 	}
963 	pthread_mutex_unlock(&notes->lock);
964 }
965 
966 static void annotate_browser__mark_jump_targets(struct annotate_browser *browser,
967 						size_t size)
968 {
969 	u64 offset;
970 	struct map_symbol *ms = browser->b.priv;
971 	struct symbol *sym = ms->sym;
972 
973 	/* PLT symbols contain external offsets */
974 	if (strstr(sym->name, "@plt"))
975 		return;
976 
977 	for (offset = 0; offset < size; ++offset) {
978 		struct disasm_line *dl = browser->offsets[offset], *dlt;
979 		struct browser_disasm_line *bdlt;
980 
981 		if (!disasm_line__is_valid_jump(dl, sym))
982 			continue;
983 
984 		dlt = browser->offsets[dl->ops.target.offset];
985 		/*
986  		 * FIXME: Oops, no jump target? Buggy disassembler? Or do we
987  		 * have to adjust to the previous offset?
988  		 */
989 		if (dlt == NULL)
990 			continue;
991 
992 		bdlt = disasm_line__browser(dlt);
993 		if (++bdlt->jump_sources > browser->max_jump_sources)
994 			browser->max_jump_sources = bdlt->jump_sources;
995 
996 		++browser->nr_jumps;
997 	}
998 }
999 
1000 static inline int width_jumps(int n)
1001 {
1002 	if (n >= 100)
1003 		return 5;
1004 	if (n / 10)
1005 		return 2;
1006 	return 1;
1007 }
1008 
1009 int symbol__tui_annotate(struct symbol *sym, struct map *map,
1010 			 struct perf_evsel *evsel,
1011 			 struct hist_browser_timer *hbt)
1012 {
1013 	struct disasm_line *pos, *n;
1014 	struct annotation *notes;
1015 	size_t size;
1016 	struct map_symbol ms = {
1017 		.map = map,
1018 		.sym = sym,
1019 	};
1020 	struct annotate_browser browser = {
1021 		.b = {
1022 			.refresh = annotate_browser__refresh,
1023 			.seek	 = ui_browser__list_head_seek,
1024 			.write	 = annotate_browser__write,
1025 			.filter  = disasm_line__filter,
1026 			.priv	 = &ms,
1027 			.use_navkeypressed = true,
1028 		},
1029 	};
1030 	int ret = -1;
1031 	int nr_pcnt = 1;
1032 	size_t sizeof_bdl = sizeof(struct browser_disasm_line);
1033 
1034 	if (sym == NULL)
1035 		return -1;
1036 
1037 	size = symbol__size(sym);
1038 
1039 	if (map->dso->annotate_warned)
1040 		return -1;
1041 
1042 	browser.offsets = zalloc(size * sizeof(struct disasm_line *));
1043 	if (browser.offsets == NULL) {
1044 		ui__error("Not enough memory!");
1045 		return -1;
1046 	}
1047 
1048 	if (perf_evsel__is_group_event(evsel)) {
1049 		nr_pcnt = evsel->nr_members;
1050 		sizeof_bdl += sizeof(struct disasm_line_samples) *
1051 		  (nr_pcnt - 1);
1052 	}
1053 
1054 	if (symbol__annotate(sym, map, sizeof_bdl) < 0) {
1055 		ui__error("%s", ui_helpline__last_msg);
1056 		goto out_free_offsets;
1057 	}
1058 
1059 	ui_helpline__push("Press ESC to exit");
1060 
1061 	notes = symbol__annotation(sym);
1062 	browser.start = map__rip_2objdump(map, sym->start);
1063 
1064 	list_for_each_entry(pos, &notes->src->source, node) {
1065 		struct browser_disasm_line *bpos;
1066 		size_t line_len = strlen(pos->line);
1067 
1068 		if (browser.b.width < line_len)
1069 			browser.b.width = line_len;
1070 		bpos = disasm_line__browser(pos);
1071 		bpos->idx = browser.nr_entries++;
1072 		if (pos->offset != -1) {
1073 			bpos->idx_asm = browser.nr_asm_entries++;
1074 			/*
1075 			 * FIXME: short term bandaid to cope with assembly
1076 			 * routines that comes with labels in the same column
1077 			 * as the address in objdump, sigh.
1078 			 *
1079 			 * E.g. copy_user_generic_unrolled
1080  			 */
1081 			if (pos->offset < (s64)size)
1082 				browser.offsets[pos->offset] = pos;
1083 		} else
1084 			bpos->idx_asm = -1;
1085 	}
1086 
1087 	annotate_browser__mark_jump_targets(&browser, size);
1088 	annotate__compute_ipc(&browser, size, sym);
1089 
1090 	browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size);
1091 	browser.max_addr_width = hex_width(sym->end);
1092 	browser.jumps_width = width_jumps(browser.max_jump_sources);
1093 	browser.nr_events = nr_pcnt;
1094 	browser.b.nr_entries = browser.nr_entries;
1095 	browser.b.entries = &notes->src->source,
1096 	browser.b.width += 18; /* Percentage */
1097 
1098 	if (annotate_browser__opts.hide_src_code)
1099 		annotate_browser__init_asm_mode(&browser);
1100 
1101 	annotate_browser__update_addr_width(&browser);
1102 
1103 	ret = annotate_browser__run(&browser, evsel, hbt);
1104 	list_for_each_entry_safe(pos, n, &notes->src->source, node) {
1105 		list_del(&pos->node);
1106 		disasm_line__free(pos);
1107 	}
1108 
1109 out_free_offsets:
1110 	free(browser.offsets);
1111 	return ret;
1112 }
1113 
1114 #define ANNOTATE_CFG(n) \
1115 	{ .name = #n, .value = &annotate_browser__opts.n, }
1116 
1117 /*
1118  * Keep the entries sorted, they are bsearch'ed
1119  */
1120 static struct annotate_config {
1121 	const char *name;
1122 	bool *value;
1123 } annotate__configs[] = {
1124 	ANNOTATE_CFG(hide_src_code),
1125 	ANNOTATE_CFG(jump_arrows),
1126 	ANNOTATE_CFG(show_linenr),
1127 	ANNOTATE_CFG(show_nr_jumps),
1128 	ANNOTATE_CFG(show_total_period),
1129 	ANNOTATE_CFG(use_offset),
1130 };
1131 
1132 #undef ANNOTATE_CFG
1133 
1134 static int annotate_config__cmp(const void *name, const void *cfgp)
1135 {
1136 	const struct annotate_config *cfg = cfgp;
1137 
1138 	return strcmp(name, cfg->name);
1139 }
1140 
1141 static int annotate__config(const char *var, const char *value,
1142 			    void *data __maybe_unused)
1143 {
1144 	struct annotate_config *cfg;
1145 	const char *name;
1146 
1147 	if (prefixcmp(var, "annotate.") != 0)
1148 		return 0;
1149 
1150 	name = var + 9;
1151 	cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
1152 		      sizeof(struct annotate_config), annotate_config__cmp);
1153 
1154 	if (cfg == NULL)
1155 		ui__warning("%s variable unknown, ignoring...", var);
1156 	else
1157 		*cfg->value = perf_config_bool(name, value);
1158 	return 0;
1159 }
1160 
1161 void annotate_browser__init(void)
1162 {
1163 	perf_config(annotate__config, NULL);
1164 }
1165