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