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