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