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