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