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