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