xref: /openbmc/linux/tools/perf/ui/browsers/annotate.c (revision 297871cb)
1 // SPDX-License-Identifier: GPL-2.0
2 #include "../browser.h"
3 #include "../helpline.h"
4 #include "../ui.h"
5 #include "../../util/annotate.h"
6 #include "../../util/debug.h"
7 #include "../../util/dso.h"
8 #include "../../util/hist.h"
9 #include "../../util/sort.h"
10 #include "../../util/map.h"
11 #include "../../util/mutex.h"
12 #include "../../util/symbol.h"
13 #include "../../util/evsel.h"
14 #include "../../util/evlist.h"
15 #include <inttypes.h>
16 #include <linux/kernel.h>
17 #include <linux/string.h>
18 #include <linux/zalloc.h>
19 #include <sys/ttydefaults.h>
20 #include <asm/bug.h>
21 
22 struct arch;
23 
24 struct annotate_browser {
25 	struct ui_browser	    b;
26 	struct rb_root		    entries;
27 	struct rb_node		   *curr_hot;
28 	struct annotation_line	   *selection;
29 	struct arch		   *arch;
30 	bool			    searching_backwards;
31 	char			    search_bf[128];
32 };
33 
browser__annotation(struct ui_browser * browser)34 static inline struct annotation *browser__annotation(struct ui_browser *browser)
35 {
36 	struct map_symbol *ms = browser->priv;
37 	return symbol__annotation(ms->sym);
38 }
39 
disasm_line__filter(struct ui_browser * browser,void * entry)40 static bool disasm_line__filter(struct ui_browser *browser, void *entry)
41 {
42 	struct annotation *notes = browser__annotation(browser);
43 	struct annotation_line *al = list_entry(entry, struct annotation_line, node);
44 	return annotation_line__filter(al, notes);
45 }
46 
ui_browser__jumps_percent_color(struct ui_browser * browser,int nr,bool current)47 static int ui_browser__jumps_percent_color(struct ui_browser *browser, int nr, bool current)
48 {
49 	struct annotation *notes = browser__annotation(browser);
50 
51 	if (current && (!browser->use_navkeypressed || browser->navkeypressed))
52 		return HE_COLORSET_SELECTED;
53 	if (nr == notes->max_jump_sources)
54 		return HE_COLORSET_TOP;
55 	if (nr > 1)
56 		return HE_COLORSET_MEDIUM;
57 	return HE_COLORSET_NORMAL;
58 }
59 
ui_browser__set_jumps_percent_color(void * browser,int nr,bool current)60 static int ui_browser__set_jumps_percent_color(void *browser, int nr, bool current)
61 {
62 	 int color = ui_browser__jumps_percent_color(browser, nr, current);
63 	 return ui_browser__set_color(browser, color);
64 }
65 
annotate_browser__set_color(void * browser,int color)66 static int annotate_browser__set_color(void *browser, int color)
67 {
68 	return ui_browser__set_color(browser, color);
69 }
70 
annotate_browser__write_graph(void * browser,int graph)71 static void annotate_browser__write_graph(void *browser, int graph)
72 {
73 	ui_browser__write_graph(browser, graph);
74 }
75 
annotate_browser__set_percent_color(void * browser,double percent,bool current)76 static void annotate_browser__set_percent_color(void *browser, double percent, bool current)
77 {
78 	ui_browser__set_percent_color(browser, percent, current);
79 }
80 
annotate_browser__printf(void * browser,const char * fmt,...)81 static void annotate_browser__printf(void *browser, const char *fmt, ...)
82 {
83 	va_list args;
84 
85 	va_start(args, fmt);
86 	ui_browser__vprintf(browser, fmt, args);
87 	va_end(args);
88 }
89 
annotate_browser__write(struct ui_browser * browser,void * entry,int row)90 static void annotate_browser__write(struct ui_browser *browser, void *entry, int row)
91 {
92 	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
93 	struct annotation *notes = browser__annotation(browser);
94 	struct annotation_line *al = list_entry(entry, struct annotation_line, node);
95 	const bool is_current_entry = ui_browser__is_current_entry(browser, row);
96 	struct annotation_write_ops ops = {
97 		.first_line		 = row == 0,
98 		.current_entry		 = is_current_entry,
99 		.change_color		 = (!annotate_opts.hide_src_code &&
100 					    (!is_current_entry ||
101 					     (browser->use_navkeypressed &&
102 					      !browser->navkeypressed))),
103 		.width			 = browser->width,
104 		.obj			 = browser,
105 		.set_color		 = annotate_browser__set_color,
106 		.set_percent_color	 = annotate_browser__set_percent_color,
107 		.set_jumps_percent_color = ui_browser__set_jumps_percent_color,
108 		.printf			 = annotate_browser__printf,
109 		.write_graph		 = annotate_browser__write_graph,
110 	};
111 
112 	/* The scroll bar isn't being used */
113 	if (!browser->navkeypressed)
114 		ops.width += 1;
115 
116 	annotation_line__write(al, notes, &ops);
117 
118 	if (ops.current_entry)
119 		ab->selection = al;
120 }
121 
is_fused(struct annotate_browser * ab,struct disasm_line * cursor)122 static int is_fused(struct annotate_browser *ab, struct disasm_line *cursor)
123 {
124 	struct disasm_line *pos = list_prev_entry(cursor, al.node);
125 	const char *name;
126 	int diff = 1;
127 
128 	while (pos && pos->al.offset == -1) {
129 		pos = list_prev_entry(pos, al.node);
130 		if (!annotate_opts.hide_src_code)
131 			diff++;
132 	}
133 
134 	if (!pos)
135 		return 0;
136 
137 	if (ins__is_lock(&pos->ins))
138 		name = pos->ops.locked.ins.name;
139 	else
140 		name = pos->ins.name;
141 
142 	if (!name || !cursor->ins.name)
143 		return 0;
144 
145 	if (ins__is_fused(ab->arch, name, cursor->ins.name))
146 		return diff;
147 	return 0;
148 }
149 
annotate_browser__draw_current_jump(struct ui_browser * browser)150 static void annotate_browser__draw_current_jump(struct ui_browser *browser)
151 {
152 	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
153 	struct disasm_line *cursor = disasm_line(ab->selection);
154 	struct annotation_line *target;
155 	unsigned int from, to;
156 	struct map_symbol *ms = ab->b.priv;
157 	struct symbol *sym = ms->sym;
158 	struct annotation *notes = symbol__annotation(sym);
159 	u8 pcnt_width = annotation__pcnt_width(notes);
160 	int width;
161 	int diff = 0;
162 
163 	/* PLT symbols contain external offsets */
164 	if (strstr(sym->name, "@plt"))
165 		return;
166 
167 	if (!disasm_line__is_valid_local_jump(cursor, sym))
168 		return;
169 
170 	/*
171 	 * This first was seen with a gcc function, _cpp_lex_token, that
172 	 * has the usual jumps:
173 	 *
174 	 *  │1159e6c: ↓ jne    115aa32 <_cpp_lex_token@@Base+0xf92>
175 	 *
176 	 * I.e. jumps to a label inside that function (_cpp_lex_token), and
177 	 * those works, but also this kind:
178 	 *
179 	 *  │1159e8b: ↓ jne    c469be <cpp_named_operator2name@@Base+0xa72>
180 	 *
181 	 *  I.e. jumps to another function, outside _cpp_lex_token, which
182 	 *  are not being correctly handled generating as a side effect references
183 	 *  to ab->offset[] entries that are set to NULL, so to make this code
184 	 *  more robust, check that here.
185 	 *
186 	 *  A proper fix for will be put in place, looking at the function
187 	 *  name right after the '<' token and probably treating this like a
188 	 *  'call' instruction.
189 	 */
190 	target = notes->offsets[cursor->ops.target.offset];
191 	if (target == NULL) {
192 		ui_helpline__printf("WARN: jump target inconsistency, press 'o', notes->offsets[%#x] = NULL\n",
193 				    cursor->ops.target.offset);
194 		return;
195 	}
196 
197 	if (annotate_opts.hide_src_code) {
198 		from = cursor->al.idx_asm;
199 		to = target->idx_asm;
200 	} else {
201 		from = (u64)cursor->al.idx;
202 		to = (u64)target->idx;
203 	}
204 
205 	width = annotation__cycles_width(notes);
206 
207 	ui_browser__set_color(browser, HE_COLORSET_JUMP_ARROWS);
208 	__ui_browser__line_arrow(browser,
209 				 pcnt_width + 2 + notes->widths.addr + width,
210 				 from, to);
211 
212 	diff = is_fused(ab, cursor);
213 	if (diff > 0) {
214 		ui_browser__mark_fused(browser,
215 				       pcnt_width + 3 + notes->widths.addr + width,
216 				       from - diff, diff, to > from);
217 	}
218 }
219 
annotate_browser__refresh(struct ui_browser * browser)220 static unsigned int annotate_browser__refresh(struct ui_browser *browser)
221 {
222 	struct annotation *notes = browser__annotation(browser);
223 	int ret = ui_browser__list_head_refresh(browser);
224 	int pcnt_width = annotation__pcnt_width(notes);
225 
226 	if (annotate_opts.jump_arrows)
227 		annotate_browser__draw_current_jump(browser);
228 
229 	ui_browser__set_color(browser, HE_COLORSET_NORMAL);
230 	__ui_browser__vline(browser, pcnt_width, 0, browser->rows - 1);
231 	return ret;
232 }
233 
disasm__cmp(struct annotation_line * a,struct annotation_line * b,int percent_type)234 static double disasm__cmp(struct annotation_line *a, struct annotation_line *b,
235 						  int percent_type)
236 {
237 	int i;
238 
239 	for (i = 0; i < a->data_nr; i++) {
240 		if (a->data[i].percent[percent_type] == b->data[i].percent[percent_type])
241 			continue;
242 		return a->data[i].percent[percent_type] -
243 			   b->data[i].percent[percent_type];
244 	}
245 	return 0;
246 }
247 
disasm_rb_tree__insert(struct annotate_browser * browser,struct annotation_line * al)248 static void disasm_rb_tree__insert(struct annotate_browser *browser,
249 				struct annotation_line *al)
250 {
251 	struct rb_root *root = &browser->entries;
252 	struct rb_node **p = &root->rb_node;
253 	struct rb_node *parent = NULL;
254 	struct annotation_line *l;
255 
256 	while (*p != NULL) {
257 		parent = *p;
258 		l = rb_entry(parent, struct annotation_line, rb_node);
259 
260 		if (disasm__cmp(al, l, annotate_opts.percent_type) < 0)
261 			p = &(*p)->rb_left;
262 		else
263 			p = &(*p)->rb_right;
264 	}
265 	rb_link_node(&al->rb_node, parent, p);
266 	rb_insert_color(&al->rb_node, root);
267 }
268 
annotate_browser__set_top(struct annotate_browser * browser,struct annotation_line * pos,u32 idx)269 static void annotate_browser__set_top(struct annotate_browser *browser,
270 				      struct annotation_line *pos, u32 idx)
271 {
272 	struct annotation *notes = browser__annotation(&browser->b);
273 	unsigned back;
274 
275 	ui_browser__refresh_dimensions(&browser->b);
276 	back = browser->b.height / 2;
277 	browser->b.top_idx = browser->b.index = idx;
278 
279 	while (browser->b.top_idx != 0 && back != 0) {
280 		pos = list_entry(pos->node.prev, struct annotation_line, node);
281 
282 		if (annotation_line__filter(pos, notes))
283 			continue;
284 
285 		--browser->b.top_idx;
286 		--back;
287 	}
288 
289 	browser->b.top = pos;
290 	browser->b.navkeypressed = true;
291 }
292 
annotate_browser__set_rb_top(struct annotate_browser * browser,struct rb_node * nd)293 static void annotate_browser__set_rb_top(struct annotate_browser *browser,
294 					 struct rb_node *nd)
295 {
296 	struct annotation_line * pos = rb_entry(nd, struct annotation_line, rb_node);
297 	u32 idx = pos->idx;
298 
299 	if (annotate_opts.hide_src_code)
300 		idx = pos->idx_asm;
301 	annotate_browser__set_top(browser, pos, idx);
302 	browser->curr_hot = nd;
303 }
304 
annotate_browser__calc_percent(struct annotate_browser * browser,struct evsel * evsel)305 static void annotate_browser__calc_percent(struct annotate_browser *browser,
306 					   struct evsel *evsel)
307 {
308 	struct map_symbol *ms = browser->b.priv;
309 	struct symbol *sym = ms->sym;
310 	struct annotation *notes = symbol__annotation(sym);
311 	struct disasm_line *pos;
312 
313 	browser->entries = RB_ROOT;
314 
315 	annotation__lock(notes);
316 
317 	symbol__calc_percent(sym, evsel);
318 
319 	list_for_each_entry(pos, &notes->src->source, al.node) {
320 		double max_percent = 0.0;
321 		int i;
322 
323 		if (pos->al.offset == -1) {
324 			RB_CLEAR_NODE(&pos->al.rb_node);
325 			continue;
326 		}
327 
328 		for (i = 0; i < pos->al.data_nr; i++) {
329 			double percent;
330 
331 			percent = annotation_data__percent(&pos->al.data[i],
332 							   annotate_opts.percent_type);
333 
334 			if (max_percent < percent)
335 				max_percent = percent;
336 		}
337 
338 		if (max_percent < 0.01 && (!pos->al.cycles || pos->al.cycles->ipc == 0)) {
339 			RB_CLEAR_NODE(&pos->al.rb_node);
340 			continue;
341 		}
342 		disasm_rb_tree__insert(browser, &pos->al);
343 	}
344 	annotation__unlock(notes);
345 
346 	browser->curr_hot = rb_last(&browser->entries);
347 }
348 
annotate_browser__find_next_asm_line(struct annotate_browser * browser,struct annotation_line * al)349 static struct annotation_line *annotate_browser__find_next_asm_line(
350 					struct annotate_browser *browser,
351 					struct annotation_line *al)
352 {
353 	struct annotation_line *it = al;
354 
355 	/* find next asm line */
356 	list_for_each_entry_continue(it, browser->b.entries, node) {
357 		if (it->idx_asm >= 0)
358 			return it;
359 	}
360 
361 	/* no asm line found forwards, try backwards */
362 	it = al;
363 	list_for_each_entry_continue_reverse(it, browser->b.entries, node) {
364 		if (it->idx_asm >= 0)
365 			return it;
366 	}
367 
368 	/* There are no asm lines */
369 	return NULL;
370 }
371 
annotate_browser__toggle_source(struct annotate_browser * browser)372 static bool annotate_browser__toggle_source(struct annotate_browser *browser)
373 {
374 	struct annotation *notes = browser__annotation(&browser->b);
375 	struct annotation_line *al;
376 	off_t offset = browser->b.index - browser->b.top_idx;
377 
378 	browser->b.seek(&browser->b, offset, SEEK_CUR);
379 	al = list_entry(browser->b.top, struct annotation_line, node);
380 
381 	if (annotate_opts.hide_src_code) {
382 		if (al->idx_asm < offset)
383 			offset = al->idx;
384 
385 		browser->b.nr_entries = notes->src->nr_entries;
386 		annotate_opts.hide_src_code = false;
387 		browser->b.seek(&browser->b, -offset, SEEK_CUR);
388 		browser->b.top_idx = al->idx - offset;
389 		browser->b.index = al->idx;
390 	} else {
391 		if (al->idx_asm < 0) {
392 			/* move cursor to next asm line */
393 			al = annotate_browser__find_next_asm_line(browser, al);
394 			if (!al) {
395 				browser->b.seek(&browser->b, -offset, SEEK_CUR);
396 				return false;
397 			}
398 		}
399 
400 		if (al->idx_asm < offset)
401 			offset = al->idx_asm;
402 
403 		browser->b.nr_entries = notes->src->nr_asm_entries;
404 		annotate_opts.hide_src_code = true;
405 		browser->b.seek(&browser->b, -offset, SEEK_CUR);
406 		browser->b.top_idx = al->idx_asm - offset;
407 		browser->b.index = al->idx_asm;
408 	}
409 
410 	return true;
411 }
412 
413 #define SYM_TITLE_MAX_SIZE (PATH_MAX + 64)
414 
annotate_browser__show_full_location(struct ui_browser * browser)415 static void annotate_browser__show_full_location(struct ui_browser *browser)
416 {
417 	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
418 	struct disasm_line *cursor = disasm_line(ab->selection);
419 	struct annotation_line *al = &cursor->al;
420 
421 	if (al->offset != -1)
422 		ui_helpline__puts("Only available for source code lines.");
423 	else if (al->fileloc == NULL)
424 		ui_helpline__puts("No source file location.");
425 	else {
426 		char help_line[SYM_TITLE_MAX_SIZE];
427 		sprintf (help_line, "Source file location: %s", al->fileloc);
428 		ui_helpline__puts(help_line);
429 	}
430 }
431 
ui_browser__init_asm_mode(struct ui_browser * browser)432 static void ui_browser__init_asm_mode(struct ui_browser *browser)
433 {
434 	struct annotation *notes = browser__annotation(browser);
435 	ui_browser__reset_index(browser);
436 	browser->nr_entries = notes->src->nr_asm_entries;
437 }
438 
sym_title(struct symbol * sym,struct map * map,char * title,size_t sz,int percent_type)439 static int sym_title(struct symbol *sym, struct map *map, char *title,
440 		     size_t sz, int percent_type)
441 {
442 	return snprintf(title, sz, "%s  %s [Percent: %s]", sym->name,
443 			map__dso(map)->long_name,
444 			percent_type_str(percent_type));
445 }
446 
447 /*
448  * This can be called from external jumps, i.e. jumps from one function
449  * to another, like from the kernel's entry_SYSCALL_64 function to the
450  * swapgs_restore_regs_and_return_to_usermode() function.
451  *
452  * So all we check here is that dl->ops.target.sym is set, if it is, just
453  * go to that function and when exiting from its disassembly, come back
454  * to the calling function.
455  */
annotate_browser__callq(struct annotate_browser * browser,struct evsel * evsel,struct hist_browser_timer * hbt)456 static bool annotate_browser__callq(struct annotate_browser *browser,
457 				    struct evsel *evsel,
458 				    struct hist_browser_timer *hbt)
459 {
460 	struct map_symbol *ms = browser->b.priv, target_ms;
461 	struct disasm_line *dl = disasm_line(browser->selection);
462 	struct annotation *notes;
463 	char title[SYM_TITLE_MAX_SIZE];
464 
465 	if (!dl->ops.target.sym) {
466 		ui_helpline__puts("The called function was not found.");
467 		return true;
468 	}
469 
470 	notes = symbol__annotation(dl->ops.target.sym);
471 	annotation__lock(notes);
472 
473 	if (!symbol__hists(dl->ops.target.sym, evsel->evlist->core.nr_entries)) {
474 		annotation__unlock(notes);
475 		ui__warning("Not enough memory for annotating '%s' symbol!\n",
476 			    dl->ops.target.sym->name);
477 		return true;
478 	}
479 
480 	target_ms.maps = ms->maps;
481 	target_ms.map = ms->map;
482 	target_ms.sym = dl->ops.target.sym;
483 	annotation__unlock(notes);
484 	symbol__tui_annotate(&target_ms, evsel, hbt);
485 	sym_title(ms->sym, ms->map, title, sizeof(title), annotate_opts.percent_type);
486 	ui_browser__show_title(&browser->b, title);
487 	return true;
488 }
489 
490 static
annotate_browser__find_offset(struct annotate_browser * browser,s64 offset,s64 * idx)491 struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
492 					  s64 offset, s64 *idx)
493 {
494 	struct annotation *notes = browser__annotation(&browser->b);
495 	struct disasm_line *pos;
496 
497 	*idx = 0;
498 	list_for_each_entry(pos, &notes->src->source, al.node) {
499 		if (pos->al.offset == offset)
500 			return pos;
501 		if (!annotation_line__filter(&pos->al, notes))
502 			++*idx;
503 	}
504 
505 	return NULL;
506 }
507 
annotate_browser__jump(struct annotate_browser * browser,struct evsel * evsel,struct hist_browser_timer * hbt)508 static bool annotate_browser__jump(struct annotate_browser *browser,
509 				   struct evsel *evsel,
510 				   struct hist_browser_timer *hbt)
511 {
512 	struct disasm_line *dl = disasm_line(browser->selection);
513 	u64 offset;
514 	s64 idx;
515 
516 	if (!ins__is_jump(&dl->ins))
517 		return false;
518 
519 	if (dl->ops.target.outside) {
520 		annotate_browser__callq(browser, evsel, hbt);
521 		return true;
522 	}
523 
524 	offset = dl->ops.target.offset;
525 	dl = annotate_browser__find_offset(browser, offset, &idx);
526 	if (dl == NULL) {
527 		ui_helpline__printf("Invalid jump offset: %" PRIx64, offset);
528 		return true;
529 	}
530 
531 	annotate_browser__set_top(browser, &dl->al, idx);
532 
533 	return true;
534 }
535 
536 static
annotate_browser__find_string(struct annotate_browser * browser,char * s,s64 * idx)537 struct annotation_line *annotate_browser__find_string(struct annotate_browser *browser,
538 					  char *s, s64 *idx)
539 {
540 	struct annotation *notes = browser__annotation(&browser->b);
541 	struct annotation_line *al = browser->selection;
542 
543 	*idx = browser->b.index;
544 	list_for_each_entry_continue(al, &notes->src->source, node) {
545 		if (annotation_line__filter(al, notes))
546 			continue;
547 
548 		++*idx;
549 
550 		if (al->line && strstr(al->line, s) != NULL)
551 			return al;
552 	}
553 
554 	return NULL;
555 }
556 
__annotate_browser__search(struct annotate_browser * browser)557 static bool __annotate_browser__search(struct annotate_browser *browser)
558 {
559 	struct annotation_line *al;
560 	s64 idx;
561 
562 	al = annotate_browser__find_string(browser, browser->search_bf, &idx);
563 	if (al == NULL) {
564 		ui_helpline__puts("String not found!");
565 		return false;
566 	}
567 
568 	annotate_browser__set_top(browser, al, idx);
569 	browser->searching_backwards = false;
570 	return true;
571 }
572 
573 static
annotate_browser__find_string_reverse(struct annotate_browser * browser,char * s,s64 * idx)574 struct annotation_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
575 						  char *s, s64 *idx)
576 {
577 	struct annotation *notes = browser__annotation(&browser->b);
578 	struct annotation_line *al = browser->selection;
579 
580 	*idx = browser->b.index;
581 	list_for_each_entry_continue_reverse(al, &notes->src->source, node) {
582 		if (annotation_line__filter(al, notes))
583 			continue;
584 
585 		--*idx;
586 
587 		if (al->line && strstr(al->line, s) != NULL)
588 			return al;
589 	}
590 
591 	return NULL;
592 }
593 
__annotate_browser__search_reverse(struct annotate_browser * browser)594 static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
595 {
596 	struct annotation_line *al;
597 	s64 idx;
598 
599 	al = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
600 	if (al == NULL) {
601 		ui_helpline__puts("String not found!");
602 		return false;
603 	}
604 
605 	annotate_browser__set_top(browser, al, idx);
606 	browser->searching_backwards = true;
607 	return true;
608 }
609 
annotate_browser__search_window(struct annotate_browser * browser,int delay_secs)610 static bool annotate_browser__search_window(struct annotate_browser *browser,
611 					    int delay_secs)
612 {
613 	if (ui_browser__input_window("Search", "String: ", browser->search_bf,
614 				     "ENTER: OK, ESC: Cancel",
615 				     delay_secs * 2) != K_ENTER ||
616 	    !*browser->search_bf)
617 		return false;
618 
619 	return true;
620 }
621 
annotate_browser__search(struct annotate_browser * browser,int delay_secs)622 static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs)
623 {
624 	if (annotate_browser__search_window(browser, delay_secs))
625 		return __annotate_browser__search(browser);
626 
627 	return false;
628 }
629 
annotate_browser__continue_search(struct annotate_browser * browser,int delay_secs)630 static bool annotate_browser__continue_search(struct annotate_browser *browser,
631 					      int delay_secs)
632 {
633 	if (!*browser->search_bf)
634 		return annotate_browser__search(browser, delay_secs);
635 
636 	return __annotate_browser__search(browser);
637 }
638 
annotate_browser__search_reverse(struct annotate_browser * browser,int delay_secs)639 static bool annotate_browser__search_reverse(struct annotate_browser *browser,
640 					   int delay_secs)
641 {
642 	if (annotate_browser__search_window(browser, delay_secs))
643 		return __annotate_browser__search_reverse(browser);
644 
645 	return false;
646 }
647 
648 static
annotate_browser__continue_search_reverse(struct annotate_browser * browser,int delay_secs)649 bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
650 					       int delay_secs)
651 {
652 	if (!*browser->search_bf)
653 		return annotate_browser__search_reverse(browser, delay_secs);
654 
655 	return __annotate_browser__search_reverse(browser);
656 }
657 
annotate_browser__show(struct ui_browser * browser,char * title,const char * help)658 static int annotate_browser__show(struct ui_browser *browser, char *title, const char *help)
659 {
660 	struct map_symbol *ms = browser->priv;
661 	struct symbol *sym = ms->sym;
662 	char symbol_dso[SYM_TITLE_MAX_SIZE];
663 
664 	if (ui_browser__show(browser, title, help) < 0)
665 		return -1;
666 
667 	sym_title(sym, ms->map, symbol_dso, sizeof(symbol_dso), annotate_opts.percent_type);
668 
669 	ui_browser__gotorc_title(browser, 0, 0);
670 	ui_browser__set_color(browser, HE_COLORSET_ROOT);
671 	ui_browser__write_nstring(browser, symbol_dso, browser->width + 1);
672 	return 0;
673 }
674 
675 static void
switch_percent_type(struct annotation_options * opts,bool base)676 switch_percent_type(struct annotation_options *opts, bool base)
677 {
678 	switch (opts->percent_type) {
679 	case PERCENT_HITS_LOCAL:
680 		if (base)
681 			opts->percent_type = PERCENT_PERIOD_LOCAL;
682 		else
683 			opts->percent_type = PERCENT_HITS_GLOBAL;
684 		break;
685 	case PERCENT_HITS_GLOBAL:
686 		if (base)
687 			opts->percent_type = PERCENT_PERIOD_GLOBAL;
688 		else
689 			opts->percent_type = PERCENT_HITS_LOCAL;
690 		break;
691 	case PERCENT_PERIOD_LOCAL:
692 		if (base)
693 			opts->percent_type = PERCENT_HITS_LOCAL;
694 		else
695 			opts->percent_type = PERCENT_PERIOD_GLOBAL;
696 		break;
697 	case PERCENT_PERIOD_GLOBAL:
698 		if (base)
699 			opts->percent_type = PERCENT_HITS_GLOBAL;
700 		else
701 			opts->percent_type = PERCENT_PERIOD_LOCAL;
702 		break;
703 	default:
704 		WARN_ON(1);
705 	}
706 }
707 
annotate_browser__run(struct annotate_browser * browser,struct evsel * evsel,struct hist_browser_timer * hbt)708 static int annotate_browser__run(struct annotate_browser *browser,
709 				 struct evsel *evsel,
710 				 struct hist_browser_timer *hbt)
711 {
712 	struct rb_node *nd = NULL;
713 	struct hists *hists = evsel__hists(evsel);
714 	struct map_symbol *ms = browser->b.priv;
715 	struct symbol *sym = ms->sym;
716 	struct annotation *notes = symbol__annotation(ms->sym);
717 	const char *help = "Press 'h' for help on key bindings";
718 	int delay_secs = hbt ? hbt->refresh : 0;
719 	char title[256];
720 	int key;
721 
722 	hists__scnprintf_title(hists, title, sizeof(title));
723 	if (annotate_browser__show(&browser->b, title, help) < 0)
724 		return -1;
725 
726 	annotate_browser__calc_percent(browser, evsel);
727 
728 	if (browser->curr_hot) {
729 		annotate_browser__set_rb_top(browser, browser->curr_hot);
730 		browser->b.navkeypressed = false;
731 	}
732 
733 	nd = browser->curr_hot;
734 
735 	while (1) {
736 		key = ui_browser__run(&browser->b, delay_secs);
737 
738 		if (delay_secs != 0) {
739 			annotate_browser__calc_percent(browser, evsel);
740 			/*
741 			 * Current line focus got out of the list of most active
742 			 * lines, NULL it so that if TAB|UNTAB is pressed, we
743 			 * move to curr_hot (current hottest line).
744 			 */
745 			if (nd != NULL && RB_EMPTY_NODE(nd))
746 				nd = NULL;
747 		}
748 
749 		switch (key) {
750 		case K_TIMER:
751 			if (hbt)
752 				hbt->timer(hbt->arg);
753 
754 			if (delay_secs != 0) {
755 				symbol__annotate_decay_histogram(sym, evsel->core.idx);
756 				hists__scnprintf_title(hists, title, sizeof(title));
757 				annotate_browser__show(&browser->b, title, help);
758 			}
759 			continue;
760 		case K_TAB:
761 			if (nd != NULL) {
762 				nd = rb_prev(nd);
763 				if (nd == NULL)
764 					nd = rb_last(&browser->entries);
765 			} else
766 				nd = browser->curr_hot;
767 			break;
768 		case K_UNTAB:
769 			if (nd != NULL) {
770 				nd = rb_next(nd);
771 				if (nd == NULL)
772 					nd = rb_first(&browser->entries);
773 			} else
774 				nd = browser->curr_hot;
775 			break;
776 		case K_F1:
777 		case 'h':
778 			ui_browser__help_window(&browser->b,
779 		"UP/DOWN/PGUP\n"
780 		"PGDN/SPACE    Navigate\n"
781 		"</>           Move to prev/next symbol\n"
782 		"q/ESC/CTRL+C  Exit\n\n"
783 		"ENTER         Go to target\n"
784 		"H             Go to hottest instruction\n"
785 		"TAB/shift+TAB Cycle thru hottest instructions\n"
786 		"j             Toggle showing jump to target arrows\n"
787 		"J             Toggle showing number of jump sources on targets\n"
788 		"n             Search next string\n"
789 		"o             Toggle disassembler output/simplified view\n"
790 		"O             Bump offset level (jump targets -> +call -> all -> cycle thru)\n"
791 		"s             Toggle source code view\n"
792 		"t             Circulate percent, total period, samples view\n"
793 		"c             Show min/max cycle\n"
794 		"/             Search string\n"
795 		"k             Toggle line numbers\n"
796 		"l             Show full source file location\n"
797 		"P             Print to [symbol_name].annotation file.\n"
798 		"r             Run available scripts\n"
799 		"p             Toggle percent type [local/global]\n"
800 		"b             Toggle percent base [period/hits]\n"
801 		"?             Search string backwards\n"
802 		"f             Toggle showing offsets to full address\n");
803 			continue;
804 		case 'r':
805 			script_browse(NULL, NULL);
806 			annotate_browser__show(&browser->b, title, help);
807 			continue;
808 		case 'k':
809 			annotate_opts.show_linenr = !annotate_opts.show_linenr;
810 			continue;
811 		case 'l':
812 			annotate_browser__show_full_location (&browser->b);
813 			continue;
814 		case 'H':
815 			nd = browser->curr_hot;
816 			break;
817 		case 's':
818 			if (annotate_browser__toggle_source(browser))
819 				ui_helpline__puts(help);
820 			continue;
821 		case 'o':
822 			annotate_opts.use_offset = !annotate_opts.use_offset;
823 			annotation__update_column_widths(notes);
824 			continue;
825 		case 'O':
826 			if (++annotate_opts.offset_level > ANNOTATION__MAX_OFFSET_LEVEL)
827 				annotate_opts.offset_level = ANNOTATION__MIN_OFFSET_LEVEL;
828 			continue;
829 		case 'j':
830 			annotate_opts.jump_arrows = !annotate_opts.jump_arrows;
831 			continue;
832 		case 'J':
833 			annotate_opts.show_nr_jumps = !annotate_opts.show_nr_jumps;
834 			annotation__update_column_widths(notes);
835 			continue;
836 		case '/':
837 			if (annotate_browser__search(browser, delay_secs)) {
838 show_help:
839 				ui_helpline__puts(help);
840 			}
841 			continue;
842 		case 'n':
843 			if (browser->searching_backwards ?
844 			    annotate_browser__continue_search_reverse(browser, delay_secs) :
845 			    annotate_browser__continue_search(browser, delay_secs))
846 				goto show_help;
847 			continue;
848 		case '?':
849 			if (annotate_browser__search_reverse(browser, delay_secs))
850 				goto show_help;
851 			continue;
852 		case 'D': {
853 			static int seq;
854 			ui_helpline__pop();
855 			ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d",
856 					   seq++, browser->b.nr_entries,
857 					   browser->b.height,
858 					   browser->b.index,
859 					   browser->b.top_idx,
860 					   notes->src->nr_asm_entries);
861 		}
862 			continue;
863 		case K_ENTER:
864 		case K_RIGHT:
865 		{
866 			struct disasm_line *dl = disasm_line(browser->selection);
867 
868 			if (browser->selection == NULL)
869 				ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
870 			else if (browser->selection->offset == -1)
871 				ui_helpline__puts("Actions are only available for assembly lines.");
872 			else if (!dl->ins.ops)
873 				goto show_sup_ins;
874 			else if (ins__is_ret(&dl->ins))
875 				goto out;
876 			else if (!(annotate_browser__jump(browser, evsel, hbt) ||
877 				     annotate_browser__callq(browser, evsel, hbt))) {
878 show_sup_ins:
879 				ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
880 			}
881 			continue;
882 		}
883 		case 'P':
884 			map_symbol__annotation_dump(ms, evsel);
885 			continue;
886 		case 't':
887 			if (symbol_conf.show_total_period) {
888 				symbol_conf.show_total_period = false;
889 				symbol_conf.show_nr_samples = true;
890 			} else if (symbol_conf.show_nr_samples)
891 				symbol_conf.show_nr_samples = false;
892 			else
893 				symbol_conf.show_total_period = true;
894 			annotation__update_column_widths(notes);
895 			continue;
896 		case 'c':
897 			if (annotate_opts.show_minmax_cycle)
898 				annotate_opts.show_minmax_cycle = false;
899 			else
900 				annotate_opts.show_minmax_cycle = true;
901 			annotation__update_column_widths(notes);
902 			continue;
903 		case 'p':
904 		case 'b':
905 			switch_percent_type(&annotate_opts, key == 'b');
906 			hists__scnprintf_title(hists, title, sizeof(title));
907 			annotate_browser__show(&browser->b, title, help);
908 			continue;
909 		case 'f':
910 			annotation__toggle_full_addr(notes, ms);
911 			continue;
912 		case K_LEFT:
913 		case '<':
914 		case '>':
915 		case K_ESC:
916 		case 'q':
917 		case CTRL('c'):
918 			goto out;
919 		default:
920 			continue;
921 		}
922 
923 		if (nd != NULL)
924 			annotate_browser__set_rb_top(browser, nd);
925 	}
926 out:
927 	ui_browser__hide(&browser->b);
928 	return key;
929 }
930 
map_symbol__tui_annotate(struct map_symbol * ms,struct evsel * evsel,struct hist_browser_timer * hbt)931 int map_symbol__tui_annotate(struct map_symbol *ms, struct evsel *evsel,
932 			     struct hist_browser_timer *hbt)
933 {
934 	return symbol__tui_annotate(ms, evsel, hbt);
935 }
936 
hist_entry__tui_annotate(struct hist_entry * he,struct evsel * evsel,struct hist_browser_timer * hbt)937 int hist_entry__tui_annotate(struct hist_entry *he, struct evsel *evsel,
938 			     struct hist_browser_timer *hbt)
939 {
940 	/* reset abort key so that it can get Ctrl-C as a key */
941 	SLang_reset_tty();
942 	SLang_init_tty(0, 0, 0);
943 
944 	return map_symbol__tui_annotate(&he->ms, evsel, hbt);
945 }
946 
symbol__tui_annotate(struct map_symbol * ms,struct evsel * evsel,struct hist_browser_timer * hbt)947 int symbol__tui_annotate(struct map_symbol *ms, struct evsel *evsel,
948 			 struct hist_browser_timer *hbt)
949 {
950 	struct symbol *sym = ms->sym;
951 	struct annotation *notes = symbol__annotation(sym);
952 	struct annotate_browser browser = {
953 		.b = {
954 			.refresh = annotate_browser__refresh,
955 			.seek	 = ui_browser__list_head_seek,
956 			.write	 = annotate_browser__write,
957 			.filter  = disasm_line__filter,
958 			.extra_title_lines = 1, /* for hists__scnprintf_title() */
959 			.priv	 = ms,
960 			.use_navkeypressed = true,
961 		},
962 	};
963 	struct dso *dso;
964 	int ret = -1, err;
965 	int not_annotated = list_empty(&notes->src->source);
966 
967 	if (sym == NULL)
968 		return -1;
969 
970 	dso = map__dso(ms->map);
971 	if (dso->annotate_warned)
972 		return -1;
973 
974 	if (not_annotated) {
975 		err = symbol__annotate2(ms, evsel, &browser.arch);
976 		if (err) {
977 			char msg[BUFSIZ];
978 			dso->annotate_warned = true;
979 			symbol__strerror_disassemble(ms, err, msg, sizeof(msg));
980 			ui__error("Couldn't annotate %s:\n%s", sym->name, msg);
981 			goto out_free_offsets;
982 		}
983 	}
984 
985 	ui_helpline__push("Press ESC to exit");
986 
987 	browser.b.width = notes->src->max_line_len;
988 	browser.b.nr_entries = notes->src->nr_entries;
989 	browser.b.entries = &notes->src->source,
990 	browser.b.width += 18; /* Percentage */
991 
992 	if (annotate_opts.hide_src_code)
993 		ui_browser__init_asm_mode(&browser.b);
994 
995 	ret = annotate_browser__run(&browser, evsel, hbt);
996 
997 	if(not_annotated)
998 		annotated_source__purge(notes->src);
999 
1000 out_free_offsets:
1001 	if(not_annotated)
1002 		zfree(&notes->offsets);
1003 	return ret;
1004 }
1005