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