annotate.c 26.5 KB
Newer Older
1
#include "../../util/util.h"
2 3 4
#include "../browser.h"
#include "../helpline.h"
#include "../libslang.h"
5 6
#include "../ui.h"
#include "../util.h"
7 8 9 10
#include "../../util/annotate.h"
#include "../../util/hist.h"
#include "../../util/sort.h"
#include "../../util/symbol.h"
11
#include "../../util/evsel.h"
12
#include <pthread.h>
13

14 15 16 17
struct browser_disasm_line {
	struct rb_node	rb_node;
	u32		idx;
	int		idx_asm;
18
	int		jump_sources;
19 20 21 22
	/*
	 * actual length of this array is saved on the nr_events field
	 * of the struct annotate_browser
	 */
23
	double		percent[1];
24 25
};

26 27 28 29
static struct annotate_browser_opt {
	bool hide_src_code,
	     use_offset,
	     jump_arrows,
30
	     show_linenr,
31 32 33 34 35 36
	     show_nr_jumps;
} annotate_browser__opts = {
	.use_offset	= true,
	.jump_arrows	= true,
};

37 38 39
struct annotate_browser {
	struct ui_browser b;
	struct rb_root	  entries;
40
	struct rb_node	  *curr_hot;
41
	struct disasm_line  *selection;
42
	struct disasm_line  **offsets;
43
	int		    nr_events;
44
	u64		    start;
45 46
	int		    nr_asm_entries;
	int		    nr_entries;
47 48
	int		    max_jump_sources;
	int		    nr_jumps;
49
	bool		    searching_backwards;
50
	u8		    addr_width;
51 52
	u8		    jumps_width;
	u8		    target_width;
53 54
	u8		    min_addr_width;
	u8		    max_addr_width;
55
	char		    search_bf[128];
56 57
};

58
static inline struct browser_disasm_line *disasm_line__browser(struct disasm_line *dl)
59
{
60
	return (struct browser_disasm_line *)(dl + 1);
61 62
}

63 64
static bool disasm_line__filter(struct ui_browser *browser __maybe_unused,
				void *entry)
65
{
66
	if (annotate_browser__opts.hide_src_code) {
67 68
		struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
		return dl->offset == -1;
69 70 71 72 73
	}

	return false;
}

74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
static int annotate_browser__jumps_percent_color(struct annotate_browser *browser,
						 int nr, bool current)
{
	if (current && (!browser->b.use_navkeypressed || browser->b.navkeypressed))
		return HE_COLORSET_SELECTED;
	if (nr == browser->max_jump_sources)
		return HE_COLORSET_TOP;
	if (nr > 1)
		return HE_COLORSET_MEDIUM;
	return HE_COLORSET_NORMAL;
}

static int annotate_browser__set_jumps_percent_color(struct annotate_browser *browser,
						     int nr, bool current)
{
	 int color = annotate_browser__jumps_percent_color(browser, nr, current);
	 return ui_browser__set_color(&browser->b, color);
}

93
static void annotate_browser__write(struct ui_browser *browser, void *entry, int row)
94
{
95
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
96
	struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
97
	struct browser_disasm_line *bdl = disasm_line__browser(dl);
98
	bool current_entry = ui_browser__is_current_entry(browser, row);
99
	bool change_color = (!annotate_browser__opts.hide_src_code &&
100 101 102
			     (!current_entry || (browser->use_navkeypressed &&
					         !browser->navkeypressed)));
	int width = browser->width, printed;
103 104
	int i, pcnt_width = 7 * ab->nr_events;
	double percent_max = 0.0;
105
	char bf[256];
106

107 108 109 110 111 112 113 114 115 116 117
	for (i = 0; i < ab->nr_events; i++) {
		if (bdl->percent[i] > percent_max)
			percent_max = bdl->percent[i];
	}

	if (dl->offset != -1 && percent_max != 0.0) {
		for (i = 0; i < ab->nr_events; i++) {
			ui_browser__set_percent_color(browser, bdl->percent[i],
						      current_entry);
			slsmg_printf("%6.2f ", bdl->percent[i]);
		}
118
	} else {
119
		ui_browser__set_percent_color(browser, 0, current_entry);
120
		slsmg_write_nstring(" ", pcnt_width);
121 122
	}

123
	SLsmg_write_char(' ');
124 125

	/* The scroll bar isn't being used */
126
	if (!browser->navkeypressed)
127 128
		width += 1;

129
	if (!*dl->line)
130
		slsmg_write_nstring(" ", width - pcnt_width);
131
	else if (dl->offset == -1) {
132 133 134 135 136
		if (dl->line_nr && annotate_browser__opts.show_linenr)
			printed = scnprintf(bf, sizeof(bf), "%-*d ",
					ab->addr_width + 1, dl->line_nr);
		else
			printed = scnprintf(bf, sizeof(bf), "%*s  ",
137 138
				    ab->addr_width, " ");
		slsmg_write_nstring(bf, printed);
139
		slsmg_write_nstring(dl->line, width - printed - pcnt_width + 1);
140
	} else {
141
		u64 addr = dl->offset;
142
		int color = -1;
143

144
		if (!annotate_browser__opts.use_offset)
145 146
			addr += ab->start;

147
		if (!annotate_browser__opts.use_offset) {
148
			printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr);
149
		} else {
150
			if (bdl->jump_sources) {
151
				if (annotate_browser__opts.show_nr_jumps) {
152 153 154 155 156 157 158
					int prev;
					printed = scnprintf(bf, sizeof(bf), "%*d ",
							    ab->jumps_width,
							    bdl->jump_sources);
					prev = annotate_browser__set_jumps_percent_color(ab, bdl->jump_sources,
											 current_entry);
					slsmg_write_nstring(bf, printed);
159
					ui_browser__set_color(browser, prev);
160 161
				}

162
				printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ": ",
163
						    ab->target_width, addr);
164
			} else {
165 166
				printed = scnprintf(bf, sizeof(bf), "%*s  ",
						    ab->addr_width, " ");
167 168
			}
		}
169

170
		if (change_color)
171
			color = ui_browser__set_color(browser, HE_COLORSET_ADDR);
172 173
		slsmg_write_nstring(bf, printed);
		if (change_color)
174
			ui_browser__set_color(browser, color);
175
		if (dl->ins && dl->ins->ops->scnprintf) {
176
			if (ins__is_jump(dl->ins)) {
177
				bool fwd = dl->ops.target.offset > (u64)dl->offset;
178

179
				ui_browser__write_graph(browser, fwd ? SLSMG_DARROW_CHAR :
180
								    SLSMG_UARROW_CHAR);
181
				SLsmg_write_char(' ');
182
			} else if (ins__is_call(dl->ins)) {
183
				ui_browser__write_graph(browser, SLSMG_RARROW_CHAR);
184
				SLsmg_write_char(' ');
185 186 187
			} else {
				slsmg_write_nstring(" ", 2);
			}
188 189 190 191
		} else {
			if (strcmp(dl->name, "retq")) {
				slsmg_write_nstring(" ", 2);
			} else {
192
				ui_browser__write_graph(browser, SLSMG_LARROW_CHAR);
193 194 195
				SLsmg_write_char(' ');
			}
		}
196

197
		disasm_line__scnprintf(dl, bf, sizeof(bf), !annotate_browser__opts.use_offset);
198
		slsmg_write_nstring(bf, width - pcnt_width - 3 - printed);
199
	}
200

201
	if (current_entry)
202
		ab->selection = dl;
203 204
}

205 206 207 208 209 210 211 212 213 214
static bool disasm_line__is_valid_jump(struct disasm_line *dl, struct symbol *sym)
{
	if (!dl || !dl->ins || !ins__is_jump(dl->ins)
	    || !disasm_line__has_offset(dl)
	    || dl->ops.target.offset >= symbol__size(sym))
		return false;

	return true;
}

215
static void annotate_browser__draw_current_jump(struct ui_browser *browser)
216 217
{
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
218 219
	struct disasm_line *cursor = ab->selection, *target;
	struct browser_disasm_line *btarget, *bcursor;
220
	unsigned int from, to;
221 222
	struct map_symbol *ms = ab->b.priv;
	struct symbol *sym = ms->sym;
223
	u8 pcnt_width = 7;
224 225 226 227

	/* PLT symbols contain external offsets */
	if (strstr(sym->name, "@plt"))
		return;
228

229
	if (!disasm_line__is_valid_jump(cursor, sym))
230
		return;
231

232 233 234
	target = ab->offsets[cursor->ops.target.offset];
	if (!target)
		return;
235

236 237
	bcursor = disasm_line__browser(cursor);
	btarget = disasm_line__browser(target);
238

239
	if (annotate_browser__opts.hide_src_code) {
240
		from = bcursor->idx_asm;
241 242
		to = btarget->idx_asm;
	} else {
243
		from = (u64)bcursor->idx;
244 245 246
		to = (u64)btarget->idx;
	}

247 248
	pcnt_width *= ab->nr_events;

249
	ui_browser__set_color(browser, HE_COLORSET_CODE);
250 251
	__ui_browser__line_arrow(browser, pcnt_width + 2 + ab->addr_width,
				 from, to);
252 253 254 255
}

static unsigned int annotate_browser__refresh(struct ui_browser *browser)
{
256
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
257
	int ret = ui_browser__list_head_refresh(browser);
258 259 260
	int pcnt_width;

	pcnt_width = 7 * ab->nr_events;
261

262
	if (annotate_browser__opts.jump_arrows)
263
		annotate_browser__draw_current_jump(browser);
264

265
	ui_browser__set_color(browser, HE_COLORSET_NORMAL);
266
	__ui_browser__vline(browser, pcnt_width, 0, browser->height - 1);
267 268 269
	return ret;
}

270 271 272 273 274 275 276 277 278 279 280 281 282 283 284
static int disasm__cmp(struct browser_disasm_line *a,
		       struct browser_disasm_line *b, int nr_pcnt)
{
	int i;

	for (i = 0; i < nr_pcnt; i++) {
		if (a->percent[i] == b->percent[i])
			continue;
		return a->percent[i] < b->percent[i];
	}
	return 0;
}

static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_line *bdl,
				   int nr_events)
285
{
286
	struct rb_node **p = &root->rb_node;
287
	struct rb_node *parent = NULL;
288
	struct browser_disasm_line *l;
289 290 291

	while (*p != NULL) {
		parent = *p;
292
		l = rb_entry(parent, struct browser_disasm_line, rb_node);
293 294

		if (disasm__cmp(bdl, l, nr_events))
295 296 297 298
			p = &(*p)->rb_left;
		else
			p = &(*p)->rb_right;
	}
299 300
	rb_link_node(&bdl->rb_node, parent, p);
	rb_insert_color(&bdl->rb_node, root);
301 302
}

303
static void annotate_browser__set_top(struct annotate_browser *browser,
304
				      struct disasm_line *pos, u32 idx)
305 306 307
{
	unsigned back;

308 309 310
	ui_browser__refresh_dimensions(&browser->b);
	back = browser->b.height / 2;
	browser->b.top_idx = browser->b.index = idx;
311

312
	while (browser->b.top_idx != 0 && back != 0) {
313
		pos = list_entry(pos->node.prev, struct disasm_line, node);
314

315
		if (disasm_line__filter(&browser->b, &pos->node))
316 317
			continue;

318
		--browser->b.top_idx;
319 320 321
		--back;
	}

322 323
	browser->b.top = pos;
	browser->b.navkeypressed = true;
324 325 326 327 328
}

static void annotate_browser__set_rb_top(struct annotate_browser *browser,
					 struct rb_node *nd)
{
329
	struct browser_disasm_line *bpos;
330
	struct disasm_line *pos;
331
	u32 idx;
332

333 334
	bpos = rb_entry(nd, struct browser_disasm_line, rb_node);
	pos = ((struct disasm_line *)bpos) - 1;
335
	idx = bpos->idx;
336
	if (annotate_browser__opts.hide_src_code)
337 338
		idx = bpos->idx_asm;
	annotate_browser__set_top(browser, pos, idx);
339
	browser->curr_hot = nd;
340 341
}

342
static void annotate_browser__calc_percent(struct annotate_browser *browser,
343
					   struct perf_evsel *evsel)
344
{
345 346
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
347
	struct annotation *notes = symbol__annotation(sym);
348 349
	struct disasm_line *pos, *next;
	s64 len = symbol__size(sym);
350 351 352 353 354 355

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

	list_for_each_entry(pos, &notes->src->source, node) {
356
		struct browser_disasm_line *bpos = disasm_line__browser(pos);
357
		const char *path = NULL;
358 359
		double max_percent = 0.0;
		int i;
360 361 362 363 364 365 366 367

		if (pos->offset == -1) {
			RB_CLEAR_NODE(&bpos->rb_node);
			continue;
		}

		next = disasm__get_next_ip_line(&notes->src->source, pos);

368 369 370 371 372 373 374 375 376 377 378 379
		for (i = 0; i < browser->nr_events; i++) {
			bpos->percent[i] = disasm__calc_percent(notes,
						evsel->idx + i,
						pos->offset,
						next ? next->offset : len,
					        &path);

			if (max_percent < bpos->percent[i])
				max_percent = bpos->percent[i];
		}

		if (max_percent < 0.01) {
380
			RB_CLEAR_NODE(&bpos->rb_node);
381 382
			continue;
		}
383 384
		disasm_rb_tree__insert(&browser->entries, bpos,
				       browser->nr_events);
385 386 387 388 389 390
	}
	pthread_mutex_unlock(&notes->lock);

	browser->curr_hot = rb_last(&browser->entries);
}

391 392
static bool annotate_browser__toggle_source(struct annotate_browser *browser)
{
393
	struct disasm_line *dl;
394
	struct browser_disasm_line *bdl;
395 396 397
	off_t offset = browser->b.index - browser->b.top_idx;

	browser->b.seek(&browser->b, offset, SEEK_CUR);
398
	dl = list_entry(browser->b.top, struct disasm_line, node);
399
	bdl = disasm_line__browser(dl);
400

401
	if (annotate_browser__opts.hide_src_code) {
402 403
		if (bdl->idx_asm < offset)
			offset = bdl->idx;
404 405

		browser->b.nr_entries = browser->nr_entries;
406
		annotate_browser__opts.hide_src_code = false;
407
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
408 409
		browser->b.top_idx = bdl->idx - offset;
		browser->b.index = bdl->idx;
410
	} else {
411
		if (bdl->idx_asm < 0) {
412 413 414 415 416
			ui_helpline__puts("Only available for assembly lines.");
			browser->b.seek(&browser->b, -offset, SEEK_CUR);
			return false;
		}

417 418
		if (bdl->idx_asm < offset)
			offset = bdl->idx_asm;
419 420

		browser->b.nr_entries = browser->nr_asm_entries;
421
		annotate_browser__opts.hide_src_code = true;
422
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
423 424
		browser->b.top_idx = bdl->idx_asm - offset;
		browser->b.index = bdl->idx_asm;
425 426 427 428 429
	}

	return true;
}

430 431 432 433 434 435
static void annotate_browser__init_asm_mode(struct annotate_browser *browser)
{
	ui_browser__reset_index(&browser->b);
	browser->b.nr_entries = browser->nr_asm_entries;
}

436 437 438 439 440 441 442 443
#define SYM_TITLE_MAX_SIZE (PATH_MAX + 64)

static int sym_title(struct symbol *sym, struct map *map, char *title,
		     size_t sz)
{
	return snprintf(title, sz, "%s  %s", sym->name, map->dso->long_name);
}

444 445
static bool annotate_browser__callq(struct annotate_browser *browser,
				    struct perf_evsel *evsel,
446
				    struct hist_browser_timer *hbt)
447 448
{
	struct map_symbol *ms = browser->b.priv;
449
	struct disasm_line *dl = browser->selection;
450
	struct annotation *notes;
451 452
	struct addr_map_symbol target = {
		.map = ms->map,
453
		.addr = map__objdump_2mem(ms->map, dl->ops.target.addr),
454
	};
455
	char title[SYM_TITLE_MAX_SIZE];
456

457
	if (!ins__is_call(dl->ins))
458 459
		return false;

460 461 462 463
	if (map_groups__find_ams(&target, NULL) ||
	    map__rip_2objdump(target.map, target.map->map_ip(target.map,
							     target.addr)) !=
	    dl->ops.target.addr) {
464 465 466 467
		ui_helpline__puts("The called function was not found.");
		return true;
	}

468
	notes = symbol__annotation(target.sym);
469 470
	pthread_mutex_lock(&notes->lock);

471
	if (notes->src == NULL && symbol__alloc_hist(target.sym) < 0) {
472 473
		pthread_mutex_unlock(&notes->lock);
		ui__warning("Not enough memory for annotating '%s' symbol!\n",
474
			    target.sym->name);
475 476 477 478
		return true;
	}

	pthread_mutex_unlock(&notes->lock);
479 480
	symbol__tui_annotate(target.sym, target.map, evsel, hbt);
	sym_title(ms->sym, ms->map, title, sizeof(title));
481
	ui_browser__show_title(&browser->b, title);
482 483 484
	return true;
}

485 486 487
static
struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
					  s64 offset, s64 *idx)
488 489 490 491
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
492
	struct disasm_line *pos;
493 494 495 496 497

	*idx = 0;
	list_for_each_entry(pos, &notes->src->source, node) {
		if (pos->offset == offset)
			return pos;
498
		if (!disasm_line__filter(&browser->b, &pos->node))
499 500 501 502 503 504 505 506
			++*idx;
	}

	return NULL;
}

static bool annotate_browser__jump(struct annotate_browser *browser)
{
507
	struct disasm_line *dl = browser->selection;
508
	s64 idx;
509

510
	if (!ins__is_jump(dl->ins))
511 512
		return false;

513
	dl = annotate_browser__find_offset(browser, dl->ops.target.offset, &idx);
514
	if (dl == NULL) {
I
Ingo Molnar 已提交
515
		ui_helpline__puts("Invalid jump offset");
516 517 518
		return true;
	}

519
	annotate_browser__set_top(browser, dl, idx);
520

521 522 523
	return true;
}

524 525 526
static
struct disasm_line *annotate_browser__find_string(struct annotate_browser *browser,
					  char *s, s64 *idx)
527 528 529 530
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
531
	struct disasm_line *pos = browser->selection;
532 533 534

	*idx = browser->b.index;
	list_for_each_entry_continue(pos, &notes->src->source, node) {
535
		if (disasm_line__filter(&browser->b, &pos->node))
536 537 538 539 540 541 542 543 544 545 546 547 548
			continue;

		++*idx;

		if (pos->line && strstr(pos->line, s) != NULL)
			return pos;
	}

	return NULL;
}

static bool __annotate_browser__search(struct annotate_browser *browser)
{
549
	struct disasm_line *dl;
550 551
	s64 idx;

552 553
	dl = annotate_browser__find_string(browser, browser->search_bf, &idx);
	if (dl == NULL) {
554 555 556 557
		ui_helpline__puts("String not found!");
		return false;
	}

558
	annotate_browser__set_top(browser, dl, idx);
559 560 561 562
	browser->searching_backwards = false;
	return true;
}

563 564 565
static
struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
						  char *s, s64 *idx)
566 567 568 569
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
570
	struct disasm_line *pos = browser->selection;
571 572 573

	*idx = browser->b.index;
	list_for_each_entry_continue_reverse(pos, &notes->src->source, node) {
574
		if (disasm_line__filter(&browser->b, &pos->node))
575 576 577 578 579 580 581 582 583 584 585 586 587
			continue;

		--*idx;

		if (pos->line && strstr(pos->line, s) != NULL)
			return pos;
	}

	return NULL;
}

static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
{
588
	struct disasm_line *dl;
589 590
	s64 idx;

591 592
	dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
	if (dl == NULL) {
593 594 595 596
		ui_helpline__puts("String not found!");
		return false;
	}

597
	annotate_browser__set_top(browser, dl, idx);
598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649
	browser->searching_backwards = true;
	return true;
}

static bool annotate_browser__search_window(struct annotate_browser *browser,
					    int delay_secs)
{
	if (ui_browser__input_window("Search", "String: ", browser->search_bf,
				     "ENTER: OK, ESC: Cancel",
				     delay_secs * 2) != K_ENTER ||
	    !*browser->search_bf)
		return false;

	return true;
}

static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs)
{
	if (annotate_browser__search_window(browser, delay_secs))
		return __annotate_browser__search(browser);

	return false;
}

static bool annotate_browser__continue_search(struct annotate_browser *browser,
					      int delay_secs)
{
	if (!*browser->search_bf)
		return annotate_browser__search(browser, delay_secs);

	return __annotate_browser__search(browser);
}

static bool annotate_browser__search_reverse(struct annotate_browser *browser,
					   int delay_secs)
{
	if (annotate_browser__search_window(browser, delay_secs))
		return __annotate_browser__search_reverse(browser);

	return false;
}

static
bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
					       int delay_secs)
{
	if (!*browser->search_bf)
		return annotate_browser__search_reverse(browser, delay_secs);

	return __annotate_browser__search_reverse(browser);
}

650 651 652 653 654 655 656 657 658 659 660 661 662
static void annotate_browser__update_addr_width(struct annotate_browser *browser)
{
	if (annotate_browser__opts.use_offset)
		browser->target_width = browser->min_addr_width;
	else
		browser->target_width = browser->max_addr_width;

	browser->addr_width = browser->target_width;

	if (annotate_browser__opts.show_nr_jumps)
		browser->addr_width += browser->jumps_width + 1;
}

663 664
static int annotate_browser__run(struct annotate_browser *browser,
				 struct perf_evsel *evsel,
665
				 struct hist_browser_timer *hbt)
666 667
{
	struct rb_node *nd = NULL;
668
	struct map_symbol *ms = browser->b.priv;
669
	struct symbol *sym = ms->sym;
670
	const char *help = "Press 'h' for help on key bindings";
671
	int delay_secs = hbt ? hbt->refresh : 0;
672
	int key;
673
	char title[SYM_TITLE_MAX_SIZE];
674

675 676
	sym_title(sym, ms->map, title, sizeof(title));
	if (ui_browser__show(&browser->b, title, help) < 0)
677
		return -1;
678

679
	annotate_browser__calc_percent(browser, evsel);
680

681 682 683
	if (browser->curr_hot) {
		annotate_browser__set_rb_top(browser, browser->curr_hot);
		browser->b.navkeypressed = false;
684
	}
685

686
	nd = browser->curr_hot;
687

688
	while (1) {
689
		key = ui_browser__run(&browser->b, delay_secs);
690

691
		if (delay_secs != 0) {
692
			annotate_browser__calc_percent(browser, evsel);
693 694 695 696 697 698 699 700 701
			/*
			 * Current line focus got out of the list of most active
			 * lines, NULL it so that if TAB|UNTAB is pressed, we
			 * move to curr_hot (current hottest line).
			 */
			if (nd != NULL && RB_EMPTY_NODE(nd))
				nd = NULL;
		}

702
		switch (key) {
703
		case K_TIMER:
704 705
			if (hbt)
				hbt->timer(hbt->arg);
706 707

			if (delay_secs != 0)
708
				symbol__annotate_decay_histogram(sym, evsel->idx);
709
			continue;
710
		case K_TAB:
711 712 713
			if (nd != NULL) {
				nd = rb_prev(nd);
				if (nd == NULL)
714
					nd = rb_last(&browser->entries);
715
			} else
716
				nd = browser->curr_hot;
717
			break;
718
		case K_UNTAB:
719 720 721
			if (nd != NULL)
				nd = rb_next(nd);
				if (nd == NULL)
722
					nd = rb_first(&browser->entries);
723
			else
724
				nd = browser->curr_hot;
725
			break;
726
		case K_F1:
727
		case 'h':
728
			ui_browser__help_window(&browser->b,
729 730 731 732 733
		"UP/DOWN/PGUP\n"
		"PGDN/SPACE    Navigate\n"
		"q/ESC/CTRL+C  Exit\n\n"
		"->            Go to target\n"
		"<-            Exit\n"
734
		"H             Cycle thru hottest instructions\n"
735 736 737 738 739 740
		"j             Toggle showing jump to target arrows\n"
		"J             Toggle showing number of jump sources on targets\n"
		"n             Search next string\n"
		"o             Toggle disassembler output/simplified view\n"
		"s             Toggle source code view\n"
		"/             Search string\n"
741
		"k             Toggle line numbers\n"
742
		"r             Run available scripts\n"
743
		"?             Search string backwards\n");
744
			continue;
745 746 747 748 749
		case 'r':
			{
				script_browse(NULL);
				continue;
			}
750 751 752 753
		case 'k':
			annotate_browser__opts.show_linenr =
				!annotate_browser__opts.show_linenr;
			break;
754
		case 'H':
755
			nd = browser->curr_hot;
756
			break;
757
		case 's':
758
			if (annotate_browser__toggle_source(browser))
759 760
				ui_helpline__puts(help);
			continue;
761
		case 'o':
762
			annotate_browser__opts.use_offset = !annotate_browser__opts.use_offset;
763
			annotate_browser__update_addr_width(browser);
764
			continue;
765
		case 'j':
766
			annotate_browser__opts.jump_arrows = !annotate_browser__opts.jump_arrows;
767
			continue;
768
		case 'J':
769
			annotate_browser__opts.show_nr_jumps = !annotate_browser__opts.show_nr_jumps;
770
			annotate_browser__update_addr_width(browser);
771
			continue;
772
		case '/':
773
			if (annotate_browser__search(browser, delay_secs)) {
774 775 776 777 778
show_help:
				ui_helpline__puts(help);
			}
			continue;
		case 'n':
779 780 781
			if (browser->searching_backwards ?
			    annotate_browser__continue_search_reverse(browser, delay_secs) :
			    annotate_browser__continue_search(browser, delay_secs))
782 783 784
				goto show_help;
			continue;
		case '?':
785
			if (annotate_browser__search_reverse(browser, delay_secs))
786 787
				goto show_help;
			continue;
788 789 790 791
		case 'D': {
			static int seq;
			ui_helpline__pop();
			ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d",
792 793 794 795 796
					   seq++, browser->b.nr_entries,
					   browser->b.height,
					   browser->b.index,
					   browser->b.top_idx,
					   browser->nr_asm_entries);
797 798
		}
			continue;
799 800
		case K_ENTER:
		case K_RIGHT:
801
			if (browser->selection == NULL)
802
				ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
803
			else if (browser->selection->offset == -1)
804
				ui_helpline__puts("Actions are only available for assembly lines.");
805 806
			else if (!browser->selection->ins) {
				if (strcmp(browser->selection->name, "retq"))
807 808
					goto show_sup_ins;
				goto out;
809
			} else if (!(annotate_browser__jump(browser) ||
810
				     annotate_browser__callq(browser, evsel, hbt))) {
811 812 813
show_sup_ins:
				ui_helpline__puts("Actions are only available for 'callq', 'retq' & jump instructions.");
			}
814
			continue;
815 816
		case K_LEFT:
		case K_ESC:
817 818
		case 'q':
		case CTRL('c'):
819
			goto out;
820 821
		default:
			continue;
822
		}
823 824

		if (nd != NULL)
825
			annotate_browser__set_rb_top(browser, nd);
826 827
	}
out:
828
	ui_browser__hide(&browser->b);
829
	return key;
830 831
}

832 833 834 835 836 837
int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel,
			     struct hist_browser_timer *hbt)
{
	return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt);
}

838
int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
839
			     struct hist_browser_timer *hbt)
840
{
841
	return map_symbol__tui_annotate(&he->ms, evsel, hbt);
842 843
}

844 845 846 847
static void annotate_browser__mark_jump_targets(struct annotate_browser *browser,
						size_t size)
{
	u64 offset;
848 849 850 851 852 853
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;

	/* PLT symbols contain external offsets */
	if (strstr(sym->name, "@plt"))
		return;
854 855 856 857 858

	for (offset = 0; offset < size; ++offset) {
		struct disasm_line *dl = browser->offsets[offset], *dlt;
		struct browser_disasm_line *bdlt;

859
		if (!disasm_line__is_valid_jump(dl, sym))
860 861
			continue;

862
		dlt = browser->offsets[dl->ops.target.offset];
863 864 865 866 867 868 869
		/*
 		 * FIXME: Oops, no jump target? Buggy disassembler? Or do we
 		 * have to adjust to the previous offset?
 		 */
		if (dlt == NULL)
			continue;

870
		bdlt = disasm_line__browser(dlt);
871 872 873 874
		if (++bdlt->jump_sources > browser->max_jump_sources)
			browser->max_jump_sources = bdlt->jump_sources;

		++browser->nr_jumps;
875 876 877
	}
}

878 879 880 881 882 883 884 885 886
static inline int width_jumps(int n)
{
	if (n >= 100)
		return 5;
	if (n / 10)
		return 2;
	return 1;
}

887 888
int symbol__tui_annotate(struct symbol *sym, struct map *map,
			 struct perf_evsel *evsel,
889
			 struct hist_browser_timer *hbt)
890
{
891
	struct disasm_line *pos, *n;
892
	struct annotation *notes;
893
	size_t size;
894 895 896 897
	struct map_symbol ms = {
		.map = map,
		.sym = sym,
	};
898 899
	struct annotate_browser browser = {
		.b = {
900
			.refresh = annotate_browser__refresh,
901 902
			.seek	 = ui_browser__list_head_seek,
			.write	 = annotate_browser__write,
903
			.filter  = disasm_line__filter,
904
			.priv	 = &ms,
905
			.use_navkeypressed = true,
906
		},
907
	};
908
	int ret = -1;
909 910
	int nr_pcnt = 1;
	size_t sizeof_bdl = sizeof(struct browser_disasm_line);
911

912
	if (sym == NULL)
913 914
		return -1;

915 916
	size = symbol__size(sym);

917
	if (map->dso->annotate_warned)
918 919
		return -1;

920 921 922 923 924 925
	browser.offsets = zalloc(size * sizeof(struct disasm_line *));
	if (browser.offsets == NULL) {
		ui__error("Not enough memory!");
		return -1;
	}

926 927 928 929 930 931
	if (perf_evsel__is_group_event(evsel)) {
		nr_pcnt = evsel->nr_members;
		sizeof_bdl += sizeof(double) * (nr_pcnt - 1);
	}

	if (symbol__annotate(sym, map, sizeof_bdl) < 0) {
932
		ui__error("%s", ui_helpline__last_msg);
933
		goto out_free_offsets;
934 935 936 937
	}

	ui_helpline__push("Press <- or ESC to exit");

938
	notes = symbol__annotation(sym);
939
	browser.start = map__rip_2objdump(map, sym->start);
940

941
	list_for_each_entry(pos, &notes->src->source, node) {
942
		struct browser_disasm_line *bpos;
943
		size_t line_len = strlen(pos->line);
944

945 946
		if (browser.b.width < line_len)
			browser.b.width = line_len;
947 948
		bpos = disasm_line__browser(pos);
		bpos->idx = browser.nr_entries++;
949
		if (pos->offset != -1) {
950
			bpos->idx_asm = browser.nr_asm_entries++;
951 952 953 954 955 956 957 958 959
			/*
			 * FIXME: short term bandaid to cope with assembly
			 * routines that comes with labels in the same column
			 * as the address in objdump, sigh.
			 *
			 * E.g. copy_user_generic_unrolled
 			 */
			if (pos->offset < (s64)size)
				browser.offsets[pos->offset] = pos;
960
		} else
961
			bpos->idx_asm = -1;
962 963
	}

964 965
	annotate_browser__mark_jump_targets(&browser, size);

966
	browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size);
967
	browser.max_addr_width = hex_width(sym->end);
968
	browser.jumps_width = width_jumps(browser.max_jump_sources);
969
	browser.nr_events = nr_pcnt;
970
	browser.b.nr_entries = browser.nr_entries;
971
	browser.b.entries = &notes->src->source,
972
	browser.b.width += 18; /* Percentage */
973 974 975 976 977 978

	if (annotate_browser__opts.hide_src_code)
		annotate_browser__init_asm_mode(&browser);

	annotate_browser__update_addr_width(&browser);

979
	ret = annotate_browser__run(&browser, evsel, hbt);
980
	list_for_each_entry_safe(pos, n, &notes->src->source, node) {
981
		list_del(&pos->node);
982
		disasm_line__free(pos);
983
	}
984 985 986

out_free_offsets:
	free(browser.offsets);
987 988
	return ret;
}
989 990 991

#define ANNOTATE_CFG(n) \
	{ .name = #n, .value = &annotate_browser__opts.n, }
992

993 994 995
/*
 * Keep the entries sorted, they are bsearch'ed
 */
996
static struct annotate_config {
997 998 999 1000 1001
	const char *name;
	bool *value;
} annotate__configs[] = {
	ANNOTATE_CFG(hide_src_code),
	ANNOTATE_CFG(jump_arrows),
1002
	ANNOTATE_CFG(show_linenr),
1003 1004 1005 1006 1007 1008 1009 1010
	ANNOTATE_CFG(show_nr_jumps),
	ANNOTATE_CFG(use_offset),
};

#undef ANNOTATE_CFG

static int annotate_config__cmp(const void *name, const void *cfgp)
{
1011
	const struct annotate_config *cfg = cfgp;
1012 1013 1014 1015

	return strcmp(name, cfg->name);
}

1016 1017
static int annotate__config(const char *var, const char *value,
			    void *data __maybe_unused)
1018
{
1019
	struct annotate_config *cfg;
1020 1021 1022 1023 1024 1025 1026
	const char *name;

	if (prefixcmp(var, "annotate.") != 0)
		return 0;

	name = var + 9;
	cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
1027
		      sizeof(struct annotate_config), annotate_config__cmp);
1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039

	if (cfg == NULL)
		return -1;

	*cfg->value = perf_config_bool(name, value);
	return 0;
}

void annotate_browser__init(void)
{
	perf_config(annotate__config, NULL);
}